一 前言
最近在嘗試學習一些視頻相關的知識,隨便一搜才知道原來國內有雷神這么一個真正神級的人物存在,尤其是在這里(傳送門)看到他的感言更是對他膜拜不已,雷神這種無私奉獻的精神應當被我輩發揚光大。那寫這篇隨筆的理由是在看他寫的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頭文件信息介紹