跳到正文

音视频数据流基础 · 第 2 篇

音频流基础:PCM、WAV 与 AAC 编码深度解析

目录1. 音频流转全链路回顾2. 探秘声音的数字躯壳:PCM2.1 采样定理与核心参数2.2 PCM 数据体积与计算规则2.3 常见的物理存储交错模式3. 给裸奔的数据穿上衣服:WAV 容器3.1 为什么我们需要 WAV?3.2 拆解 RIFF/WAVE 文件头3.3 实战:在十六进制中辨认 WAV4. 极致的听觉欺骗:AAC 编码与心理声学4.1 为什么 AAC 能干掉 MP3?4.2 常见 Profile 与生产环境参数5. AAC 的运输载体:ADTS 帧头深度拆解5.1 ADTS 7 字节 Header 逐字段解析5.2 灵魂拷问:既然浪费带宽,为何还要带 ADTS 头?5.3 容器差异:MP4 中的 AudioSpecificConfig6. 纵向对比总结:PCM、WAV、AAC、MP4 到底是什么关系?7. 常用 FFmpeg 音频处理命令速查

1. 音频流转全链路回顾

在开始枯燥的参数解析之前,我们先在脑海中建立音频数据流转的宏观链路。从你对着麦克风说话,到观众在耳机里听到声音,音频数据经历了如下蜕变:

麦克风采集
  → 得到 PCM 原始音频(体积巨大)
  → 送入编码器进行 AAC 压缩(体积骤减)
  → 封装为 ADTS 裸流,或装进 MP4 / FLV 等容器
  → 网络传输
  → 客户端解封装
  → AAC 解码还原
  → 得到 PCM 数据
  → 送入声卡播放

在这个链路中,PCM 是声音最原始的物理切片,而 AAC 则是为了网络传输而发明的“听觉欺骗”压缩魔法。


2. 探秘声音的数字躯壳:PCM

PCM(Pulse Code Modulation,脉冲编码调制) 是一种将连续的模拟声波,转换为计算机能够理解的离散数字样本的方法。这项技术早在上世纪 40 年代就由贝尔实验室奠定了理论基础。

2.1 采样定理与核心参数

要理解 PCM,必须掌握以下三个核心物理参数:

参数含义常见工程取值
Sample Rate(采样率)一秒钟内对声波进行多少次切片记录44100 Hz、48000 Hz
Bit Depth(位深)每次切片记录时,用多少个 bit 来表示声音的振幅大小16-bit、24-bit、32-bit float
Channels(声道数)同时记录多少条独立的声音轨迹1(单声道)、2(立体声)

硬核追问:为什么 CD 音质的采样率偏偏是 44.1 kHz? 根据奈奎斯特-香农采样定理,要想无失真地从离散样本中重建出原始波形,采样率必须大于等于信号最高频率的 2 倍。 人类耳朵能听到的极限频率大约是 20 kHz。因此,采样率至少要是 40 kHz。当年为了同时兼容 PAL 和 NTSC 两种电视制式的录像带帧率整除,最终将 CD 的标准定在了 44.1 kHz。同理,早期电话为了节省带宽,只保留了人声核心频率(4 kHz 以内),所以电话的采样率通常只有 8 kHz。

位深(Bit Depth)的物理意义: 16-bit 意味着把声音的振幅从静音到震耳欲聋划分成了 2¹⁶ = 65536 个等级。等级越细腻(如 24-bit 的 1677 万个等级),记录的声音细节越丰富,底噪(量化噪声)也就越小。

2.2 PCM 数据体积与计算规则

PCM 数据是毫无压缩的纯净水,它的体积计算极其简单粗暴:

码率 (bps) = 采样率 × 位深 × 声道数 体积 (Bytes) = 码率 × 时长(秒) / 8

计算举例: 一首 3 分钟(180秒)的标准 CD 音质歌曲(44.1kHz / 16-bit / 双声道),其裸 PCM 体积为: 44100 × 16 × 2 × 180 / 8 = 31,752,000 Bytes ≈ 30.2 MB 仅仅 3 分钟就占用了 30 MB,这在流媒体时代是完全不可接受的,这也是为什么我们需要引入 AAC 等编码技术。

2.3 常见的物理存储交错模式

在内存中,双声道 PCM 的排列方式通常有两种:

  • 交错模式(Interleaved):像拉链一样咬合,L R L R L R...(绝大多数设备和底层 API 默认采用这种格式)。
  • 平铺模式(Planar):各自独立存放,先存满左声道,再存右声道 LLLLL... RRRRR...(FFmpeg 内部处理时常用此格式,对应带 p 的后缀,如 fltp)。

3. 给裸奔的数据穿上衣服:WAV 容器

3.1 为什么我们需要 WAV?

想象一下,你手里有一个纯 .pcm 文件,当你把它丢进播放器时,播放器是一脸懵逼的——它完全不知道这个文件是 44.1kHz 还是 48kHz,是单声道还是双声道。 PCM 就像是一堆纯粹的照片像素点,而 WAV 就像是给这堆像素点套上了一个相框,并在相框的背面写上了说明书。

严格来说,WAV 不是一种编码算法,而是一个基于 RIFF 标准的容器格式,它最常见的用途就是用来装 PCM 裸数据。

3.2 拆解 RIFF/WAVE 文件头

一个标准的 WAV 文件,其头部(Header)通常占用 44 个字节。极其重要的一点:WAV 容器头部的数值数据是严格按照小端模式(Little-Endian)存储的。

RIFF Header (12 bytes)
  ChunkID:       "RIFF"           (4 bytes) 默认标志
  ChunkSize:     文件总大小 - 8     (4 bytes)
  Format:        "WAVE"           (4 bytes) 默认标志

fmt Chunk (24 bytes,记录音频格式元数据)
  Subchunk1ID:   "fmt "           (4 bytes) 一般为"fmt "
  Subchunk1Size: 16               (4 bytes) fmt块大小,不包含 ID 和 Size 字段本身
  AudioFormat:   1                (2 bytes) 音频格式(1=PCM, 3=IEEE float, 6=A-law, 7=μ-law)
  NumChannels:   1 / 2            (2 bytes) 声道数(1=mono, 2=stereo)
  SampleRate:    44100 / 48000    (4 bytes) 采样率
  ByteRate:      ...              (4 bytes) 每秒字节数 = SampleRate × NumChannels × (BitsPerSample/8)
  BlockAlign:    ...              (2 bytes) 块对齐 = NumChannels × (BitsPerSample/8)
  BitsPerSample: 16 / 24 / 32     (2 bytes) 位深度

------
(注:在 fmt chunk 与 data chunk 之间,可能还夹杂着 list chunk、fact chunk 等可选分块。它们均遵循 RIFF 通用的 Size+Type+Data 结构)
------

data Chunk (8 bytes + 实际数据)
  Subchunk2ID:   "data"           (4 bytes) 默认标志
  Subchunk2Size: PCM 数据大小       (4 bytes) 下面挂载的真实 PCM 数据的大小
  Data:          PCM 原始数据       (WAV 里的多声道 PCM 一般采用 interleaved 交错存储,如 L0 R0 L1 R1...)

3.3 实战:在十六进制中辨认 WAV

如果我们用 010 Editor 或 HexFiend 打开一个 WAV 文件,你能非常清晰地看到 RIFF、WAVE、fmt 和 data 这几个标志性的 ASCII 锚点。

010 Editor WAV Header 解析 (图:清晰的 RIFF 分块结构,播放器正是通过读取这些块来正确初始化声卡)


4. 极致的听觉欺骗:AAC 编码与心理声学

4.1 为什么 AAC 能干掉 MP3?

AAC(Advanced Audio Coding) 诞生于 1997 年。它的设计初衷就是为了干掉 MP3。 AAC 的核心思想并不是“无脑压缩波形”,而是利用了极其精妙的 “心理声学模型(Psychoacoustic Model)” 进行了彻头彻尾的听觉欺骗:

  1. 频域变换(MDCT):把时域的声波变换到频域,暴露出声音的频率成分。
  2. 同时掩蔽(Simultaneous Masking):在同一个时刻,如果有一个震耳欲聋的鼓声,那么它旁边频率极近的一声微弱叹息,人耳是根本听不到的。AAC 编码器会果断把那声叹息的数据扔掉,不分配任何 bit。
  3. 时间掩蔽(Temporal Masking):在一声巨响发生前后的极短时间内(前向 5ms,后向 50-200ms),人耳依然处于“致盲”状态,这期间的细微声音同样会被丢弃。

凭借更精细的滤波器组和优化的编码策略,在同等听感下,128 kbps 的 AAC 能够媲美 192 kbps 的 MP3。

4.2 常见 Profile 与生产环境参数

AAC 并非一种单一算法,而是一组工具集的统称,主要分为以下几个 Profile:

Profile原理机制适用场景
AAC-LCLow Complexity(低复杂度)。标准的 AAC 核心。最通用,兼容性无敌,广泛用于直播、点播。
HE-AACLC + SBR(频段复制)。高频部分只记录特征,由解码器模拟生成。低码率音乐流媒体(如 64kbps)。
HE-AAC v2HE-AAC + PS(参数立体声)。彻底丢弃一个声道,只记录左右声道差异参数。极低码率的极限压榨(如 32kbps 立体声)。
AAC-LD/ELDLow Delay。极度缩短算法带来的缓冲延迟。实时通信、视频会议、连麦。

在流媒体直播工程中,最黄金的搭配方案是:AAC-LC, 44.1k/48k, 2ch, 码率96k~128k。


5. AAC 的运输载体:ADTS 帧头深度拆解

在了解 H.264 时,我们知道 NALU 依赖起始码(Start Code)来分割。对于 AAC 裸流(.aac)而言,它的“分割器”叫做 ADTS(Audio Data Transport Stream)。

5.1 ADTS 7 字节 Header 逐字段解析

每一帧独立的 AAC 压缩数据前面,都会固定拼上一个 7 字节(如果带 CRC 校验则是 9 字节)的 ADTS Header。 在十六进制查看器中,它最大的特征就是以 0xFFF (12 bit 的连续 1) 作为同步字(Syncword)。

010 Editor ADTS Header 解析
010 Editor ADTS Header 解析

这 7 个字节(56 bits)的内部结构非常紧凑。在网络流媒体标准中,ADTS 头部数据是严格按照大端模式(Big-Endian)进行位(bit)级别拼接和读取的:

-- 固定不变的属性(28 bits,通常每帧相同) --
syncword                  12 bits  固定 0xFFF,用于帧同步。实际抓包中,开头 2 字节常见 FF F1 (MPEG-4, 无CRC) 或 FF F9 (MPEG-2, 无CRC)
ID                         1 bit   MPEG 标识,通常 0 表示 MPEG-4,1 表示 MPEG-2
layer                      2 bits  固定 00
protection_absent          1 bit   0=有 CRC校验(9字节头), 1=无 CRC校验(7字节头)
profile                    2 bits  AAC Profile,0=Main, 1=LC, 2=SSR, 3=LTP
sampling_frequency_index   4 bits  采样率索引(见下表)
private_bit                1 bit   保留位
channel_configuration      3 bits  声道数(0=ADTS内部定义, 1=mono单声道, 2=stereo立体声)
original_copy              1 bit   
home                       1 bit   

-- 每帧可变的属性(28 bits,逐帧变动) --
copyright bits             2 bits  版权信息
aac_frame_length          13 bits  ADTS 头 + AAC 数据 的总字节长度
adts_buffer_fullness      11 bits  代表 VBR 或 CBR。大多数软件忽略此配置,0x7FF 代表 VBR 可变码率
number_of_raw_data_blocks  2 bits  通常为 0,表示当前 ADTS 包含 1 个原始 AAC 数据帧

采样率索引(sampling_frequency_index)速查表: 在解析 ADTS 时,拿到这个 4 bit 的枚举值,去查表就能知道真实的采样率:

0 → 96000 Hz    4 → 44100 Hz    8 → 16000 Hz
1 → 88200 Hz    5 → 32000 Hz    9 → 12000 Hz
2 → 64000 Hz    6 → 24000 Hz    10 → 11025 Hz
3 → 48000 Hz    7 → 22050 Hz    11 → 8000 Hz

5.2 灵魂拷问:既然浪费带宽,为何还要带 ADTS 头?

问题:以 AAC-LC 48kHz 为例,一帧大约包含 1024 个样本,压缩后 Payload 大小在几百字节左右。每帧都额外塞 7 个字节的 ADTS 头,难道不是在浪费网络带宽吗?

解答:这 7 个字节的开销(不到 1%)换来的是极其珍贵的自同步能力。 在不稳定的网络流媒体(如 HLS 切片、TS 广播流)中,客户端可能随时从中间切入播放,或者遇到网络丢包。一旦丢包,播放器只需往后疯狂扫描寻找 0xFFF,就能立刻重新对齐帧边界,满血复活,不需要依赖任何全局头文件! 这就是为什么 .aac 裸流、MPEG-TS、FLV 经常使用 ADTS 的原因。

5.3 容器差异:MP4 中的 AudioSpecificConfig

如果你把 AAC 封装进 .mp4 文件里,那么 ADTS 头就光荣下岗了。 MP4 作为一个高级的结构化容器,它会在全局的 moov/trak/mdia/minf/stbl/stsd 盒子中,存入一个叫做 AudioSpecificConfig (ASC) 的微小数据结构(通常只有 2 个字节)。

AudioSpecificConfig (2 Bytes 示例结构):
┌───────────────┬──────────────┬────────────┐
│ ObjectType    │ samplingFreq │ chanConfig │
│ 5 bit (2=LC)  │ 4 bit (4=44k)│ 4 bit      │
└───────────────┴──────────────┴────────────┘

解码器在读取 MP4 时,只需在开头读取一次 ASC 初始化好解码器,后续读取的媒体帧就全是纯净的 AAC Payload 了,做到了真正的零开销。


6. 纵向对比总结:PCM、WAV、AAC、MP4 到底是什么关系?

很多初学者容易被这四个英文单词绕晕,我们最后做个总结表格:

文件 / 术语本质定位是否压缩?是否自带格式元数据?
PCM (.pcm)原始波形。没有任何外衣的裸数据。否,体积巨大否,播放器无法直接解析,全是盲猜。
WAV (.wav)纯净水瓶。专门用来装 PCM 的容器。否(绝大多数情况)是,带有 RIFF 头,双击就能精准播放。
AAC浓缩药丸。高科技压缩后的核心音频流。是,体积极小/
ADTS (.aac)胶囊外壳。AAC流在裸奔时为了安全套上的头。/是,逐帧携带。丢包也能瞬间恢复。
MP4 (.m4a)大集装箱。能同时装视频和 AAC 的高级箱子。/是,全局存了一份 ASC,内部帧无需再带头。

7. 常用 FFmpeg 音频处理命令速查

在日常开发排查中,熟练掌握音频的抽离和转换是基本功:

# 1. 提取 AAC(保持原始编码,不重新压缩):
ffmpeg -i input.mp4 -vn -c:a copy output.aac

# 2. 从视频中提取音频并转为 WAV(解压缩为 PCM):
ffmpeg -i input.mp4 -vn -c:a pcm_s16le output.wav

# 3. 提取为无任何头部的裸 PCM 数据(S16LE):
ffmpeg -i input.mp4 -vn -c:a pcm_s16le -f s16le output.pcm

# 4. 将 WAV 压缩为带 ADTS 头的 AAC 裸流:
ffmpeg -i input.wav -c:a aac -b:a 128k -f adts output.aac

# 5. 查看 AAC 音频或 WAV 的详细容器格式信息:
ffprobe -show_streams input.aac

音视频数据流基础

共 5 篇

  1. H264流
  2. 音频流基础:PCM、WAV 与 AAC 编码深度解析
  3. 视频底层密码:YUV 颜色空间、内存对齐与格式转换揭秘
  4. 音视频容器格式剖析:MP4、FLV 与 TS
  5. FFMpeg 命令记录