iOS/Android 客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助 iOS/Android 平台的音视频能力上手去实践音视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染
过程,并借助音视频工具来分析和理解对应的音视频数据。
在音视频工程示例这个栏目,我们将通过拆解采集 → 编码 → 封装 → 解封装 → 解码 → 渲染
流程并实现 Demo 来向大家介绍如何在 iOS/Android 平台上手音视频开发。
这里是第十二篇:iOS 视频解码 Demo。这个 Demo 里包含以下内容:
-
1)实现一个视频解封装模块;
-
2)实现一个视频解码模块;
-
3)串联视频解封装和解码模块,将解封装的 H.264/H.265 数据输入给解码模块进行解码,并存储解码后的 YUV 数据;
-
4)详尽的代码注释,帮你理解代码逻辑和原理。
在本文中,我们将详解一下 Demo 的具体实现和源码。读完本文内容相信就能帮你掌握相关知识。
1、视频解封装模块
视频解封装模块即 KFMP4Demuxer
,复用了《iOS 音频解封装 Demo》中介绍的 demuxer,这里就不再重复介绍了,其接口如下:
KFMP4Demuxer.h
#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
#import "KFDemuxerConfig.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, KFMP4DemuxerStatus) {
KFMP4DemuxerStatusUnknown = 0,
KFMP4DemuxerStatusRunning = 1,
KFMP4DemuxerStatusFailed = 2,
KFMP4DemuxerStatusCompleted = 3,
KFMP4DemuxerStatusCancelled = 4,
};
@interface KFMP4Demuxer : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfig:(KFDemuxerConfig *)config;
@property (nonatomic, strong, readonly) KFDemuxerConfig *config;
@property (nonatomic, copy) void (^errorCallBack)(NSError *error);
@property (nonatomic, assign, readonly) BOOL hasAudioTrack; // 是否包含音频数据。
@property (nonatomic, assign, readonly) BOOL hasVideoTrack; // 是否包含视频数据。
@property (nonatomic, assign, readonly) CGSize videoSize; // 视频大小。
@property (nonatomic, assign, readonly) CMTime duration; // 媒体时长。
@property (nonatomic, assign, readonly) CMVideoCodecType codecType; // 编码类型。
@property (nonatomic, assign, readonly) KFMP4DemuxerStatus demuxerStatus; // 解封装器状态。
@property (nonatomic, assign, readonly) BOOL audioEOF; // 是否音频结束。
@property (nonatomic, assign, readonly) BOOL videoEOF; // 是否视频结束。
@property (nonatomic, assign, readonly) CGAffineTransform preferredTransform; // 图像的变换信息。比如:视频图像旋转。
- (void)startReading:(void (^)(BOOL success, NSError *error))completeHandler; // 开始读取数据解封装。
- (void)cancelReading; // 取消读取。
- (BOOL)hasAudioSampleBuffer; // 是否还有音频数据。
- (CMSampleBufferRef)copyNextAudioSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份音频采样。
- (BOOL)hasVideoSampleBuffer; // 是否还有视频数据。
- (CMSampleBufferRef)copyNextVideoSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份视频采样。
@end
NS_ASSUME_NONNULL_END
2、视频解码模块
接下来,我们来实现一个视频解码模块 KFVideoDecoder
,在这里输入解封装后的编码数据,输出解码后的数据。
KFVideoDecoder.h
#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
NS_ASSUME_NONNULL_BEGIN
@interface KFVideoDecoder : NSObject
@property (nonatomic, copy) void (^pixelBufferOutputCallBack)(CVPixelBufferRef pixelBuffer, CMTime ptsTime); // 视频解码数据回调。
@property (nonatomic, copy) void (^errorCallBack)(NSError *error); // 视频解码错误回调。
- (void)decodeSampleBuffer:(CMSampleBufferRef)sampleBuffer; // 解码。
- (void)flush; // 清空解码缓冲区。
- (void)flushWithCompleteHandler:(void (^)(void))completeHandler; // 清空解码缓冲区并回调完成。
@end
NS_ASSUME_NONNULL_END
上面是 KFVideoDecoder
接口的设计,主要是有视频解码数据回调
和错误回调
的接口,另外就是解码
和清空解码缓冲区
的接口。
在上面的解码
接口中,我们使用的是依然 CMSampleBufferRef[1] 作为参数。而解码器数据回调
接口则使用 CVPixelBufferRef[2] 作为返回值类型。
在解码
接口中,我们通过 CMSampleBufferRef
打包的是解封装后得到的 H.264/H.265 编码数据。
【学习地址】:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
【文章福利】:免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux