音视频学习(五十二):ADTS

什么是ADTS?

ADTS (Audio Data Transport Stream)是一种用于 AAC(Advanced Audio Coding)音频数据传输 的帧封装格式。它常见于 AAC 裸流(raw AAC) 的存储与传输场景,比如在线广播、流媒体推送和文件存储。

它的特点是:

  • 每个 ADTS 帧都包含一个 固定大小的头部(ADTS Header) + AAC 原始数据
  • 支持 流式传输,即使中途加入也可以同步解码。
  • 广泛用于 HTTP 直播(HLS)RTSPMPEG-TS 封装等场景。

一个 ADTS 帧的基本结构如下:

bash 复制代码
[ADTS Header][AAC Raw Data]

ADTS的作用

AAC 编码器输出的 原始数据(RAW) 没有时间戳,也无法直接判断每帧边界。ADTS 头部的主要作用:

  • 同步标识(Sync Word):用于标记帧的起始位置,方便解码器在任意位置开始解码。
  • 音频参数描述:例如采样率、声道数、编码配置等。
  • 帧长度信息:指示当前 AAC 数据的字节数,方便提取和播放。
  • 错误检测(可选):CRC 校验。

ADTS的帧结构

ADTS 帧由 固定头(Fixed Header) + 可变头(Variable Header) 组成(共 7 或 9 字节,如果有 CRC 校验则为 9 字节)。

结构示意图

css 复制代码
ADTS Header (7 or 9 bytes)
┌──────────────┬──────────────────────────────────────────────┐
│ 固定头部     │ syncword、版本、层、保护标志、采样率等        │
├──────────────┼──────────────────────────────────────────────┤
│ 可变头部     │ 帧长度、缓冲区满标志、AAC 原始数据块数等      │
└──────────────┴──────────────────────────────────────────────┘
AAC Raw Data

位分布示意图(无 CRC 时)

css 复制代码
  |<---  Syncword (0xFFF) --->|
  111111111111 ID layer prot_abs profile sfreq priv chcfg orig home
  |   12   | 1 | 2 |    1    |  2  |  4  |  1  |  3  | 1  |  1  |
  cpy_bit cpy_start       frame_length(13)         fullness(11)   blk(2)
  |   1   |    1    |           13 bits          |     11 bits   | 2 |

二进制布局(无 CRC,7 字节)

位数 字段名 含义
12 bit syncword 同步字 0xFFF,标识帧起始
1 bit ID MPEG 版本(0 = MPEG-4,1 = MPEG-2)
2 bit layer 固定为 00
1 bit protection_absent 1 表示无 CRC 校验
2 bit profile AAC Profile(0=Main,1=LC,2=SSR,3=保留)
4 bit sampling_frequency_index 采样率索引(如 4 = 44100Hz)
1 bit private_bit 私有标志位
3 bit channel_configuration 声道数(如 2 = 立体声)
1 bit original_copy 原版/拷贝标志
1 bit home 主页标志
1 bit copyright_identification_bit 版权位
1 bit copyright_identification_start 版权起始位
13 bit frame_length 整个 ADTS 帧的长度(Header + Data)
11 bit adts_buffer_fullness 缓冲区满度(0x7FF 表示码率可变)
2 bit number_of_raw_data_blocks_in_frame 每帧包含的 AAC 原始块数(一般为 0)

ADTS字段解析

Syncword(同步字)

  • 固定为二进制 111111111111(0xFFF),保证帧的唯一定位。

Profile

表示 AAC 的编码类型:

  • 0:Main
  • 1:LC(Low Complexity,常用)
  • 2:SSR
  • 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

Frame Length(帧长度)

  • 单位:字节
  • 计算公式:
ini 复制代码
frame_length = header_length + AAC_raw_data_length
  • 无 CRC 校验时,header_length = 7 字节;有 CRC 校验时,header_length = 9 字节。

AAC解码

总述

csharp 复制代码
输入数据(ADTS/LATM/裸AAC)
        ↓
[1] 数据解析/同步定位
        ↓
[2] 解析 AAC 头信息 (采样率、声道数、Profile)
        ↓
[3] 提取 AAC 码流数据块
        ↓
[4] 频域解码流程
    ├── 逆量化 (Inverse Quantization)
    ├── 频域处理 (TNS, PNS, IS, MS)
    ├── IMDCT(逆修正离散余弦变换)
    └── 短块/长块拼接
        ↓
[5] 立体声处理 / 混合
        ↓
[6] PCM 输出(时域信号)

解码流程

步骤 1:数据解析 / 同步定位

  • 如果是 ADTS 流
    • 搜索 syncword 0xFFF(12bit)来找到帧头位置
    • 读取 aac_frame_length 得到帧大小
  • 如果是 LATM/LOAS 流
    • 按 LATM 格式解析 AudioMuxElement
  • 如果是裸 AAC(.aac):
    • 必须由外部提供配置信息(AudioSpecificConfig)

步骤 2:解析 AAC 头信息

  • 从 ADTS 头中提取:
    • profile(AAC LC/HE/HEv2 等)
    • 采样率(sampling_frequency_index)
    • 声道数(channel_configuration)
  • 初始化解码器状态:
    • 创建滤波器系数
    • 分配窗口缓冲
    • 初始化 SBR(Spectral Band Replication)、PS(Parametric Stereo)等扩展模块(如果有)

步骤 3:提取 AAC 数据块

  • 去掉帧头(ADTS 为 7 或 9 字节)
  • 剩余部分是 AAC 原始数据块(Raw Data Block)
  • 如果 number_of_raw_data_blocks_in_frame > 0,则一帧内有多个音频块,要分别解析

步骤 4:频域解码流程 (AAC 是基于 MDCT 的频域编码)

  1. Huffman 解码
    • 将压缩的频域系数恢复成量化值
  2. 逆量化(Inverse Quantization)
    • 将量化频谱值恢复到浮点频谱
  3. 工具处理(Tool Processing)
    • TNS(Temporal Noise Shaping):时间域噪声整形
    • PNS(Perceptual Noise Substitution):噪声替代
    • IS(Intensity Stereo) / MS(Mid/Side Stereo):立体声压缩技术
  4. IMDCT(Inverse Modified Discrete Cosine Transform)
    • 将频域数据转为时域样本
  5. 窗函数与重叠相加(Overlap-Add)
    • AAC 长块为 1024 样本,短块为 128 样本,通过 OLA 合成连续 PCM

步骤 5:立体声处理 / 混合

  • 如果是多声道 AAC(例如 5.1):
    • 执行声道映射(Channel Mapping)
    • 可选:下混(Downmix)到立体声或单声道
  • 如果是 HE-AAC:
    • SBR 解码:从低采样率重建高频
    • PS 解码:从单声道重建立体声

步骤 6:PCM 输出

  • 最终得到 16bit、24bit 或 32bit 浮点的 PCM 数据
  • 可直接送给音频播放设备(ALSA / WASAPI / OpenSL ES)
  • 或存储为 WAV/RAW PCM 文件

示例(C++ 解析 ADTS Header)

c++ 复制代码
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdint>

struct AdtsHeader {
    int id;                         // MPEG 版本(0=MPEG-4, 1=MPEG-2)
    int profile;                    // AAC Profile
    int sampling_frequency_index;   // 采样率索引
    int sampling_frequency;         // 实际采样率
    int channel_configuration;      // 声道数
    int frame_length;                // 整个ADTS帧长度
    bool protection_absent;         // 是否有CRC
};

// 采样率索引表
static const int sampling_frequencies[] = {
    96000, 88200, 64000, 48000, 44100, 32000,
    24000, 22050, 16000, 12000, 11025, 8000, 7350
};

// 解析 ADTS 头
bool parse_adts_header(const uint8_t* data, AdtsHeader& header) {
    // 检查同步字 0xFFF
    if ((data[0] != 0xFF) || ((data[1] & 0xF0) != 0xF0)) {
        return false;
    }

    header.id = (data[1] & 0x08) >> 3;
    header.protection_absent = data[1] & 0x01;
    header.profile = ((data[2] & 0xC0) >> 6) + 1; // 加1以符合MPEG定义
    header.sampling_frequency_index = (data[2] & 0x3C) >> 2;

    if (header.sampling_frequency_index < 13) {
        header.sampling_frequency = sampling_frequencies[header.sampling_frequency_index];
    } else {
        header.sampling_frequency = 0; // 未知采样率
    }

    header.channel_configuration = ((data[2] & 0x01) << 2) | ((data[3] & 0xC0) >> 6);
    header.frame_length = ((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] & 0xE0) >> 5);

    return true;
}

// 主程序
int main() {
    std::string filename = "test.aac"; // ADTS AAC 文件路径
    std::ifstream file(filename, std::ios::binary);

    if (!file) {
        std::cerr << "无法打开文件: " << filename << std::endl;
        return 1;
    }

    while (true) {
        uint8_t header_buf[7];
        file.read(reinterpret_cast<char*>(header_buf), 7);
        if (file.gcount() < 7) break; // 文件结束

        AdtsHeader header;
        if (!parse_adts_header(header_buf, header)) {
            std::cerr << "未找到同步字,可能不是ADTS流" << std::endl;
            break;
        }

        // 输出解析结果
        std::cout << "Frame: "
                  << " MPEG" << (header.id == 0 ? "4" : "2")
                  << " Profile=" << header.profile
                  << " Fs=" << header.sampling_frequency << "Hz"
                  << " Channels=" << header.channel_configuration
                  << " Length=" << header.frame_length
                  << " CRC=" << (header.protection_absent ? "No" : "Yes")
                  << std::endl;

        // 跳过当前帧剩余部分
        int data_size = header.frame_length - 7;
        file.seekg(data_size, std::ios::cur);
    }

    file.close();
    return 0;
}

总结

  • ADTS 是 AAC 裸流 的帧封装格式,适合流式播放。
  • 帧头信息提供了解码所需的全部参数。
  • 在网络传输中,解码器只需找到同步字并解析帧头,就能从任意位置开始播放。
相关推荐
心一信息20 小时前
非常简单!从零学习如何免费制作一个lofi视频
学习·音视频
阿飞__1 天前
C++使用FFmpeg进行视频推流
c++·ffmpeg·音视频
EasyCVR1 天前
视频汇聚系统EasyCVR调用设备录像保活时视频流不连贯问题解决方案
数据库·ubuntu·音视频·云存储·云端录像
aqi001 天前
FFmpeg开发笔记(八十)使用百变魔音AiSound实现变声特效
android·ffmpeg·音视频·直播·流媒体
音视频牛哥1 天前
从 AI 到实时视频通道:基于模块化架构的低延迟直播全链路实践
人工智能·opencv·yolo·计算机视觉·音视频·大牛直播sdk·ai人工智能
王江奎2 天前
FFmpeg 视频旋转信息处理:3.4 vs 7.0.2
ffmpeg·音视频
EasyGBS2 天前
20250808:EasyGBS 对接大华 ICC 平台问题处理
服务器·音视频·技术分享
音视频牛哥2 天前
音视频直播全链路技术手册:核心术语与实战应用解析
音视频·大牛直播sdk·音视频直播全链路技术手册·音视频术语·音视频专业术语·音视频名词解释·音视频直播术语
aqi002 天前
FFmpeg开发笔记(七十九)专注于视频弹幕功能的国产弹弹播放器
android·ffmpeg·音视频·直播·流媒体