要对数据进行硬解码,要用到VideoToolboox
#import <VideoToolbox/VideoToolbox.h>
或
@import VideoToolbox;
SPS: Sequence Parameter Sets
PPS: Picture Parameter Set
一个decompression session支持解压一系列的视频帧.
以下是解压的步骤:
0.(可选)创建一个视频格式信息.
当你要解压H264格式的视频,你要用SPS,PPS数据来创建一个CMVideoFormatDescription
,得到它的格式信息
CMVideoFormatDescriptionCreateFromH264ParameterSets(CFAllocatorRef _Nullable allocator,//分配器,如果传NULL就是默认的分配器
size_t parameterSetCount,//属性集的数量.这个属性必须至少是2.
const uint8_t *const _Nonnull * _Nonnull parameterSetPointers,//指向C数组,数组里成员是SPS和PPS的指针.
const size_t * _Nonnull parameterSetSizes,//指向C数组,数据里是每个属性集的字节长度.
int NALUnitHeaderLength,//字节长度,AVC视频采样或AVC属性集采样的NALUnitLength字段的字节长度.传1,2或4.
CMFormatDescriptionRef _Nullable * _Nonnull formatDescriptionOut)//用于接收新的video CMFormatDescription
示例
const uint8_t* const parameterSetPointers[2] = { _sps, _pps };
const size_t parameterSetSizes[2] = { _spsSize, _ppsSize };
OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
2, //param count
parameterSetPointers,
parameterSetSizes,
4, //nal start code size
&_decoderFormatDescription);
if(status == noErr) {
//continue
}
1.创建一个decompression session.
调用VTDecompressionSessionCreate(_:_:_:_:_:_:)
VTDecompressionSessionCreate(CFAllocatorRef _Nullable allocator,//分配器,传NULL会使用默认的分配器.
CMVideoFormatDescriptionRef _Nonnull videoFormatDescription,//源视频帧的描述(信息)
CFDictionaryRef _Nullable videoDecoderSpecification,//特定的视频解码器.传NULL会让VideoToolbox自己选择一个.
CFDictionaryRef _Nullable destinationImageBufferAttributes,//目标图片缓存的属性.传NULL会设为没有requirements.
const VTDecompressionOutputCallbackRecord * _Nullable outputCallback,//回调输出.如果,你要用VTDecompressionSessionDecodeFrameWithOutputHandler(_:_:_:_:_:)来解码视频帧,你可以设为NULL.不然就得不到输出数据.
VTDecompressionSessionRef _Nullable * _Nonnull decompressionSessionOut)//用于接收新的decompression session
示例:
VTDecompressionOutputCallbackRecord callBackRecord;
callBackRecord.decompressionOutputCallback = didDecompress;
callBackRecord.decompressionOutputRefCon = (__bridge void *)self;
status = VTDecompressionSessionCreate(kCFAllocatorDefault,
_decoderFormatDescription,
NULL,
(__bridge CFDictionaryRef)destinationPixelBufferAttributes,
&callBackRecord,
&_deocderSession);
2.(可选)配置decompress session的属性 .
Decompression Properties
调用VTSessionSetProperty(_ : _ : _:) 或VTSessionSetProperties(_ :_ :)
示例
VTSessionSetProperty(_deocderSession, kVTDecompressionPropertyKey_ThreadCount, (__bridge CFTypeRef)[NSNumber numberWithInt:1]);
VTSessionSetProperty(_deocderSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
3.解码视频帧:
3.1 把二进制的转成sample buffer
CMBlockBufferCreateWithMemoryBlock(CFAllocatorRef _Nullable structureAllocator,//分配器,传NULL会使用默认的
void * _Nullable memoryBlock,//内存块的地址,
size_t blockLength,//内存块的总的字节长度.
CFAllocatorRef _Nullable blockAllocator,//如果memoryBlock是NULL的话,就用这个来分配内存.
//如果memoryBlock不是NULL,这个分配就用来释放它,
//转NULL就是使用默认的.传kCFAllocatorNull,就是说不要解除分配(释放)的
const CMBlockBufferCustomBlockSource * _Nullable customBlockSource,//如果非空,它被用来分配和释放memory block(block alloctor属性会被忽略).
//如果提供并且memoryBlock是NULL,它的allocateBlock()必须非空,allocate将被调用一次.
//当CMBlockBuffer被处理时,freeBlock()将被调用.
size_t offsetToData,//memoryBlock里的内存偏移值.
size_t dataLength,//相关数据字节数,从偏移量开始.
CMBlockBufferFlags flags,//功能和控制标识
CMBlockBufferRef _Nullable * _Nonnull newBBufOut)//接收新的CMBlockBuffer对象,retain数为1.必须不为NULL.
示例
CMBlockBufferRef blockBuffer = NULL;
OSStatus status = CMBlockBufferCreateWithMemoryBlock(NULL,
(void *)frame,
frameSize,
kCFAllocatorNull,
NULL,
0,
frameSize,
FALSE,
&blockBuffer);
3.2
使用VTDecompressionSessionDecodeFrame(_ :_ :_ :_ :_ :)
VTDecompressionSessionDecodeFrame(VTDecompressionSessionRef _Nonnull session,//先前创建的decompression session
CMSampleBufferRef _Nonnull sampleBuffer, //要解压的数据
VTDecodeFrameFlags decodeFlags,//有两选择'kVTDecodeFrame_EnableTemporalProcessing'和'kVTDecodeFrame_EnableAsynchronousDecompression',详见官网.
void * _Nullable sourceFrameRefCon,//你对帧的引用.注意如果sampleBuffer包含多个帧,回调方法就会调用多次,包含这个引用.
VTDecodeInfoFlags * _Nullable infoFlagsOut)//用于接收解码操作信息
示例
-(CVPixelBufferRef)decode:(uint8_t *)frame withSize:(uint32_t)frameSize
{
CVPixelBufferRef outputPixelBuffer = NULL;
CMBlockBufferRef blockBuffer = NULL;
OSStatus status = CMBlockBufferCreateWithMemoryBlock(NULL,
(void *)frame,
frameSize,
kCFAllocatorNull,
NULL,
0,
frameSize,
FALSE,
&blockBuffer);
/**
*/
if(status == kCMBlockBufferNoErr) {
CMSampleBufferRef sampleBuffer = NULL;
const size_t sampleSizeArray[] = {frameSize};
status = CMSampleBufferCreateReady(kCFAllocatorDefault,
blockBuffer,
_decoderFormatDescription ,
1, 0, NULL, 1, sampleSizeArray,
&sampleBuffer);
if (status == kCMBlockBufferNoErr && sampleBuffer) {
VTDecodeFrameFlags flags = 0;
VTDecodeInfoFlags flagOut = 0;
OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(_deocderSession,
sampleBuffer,
flags,
&outputPixelBuffer,
&flagOut);
/**
*/
if(decodeStatus == kVTInvalidSessionErr) {
NSLog(@"IOS8VT: Invalid session, reset decoder session");
} else if(decodeStatus == kVTVideoDecoderBadDataErr) {
NSLog(@"IOS8VT: decode failed status=%d(Bad data)", (int)decodeStatus);
} else if(decodeStatus != noErr) {
NSLog(@"IOS8VT: decode failed status=%d", (int)decodeStatus);
}
CFRelease(sampleBuffer);
}
CFRelease(blockBuffer);
}
return outputPixelBuffer;
}
4.回调函数,callout back
//解码回调函数
static void didDecompress( void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef pixelBuffer, CMTime presentationTimeStamp, CMTime presentationDuration ){
CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon;
*outputPixelBuffer = CVPixelBufferRetain(pixelBuffer);
H264HwDecoderImpl *decoder = (__bridge H264HwDecoderImpl *)decompressionOutputRefCon;
if (decoder.delegate!=nil)
{
[decoder.delegate displayDecodedFrame:pixelBuffer];
}
}
网友评论