微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

解密H264、AAC硬件解码的关键扩展数据处理

    通过上一篇文章,我们用ffmpeg分离出一个多媒体容器中的音视频数据,但是很可能这些数据是不能被正确解码的。为什么呢?因为在解码这些数据之前,需要对解码器做一些配置,典型的就是目前流行的高清编码“黄金搭档”组合H264 + AAC的搭配。本文将讲述H264AAC的关键解码配置参数的解析,如果没有这些配置信息,数据帧往往不完整,导致了解码器不能解码。

  • H264的配置信息解析

    前面我们知道,ffmpegavformat_find_stream_info函数可以取得音视频媒体多种,比如播放持续时间、音视频压缩格式、音轨信息、字幕信息、帧率、采样率等。在信息结果中有一项扩展数据描述(avcodec.h文件中):

          AVCodecContext定义如下:

    如果视频流是H264,这个exTradate里面就包含了H264的配置信息,这个扩展数据有如下定义:

    详细解释可以参考ISO-14496-15 AVC file format文档。里面最重要的就是NAL长度和SPSPPS数据和对应的长度信息。对该数据的解析在ffmpeg里面有现成的函数ff_h264_decode_exTradata,在我的项目里面是自己写的扩展数据解析。

  • AAC的配置信息解析及设置

    如果音频数据是AAC流,在解码时需要ADTS(Audio Data Transport Stream)头部,不管是容器封装还是流媒体,没有这个,一般都是不能播放的。很多朋友在做AAC流播放时遇到播不出声音,很可能就是这个原因导致。

    ADTS所需的数据仍然是放在上面的扩展数据exTradata中,我们需要先解码这个扩展数据,然后再从解码后的数据信息里面重新封装成头信息,加到每一帧数据之前再送解码器,这样就可以正常解码了。

    exTradate数据定义如下:

     

        详细信息及说明请参考“ISO-IEC-14496-3 (Audio)”的AudioSpecificConfig部分。里面最重要的部分有采样频率、通道配置和音频对象类型,这几个一般都是AAC解码器需要的配置参数。

        这个数据在ffmpeg中也有相应的解码函数avpriv_aac_parse_header。在我的项目中,我没有使用这个函数,而是自己实现的:

  • ?
    1
    2
    3
    4
    5
    6
    7
    typedef  struct
    {
           int  write_adts;
    objecttype;
    sample_rate_index;
    channel_conf;
    }ADTSContext;

  • 7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    aac_decode_exTradata(ADTSContext *adts, unsigned  char  *pbuf,  bufsize)
    aot, aotext, samfreindex;
    i, channelconfig;
           unsigned  *p = pbuf;
      
           if  (!adts || !pbuf || bufsize<2)
    {
                 return  -1;
    }
    aot = (p[0]>>3)&0x1f;
    (aot == 31)
    {
                 aotext = (p[0]<<3 | (p[1]>>5)) & 0x3f;
    aot = 32 + aotext;
    samfreindex = (p[1]>>1) & 0x0f;
                
    (samfreindex == 0x0f)
    {
                       channelconfig = ((p[4]<<3) | (p[5]>>5)) & 0x0f;
    }
    else
    {
    channelconfig = ((p[1]<<3)|(p[2]>>5)) & 0x0f;
    }
    }
    else
    {
    samfreindex = ((p[0]<<1)|p[1]>>7) & 0x0f;
    (samfreindex == 0x0f)
    {
    channelconfig = (p[4]>>3) & 0x0f;
    }
    else
    {
    channelconfig = (p[1]>>3) & 0x0f;
    }
    }
      
    #ifdef AOT_PROFILE_CTRL
    (aot < 2) aot = 2;
    #endif
    adts->objecttype = aot-1;
    adts->sample_rate_index = samfreindex;
    adts->channel_conf = channelconfig;
    adts->write_adts = 1;
      
    0;
    }

           上面的pbuf就是exTradata

    接下来,再用ADTSContext数据编码为ADTS头信息插入每一个AAC帧前面:

  • 33
    aac_set_adts_head(ADTSContext *acfg,monospace!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-size:1em!important; min-height:inherit!important; background:none!important">*buf,monospace!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; font-size:1em!important; min-height:inherit!important; background:none!important">size)
    {       
    byte;
      
    (size < ADTS_HEADER_SIZE)
    {
    -1;
    }
          
    buf[0] = 0xff;
    buf[1] = 0xf1;
    byte = 0;
    byte |= (acfg->objecttype & 0x03) << 6;
    byte |= (acfg->sample_rate_index & 0x0f) << 2;
    byte |= (acfg->channel_conf & 0x07) >> 2;
    buf[2] = byte;
    byte = 0;
    byte |= (acfg->channel_conf & 0x07) << 6;
    byte |= (ADTS_HEADER_SIZE + size) >> 11;
    buf[3] = byte;
    byte = 0;
    byte |= (ADTS_HEADER_SIZE + size) >> 3;
    buf[4] = byte;
    byte = 0;
    byte |= ((ADTS_HEADER_SIZE + size) & 0x7) << 5;
    byte |= (0x7ff >> 6) & 0x1f;
    buf[5] = byte;
    byte = 0;
    byte |= (0x7ff & 0x3f) << 2;
    buf[6] = byte;
      
    0;
      这个头部是固定的7字节长度,所以可提前空出这7个字节供ADTS占用。

      通过以上对H264AAC的扩展数据处理,播放各种“黄金搭档”的多媒体文件、流媒体、视频点播等都应该没有问题了。

     

      想第一时间获得更多原创文章,请关注个人微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或者搜索微信号coder_online即可关注,里面有大量AndroidChromiumLinux等相关文章等着您,我们还可以在线交流。

            如需转载本文,请注明出处:谢谢合作!

    版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐