美文网首页VideoToolboxiOS开发笔记
对视频帧的实时硬解码,解压

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

作者: 轻云绿原 | 来源:发表于2017-08-07 08:36 被阅读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];
        }
    }
    

    相关文章

      网友评论

        本文标题:对视频帧的实时硬解码,解压

        本文链接:https://www.haomeiwen.com/subject/fmovlxtx.html