嵌入式音视频开发------AAC编码
1. AAC 编码概述
在嵌入式音视频开发中,AAC(Advanced Audio Coding,高级音频编码)是一种非常常见的有损音频压缩技术,广泛应用于手机、机顶盒、车机、智能摄像头、会议终端、对讲设备以及各类流媒体系统中。AAC 属于 MPEG 音频标准体系的一部分,核心思想是:利用人耳听觉特性,在尽量不明显损伤主观音质的前提下,减少音频数据量。
从工程实现上看,AAC 不是单一格式,而是一个编码家族。常见的包括:
- AAC-LC:最常见,复杂度较低,兼容性最好;
- HE-AAC v1:在 AAC-LC 基础上增加 SBR(频带复制),适合较低码率;
- HE-AAC v2:进一步增加 PS(参数化立体声),适合更低码率立体声;
- AAC-LD / AAC-ELD:面向双向通信和低时延场景;
- xHE-AAC:覆盖更宽码率范围,并强化响度控制与自适应流媒体能力。
对于嵌入式系统来说,AAC 编码的真正难点通常不是"能不能编码",而是:
- 能否在有限 CPU 下实时编码;
- 能否在有限内存下稳定运行;
- 能否满足端到端时延要求;
- 能否在目标平台上保持可接受音质;
- 能否兼顾封装、传输、兼容性和授权成本。
2. 为什么需要进行 AAC 音频压缩处理
2.1 减少存储空间占用
PCM 是未压缩音频,数据量非常大。以 CD 音质音频为例:
- 采样率:44.1 kHz
- 位宽:16 bit
- 声道:2
则码率约为:
text
44,100 × 16 × 2 = 1,411,200 bps ≈ 1411 kbps
文件大小计算公式为:
text
文件大小(Byte) = 比特率(bps) × 时间(s) / 8
简化写法:
text
文件大小(MB) ≈ 比特率(kbps) × 时间(s) / (8 × 1000)
因此,5 分钟未压缩 PCM 音频体积非常可观,而 AAC 在 128 kbps 左右即可获得较好的听感,大幅降低存储压力。AAC 的收益来自"在主观可接受音质下显著降低码率",而不是单纯追求压缩倍数。
2.2 节省传输带宽
在直播、点播、语音通信和远程监控中,网络带宽通常是硬约束。AAC 压缩后可显著降低传输码率,减少网络抖动带来的播放卡顿,并提高多路并发时的系统承载能力。对于流媒体系统而言,音频并不只是"附属数据",它的稳定性直接影响整体体验。
2.3 在较低码率下保持较好音质
AAC 相比更早期的 MP3,在同等码率下通常能提供更好的主观音质,这是它长期成为移动互联网音频主流格式的重要原因之一。其本质在于 AAC 使用了更先进的滤波器组、量化和熵编码机制,并结合心理声学模型去掉听觉上不敏感的信息。
2.4 适配嵌入式产品的功耗与资源约束
嵌入式开发往往需要在以下约束中平衡:
- MCU/SoC 主频有限;
- SRAM/DDR 容量有限;
- 音频链路要求持续实时;
- 功耗预算严格;
- 可能还要与视频编码、网络传输、AI 算法共用资源。
因此,AAC 的意义不仅在于"压小文件",更在于为系统级资源分配创造空间。
3. AAC 的核心特点与优势
3.1 更高的压缩效率
AAC 在相同听感目标下,通常比 MP3 需要更低的码率。这也是它在流媒体、广播和移动端广泛普及的原因。
3.2 更好的主观音质
AAC 对中高频细节、瞬态信息和立体声场的保持通常优于早期格式,尤其是在中低码率下优势更明显。
3.3 支持多种编码工具
AAC 不是只靠"量化压缩"在工作,它通常会配合多个编码工具:
- MDCT:把时域信号变换到频域;
- TNS:时域噪声整形;
- PNS:感知噪声替代;
- SBR:高频重建;
- PS:参数化立体声;
- Huffman 编码:进一步减少比特冗余。
3.4 兼容性强,生态成熟
AAC 已被广泛支持于:
- MP4 / M4A 容器;
- Android MediaCodec;
- Apple AudioToolbox / CoreAudio;
- 广播与流媒体系统;
- 各类软硬件播放器。
4. AAC 标准谱系与常见规格
4.1 AAC-LC(Low Complexity)
AAC-LC 是最常见的 AAC 配置,特点是:
- 复杂度适中;
- 音质和兼容性平衡较好;
- 最适合中高码率音频;
- 几乎是"默认 AAC"代名词。
适用场景:
- 本地录音;
- 视频文件音轨;
- 普通流媒体;
- 对兼容性要求高的嵌入式播放系统。
4.2 HE-AAC(v1)
HE-AAC 在 AAC-LC 上增加了 SBR(Spectral Band Replication),通过低频编码加高频重建方式,在较低码率下仍然保持较好的听感。它特别适合带宽有限场景。
适用场景:
- 网络广播;
- 中低码率音频直播;
- 对带宽敏感的移动端业务。
4.3 HE-AAC v2
HE-AAC v2 在 HE-AAC 基础上增加 PS(Parametric Stereo),进一步压缩立体声信息,在超低码率立体声音频中效果更好。
适用场景:
- 超低码率立体声流媒体;
- 带宽更紧张的移动网络环境。
4.4 AAC-LD(Low Delay)
AAC-LD 不是为"音乐文件压缩"设计的,而是为实时通信设计的。其重点不再是压得多小,而是尽量降低算法时延。研究资料表明,AAC-LD 通过缩短帧长、减少 look-ahead 和削弱 bit reservoir,可以把算法时延压缩到适合双向通话的范围。
适用场景:
- 语音会议;
- 双向对讲;
- 音视频互动系统。
4.5 AAC-ELD(Enhanced Low Delay)
AAC-ELD 可以理解为 AAC-LD 的增强版,进一步引入低时延 SBR 和更适合低时延场景的滤波器组。在较低码率下仍能保持较好通话质量。Fraunhofer 的资料中明确提到,AAC-ELD 相比 AAC-LD 能在更低码率下保持高质量通信体验。
适用场景:
- 高质量语音会议;
- 低时延互动直播;
- 音视频对讲系统。
4.6 xHE-AAC
xHE-AAC 更偏向"现代流媒体工程方案",强调:
- 极宽的码率覆盖范围;
- 对语音和音乐都友好;
- 与自适应码率流媒体结合;
- 强制包含 MPEG-D DRC(动态范围控制)能力。
适用场景:
- 自适应流媒体;
- 广播与OTT音频;
- 复杂终端生态的大规模内容分发。
5. AAC 编码的基本原理
AAC 编码流程大致如下:
text
PCM输入
↓
分帧
↓
加窗
↓
MDCT频域变换
↓
心理声学分析
↓
量化与比特分配
↓
熵编码(Huffman)
↓
封装为 AAC 比特流
5.1 分帧
AAC 是基于帧的编码体系。不同 profile 的帧长度不同:
- AAC-LC:通常 1024 个采样点;
- AAC-LD / ELD:通常 480 或 512 个采样点;
- HE-AAC:内部处理粒度还会更复杂。
这意味着在嵌入式系统中,采集线程、环形缓冲区和编码线程必须围绕"每帧样本数"设计,否则就会引入额外缓存等待与不可控时延。
5.2 频域变换
AAC 通常使用 MDCT(Modified Discrete Cosine Transform) 将时域信号映射到频域。频域表示更适合分析哪些频段"值得保留",哪些频段"可以近似"。MDCT 是 AAC 压缩的核心数学基础之一。
5.3 心理声学模型
AAC 会利用人耳的掩蔽效应:
- 强信号会掩蔽邻近频率的弱信号;
- 某些高频成分对主观听感贡献较小;
- 某些噪声在特定频段内不易被察觉。
因此编码器不会平均对待所有频率成分,而是把有限比特优先给"人耳更敏感"的部分。
5.4 量化与比特分配
编码器需要决定:
- 每个频带分多少比特;
- 哪些频带可以更粗量化;
- 是否启用 TNS/PNS 等工具;
- 在目标码率下怎样平衡失真与复杂度。
5.5 熵编码与比特流封装
量化后的频谱系数会通过 Huffman 编码进一步压缩,然后打包成 AAC 原始数据块,再依据不同封装方式输出为 ADTS、LATM/LOAS,或写入 MP4 中。
6. AAC 文件格式与封装形式
AAC 编码后的数据,本质上是按照 AAC 标准生成的压缩音频帧数据。但在实际使用中,这些数据并不会总是以完全相同的形式保存或传输,而是会根据场景的不同,采用不同的封装方式。
在工程中,经常会听到下面几个概念:
- AAC 裸流(Raw AAC)
- ADIF
- ADTS
- LATM / LOAS
- MP4 / M4A 中的 AAC
很多初学者容易把它们混在一起。实际上,它们的关系可以理解为:
AAC 是编码方式,ADIF/ADTS/LATM/MP4 是 AAC 数据的组织或封装方式。
也就是说,AAC 解决的是"怎么压缩音频",而 ADIF、ADTS 等解决的是"压缩后的数据怎么存、怎么传"。
6.1 AAC 裸流(Raw AAC)
AAC 裸流指的是仅包含 AAC 原始编码数据块的比特流,不包含额外的文件头或传输头信息。
它的特点是:
- 数据最精简;
- 只保留编码后的音频有效载荷;
- 如果没有额外的上下文信息,解码器往往无法直接知道采样率、声道数、Profile 等参数;
- 更适合嵌入在其他容器或协议中使用。
所以,裸 AAC 一般不会单独拿来做通用文件分发,而是常作为底层有效负载存在。
6.2 ADIF 格式
ADIF 的全称是:Audio Data Interchange Format
它是一种面向文件存储的 AAC 数据组织方式。
ADIF 的特点是:
- 整个文件只有一个统一的头部;
- 头部中保存了 AAC 解码需要的重要信息;
- 音频数据紧跟在头部之后连续存放;
- 解码时通常需要从文件开头开始解析。
这意味着 ADIF 更适合:
- 本地文件存储;
- 一次性完整读取的场景;
- 不强调随机接入和中途同步的系统。
但它也有明显限制:
- 如果从流中间开始接收数据,往往无法直接解码;
- 一旦文件前部损坏,整个流的可解析性会受到影响;
- 不适合网络实时传输。
所以,ADIF 更偏"文件交换格式",不适合流媒体实时传输场景。
6.3 ADTS 格式
ADTS 的全称是:Audio Data Transport Stream
它是一种面向传输的 AAC 数据封装方式,也是嵌入式音视频开发中最常见的 AAC 裸流封装形式之一。
ADTS 的最大特点是:
每一帧 AAC 数据前面都会带一个 ADTS Header。
也就是说,ADTS 文件或数据流是由很多"ADTS Header + AAC Raw Data"这样的结构连续组成的。
它的优点非常明显:
- 每一帧都有同步信息;
- 可以从流中任意位置尝试同步;
- 非常适合边收边播;
- 适合网络传输、直播、TS 流、RTP 等实时场景;
- 调试时也更方便,容易从码流中定位帧边界。
这也是为什么在实际开发中,我们经常会看到 .aac 文件其实就是 ADTS 封装的 AAC 数据流。
6.4 ADIF 与 ADTS 的区别
ADIF 和 ADTS 的本质区别,可以概括为:
| 对比项 | ADIF | ADTS |
|---|---|---|
| 头部数量 | 整个文件只有一个头 | 每一帧都有头 |
| 适用场景 | 文件存储 | 流式传输 |
| 是否支持中途加入 | 不适合 | 适合 |
| 随机访问能力 | 较弱 | 较强 |
| 容错性 | 前部出错影响大 | 单帧出错影响相对局部 |
| 嵌入式开发常见程度 | 较少 | 很常见 |
从嵌入式工程角度看:
- 做本地文件归档时,可以使用更高层容器;
- 做实时传输、推流、抓包分析、边采边播时,ADTS 更实用。
6.5 LATM / LOAS
除了 ADIF 和 ADTS,在一些广播、数字电视和特定传输协议中,还会见到 LATM / LOAS 这种封装方式。
它们同样是 AAC 的传输封装方式,但更多出现在:
- 数字广播;
- MPEG-TS 相关系统;
- 某些专用流媒体协议。
对于一般的嵌入式音视频开发入门来说,最常接触的仍然是 ADTS。因此在初期学习阶段,重点掌握 ADTS 即可。
6.6 MP4 / M4A 中的 AAC
很多人看到 .mp4 或 .m4a 文件中是 AAC 音频,就误以为它们等同于 ADIF 或 ADTS,其实并不是。
MP4 / M4A 属于容器格式 ,AAC 只是其中的音频编码格式之一。
也就是说:
- AAC 负责压缩音频;
- MP4 / M4A 负责把音频、视频、时间戳、索引等数据组织起来。
在 MP4/M4A 中,AAC 一般不会直接以 ADTS 形式存储,而是通过容器中的配置信息描述编码参数。
所以要区分两个层次:
- 编码格式:AAC
- 封装容器:MP4/M4A/TS 等
6.7 工程中的理解方式
可以把几种形式简单理解为:
text
PCM 原始音频
↓
AAC 编码
↓
AAC 原始压缩帧
├─ ADIF 封装(单文件头)
├─ ADTS 封装(每帧头)
├─ LATM/LOAS 封装
└─ 封入 MP4 / M4A / TS 等容器
因此,在实际开发里看到"AAC音频",你需要进一步问清楚两件事:
-
它的编码 Profile 是什么?
比如 AAC-LC、HE-AAC、AAC-LD。
-
它是怎么封装的?
比如 ADTS、MP4、TS、LATM。
只有把"编码"和"封装"分开理解,后面分析码流、写头、做封装、调播放器时才不会混乱。
7. ADIF 与 ADTS 详解
在 AAC 的各种封装方式中,ADIF 和 ADTS 是最基础、也最容易在学习阶段接触到的两种形式。其中,ADTS 在嵌入式开发中尤为常见,因此值得单独展开说明。
7.1 ADIF 详解
ADIF(Audio Data Interchange Format)是一种面向文件存储的 AAC 数据格式。
它的核心特点是:
整个 AAC 文件只在最开始位置包含一个 ADIF Header,后续紧跟连续的 AAC 音频数据。
也就是说,ADIF 的组织形式更接近:
text
ADIF Header + AAC Data + AAC Data + AAC Data + ...
ADIF 的优点
-
结构相对紧凑
只保存一个头部,不需要每帧重复保存控制信息。
-
适合完整文件存储
当文件是完整读取、完整解码时,ADIF 是可行的。
ADIF 的缺点
-
必须从头开始解码
如果只拿到中间的一段数据,由于缺少前部头信息,通常无法直接解码。
-
不适合流式传输
网络中途加入、丢包恢复、随机定位都不方便。
-
鲁棒性较差
头部一旦损坏,后面整个数据流都可能无法正确解析。
所以,ADIF 更适合作为"静态文件交换格式",而不是"实时音频传输格式"。
7.2 ADTS 详解
ADTS(Audio Data Transport Stream)是 AAC 在流式传输中最常用的一种封装方式。
ADTS 的结构可以表示为:
text
ADTS Header + AAC Frame
ADTS Header + AAC Frame
ADTS Header + AAC Frame
...
也就是说,每一帧 AAC 原始数据前,都有一个固定格式的 ADTS Header。
这种设计带来几个明显优势:
1)便于同步
每帧头部中都包含同步字(syncword),解码器可以通过同步字快速找到帧边界。
2)适合流式传输
因为每帧都是独立可识别的,所以:
- 可以从中途开始接收;
- 可以较容易从错误中恢复;
- 更适合直播、推流、边采边播。
3)便于调试分析
抓到一段 AAC 数据流时,只要找 0xFFF 同步头,就可以尝试定位帧结构,因此 ADTS 对开发调试非常友好。
这也是为什么在实际项目中,我们经常把 AAC 编码器输出直接组织成 ADTS 流,送给文件保存模块、推流模块或播放器测试。
7.3 ADTS 帧结构
一个完整的 ADTS 帧通常由两部分组成:
text
ADTS Header + AAC Raw Data
其中:
- ADTS Header:描述这一帧的参数和长度;
- AAC Raw Data:真正的 AAC 压缩音频数据。
ADTS Header 通常长度为:
- 7 字节:无 CRC
- 9 字节:有 CRC
实际开发中,大多数情况下使用的是 7 字节头。
7.4 ADTS Header 结构说明
ADTS Header 可分为两部分:
- 固定头(Fixed Header)
- 可变头(Variable Header)
这种划分方式非常重要,因为:
- 固定头中的很多字段在整段音频流里通常是不变的;
- 可变头中的一些字段会随着每一帧变化,比如帧长度。
7.5 ADTS Fixed Header(固定头)
固定头主要描述 AAC 流的基本属性,例如:
- 编码对象类型
- 采样率
- 声道数
- 是否有 CRC
常见字段如下:
| 字段 | 位数 | 含义 |
|---|---|---|
| syncword | 12 | 同步字,固定为 0xFFF |
| ID | 1 | MPEG 标识 |
| layer | 2 | 固定为 00 |
| protection_absent | 1 | 是否省略 CRC,1 表示无 CRC |
| profile | 2 | AAC Profile 类型 |
| sampling_frequency_index | 4 | 采样率索引 |
| private_bit | 1 | 私有位 |
| channel_configuration | 3 | 声道配置 |
| original/copy | 1 | 原始/复制标志 |
| home | 1 | Home 标志 |
这些字段中,最常需要关注的是:
1)syncword
固定为 0xFFF,这是解码器进行帧同步的关键标志。
2)profile
用于表示 AAC 的对象类型,常见值可理解为:
0:Main1:LC2:SSR3:保留
在实际工程中,最常见的是 AAC-LC。
3)sampling_frequency_index
它不是直接写采样率数值,而是写一个索引值。常见关系如下:
| 索引值 | 采样率 |
|---|---|
| 0 | 96000 |
| 1 | 88200 |
| 2 | 64000 |
| 3 | 48000 |
| 4 | 44100 |
| 5 | 32000 |
| 6 | 24000 |
| 7 | 22050 |
| 8 | 16000 |
| 9 | 12000 |
| 10 | 11025 |
| 11 | 8000 |
| 12 | 7350 |
因此,开发时如果采样率是 44100Hz,那么这个字段写的不是 44100,而是索引值 4。
4)channel_configuration
用于标识声道布局,常见值例如:
1:单声道2:双声道
7.6 ADTS Variable Header(可变头)
可变头中包含与当前帧更强相关的信息,常见字段如下:
| 字段 | 位数 | 含义 |
|---|---|---|
| copyright_identification_bit | 1 | 版权标识位 |
| copyright_identification_start | 1 | 版权标识开始 |
| aac_frame_length | 13 | 当前 AAC 帧总长度 |
| adts_buffer_fullness | 11 | 缓冲区满度 |
| number_of_raw_data_blocks_in_frame | 2 | 帧中原始数据块数量 |
这里最重要的是:
1)aac_frame_length
表示当前完整 ADTS 帧的长度,注意这里的长度包括:
- ADTS Header 长度
- AAC Raw Data 长度
也就是说:
text
aac_frame_length = ADTS头长度 + AAC数据长度
如果这个字段写错,播放器往往就无法正确切帧。
2)adts_buffer_fullness
这个字段主要与码率控制和缓冲状态有关。很多 VBR 场景下经常看到特定默认值,工程调试时通常不会首先关注它,但做完整实现时仍要按规范填写。
3)number_of_raw_data_blocks_in_frame
表示当前 ADTS 帧里包含几个 AAC Raw Data Block。
通常最常见的是:0
它表示这一帧只包含 1 个 AAC 原始数据块。