【雷神源碼解析】無基礎看懂AAC碼流解析,看不懂你打我


一 前言

  最近在嘗試學習一些視頻相關的知識,隨便一搜才知道原來國內有雷神這么一個真正神級的人物存在,尤其是在這里(傳送門)看到他的感言更是對他膜拜不已,雷神這種無私奉獻的精神應當被我輩發揚光大。那寫這篇隨筆的理由是在看他寫的AAC音頻碼流解析文章時(傳送門)遇到一些問題,因為雷神畢竟等級與初學者不同,一些在他看來很基礎的東西菜鳥(比如我)一看就懵逼了,看得是雲里霧里,而且我在評論中也看到有人提問相同的問題,但是並沒有人給出解答,我自己花了將近三個小時仔細看了AAC碼流的介紹才明白,這里也獻丑講解一下。

二 AAC碼流數據存儲格式

  這里先把雷神的話看一遍

這當然是沒問題的,不過雷神說的有點過於簡單了,ADTS frame內部的結構的什么樣子的?數據存儲在ADTS frame的哪一部分?這些並沒有說清楚,所以下面看代碼時就會搞不懂。我通過AAC Audio ES Viewer打開了一個AAC碼流文件,這個軟件能將一個AAC碼流文件解析成一個個的ADTS frame,咱們來看一下(圖片較大,如果看不清可以在新窗口打開查看)

我這里選擇了第一個ADTS段,看右邊的部分,可以看到一個ADTS內部其實又有四個部分組成:adts_fixed_header/adts_variable_header/adts_error_check/raw_data_block,其中后兩個部分中並沒有什么東西,咱們就先不管它們,重點分析下前兩個部分。上面圖中每一個部分后面都標了所占的bit,咱們可以計算一下,可以知道總共是56bit,也就是7個byte。也就是說ADTS header占7個字節,header也有可能占9個字節,看adts_fixed_header部分中的protection_absent,當這個值為0時,占7字節,為1時會占9個字節,當然這個就先說到這里,不是今天的重點,先不討論。接下來咱們說下前面兩個部分中比較重要的參數含義:

adts_fixed_header

  •   syncword:同步字,占12bit,值固定,都是0xFFF,轉成二進制就是111111111111,這是每個ADTS frame的開頭,就像上面雷神說的,咱們可以找到這個值,就能把AAC碼流一個一個的分割開
  •   ID:表示使用的MPEG的版本,0表示MPEG-4,1表示MPEG-2
  •   layer:同syncword,值固定,都是00
  •   protection_absent:是否有同步校驗,如果有值是0,沒有是1
  •   profile:使用的AAC級別
  •   sampling_frequency_index:采樣率,上圖中可以看到是48000 Hz
  •   channel_configuration:聲道數,上圖中可以看到兩個聲道,LF RF表示左右聲道

adts_variable_header

  • aac_frame_length:ADTS frame長度,包括header和data部分(這個很關鍵)

好了,上面就是比較重要的參數介紹,知道這些,有助於理解雷神的代碼思路。

三 代碼解析

先把雷神的代碼抄過來

  1 int getADTSframe(unsigned char* buffer, int buf_size, unsigned char* data ,int* data_size){
  2     int size = 0;
  3 
  4     if(!buffer || !data || !data_size ){
  5         return -1;
  6     }
  7 
  8     while(1){
  9         if(buf_size  < 7 ){
 10             return -1;
 11         }
 12         //Sync words
 13         if((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0) ){
 14             size |= ((buffer[3] & 0x03) <<11);     //high 2 bit
 15             size |= buffer[4]<<3;                //middle 8 bit
 16             size |= ((buffer[5] & 0xe0)>>5);        //low 3bit
 17             break;
 18         }
 19         --buf_size;
 20         ++buffer;
 21     }
 22 
 23     if(buf_size < size){
 24         return 1;
 25     }
 26 
 27     memcpy(data, buffer, size);
 28     *data_size = size;
 29 
 30     return 0;
 31 }
 32 
 33 int simplest_aac_parser(char *url)
 34 {
 35     int data_size = 0;
 36     int size = 0;
 37     int cnt=0;
 38     int offset=0;
 39 
 40     //FILE *myout=fopen("output_log.txt","wb+");
 41     FILE *myout=stdout;
 42 
 43     unsigned char *aacframe=(unsigned char *)malloc(1024*5);
 44     unsigned char *aacbuffer=(unsigned char *)malloc(1024*1024);
 45 
 46     FILE *ifile = fopen(url, "rb");
 47     if(!ifile){
 48         printf("Open file error");
 49         return -1;
 50     }
 51 
 52     printf("-----+- ADTS Frame Table -+------+\n");
 53     printf(" NUM | Profile | Frequency| Size |\n");
 54     printf("-----+---------+----------+------+\n");
 55 
 56     while(!feof(ifile)){
 57         data_size = fread(aacbuffer+offset, 1, 1024*1024-offset, ifile);
 58         unsigned char* input_data = aacbuffer;
 59 
 60         while(1)
 61         {
 62             int ret=getADTSframe(input_data, data_size, aacframe, &size);
 63             if(ret==-1){
 64                 break;
 65             }else if(ret==1){
 66                 memcpy(aacbuffer,input_data,data_size);
 67                 offset=data_size;
 68                 break;
 69             }
 70 
 71             char profile_str[10]={0};
 72             char frequence_str[10]={0};
 73 
 74             unsigned char profile=aacframe[2]&0xC0;
 75             profile=profile>>6;
 76             switch(profile){
 77             case 0: sprintf(profile_str,"Main");break;
 78             case 1: sprintf(profile_str,"LC");break;
 79             case 2: sprintf(profile_str,"SSR");break;
 80             default:sprintf(profile_str,"unknown");break;
 81             }
 82 
 83             unsigned char sampling_frequency_index=aacframe[2]&0x3C;
 84             sampling_frequency_index=sampling_frequency_index>>2;
 85             switch(sampling_frequency_index){
 86             case 0: sprintf(frequence_str,"96000Hz");break;
 87             case 1: sprintf(frequence_str,"88200Hz");break;
 88             case 2: sprintf(frequence_str,"64000Hz");break;
 89             case 3: sprintf(frequence_str,"48000Hz");break;
 90             case 4: sprintf(frequence_str,"44100Hz");break;
 91             case 5: sprintf(frequence_str,"32000Hz");break;
 92             case 6: sprintf(frequence_str,"24000Hz");break;
 93             case 7: sprintf(frequence_str,"22050Hz");break;
 94             case 8: sprintf(frequence_str,"16000Hz");break;
 95             case 9: sprintf(frequence_str,"12000Hz");break;
 96             case 10: sprintf(frequence_str,"11025Hz");break;
 97             case 11: sprintf(frequence_str,"8000Hz");break;
 98             default:sprintf(frequence_str,"unknown");break;
 99             }
100 
101 
102             fprintf(myout,"%5d| %8s|  %8s| %5d|\n",cnt,profile_str ,frequence_str,size);
103             data_size -= size;
104             input_data += size;
105             cnt++;
106         }   
107 
108     }
109     fclose(ifile);
110     free(aacbuffer);
111     free(aacframe);
112 
113     return 0;
114 }

然后說一下當初我看的時候迷惑的地方。

1、代碼第9行,為什么要判斷size是否小於7?

答:第二部分時有說,一個ADTS header最少占7字節,當小於7字節時,說明不是一個ADTS frame或數據不完整,沒必要解析了。

2、第13行,((buffer[1] & 0xf0) == 0xf0),為什么要進行位運算?

答:第二部分也有說,同步字占12bit,也就是它占了1.5個字節,第一個字節和第二個字節的前四位,0xF0用二進制表示是11110000,和buffer[1]進行&運算后如果還是11110000,說明第二個字節的前四位是1111,再加上前面的buffer[0]=0xFF,就可以判定buffer的前12bit是111111111111,也就取得了syncword。

3、取size的三行代碼到底是什么鬼????

size |= ((buffer[3] & 0x03) <<11);     //high 2 bit
size |= buffer[4]<<3;                //middle 8 bit
size |= ((buffer[5] & 0xe0)>>5);        //low 3bit

其實雷神注釋中已經說了,但是不了解數據結構的依然會懵逼。第二部分說了,ADTS header中有ADTS frame的大小,但是根據上面同步字咱們可以看出來,這些數據並不是以字節為單位連續排列的,而是按位排列的,這就有點糾結了不是?那size存儲在哪一位中,從哪里開始?在哪里結束?頭大!!別急,我畫了一張圖(圖片比較大,如果看不情,可以在新窗口中查看,或點這里下載

從這一張圖中可以很清晰的看到,frame_length存儲在第4個字節的后兩位,第5個字節,第6個字節的前三位。好了,知道這些再看上面的三行代碼,不難理解了吧,如果還理解不了,說明得補充一下編程知識啦。

4、74和83行什么意思?

答:這兩行代碼分別是求取profile和sampling_frequency_index值的,理解了上面的第2和第3個問題,這個問題也就不是問題啦。

四 結言

以上是我學習時的問題,由於我在視頻方面是純新手,所以我的問題應該大部分人都會有,上面四個問題理解了后,整體代碼對你就沒有秘密而言了。我不希望別人也像我一樣花幾個小時搞明白,太浪費時間了。

 參考資料:AAC的ADTS頭文件信息介紹

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM