H264Decoding 硬解码

作者: 那一片阳光 | 来源:发表于2016-05-18 19:07 被阅读979次
    视频流的解码分为软解和硬解。软解:利用CPU做视频的编码和解码,俗称软编软解。这个方法比较通用,但是占用CPU资源,编解码的效率不高。硬解:一般系统都会提供GPU或者专用处理器来对视频流进行编解码,也就是硬件编码和解码。简称硬编解码,调用VideoToolbox的框架进行基本数据结构。编解码前后的视频图像均封装在CMSampleBuffer中,如果是编码后的图像,以CMBlockBuffe方式存储;解码后的图像,以CVPixelBuffer存储。CMSampleBuffer里面还有另外的时间信息CMTime和视频描述信息CMVideoFormatDesc。

    --

    苹果自带的框架只有在ios8.0才能支持硬编解码 (VideoToolbox.Framework),

    解码主要需要以下三个函数
    VTDecompressionSessionCreate 创建解码session
    VTDecompressionSessionDecodeFrame 解码一个frame
    VTDecompressionSessionInvalidate 销毁解码 session

    • 编解码前后的视频图像均封装在CMSampleBuffer中,如果是编码后的图像,以CMBlockBuffe方式存储;解码后的图像,以CVPixelBuffer存储。CMSampleBuffer里面还有另外的时间信息CMTime和视频描述信息CMVideoFormatDesc。
    硬解码使用方法。
    • 将H264码流转换成解码前的CMSampleBuffer:解码前的CMSampleBuffer = CMTime + FormatDesc + CMBlockBuffer。需要从H264的码流里面提取出以上的三个信息。最后组合成CMSampleBuffer,提供给硬解码接口来进行解码工作。H264的码流由NALU单元组成,NALU单元包含视频图像数据和H264的参数信息。其中视频图像数据就是CMBlockBuffer,而H264的参数信息则可以组合成FormatDesc。具体来说参数信息包含SPS(Sequence Parameter Set)和PPS(Picture Parameter Set)。

    • 码流结构:提取sps和pps生成format description。

      a,每个NALU的开始码是0x00 00 01,按照开始码定位NALU。

      b,通过类型信息找到sps和pps并提取,开始码后第一个byte的后5 位,7代表sps,8代表pps。

      c,CMVideoFormatDescriptionCreateFromH264ParameterSets函 数来构建CMVideoFormatDescriptionRef。具体代码可以见demo。

    • 提取视频图像数据生成CMBlockBuffer。

      a,通过开始码,定位到NALU。

      b,确定类型为数据后,将开始码替换成NALU的长度信息(4 Bytes)。

      c,CMBlockBufferCreateWithMemoryBlock接口构造CMBlockBufferRef。具体代码可以见demo。

    • 根据需要,生成CMTime信息。(实际测试时,加入time信息后,有不稳定的图像,不加入time信息反而没有,需要进一步研究,这里建议不加入time信息)

    根据上述得到CMVideoFormatDescriptionRef、CMBlockBufferRef和可选的时间信息,使用CMSampleBufferCreate接口得到CMSampleBuffer数据这个待解码的原始的数据。

    具体方式参考demo

    相关文章

      网友评论

      • 你也想起舞吧:请问楼主,如果转换H265格式的解码,是不是把CMVideoFormatDescriptionCreateFromH264ParameterSets替换为CMVideoFormatDescriptionCreateFromHEVCParameterSets方法就行了?麻烦回答下,谢谢🙏
      • 陈_某_某:-(VideoPacket*)nextPacket
        {
        if(_bufferSize < _bufferCap && self.fileStream.hasBytesAvailable) {
        NSInteger readBytes = [self.fileStream read:_buffer + _bufferSize maxLength:_bufferCap - _bufferSize];
        _bufferSize += readBytes;
        }

        if(memcmp(_buffer, KStartCode, 4) != 0) {
        return nil;
        }

        if(_bufferSize >= 5) {
        uint8_t *bufferBegin = _buffer + 4;
        uint8_t *bufferEnd = _buffer + _bufferSize;
        while(bufferBegin != bufferEnd) {
        if(*bufferBegin == 0x01) {
        if(memcmp(bufferBegin - 3, KStartCode, 4) == 0) {
        NSInteger packetSize = bufferBegin - _buffer - 3;
        VideoPacket *vp = [[VideoPacket alloc] initWithSize:packetSize];
        memcpy(vp.buffer, _buffer, packetSize);

        memmove(_buffer, _buffer + packetSize, _bufferSize - packetSize);
        _bufferSize -= packetSize;

        return vp;
        }
        }
        ++bufferBegin;
        }
        }

        return nil;
        }
        这个方法能详细说一下嘛,fileStream一次读取的是一个序列吗??
        while循环里边面的我加了个nslog就不出视频,,也不知道怎么回事
        S__L:这里我也么看懂请问您知道咋回事了么?
      • ff60a12390d8:请问楼主,如果一整帧的数据是多个nalu(slice)构成的,怎么解码呢,按找demo中的方式解出来会花屏
        5d51964693b5:@Smallworld 请问你没有看楼主的demo啊 我在虚拟机上怎么一卡卡的 你的demo能让我看看 给个地址可以吗 谢谢
        ff60a12390d8:@_iOSer解决了,就是得知道哪些slice组成一帧的数据,然后把每个slice通过数据长度(四个字节)+slice数据的形式拼接起来,然后再解码就可以了
        陈_某_某:@Smallworld 多slice的情况你解决了吗,最近也遇到这个问题

      本文标题:H264Decoding 硬解码

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