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


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

    • H264的配置信息解析

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

              AVCodecContext定义如下:

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

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

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

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

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

      extradate数据定义如下:

       

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

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

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

               上面的pbuf就是extradata。

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

    • [cpp] view plaincopy
       
      1. int aac_set_adts_head(ADTSContext *acfg, unsigned char *buf, int size)  
      2. {         
      3.       unsigned char byte;  
      4.    
      5.       if (size < ADTS_HEADER_SIZE)  
      6.       {  
      7.             return -1;  
      8.       }  
      9.        
      10.       buf[0] = 0xff;  
      11.       buf[1] = 0xf1;  
      12.       byte = 0;  
      13.       byte |= (acfg->objecttype & 0x03) << 6;  
      14.       byte |= (acfg->sample_rate_index & 0x0f) << 2;  
      15.       byte |= (acfg->channel_conf & 0x07) >> 2;  
      16.       buf[2] = byte;  
      17.       byte = 0;  
      18.       byte |= (acfg->channel_conf & 0x07) << 6;  
      19.       byte |= (ADTS_HEADER_SIZE + size) >> 11;  
      20.       buf[3] = byte;  
      21.       byte = 0;  
      22.       byte |= (ADTS_HEADER_SIZE + size) >> 3;  
      23.       buf[4] = byte;  
      24.       byte = 0;  
      25.       byte |= ((ADTS_HEADER_SIZE + size) & 0x7) << 5;  
      26.       byte |= (0x7ff >> 6) & 0x1f;  
      27.       buf[5] = byte;  
      28.       byte = 0;  
      29.       byte |= (0x7ff & 0x3f) << 2;  
      30.       buf[6] = byte;  
      31.    
      32.       return 0;  
      33. }  

      这个头部是固定的7字节长度,所以可提前空出这7个字节供ADTS占用。

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

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

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

  • 相关阅读:
    no route to host
    修改文件失败,提示E509: 无法创建备份文件 (请加 ! 强制执行)
    OAuth2.0的四种授权模式
    Django--JWT认证
    Django--数据库ORM操作
    DRF中限流、过滤、排序、分页
    Django框架中序列化与反序列化与字段验证
    21.Django三种风格的模型继承
    20.第三方微博登录Django+Vue
    博客园图片点击放大功能
  • 原文地址:https://www.cnblogs.com/qiugc846717529/p/4425841.html
Copyright © 2020-2023  润新知