两种 AAC 码流封装详解:Raw(ASC) vs ADTS
一、核心概念
AAC(Advanced Audio Coding)是一种音频压缩编码格式。同样的 AAC 编码数据,在"打包传输"时有两种不同的封装方式,区别在于配置信息(采样率、声道数、profile 等)放在哪里。
二、Raw / ASC(裸流)方式
1. 什么是 Raw AAC
┌─────────┐ ┌─────────┐ ┌─────────┐
│ AAC帧1 │ │ AAC帧2 │ │ AAC帧3 │ ← 只有纯音频数据
└─────────┘ └─────────┘ └─────────┘
- 每一帧只有纯压缩音频数据,没有任何头部信息
- 帧与帧之间没有同步标记
- 码流本身无法自我描述(不知道采样率、声道数等)
2. 什么是 ASC(AudioSpecificConfig)
配置信息被单独抽出来,存放在一个叫 ASC 的数据结构里:
ASC(通常 2 字节,也可能更长):
┌──────────────┬──────────────┬──────────────┐
│ Object Type │ Sample Rate │ Channel │
│ (profile) │ Index │ Config │
│ 5 bits │ 4 bits │ 4 bits │
└──────────────┴──────────────┴──────────────┘
ASC 示例 (常见的 0x12 0x10):
0x12 0x10 = 0001 0010 0001 0000
┌───┐┌──┐┌───┐
00010 0100 0010 ...
AAC LC 44.1kHz 立体声
3. ASC 存放在哪里?
ASC 作为 extradata (额外数据),由容器格式单独提供:
| 容器 | ASC 存放位置 |
|---|---|
| MP4/MOV | esds box 中 |
| FLV | AAC sequence header(首个音频 tag) |
| MKV | CodecPrivate 字段 |
4. 数据流向示意
┌─────────────────┐
│ 容器(MP4/FLV) │
└─────────────────┘
│ │
extradata raw帧数据
(ASC配置) (无头纯数据)
│ │
└────┬───────┘
▼
解码器需要两者结合才能解码
三、ADTS 方式
1. 什么是 ADTS
ADTS = Audio Data Transport Stream(音频数据传输流)
每一帧前面都加一个同步头,配置信息内嵌:
┌──────────┬─────────┐ ┌──────────┬─────────┐
│ADTS头(7/9)│ AAC数据 │ │ADTS头(7/9)│ AAC数据 │
└──────────┴─────────┘ └──────────┴─────────┘
每一帧都自带头部信息
2. ADTS 头结构(7 字节 = 56 bits)
固定头部(fixed header):
┌─────────────┬───────┬──────────┬─────────┐
│ Syncword │ ... │ profile │ sf_index│
│ 0xFFF │ │ 2 bits │ 4 bits │
│ (12 bits) │ │ │ │
└─────────────┴───────┴──────────┴─────────┘
↑ ↑ ↑
同步标记 profile 采样率索引
┌──────────┬──────────────┐
│ channel │ frame_length │
│ config │ (13 bits) │
│ 3 bits │ │
└──────────┴──────────────┘
↑ ↑
声道数 本帧总长度
关键字段说明:
| 字段 | 作用 |
|---|---|
| Syncword (0xFFF) | 同步字,用于在码流中定位帧的起始 |
| profile | AAC 规格(LC/Main/SSR) |
| sampling_freq_index | 采样率索引(如 4=44.1kHz) |
| channel_config | 声道配置 |
| frame_length | 整帧长度(含头),用于找到下一帧 |
注:如果不带 CRC 校验是 7 字节 ,带 CRC 校验是 9 字节。
3. ADTS 的特点:自描述
┌──────────────────────────────┐
│ ADTS 码流 │
│ 每帧头里都包含完整配置信息 │
│ ┌────────────────────────┐ │
│ │ 采样率 ✓ 声道 ✓ │ │
│ │ profile ✓ 帧长度 ✓ │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
↓
不需要任何外部 extradata
解码器读头部即可解码
四、两者对比总表
| 对比项 | Raw / ASC | ADTS |
|---|---|---|
| 每帧是否有头 | ❌ 无 | ✅ 有(7/9字节) |
| 同步字 | ❌ 无 | ✅ 0xFFF |
| 配置信息位置 | 外部 extradata(ASC) | 内嵌每帧头 |
| 是否自描述 | ❌ 否 | ✅ 是 |
| 依赖容器 | ✅ 需要容器提供ASC | ❌ 可独立存在 |
| 额外开销 | 小(仅一次ASC) | 大(每帧+7/9字节) |
| 典型应用 | MP4、FLV、MKV | .aac文件、直播流、TS流 |
五、应用场景与选择
Raw/ASC 适用场景
- 存储到容器(MP4/FLV):容器已能提供配置,再加 ADTS 头是冗余浪费
- 追求最小体积
ADTS 适用场景
- 裸 .aac 文件:双击即可播放(自描述)
- 直播流 / 广播流:接收端可能中途加入,每帧自带配置可立即解码
- MPEG-TS 流:电视广播等
六、实战:相互转换
ASC → ADTS(封装时添加头部)
从 ASC 解析出 profile、采样率、声道,构造 ADTS 头:
c
// 伪代码:构造 ADTS 7字节头
void make_adts_header(uint8_t *header, int frame_len,
int profile, int sf_index, int channels) {
int total = frame_len + 7;
header[0] = 0xFF; // syncword 高8位
header[1] = 0xF1; // syncword低4位+MPEG4+无CRC
header[2] = ((profile - 1) << 6) // profile
| (sf_index << 2) // 采样率索引
| (channels >> 2); // 声道高位
header[3] = ((channels & 3) << 6) // 声道低位
| (total >> 11); // 帧长高位
header[4] = (total >> 3) & 0xFF; // 帧长中间
header[5] = ((total & 7) << 5) | 0x1F; // 帧长低位
header[6] = 0xFC;
}
FFmpeg 中的转换
bash
# 提取裸AAC(带ADTS)从MP4 → 自动加ADTS头
ffmpeg -i input.mp4 -c:a copy output.aac
# 内部使用 bitstream filter:
# aac_adtstoasc : ADTS → ASC(去头)
# 用于将 .aac 封装进 MP4 时
ffmpeg -i input.aac -c:a copy -bsf:a aac_adtstoasc output.mp4
关键过滤器
aac_adtstoasc:把 ADTS 流转成 ASC(去掉每帧头,提取配置生成extradata),常用于 .aac → .mp4。
七、记忆要点总结
Raw/ASC = 纯数据 + 配置另放(靠容器)→ 省空间,但不能独立
ADTS = 数据 + 每帧带头(自描述) → 能独立,但有冗余
一句话理解:
ADTS 就是给每个裸 AAC 帧"贴标签",让它脱离容器也能被识别;而 Raw/ASC 把标签集中放一处,依赖容器统一管理。