解密H264、AAC硬件解碼的關鍵擴展數據處理

通過上一篇文章,我們用ffmpeg分離出一個多媒體容器中的音視頻數據,但是很可能這些數據是不能被正確解碼的。爲什麼呢?因爲在解碼這些數據之前,需要對解碼器做一些配置,典型的就是目前流行的高清編碼“黃金搭檔”組合H264 + AAC的搭配。本文將講述H264AAC的關鍵解碼配置參數的解析,如果沒有這些配置信息,數據幀往往不完整,導致瞭解碼器不能解碼。

  • H264的配置信息解析

    前面我們知道,ffmpegavformat_find_stream_info函數可以取得音視頻媒體多種,比如播放持續時間、音視頻壓縮格式、音軌信息、字幕信息、幀率、採樣率等。在信息結果中有一項擴展數據描述(avcodec.h文件中):

001728_NKXZ_2336532.jpg

          AVCodecContext定義如下:

001835_LPpG_2336532.jpg

    如果視頻流是H264,這個extradate裏面就包含了H264的配置信息,這個擴展數據有如下定義:

002036_exwu_2336532.jpg

    詳細解釋可以參考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數據定義如下:

     002139_FvtV_2336532.jpg

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

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

  • ?

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

  • ?

    1
    2
    3
    4
    5
    6
    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
    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幀前面:

  • ?

    1
    2
    3
    4
    5
    6
    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
    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等相關文章等着您,我們還可以在線交流。

003407_qI1q_2336532.jpg


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