解密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中,我們需要先解碼這個擴展數據,然後再從解碼後的數據信息裏面重新封裝成ADTS頭信息,加到每一幀AAC數據之前再送解碼器,這樣就可以正常解碼了。

    extradate數據定義如下:

     

        詳細信息及說明請參考“ISO-IEC-14496-3 (Audio)”的AudioSpecificConfig部分。裏面最重要的部分有采樣頻率、通道配置和音頻對象類型,這幾個一般都是AAC解碼器需要的配置參數。

        這個數據在ffmpeg中也有相應的解碼函數:avpriv_aac_parse_header。在我的項目中,我沒有使用這個函數,而是自己實現的

typedef struct  

{

      int write_adts;  

      int objecttype;  

      int sample_rate_index;  

      int channel_conf;  

}ADTSContext;  

typedef struct
{
      int write_adts;
      int objecttype;
      int sample_rate_index;
      int channel_conf;
}ADTSContext;

        int aac_decode_extradata(ADTSContext *adts, unsigned char *pbuf, int bufsize)  


{  

      int aot, aotext, samfreindex;  

      int i, channelconfig;  

      unsigned char *p = pbuf;  

   

      if (!adts || !pbuf || bufsize<2)  

      {  

            return -1;  

      }  

      aot = (p[0]>>3)&0x1f;  

      if (aot == 31)  

      {  

            aotext = (p[0]<<3 | (p[1]>>5)) & 0x3f;  

            aot = 32 + aotext;  

            samfreindex = (p[1]>>1) & 0x0f;  

             

            if (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;  

            if (samfreindex == 0x0f)  

            {  

                  channelconfig = (p[4]>>3) & 0x0f;  

            }  

            else  

            {  

                  channelconfig = (p[1]>>3) & 0x0f;  

            }  

      }  

   

#ifdef AOT_PROFILE_CTRL  

      if (aot < 2) aot = 2;  

#endif  

      adts->objecttype = aot-1;  

      adts->sample_rate_index = samfreindex;  

      adts->channel_conf = channelconfig;  

      adts->write_adts = 1;  

   

      return 0;  

}  

int aac_decode_extradata(ADTSContext *adts, unsigned char *pbuf, int bufsize)
{
      int aot, aotext, samfreindex;
      int i, channelconfig;
      unsigned char *p = pbuf;
 
      if (!adts || !pbuf || bufsize<2)
      {
            return -1;
      }
      aot = (p[0]>>3)&0x1f;
      if (aot == 31)
      {
            aotext = (p[0]<<3 | (p[1]>>5)) & 0x3f;
            aot = 32 + aotext;
            samfreindex = (p[1]>>1) & 0x0f;
           
            if (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;
            if (samfreindex == 0x0f)
            {
                  channelconfig = (p[4]>>3) & 0x0f;
            }
            else
            {
                  channelconfig = (p[1]>>3) & 0x0f;
            }
      }
 
#ifdef AOT_PROFILE_CTRL
      if (aot < 2) aot = 2;
#endif
      adts->objecttype = aot-1;
      adts->sample_rate_index = samfreindex;
      adts->channel_conf = channelconfig;
      adts->write_adts = 1;
 
      return 0;
}

           上面的pbuf就是extradata

    接下來,再用ADTSContext數據編碼爲ADTS頭信息插入每一個AAC幀前面:

        int aac_set_adts_head(ADTSContext *acfg, unsigned char *buf, int size)  


{         

      unsigned char byte;  

   

      if (size < ADTS_HEADER_SIZE)  

      {  

            return -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;  

   

      return 0;  

}  

int aac_set_adts_head(ADTSContext *acfg, unsigned char *buf, int size)
{       
      unsigned char byte;
 
      if (size < ADTS_HEADER_SIZE)
      {
            return -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;
 
      return 0;
}

  這個頭部是固定的7字節長度,所以可提前空出這7個字節供ADTS佔用。

  通過以上對H264AAC的擴展數據處理,播放各種“黃金搭檔”的多媒體文件、流媒體、視頻點播等都應該沒有問題了。

 

  想第一時間獲得更多原創文章,請關注個人微信公衆平臺:程序員互動聯盟(coder_online),掃一掃下方二維碼或者搜索微信號coder_online即可關注,裏面有大量AndroidChromiumLinux等相關文章等着您,我們還可以在線交流。

        如需轉載本文,請註明出處:謝謝合作!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章