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,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击1079654574加群领取哦~

在解码器数据回调接口中,我们通过 CVPixelBufferRef 打包的是对 H.264/H.265 解码后得到的 YUV 数据。
KFVideoDecoder.m
#import "KFVideoDecoder.h"
#import <VideoToolBox/VideoToolBox.h>
#define KFDecoderRetrySessionMaxCount 5
#define KFDecoderDecodeFrameFailedMaxCount 20
@interface KFVideoDecoderInputPacket : NSObject
@property (nonatomic, assign) CMSampleBufferRef sampleBuffer;
@end
@implementation KFVideoDecoderInputPacket
@end
@interface KFVideoDecoder ()
@property (nonatomic, assign) VTDecompressionSessionRef decoderSession; // 视频解码器实例。
@property (nonatomic, strong) dispatch_queue_t decoderQueue;
@property (nonatomic, strong) dispatch_

这篇博客介绍了如何在iOS上实现视频解码,从MP4文件解封装H.264/H.265编码数据,然后解码为YUV格式。内容涵盖视频解封装模块、解码模块的实现,以及解码后的YUV数据存储和播放验证。通过详细代码注释帮助理解音视频开发流程。
:视频解码,MP4 → H.264H.265 → YUV 的源码&spm=1001.2101.3001.5002&articleId=129628319&d=1&t=3&u=5a57f899f1ec452d9fe60e5a217c5df6)
3840

被折叠的 条评论
为什么被折叠?



