AAC ADTS(Audio Data Transport Stream)帧格式
ADTS 帧结构图
复制代码
┌─────────────────────────────────────────────────────────┐
│ ADTS 帧 │
├─────────┬─────┬─────┬─────┬─────┬─────┬─────┬───────────┤
│ │ │ │ │ │ │ │ │
│同步字 │帧头 │帧头 │帧头 │帧头 │帧头 │帧头 │ │
│(12 bits)│(16 │(16 │(16 │(16 │(16 │(16 │ AAC │
│ │bits)│bits)│bits)│bits)│bits)│bits)│ 载荷数据 │
│ │ │ │ │ │ │ │ │
├─────────┼─────┴─────┴─────┴─────┴─────┴─────┼───────────┤
│ syncword│ header │ raw data │
│ │ (56 bits) │ block │
└─────────┴───────────────────────────────────┴───────────┘
详细的 ADTS 帧头结构(7字节/56位)
字节布局:
复制代码
字节 0-1: ┌─────────┬─────────┬─────────┬─────────┐
│ syncword │ MPEG版本│ 层 │ 保护 │
│ (12 bits)│ (1 bit) │ (2 bits)│ (1 bit) │
└─────────┴─────────┴─────────┴─────────┘
字节 2: ┌─────────┬─────────┬─────────┬─────────┐
│ 配置索引 │ 采样率 │ 私有位 │ 声道数 │
│ (2 bits)│ (4 bits)│ (1 bit) │ (3 bits)│
└─────────┴─────────┴─────────┴─────────┘
字节 3-4: ┌─────────┬─────────┬─────────┬─────────┐
│ 原始度 │ 帧长度 │ 帧长度 │ 帧长度 │
│ (1 bit) │ (高位) │ (中位) │ (低位) │
│ │ (2 bits)│ (8 bits)│ (3 bits)│
└─────────┴─────────┴─────────┴─────────┘
字节 5-6: ┌───────────────────────────────────────┐
│ 缓冲区满度 (11 bits) │
│ + 帧数 (2 bits) │
│ + CRC校验 (16 bits) [可选] │
└───────────────────────────────────────┘
每个字段的详细说明
1. 同步字(Syncword,12位)
- 固定值:
0xFFF (二进制:111111111111)
- 用于帧同步,标志ADTS帧的开始
2. MPEG版本(1位)
3. 层(Layer,2位)
4. 保护位(Protection Absent,1位)
1 = 没有CRC校验
0 = 有CRC校验(16位CRC在帧头末尾)
5. 配置索引(Profile,2位)
复制代码
00 = AAC Main
01 = AAC LC (Low Complexity) - 最常用
10 = AAC SSR (Scalable Sample Rate)
11 = AAC LTP (Long Term Prediction)
6. 采样率索引(Sampling Frequency Index,4位)
复制代码
0000 = 96000 Hz 1000 = 8000 Hz
0001 = 88200 Hz 1001 = 7350 Hz
0010 = 64000 Hz 1010 = Reserved
0011 = 48000 Hz 1011 = Reserved
0100 = 44100 Hz 1100 = Reserved
0101 = 32000 Hz 1101 = Reserved
0110 = 24000 Hz 1110 = Reserved
0111 = 22050 Hz 1111 = Escape value
7. 私有位(Private Bit,1位)
8. 声道配置(Channel Configuration,3位)
复制代码
000 = 未定义/自定义
001 = 1声道:中置
010 = 2声道:左、右
011 = 3声道:左、右、中置
100 = 4声道:左、右、中置、环绕
101 = 5声道:左、右、中置、左环绕、右环绕
110 = 5.1声道:左、右、中置、左环绕、右环绕、LFE
111 = 7.1声道:左、右、中置、左环绕、右环绕、左后环绕、右后环绕、LFE
9. 原始度/版权(Originality/Copyright,1位)
10. 帧长度(Frame Length,13位)
- 表示整个ADTS帧的长度(包括头部)
- 单位:字节
- 计算公式:
帧长度 = (protection_absent ? 7 : 9) + AAC数据长度
11. 缓冲区满度(Buffer Fullness,11位)
- 表示编码器缓冲区的满度
- 值越大表示缓冲区越满
- 固定比特率(CBR)时通常为
0x7FF
12. 帧数(Number of AAC Frames,2位)
- 每个ADTS帧中包含的AAC帧数
- 通常为
0(每ADTS帧包含1个AAC帧)
13. CRC校验(可选,16位)
- 仅当保护位为
0 时存在
- 对整个ADTS帧(包括头部)进行CRC校验
C++ 代码实现:ADTS头生成
cpp
复制代码
#include <cstdint>
#include <cstring>
class ADTSHeader {
private:
uint8_t header[7];
public:
// 生成ADTS头
void generate(int sample_rate, int channels, int aac_length, int profile = 1) {
// 默认:AAC LC, 无CRC保护
// 采样率索引
uint8_t freq_idx = 4; // 默认44100Hz
switch (sample_rate) {
case 96000: freq_idx = 0; break;
case 88200: freq_idx = 1; break;
case 64000: freq_idx = 2; break;
case 48000: freq_idx = 3; break;
case 44100: freq_idx = 4; break;
case 32000: freq_idx = 5; break;
case 24000: freq_idx = 6; break;
case 22050: freq_idx = 7; break;
case 16000: freq_idx = 8; break;
case 12000: freq_idx = 9; break;
case 11025: freq_idx = 10; break;
case 8000: freq_idx = 11; break;
case 7350: freq_idx = 12; break;
}
// 声道配置
uint8_t chan_config = channels;
if (channels == 1) chan_config = 1; // 单声道
else if (channels >= 2) chan_config = 2; // 立体声
// 计算帧长度(头部7字节 + AAC数据长度)
uint32_t frame_length = aac_length + 7;
// 构造ADTS头
// 字节1:同步字高8位 + MPEG版本 + 层 + 保护位
header[0] = 0xFF; // 同步字高8位
// 字节2:同步字低4位 + 配置索引 + 采样率索引高1位
header[1] = 0xF1; // 同步字低4位(1111) + 保护位(1) + 层(00) + MPEG-4(0)
header[1] |= ((profile & 0x03) << 6); // 设置配置索引
// 字节3:采样率索引低3位 + 私有位 + 声道配置高1位
header[2] = ((freq_idx & 0x0F) << 2); // 采样率索引
header[2] |= ((chan_config & 0x07) >> 1); // 声道配置高1位
// 字节4:声道配置低2位 + 原始度 + 帧长度高2位
header[3] = ((chan_config & 0x07) << 6); // 声道配置低2位
header[3] |= ((frame_length & 0x1800) >> 11); // 帧长度高2位
// 字节5:帧长度中8位
header[4] = ((frame_length & 0x07F8) >> 3); // 帧长度中8位
// 字节6:帧长度低3位 + 缓冲区满度高3位
header[5] = ((frame_length & 0x0007) << 5); // 帧长度低3位
header[5] |= 0x1F; // 缓冲区满度高3位(设为最大值)
// 字节7:缓冲区满度低8位 + 帧数
header[6] = 0xFC; // 缓冲区满度低8位 + 帧数(0)
}
// 获取ADTS头数据
const uint8_t* getHeader() const {
return header;
}
// 获取ADTS头长度(固定7字节)
static constexpr int getHeaderSize() {
return 7;
}
// 打印ADTS头信息
void printInfo() const {
printf("ADTS Header:\n");
printf(" Syncword: 0xFFF\n");
printf(" Protection absent: %d\n", (header[1] & 0x01));
printf(" Profile: %d\n", (header[1] >> 6) & 0x03);
printf(" Sampling freq index: %d\n", (header[2] >> 2) & 0x0F);
printf(" Channel config: %d\n", ((header[2] & 0x01) << 2) | ((header[3] >> 6) & 0x03));
printf(" Frame length: %d\n",
((header[3] & 0x03) << 11) |
(header[4] << 3) |
((header[5] >> 5) & 0x07));
}
};
使用示例
cpp
复制代码
// 在编码AAC时添加ADTS头
void encodeWithADTS(AVCodecContext* ctx, AVPacket* pkt, FILE* output) {
// 编码得到AAC数据包
// ...
// 生成ADTS头
ADTSHeader adts;
adts.generate(ctx->sample_rate,
ctx->channels, // 或 ctx->ch_layout.nb_channels (新版本)
pkt->size,
1); // AAC LC profile
// 写入ADTS头
fwrite(adts.getHeader(), 1, ADTSHeader::getHeaderSize(), output);
// 写入AAC数据
fwrite(pkt->data, 1, pkt->size, output);
}
ADTS与原始AAC的区别
ADTS格式的优点:
- 自包含:每个帧都包含解码所需的所有信息
- 流式友好:适合网络流媒体传输
- 容错性好:通过同步字可以重新同步
- 无需全局头:可以在任意位置开始解码
原始AAC格式:
- 没有ADTS头,只有纯AAC数据
- 需要外部提供配置信息(采样率、声道数等)
- 更节省空间(每个帧节省7字节)
- 需要容器格式(如MP4、TS)来携带元数据
ADTS在FFmpeg中的使用
cpp
复制代码
// 设置编码器标志
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
// 在编码时检查是否需要添加ADTS头
void writeAudioFrame(AVCodecContext* ctx, AVPacket* pkt, FILE* output) {
if (ctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
// 需要手动添加ADTS头
uint8_t adts_header[7];
get_adts_header(ctx, adts_header, pkt->size);
fwrite(adts_header, 1, 7, output);
}
fwrite(pkt->data, 1, pkt->size, output);
}