苹果在iOS 8.0系统之前若要做音视频开发需使用第三方软件进行编解码(FFmpeg软解码H264视频流可看到这里),学习成本较大,项目开发进度也可能超出预期。在iOS 8.0之后开放了视频编解码框架VideoToolbox,在此之后对于音视频开发变得相对简单。
一、硬解码名词(结构)解释
1、VTDecompressionSessionRef:解码器对象数据结构;
2、CMVideoFormatDescriptionRef:图形解码相关格式及描述;
3、CVPixelBufferRef:编码前和解码后的图像数据结构;
4、CMBlockBufferRef:存在解码前图像数据内存结构;
5、CMSampleBufferRef:存放解码前的视频图像的容器数据结构;
6、AVSampleBufferDisplayLayer:以CMSampleBufferRef进行解码并显示Layer图层;
7、SPS、PPS:h.264解码参数信息;IDR:h.264视频流I帧;
二、H264硬解码流程图
三:IDR(I帧)网络裸流数据结构
一般情况下网络视频裸流I帧中基本会包含SPS、PPS、SEI、IDR帧数据,如下图所示,但是部分只含有IDR帧数据,其他解码参数信息被单独已Slice获取。
四、硬解码相关接口
1、初始化H264硬解解码器
1)使用CMVideoFormatDescriptionCreateFromH264ParameterSets函数构建解码描述结构CMVideoFormatDescriptionRef:
const uint8_t *const parameterSetPointers[2] = {pSPS,pPPS};
const size_t parameterSetSizes[2] = {mSpsSize, mPpsSize};
OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
2, //参数个数,主要包含SPS、PPS
parameterSetPointers,
parameterSetSizes,
4, //NALU起始位个数
&mDecoderFormatDescription);
2)使用VTDecompressionSessionCreate函数构建解码器结构VTDecompressionSessionRef:
uint32_t pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; //NV12
const void *keys[] = { kCVPixelBufferPixelFormatTypeKey };
const void *values[] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &pixelFormatType) }; //32位
CFDictionaryRef attrs = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
VTDecompressionOutputCallbackRecord callBackRecord;
callBackRecord.decompressionOutputCallback = didDecompress;
callBackRecord.decompressionOutputRefCon = NULL;
status = VTDecompressionSessionCreate(kCFAllocatorDefault,
mDecoderFormatDescription,
NULL, attrs,
&callBackRecord,
&mDeocderSession);
CFRelease(attrs);
2、H264硬件解码
1)将视频裸流数据构建成CMBlockBufferRef,主要目的是进一步转换为CMSampleBufferRef:
CMBlockBufferRef blockBuffer = NULL;
OSStatus status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, (void *)videoBuffer, videoBufferSize, kCFAllocatorNull, NULL, 0, videoBufferSize, 0, &blockBuffer);
CMSampleBufferRef sampleBuffer = NULL;
const size_t sampleSizeArray[] = { videoBufferSize };
OSStatus status = CMSampleBufferCreateReady(kCFAllocatorDefault, blockBuffer, mDecoderFormatDescription , 1, 0, NULL, 1, sampleSizeArray, &sampleBuffer);
2)将CMSampleBufferRef结构送入VTDecompressionSessionDecodeFrame函数进行解码处理:
VTDecodeFrameFlags flags = 0;
VTDecodeInfoFlags flagOut = 0;
CVPixelBufferRef outputPixelBuffer = NULL;
OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(mDeocderSession, sampleBuffer, flags, &outputPixelBuffer, &flagOut);
3)若使用AVSampleBufferDisplayLayer图层进行直接显示,可忽略上一步的还行,直接将CMSampleBufferRef送入AVSampleBufferDisplayLayer进行显示:
CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, YES);
CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue);
if ([self.displayLayer isReadyForMoreMediaData]) {
@weaki