发表时间:2022-03-26来源:网络
一 前言
最近在尝试学习一些视频相关的知识,随便一搜才知道原来国内有雷神这么一个真正神级的人物存在,尤其是在这里(传送门)看到他的感言更是对他膜拜不已,雷神这种无私奉献的精神应当被我辈发扬光大。那写这篇随笔的理由是在看他写的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) >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)
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-02-14