VideoToolboxiOS开发笔记

对视频帧的实时硬解码,解压

2017-08-07  本文已影响70人  轻云绿原

要对数据进行硬解码,要用到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];
    }
}
上一篇下一篇

猜你喜欢

热点阅读