aac音频分析
2025.7.08🐱

一、概述

AAC(Advanced Audio Coding),中文名:高级音频编码。出现于1997年,基于MPEG-2的音频编码技术。由Fraunhofer IIS、杜比实验室、AT&T、索尼等公司共同开发,目的是取代MP3格式。

2000年,MPEG-4标准出现后,AAC重新集成了其特性,加入了SBR技术和PS技术,为了区别于传统的MPEG-2 AAC又称为MPEG-4 AAC。

本文从aac编码的基础概念出发展开,后面包含aac编码规格、AAC格式的文件怎么组成的。

二、aac编码规格

aac有9种规格(Profile),以适应不同场景

MPEG-2 AAC LC:低复杂度规格(Low Complexity) 注:比较简单,没有增益控制,但提高了编码效率,在中等码率的编码效率以及音质方面,都能找到平衡点。
MPEG-2 AAC Main:主规格
MPEG-2 AAC SSR:可变采样率规格(Scaleable Sample Rate)
MPEG-4 AAC LC:低复杂度规格(Low Complexity)—现在的手机比较常见的MP4文件中的音频部份就包括了该规格音频文件
MPEG-4 AAC Main:主规格 注:包含了除增益控制之外的全部功能,其音质最好
MPEG-4 AAC SSR:可变采样率规格(Scaleable Sample Rate)
MPEG-4 AAC LTP:长时期预测规格(Long Term Predicition)
MPEG-4 AAC LD:低延迟规格(Low Delay)
MPEG-4 AAC HE:高效率规格(High Efficiency)—这种规格适合用于低码率编码,有Nero ACC 编码器支持

最早是基于MPEG-2标准,称为:MPEG-2 AAC。后来MPEG-4标准在原来基础上增加了一些新技术,称为:MPEG-4 AAC。

流行的Nero AAC编码程序只支持LC,HE,HEv2这三种规格,编码后的AAC音频,规格显示都是LC。HE其实就是AAC(LC)+ SBR技术,HEv2就是AAC(LC)+ SBR + PS技术;

HE:HE-AAC v1(又称AACPlusV1,SBR),用容器的方法实现了AAC(LC)+SBR技术。SBR其实代表的是Spectral Band Replication(频段复制)。简要叙述一下,音乐的主要频谱集中在低频段,高频段幅度很小,但很重要,决定了音质。如果对整个频段编码,若是为了保护高频就会造成低频段编码过细以致文件巨大;若是保存了低频的主要成分而失去高频成分就会丧失音质。SBR把频谱切割开来,低频单独编码保存主要成分,高频单独放大编码保存音质,“统筹兼顾”了,在减少文件大小的情况下还保存了音质,完美的化解这一矛盾。

HEv2:用容器的方法包含了HE-AAC v1和PS技术。PS指“parametric stereo”(参数立体声)。原来的立体声文件文件大小是一个声道的两倍。但是两个声道的声音存在某种相似性,根据香农信息熵编码定理,相关性应该被去掉才能减小文件大小。所以PS技术存储了一个声道的全部信息,然后,花很少的字节用参数描述另一个声道和它不同的地方。

三、aac封装格式

AAC有两种封装格式:ADIFADTS

  • ADIF:全称 Audio Data Interchange Format,音频数据交换格式,该格式一般应用在将音频通过写文件方式存储在磁盘里的场景,不能进行随机访问,不允许在文件中间开始进行解码;只有拿到整个文件时才能开始进行渲染播放;
  • ADTS:全称 Audio Data Transport Stream,音频数据传输流,该格式的特征是用同步字节进行将 AAC 音频截断,然后可以允许客户端在任何地方进行解码播放,适合网络传输场景;简单来说,ADTS可以在任意帧进行解码,每一个帧都有头信息,但ADIF却只有一个统一的头,所以必须得到所有的数据后才能解码。一个帧就能单独解码。

两种Header的形式也不同,目前一般编码后和抽取出的基本都是ADTS格式音频流。

✨3.1 ADIF 格式

这个格式比较少见,简单认识一下即可,下图是ADIF格式的序列,由adif_header、byte_alignmentraw_data_stream三部分组成。byte_alignment是用来做字节对齐的,也就是说,ADIF格式由一个ADIF头信息(adif_header)原始数据流(raw_data_stream) 构成。

下面看看ADIF头信息包括哪些内容,如下图:

✨3.2 ADTS 格式

ADTS 格式的AAC音频流是由一个个的ADTS帧组成的,下图是ADTS序列的语法,整个序列由若干个adts_frame组成。


每个ADTS帧都是由ADTS头部AAC音频数据组成,下图是adts_frame的语法,adts_fixed_header(固定头部信息)adts_variable_header(可变头部信息)都属于ADTS头部数据,raw_data_block表示AAC音频原始数据块

下图是网络上对AAC音频流总结的一张图片,可以帮助我们宏观地认识AAC音频流的ADTS格式。

✨3.2.1 ADTS头部

ADTS头部有两部分,分为固定头部(adts_fixed_header)可变头(adts_variable_header)

✨3.2.1.1 固定头部(adts_fixed_header)

ADTS固定头部是 解码器识别音频格式和参数的关键部分,它告诉解码器:

  • 这是一个合法的ADTS帧(通过syncword)
  • 音频是MPEG-4还是MPEG-2
  • 使用哪种AAC Profile
  • 采样率和声道数是多少
  • 是否有CRC校验等附加信息

字段解析:

  • syncword:占用12bit;
    所有的bit位都是1。总是0xFFF,代表一个ADTS帧的开始,作为分界符,用于同步每帧起始位置
  • ID:占用 1 bit;
    表示MPEG版本0代表MPEG-4, 1代表MPEG-2,一般用 0,因为都是属于 MPEG 的规范.。
  • layer:占用 2 bit;
    固定为 00,保留字段
  • protection_absent:占用 1 bit;(用于判断是否禁用CRC校验
    设置 1 表示没有CRC,整个ADST头为7字节;0 表示有CRC,整个ADST头为9字节。关于CRC是什么及它的作用:CRC(Cyclic Redundancy Check,循环冗余校验)是一种错误检测机制,用于在数据传输或存储过程中检测比特级错误(比如信号干扰导致的位翻转)。在ADTS中,CRC字段是可选的,由protection_absent位控制:protection_absent = 0 → 存在CRC字段(16位校验码,紧跟在固定头部之后)。protection_absent = 1 → 无CRC校验,节省2字节开销。音频流(如网络传输、无线广播)可能因噪声或干扰导致数据损坏,CRC能发现错误帧并触发重传或丢弃,避免播放杂音,它就是一个音频帧的“封条”,确保数据完整性
  • profile_ObjectType:占用 2 bit,表示使用的AAC规格(profile)
    该字段的解释取决于ID位的值。如果ID等于1,则该字段包含与ISO/IEC 13818-7中定义的ADTS流中的配置文件字段相同的信息,也就是MPEG-2的规格;当ID为0是表示的是MPEG-4的规格,该字段的值等于 Audio Object Type 的值减1。字段取值如下面图片的表格
  • sampling_frequency_index:占用 4 bit;
    表示采样率下标索引,字段取值及解释如下图: 例如0对应96000HZ
  • private_bit:占用 1 bit,作为私有位,用户自定义,编码时设置为0,解码时忽略;
    详细解释参见 ISO/IEC 11172-3, subclause 2.4.2.3 (Table 8)
  • channel_configuration:占用 3 bit;
    通道配置即声道数,一般 2 表示立体声双声道。更多的值参考下图:
  • original_copy:占用 1 bit,原始/复制标志,0表示当前帧是拷贝是非首次发行而经过复制再编码的版本,1表示是原始是第一次发行的原始母带/母盘
  • home:占用 1 bit,该位纯粹保留给广播或版权系统使用,实际编码器/播放器都会填 0,表示“无特殊家用标志”,1表示家用,编码时设置为0,解码时忽略,对解码、播放、版权均无影响,可忽略

✨3.2.1.2 可变头部(adts_variable_header)

ADTS 可变头部提供了 帧级别的动态信息,结合固定头部,解码器就能完整解析每一帧的格式和内容,它的关键作用包括:

  • 定位帧边界(通过 aac_frame_length)。
  • 控制解码器缓冲(通过 adts_buffer_fullness)。
  • 支持版权标记(通过 copyright_identification_* 位)。
  • 支持多 AAC 数据块打包(通过 number_of_raw_data_blocks_in_frame)。

字段解析:

  • copyright_identification_bit:占用 1 bit,版权标识位,与下一字段配合使用,(0表示不包含版权标识,1标识包含)编码时设置为0,解码时忽略;
  • copyright_identification_start:占用 1 bit,版权标识开始标志,编码时设置为0,解码时忽略;
  • frame_length:占用 13 bit,当前 ADTS 帧的长度包括 ADTS 头(固定+可变)和 AAC 原始流,单位byte;
  • adts_buffer_fullness:占用 11 bit,解码器缓冲区占用程度,用于码率控制,0x7FF 表示码率可变的码流,0x000 表示固定码率的码流;
    实际的AAC文件中,这个字段会出现不等于0x7FF、0x000的情况,下面是GPT的回复:adts_buffer_fullness字段表示AAC解码器中的缓冲区当前的填充量。这个字段的值代表了解码器输入缓冲区中未使用的字节数,可以用来衡量解码器缓冲区的剩余空间。理想状态下,这个值应该保持在一个合适的范围内,以确保解码器能够持续地接收和处理音频数据,而不会发生溢出或欠流的情况。
  • number_of_raw_data_blocks_in_frame:占用 2 bit;
    该字段表示当前ADST帧中所包含的AAC帧的个数减一。为了最大的兼容性通常每个ADTS frame 包含一个AAC frame,所以该值一般为0。一个AAC原始帧包含一段时间内1024个采样及相关数据

四、 AAC格式音频文件解析 (编译器+代码实践)

现在较常使用的AAC文件格式是使用ADTS帧来保存的,所以后面就以ADTS格式的AAC文件的解析过程进行展开,通过实践巩固加强对ADTS帧结构的认识。

✨4.1 aac文件解析(编译器+ffmpeg命令)

✨4.1.1 编译器查看ADTS头各字段

我使用的是一个在线网页的Hex16进制编辑器来查看aac文件的 HexEd.it — 基于浏览器的十六进制编辑器 如下图 我们可以对他们进行转为二进制然后按位来看

没有CRC的情况下,文件开头的7个字节是ADTS帧头部,这里7个字节的数据是:

FF F1 50 80 03 9F FC

我们将这7个字节转为二进制来看

字节二进制(共56位)
FF11111111
F111110001
5001010000
8010000000
0300000011
9F10011111
FC11111100

然后按 ADTS 结构解析:

前7字节的二进制是这样的:11111111 11110001 01010000 10000000 00000011 10011111 11111100 然后下面就按每个字段的位宽进行查看 转为十进制然后比对

字段名位(bit)值(二进制)十进制含义
syncword121111111111110xFFF同步字正确
ID100MPEG-4
layer2000保留,必须为0
protection_absent111✅ 无 CRC 校验
profile_ObjectType2011✅ AAC LC(Low Complexity)
sampling_frequency_index401004✅ 采样率 = 44100Hz(查表)
private_bit100✅ 未使用
channel_configuration30102立体声(2 声道
original_copy100当前帧是 拷贝
home100未使用
copyright_identification_bit100无版权标识
copyright_identification_start100非版权序列起始
aac_frame_length13000000001110028(字节)帧总长度
adts_buffer_fullness11111111111110x7FF=20470x7FF码率可变的码流
number_of_raw_data_blocks_in_frame2000每帧含 1aac数据块

这样看可能更直观点:

这个是固定头部的各字段:

bit 0-11  : syncword           111111111111
bit 12    : ID                 0
bit 13-14 : layer              00
bit 15    : protection_absent  1
bit 16-17 : profile            01
bit 18-21 : sf_index           0100   → 44.1 kHz
bit 22    : private_bit        0
bit 23-25 : channel_config     010    → 2 声道
bit 26    : original_copy      0
bit 27    : home               0

这个是可变头部的各字段:

bit 28    : copyright_id_bit   0
bit 29    : copyright_start    0
bit 30-42 : aac_frame_length   0000000011100  → 28字节帧长度(头+后面数据)
bit 43-53 : adts_buffer_fullness 11111111111 → 0x7FF码率可变的码流
bit 54-55 : raw_blocks          00 → 每帧含 1aac数据块

可以得到以下结论:

这是一帧 ADTS-AAC LC 数据,格式为 MPEG-4、44.1 kHz、立体声、无 CRC
帧总长度 28 字节,采用 VBR 码率,且 每帧仅含 1 个 AAC 原始数据块

✨4.1.2 FFmpeg检查分析是否正确

可以通过FFmpeg检查ADTS帧结构是否和分析的一样

ffmpeg -v error -i input.aac -c copy -f adts NUL

这个命令可以看看“这个文件是不是标准的 ADTS”,若文件有错误,FFmpeg 会在 stderr 直接报 “ADTS syncword missing” 或 “CRC mismatch”。无任何报错即说明头信息与你分析的一致,文件可被正常识别。结果证明是标准的ADTS结构。

ffprobe -v error -show_entries stream=sample_rate,profile,channels,channel_layout -select_streams a:0 input.aac
命令片段作用
ffprobeFFmpeg 套件里的“媒体信息探测器”,只读文件、不解码输出。
-v error把日志级别设为 error,只打印真正的错误,忽略 info/debug 信息,输出更干净。
-show_entries stream=sample_rate,profile,channels,channel_layout告诉 ffprobe:只把 stream(流)级别 里的 sample_rateprofilechannelschannel_layout 四个字段打印出来,别的都不显示。
-select_streams a:0在所有流里只选 第 0 条音频流(a:0 = 第 1 条音频流)。
input.aac要探测的文件。

通过ffprobe进行输出结构化信息,可以看到和我们解析的是一样的,aac规格为LC的(对应profile_ObjectType字段),采样率为44100(对应profile_ObjectType字段),channels=2和stereo立体声2声道(对应channel_configuration字段)

✨4.2 C语言代码解析AAC文件

/**
 * 最简单的视音频数据处理示例
 * Simplest MediaData Test
 *
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本项目包含如下几种视音频测试示例:
 *  (1)像素数据处理程序。包含RGB和YUV像素格式处理的函数。
 *  (2)音频采样数据处理程序。包含PCM音频采样格式处理的函数。
 *  (3)H.264码流分析程序。可以分离并解析NALU。
 *  (4)AAC码流分析程序。可以分离并解析ADTS帧。
 *  (5)FLV封装格式分析程序。可以将FLV中的MP3音频码流分离出来。
 *  (6)UDP-RTP协议分析程序。可以将分析UDP/RTP/MPEG-TS数据包。
 *
 * This project contains following samples to handling multimedia data:
 *  (1) Video pixel data handling program. It contains several examples to handle RGB and YUV data.
 *  (2) Audio sample data handling program. It contains several examples to handle PCM data.
 *  (3) H.264 stream analysis program. It can parse H.264 bitstream and analysis NALU of stream.
 *  (4) AAC stream analysis program. It can parse AAC bitstream and analysis ADTS frame of stream.
 *  (5) FLV format analysis program. It can analysis FLV file and extract MP3 audio stream.
 *  (6) UDP-RTP protocol analysis program. It can analysis UDP/RTP/MPEG-TS Packet.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int getADTSframe(unsigned char* buffer, int buf_size, unsigned char* data ,int* data_size){
	int size = 0;

	if(!buffer || !data || !data_size ){
		return -1;
	}

	while(1){
		if(buf_size  < 7 ){
			return -1;
		}
		//Sync words
		if((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0) ){
			size |= ((buffer[3] & 0x03) <<11);     //high 2 bit
			size |= buffer[4]<<3;                //middle 8 bit
			size |= ((buffer[5] & 0xe0)>>5);        //low 3bit
			break;
		}
		--buf_size;
		++buffer;
	}

	if(buf_size < size){
		return 1;
	}

	memcpy(data, buffer, size);
	*data_size = size;

	return 0;
}

int simplest_aac_parser(char *url)
{
	int data_size = 0;
	int size = 0;
	int cnt=0;
	int offset=0;

	//FILE *myout=fopen("output_log.txt","wb+");
	FILE *myout=stdout;

	unsigned char *aacframe=(unsigned char *)malloc(1024*5);
	unsigned char *aacbuffer=(unsigned char *)malloc(1024*1024);

	FILE *ifile = fopen(url, "rb");
	if(!ifile){
		printf("Open file error\n");
		return -1;
	}

	printf("-----+- ADTS Frame Table -+------+\n");
	printf(" NUM | Profile | Frequency| Size |\n");
	printf("-----+---------+----------+------+\n");

	while(!feof(ifile)){
		data_size = fread(aacbuffer+offset, 1, 1024*1024-offset, ifile);
		unsigned char* input_data = aacbuffer;

		while(1)
		{
			int ret=getADTSframe(input_data, data_size, aacframe, &size);
			if(ret==-1){
				break;
			}else if(ret==1){
				memcpy(aacbuffer,input_data,data_size);
				offset=data_size;
				break;
			}

			char profile_str[10]={0};
			char frequence_str[10]={0};

			unsigned char profile=aacframe[2]&0xC0;
			profile=profile>>6;
			switch(profile){
			case 0: sprintf(profile_str,"Main");break;
			case 1: sprintf(profile_str,"LC");break;
			case 2: sprintf(profile_str,"SSR");break;
			default:sprintf(profile_str,"unknown");break;
			}

			unsigned char sampling_frequency_index=aacframe[2]&0x3C;
			sampling_frequency_index=sampling_frequency_index>>2;
			switch(sampling_frequency_index){
			case 0: sprintf(frequence_str,"96000Hz");break;
			case 1: sprintf(frequence_str,"88200Hz");break;
			case 2: sprintf(frequence_str,"64000Hz");break;
			case 3: sprintf(frequence_str,"48000Hz");break;
			case 4: sprintf(frequence_str,"44100Hz");break;
			case 5: sprintf(frequence_str,"32000Hz");break;
			case 6: sprintf(frequence_str,"24000Hz");break;
			case 7: sprintf(frequence_str,"22050Hz");break;
			case 8: sprintf(frequence_str,"16000Hz");break;
			case 9: sprintf(frequence_str,"12000Hz");break;
			case 10: sprintf(frequence_str,"11025Hz");break;
			case 11: sprintf(frequence_str,"8000Hz");break;
			default:sprintf(frequence_str,"unknown");break;
			}


			fprintf(myout,"%5d| %8s|  %8s| %5d|\n",cnt,profile_str ,frequence_str,size);
			data_size -= size;
			input_data += size;
			cnt++;
		}   

	}
	fclose(ifile);
	free(aacbuffer);
	free(aacframe);

	return 0;
}

int main()
{
	simplest_aac_parser((char*)"./audio_chn0.aac");
	return 0;
}

这块代码是雷霄骅大佬的AAC音频码流解析里面的,保存后进行gcc simplest_mediadata_aac.cpp编译,运行结果:

五、个人思考

通过Hex16进制编译器查看aac文件,然后转为2进制根据每个字段的位宽进行分析字段的值和属性,这个方法可以更好的去认识ATDS的固定头部和可变头部,同时也可以借助ffmpeg和前人的一些代码进行输出结构最后验证。

参考链接:

理解AAC音频文件格式:ADIF与ADTS-CSDN博客

视音频数据处理入门:AAC音频码流解析_声码流-CSDN博客

【网络通信 — 直播】音频流编码 — AAC 基础_直播流音频编码格式-CSDN博客

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇