1. AAC 概述
AAC 全称 Advanced Audio Coding(高级音频编码),是由 MPEG 组织制定的有损音频编码标准,1997 年随 MPEG-2 标准推出,2000 年升级为 MPEG-4 AAC,是目前应用最广泛的音频编码格式之一。
1.1 基本参数
- 格式:AAC、AAC-LC、HE-AAC、AAC-ELD 等
- 采样率:8--96 kHz
- 码率:8--576 kbps
- 声道数:支持多达 48 个声道(如 5.1、7.1 环绕声)
1.2 AAC 与 MP3 对比
| 特性 | AAC | MP3 |
|---|---|---|
| 压缩效率 | 更高,同码率音质更好 | 基准 |
| 采样率范围 | 8--96 kHz | 16--48 kHz |
| 声道支持 | 最多 48 声道 | 最多 2 声道(立体声) |
| 编码工具 | TNS、PNS、SBR、PS 等 | 较少 |
| 专利授权 | 较复杂 | 已过期 |
1.3 AAC 的核心编码技术
PNS(Perceptual Noise Substitution),知觉噪声替换模块
当编码器发现类似噪音的信号时,并不对其进行量化,而是作个标记就忽略过去,当解码时再还原出来,这样就提高了效率。在具体操作上,PNS 模块对每个尺度因子带侦测频率 4 kHz 以下的信号成分。如果这个信号既不是音调,在时间上也无强烈的能量变动,就被认为是噪声信号。
SBR(Spectral Band Replication),频段复制技术
音乐的主要频谱集中在低频段,高频段幅度很小,但很重要,决定了音质。对整个频段编码时,若为保护高频就会造成低频段编码过细导致编码效率较低;若只保存低频的主要成分而丢掉高频成分又会损失音质。SBR 把频谱切开,低频单独编码只保存主要成分,提高编码效率;高频单独放大编码,兼顾音质。
PS(Parametric Stereo),参数立体声技术
原本立体声双声道的编码输出是一个声道的两倍,但是两个声道的声音存在某种相似性。PS 存储一个声道的全部信息,然后花较少的字节用参数描述另一个声道的差异部分来提升编码效率。
1.4 AAC 类型演进
MPEG2-AAC → 基础规范(MPEG2 13818-7)
MPEG4-AAC → 扩展规范(MPEG4 14496-3)
MPEG4 HE-AAC → 增加 SBR 工具
MPEG4 HE-AAC v2 → 增加 PS 工具

2、AAC音频两种封装格式
AAC 原始音频流需要经过封装才能存储或传输。根据应用场景不同,AAC 主要采用两种封装格式:
ADIF (Audio Data Interchange Format,音频数据交换格式)
ADTS(Audio Data Transport Stream,音频数据传输流)
2.1 ADTS 格式
ADTS 是一种每帧带头的封装格式,每个 AAC 帧前面都附带一个独立的帧头。用于流传输/实时推送场景(比如RTSP推流、直播),方便拆分帧同步。
ADTS 帧头结构(固定头部 7 字节):
| 字节偏移 | 字段 | 长度(bit) | 说明 |
|---|---|---|---|
| 0--1 | 同步字 | 12 | 固定为 0xFFF,用于帧同步 |
| 1 | MPEG 版本 | 1 | 0=MPEG-4,1=MPEG-2 |
| 1 | 层 | 2 | 固定为 00 |
| 1 | 保护位 | 1 | 0=有 CRC 校验,1=无 CRC |
| 2 | 音频对象类型 | 2 | AAC LC / AAC HE 等 |
| 2--3 | 采样率索引 | 4 | 对应预定义的采样率表 |
| 3 | 私有位 | 1 | 保留位 |
| 3 | 声道配置 | 3 | 声道数及布局 |
| 3--4 | 原始性 | 1 | 0=非原始帧,1=原始帧 |
| 4 | 版权标志位 | 1 | 版权信息 |
| 4 | 版权标志起始位 | 1 | 版权信息 |
| 5--6 | 帧长度 | 13 | 当前帧总长度(含帧头) |
| 6 | 缓冲区 fullness | 11 | 缓冲区填充度 |
| 7 | 帧数 | 2 | 当前帧在缓冲区中的序号 |
ADTS 数据布局:
┌─────────┬──────────────┬─────────┬──────────────┐
│ ADTS头 │ AAC 帧数据 │ ADTS头 │ AAC 帧数据 │
│ (7/9字节)│ │ (7/9字节)│ │
└─────────┴──────────────┴─────────┴──────────────┘
← 第1帧 → ← 第2帧 →
ADTS 格式的特点:
- 每帧独立:每个 AAC 帧都带有完整的帧头,支持随机访问和跳转。
- 同步字
0xFFF:解码器通过搜索同步字来定位帧起始位置,便于错误恢复。 - 支持 CRC 校验:可选添加 2 字节 CRC 校验码(保护位=0 时),增强传输可靠性。
- 适用于流媒体:广泛用于 DTV(数字电视)、DAB(数字音频广播)、网络直播等场景。
错误处理策略:
- 如果出现头部错误 ,解码器需要找到下一个同步字
0xFFF,跳过当前帧。- 如果出现原始数据错误,需要静音当前帧,并找到下一个同步字继续解码。

2.2 ADIF 格式
ADIF 是一种单帧头封装格式,整个文件只有一个头部信息,适用于本地文件存储场景,开发中不常用。
ADIF 头部结构:
ADIF 文件开头包含一个固定长度的头部,用于描述音频数据的全局参数:
| 字段 | 说明 |
|---|---|
| 同步字 | 固定标识,用于识别 ADIF 格式 |
| 采样率 | 音频采样率(如 44100 Hz、48000 Hz) |
| 声道数 | 单声道/双声道/多声道配置 |
| 比特率 | 编码后的平均比特率 |
| 版权标志 | 可选字段,标识版权信息 |
ADIF 数据布局:
┌─────────────────────────────────────────┐
│ ADIF 头部(固定长度) │
├─────────────────────────────────────────┤
│ │
│ AAC 音频帧数据(连续) │
│ │
│ AAC 音频帧数据(连续) │
│ │
│ ...... │
└─────────────────────────────────────────┘
ADIF 格式的特点:
- 头部仅出现一次,后续全是裸 AAC 帧数据,无帧头开销,存储效率高。
- 不支持随机访问,必须从头开始解码,无法直接跳转到中间位置。
- 适用于本地文件 ,如
.aac文件(部分实现采用 ADIF 封装)。
注意:在处理 ADIF 格式的音频数据时,如果出现原始数据错误,部分解码器实现可能会自动切换到 ADTS 解码模式作为容错机制。

2.3 ADIF 与 ADTS 对比总结
| 对比维度 | ADIF | ADTS |
|---|---|---|
| 头部方式 | 单帧头(全局一个) | 每帧带头 |
| 存储效率 | 高(无帧头开销) | 较低(每帧 7--9 字节开销) |
| 随机访问 | 不支持 | 支持 |
| 流式传输 | 不适合 | 非常适合 |
| 错误恢复 | 差(头部损坏则全文件失效) | 好(可跳过损坏帧) |
| 典型应用 | 本地文件存储 | 流媒体、广播、传输 |
2.4 AAC 文件的解码处理流程
无论是 ADIF 还是 ADTS 封装,解码器都需要遵循以下处理流程:
判断文件格式
│
├── 确定为 ADIF → 解析 ADIF 头部信息 → 跳至解码阶段
│
└── 确定为 ADTS → 循环处理每帧:
├── 搜索同步字 0xFFF
├── 解析 ADTS 帧头
├── 若有 CRC 校验 → 进行错误检测
└── 进入解码阶段
解码阶段(每帧处理):
#mermaid-svg-ZwfXRbrwbeOU3pen{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZwfXRbrwbeOU3pen .error-icon{fill:#552222;}#mermaid-svg-ZwfXRbrwbeOU3pen .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZwfXRbrwbeOU3pen .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZwfXRbrwbeOU3pen .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZwfXRbrwbeOU3pen .marker.cross{stroke:#333333;}#mermaid-svg-ZwfXRbrwbeOU3pen svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZwfXRbrwbeOU3pen p{margin:0;}#mermaid-svg-ZwfXRbrwbeOU3pen .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZwfXRbrwbeOU3pen .cluster-label text{fill:#333;}#mermaid-svg-ZwfXRbrwbeOU3pen .cluster-label span{color:#333;}#mermaid-svg-ZwfXRbrwbeOU3pen .cluster-label span p{background-color:transparent;}#mermaid-svg-ZwfXRbrwbeOU3pen .label text,#mermaid-svg-ZwfXRbrwbeOU3pen span{fill:#333;color:#333;}#mermaid-svg-ZwfXRbrwbeOU3pen .node rect,#mermaid-svg-ZwfXRbrwbeOU3pen .node circle,#mermaid-svg-ZwfXRbrwbeOU3pen .node ellipse,#mermaid-svg-ZwfXRbrwbeOU3pen .node polygon,#mermaid-svg-ZwfXRbrwbeOU3pen .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZwfXRbrwbeOU3pen .rough-node .label text,#mermaid-svg-ZwfXRbrwbeOU3pen .node .label text,#mermaid-svg-ZwfXRbrwbeOU3pen .image-shape .label,#mermaid-svg-ZwfXRbrwbeOU3pen .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZwfXRbrwbeOU3pen .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZwfXRbrwbeOU3pen .rough-node .label,#mermaid-svg-ZwfXRbrwbeOU3pen .node .label,#mermaid-svg-ZwfXRbrwbeOU3pen .image-shape .label,#mermaid-svg-ZwfXRbrwbeOU3pen .icon-shape .label{text-align:center;}#mermaid-svg-ZwfXRbrwbeOU3pen .node.clickable{cursor:pointer;}#mermaid-svg-ZwfXRbrwbeOU3pen .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZwfXRbrwbeOU3pen .arrowheadPath{fill:#333333;}#mermaid-svg-ZwfXRbrwbeOU3pen .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZwfXRbrwbeOU3pen .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZwfXRbrwbeOU3pen .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZwfXRbrwbeOU3pen .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZwfXRbrwbeOU3pen .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZwfXRbrwbeOU3pen .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZwfXRbrwbeOU3pen .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZwfXRbrwbeOU3pen .cluster text{fill:#333;}#mermaid-svg-ZwfXRbrwbeOU3pen .cluster span{color:#333;}#mermaid-svg-ZwfXRbrwbeOU3pen div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZwfXRbrwbeOU3pen .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZwfXRbrwbeOU3pen rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZwfXRbrwbeOU3pen .icon-shape,#mermaid-svg-ZwfXRbrwbeOU3pen .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZwfXRbrwbeOU3pen .icon-shape p,#mermaid-svg-ZwfXRbrwbeOU3pen .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZwfXRbrwbeOU3pen .icon-shape .label rect,#mermaid-svg-ZwfXRbrwbeOU3pen .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZwfXRbrwbeOU3pen .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZwfXRbrwbeOU3pen .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZwfXRbrwbeOU3pen :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 无噪声解码
(哈夫曼解码)
反量化
(Dequantize)
联合立体声
(Joint Stereo)
知觉噪声替换
(PNS)
瞬时噪声整形
(TNS)
IMDCT
(反离散余弦变换)
频段复制
(SBR)
输出 PCM 码流
各模块作用简述:
- 无噪声解码(Huffman Decoding):对 AAC 编码后的哈夫曼码流进行解码,还原量化后的频域系数。
- 反量化(Dequantize):将量化后的系数还原为实际的频域值。
- 联合立体声(Joint Stereo):利用左右声道间的冗余信息,恢复立体声信号。
- 知觉噪声替换(PNS):对噪声成分进行参数化重建,提升编码效率。
- 瞬时噪声整形(TNS):在频域对瞬态信号进行整形,减少预回声效应。
- IMDCT:将频域信号转换回时域信号。
- 频段复制(SBR):对高频信号进行重建,补偿高频信号的丢失(HE-AAC 特性)。
最终,解码器输出左右声道的 PCM 码流,由主控模块放入输出缓冲区,送至声音播放设备进行播放。
2.5 AAC编码核心原理
AAC利用人耳听觉特性实现高效压缩,核心技术:
听觉掩蔽:去除人耳不敏感的频率成分(比如大音量会掩盖同区域小音量信号)
时域噪声整形:把量化噪声转移到人耳不敏感的频段,听感更干净
MDCT变换:把时域音频转换到频域处理,压缩效率更高
常见的AAC编码Profile:
AAC-LC:低复杂度,音质好,是目前绝大多数场景的标配(低延迟、硬件支持好)
HE-AAC/HE-AACv2:适合极低码率(比如128kbps以下),用了SBR技术增强低频,适合语音广播
2.6 参考实现
AAC 解码的 C 语言参考实现可参考以下资源:
- AAC 解码器 C 语言实现示例
- FAAD2(Freeware Advanced Audio Decoder 2)开源项目
- FFmpeg 中的 AAC 解码模块(
libavcodec/aacdec.c)
3. AAC 编码实战
最常用的是基于FFmpeg/libfdk_aac开发,步骤如下:
3.1 环境配置(linux下交叉编译)
如果要使用高质量的libfdk_aac,先安装/编译依赖:
Ubuntu主机安装依赖
bash
sudo apt-get install libfdk-aac-dev
交叉编译RV1126版本时,在配置FFmpeg时添加参数:
--enable-libfdk-aac --enable-nonfree
验证集成:
运行ffmpeg -codecs | grep aac,输出包含libfdk_aac说明配置成功。
3.2 基础解码流程(从AAC裸流到PCM)
完整流程对应代码逻辑:
- 读取数据:从文件/网络缓冲区读取AAC数据
- 帧同步:查找ADTS同步头0xFFF,分割出完整的一帧AAC数据
- 参数提取:从ADTS头中解析出采样率、声道数、帧长度
- 初始化解码器:使用FFmpeg/FAAD2初始化解码上下文
- 循环解码:把每帧AAC送入解码器,输出PCM音频数据
- 后处理:PCM可以送去播放、重编码或者和视频封装
简单ADTS帧解析C代码示例
cpp
// ADTS头结构体定义
typedef struct {
uint16_t syncword; // 同步字0xFFF
uint8_t profile; // 编码规格
uint8_t sampling_idx; // 采样率索引
uint8_t channel_cfg; // 声道配置
uint16_t frame_length; // 整个帧长度
} ADTSHeader;
bool parse_adts_header(const uint8_t* data, ADTSHeader* header) {
// 校验同步字
header->syncword = (data[0] << 4) | (data[1] >> 4);
if (header->syncword != 0xFFF) return false;
// 解析核心参数
header->profile = (data[2] >> 6) & 0x3;
header->sampling_idx = (data[2] >> 2) & 0xF;
header->channel_cfg = ((data[2] & 0x1) << 2) | (data[3] >> 6);
// 计算帧长度
header->frame_length = ((data[3] & 0x3) << 11) | (data[4] << 3) | (data[5] >> 5);
return true;
}
4、开发常见问题与优化
- 兼容性问题:嵌入式硬件解码优先只支持AAC-LC,尽量不要用HE-AAC除非明确支持
- 流同步问题:推流/推流时如果出现杂音,首先检查ADTS同步字检测是否正确,添加长度合法性校验避免缓冲区溢出
- 性能优化:批量解析多帧、复用缓冲区减少内存分配,嵌入式低端平台优先开启硬件解码(RK RV1126支持AAC硬件编解码)
- 如果在RV1126平台使用:可以直接使用MPP提供的硬件AAC编解码接口,比软解码节省CPU占用。