解密硬件解碼關鍵技術

通過上一篇文章,我們用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。在我的項目中,我沒有使用這個函數,而是自己實現的:

  • 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佔用。

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

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

                                                                             

    摘自:http://my.oschina.net/u/2336532/blog/400790

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