h264硬解码

了解h264的桢结构

起始标志:4个字节 0000 0001
起始标志之后的第一个字节的低5位表示nalu_type = buf[4]&0x1f
5为I桢、7为sps、8为pps、1为p桢
构成顺序为sps+pps+I+p+p+…+p

1
00000001674d001f95a814016e8400000fa00001d4c0100000000168ee3c800000000106e501a8800000000165b800000f9bf0bffeced6afe1b11a32d639e05db83b1341baefb9ded91702fbd91a7c6c4700bfbcf1ae33ebce56e84c01f0d6b06c77669fc13c4903f755227

框架

framework VideoToolbox.h

数据结构

  • CMSampleBuffer:存放编解码前后的视频图像的容器数据结构。
  • CMBlockBuffer:编码后,结果图像的数据结构。
  • CVPixelBuffer:编码前和解码后的图像数据结构。
  • VTDecompressionRef:session
  • CMVideoFormatDescriptionRef:视频信息描述
    关系如下图
    image

方法

解码前期需要以下三个函数

  • CMVideoFormatDescriptionCreateFromH264ParameterSets 设置描述信息
  • VTDecompressionSessionCreate 创建解码 session
  • CMBlockBufferCreateWithMemoryBlock (uint_8 *) -> CMBlockBuffer
  • CMSampleBufferCreateReady CMBlockBuffer -> CMSampleBuffer

  • VTDecompressionSessionDecodeFrame 解码 CMSampleBuffer -> CVPixelBuffer

  • VTDecompressionSessionInvalidate 销毁解码 session

解码流程

  1. 设置CMVideoFormatDescriptionRef _description视频信息描述信息:

通过CMVideoFormatDescriptionCreateFromH264ParameterSets方法,带入sps、pps、&_description,可以提取出sps、pps中携带的视频属性信息。

  1. 创建 decode session
    1
    2
    3
    4
    5
    OSStatus status = VTDecompressionSessionCreate(kCFAllocatorDefault,
    decoderFormatDescription,
    NULL, attrs,
    &callBackRecord,
    &deocderSession);

decoderFormatDescription即为description 1中已经设置OK,

这个Session的作用是把callBackRecord函数、description描述信息连接起来,callBackRecord 是用来指定回调函数的,解码器支持异步模式,解码后会调用这里的回调函数

  1. 创建CMSampleBuffer
  • 先用CMBlockBufferCreateWithMemoryBlock从H.264数据创建一个CMBlockBufferRef实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    CMBlockBufferCreateWithMemoryBlock(NULL,
    (void *)frame,
    frameSize,
    kCFAllocatorNull,
    NULL,
    0,
    frameSize,
    FALSE,
    &blockBuffer);
  • 然后用 CMSampleBufferCreateReady创建CMSampleBufferRef实例。当然这个CMSampleBuffer按照最上面的关系,应该是进行了一次封装,将blockbuffer封装进samplebuffer。以备之后加码使用。

    1
    2
    3
    4
    5
    status = CMSampleBufferCreateReady(kCFAllocatorDefault,
    blockBuffer,
    _decoderFormatDescription ,
    1, 0, NULL, 1, sampleSizeArray,
    &sampleBuffer);

这样可以得到一个CMSampleBufferRef的实例。

  1. 解码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    VTDecodeFrameFlags flags = 0;
    //kVTDecodeFrame_EnableTemporalProcessing | kVTDecodeFrame_EnableAsynchronousDecompression;
    VTDecodeInfoFlags flagOut = 0;
    CVPixelBufferRef outputPixelBuffer = NULL;
    OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(deocderSession,
    sampleBuffer,
    flags,
    &outputPixelBuffer,
    &flagOut);

解码成功之后,outputPixelBuffer里就是一帧 NV12格式的YUV图像了。

  1. 显示
    配合Apple提供的Layer类,”AAPLEAGLLayer.h”,在controller中声明一个该类的实例_glLayer,在解码的回调中将解码出的CVPixelBuffer赋值给 _glLayer.pixelBuffer属性即可显示出来。

口答流程:

  1. 主要用到了四个数据结构和六个方法,列在纸上,说到哪里指到哪里;四个数据结构都是干什么用的,以及它们之间的关系。
  2. 画一个整体的流程图
  3. image
  4. 对于拿到的NSData数据我们要如何处理,首先要知道h264的数据格式,sps、pps、i、p的排列以及作用
  5. sps,pps设置CMVideoFormatDescriptionRef 视频信息描述,通过一个H264ParameterSets函数
  6. 刚刚提到四个函数,用了一个,剩下的三个怎么用?首先我们要调用VTDecompressionSessionCreate创建session,这就用到了刚刚的description,同时这个过程也把解码后的callBack声明好了
  7. 接下来要用VTDecompressionSessionDecodeFrame进行解码了,但是传入的参数应该时CMSampleBuffer类型,所以在这之前我们需要把(uint8_t *)类型的参数进行转化。
  8. 这就要用到CMBlockBufferCreateWithMemoryBlockCMSampleBufferCreateReady这个两个函数了,作用我的理解就是数据结构的转化uint_8* -> CMBlockBuffer 、CMBlockBuffer -> CMSampleBuffer
  9. 终于到了解码过程,从解码函数的参数我们可以看到,把CMSampleBuffer、CVPixelBuffer和session联系起来了,而session又与description、callBack关联,输出的CVPixelBuffer直接添加到callback中,进行显示就可以了。
那强 wechat
加个微信 成为朋友吧