引言
音频编解码器决定了"用多少比特描述一段声音"。有损编码的核心不是"压缩波形",而是利用听觉掩蔽效应丢弃听不到的信息:把信号分解到频带,按心理声学模型计算每个频带的掩蔽阈值,允许低于阈值的量化噪声任意大。理解这一点,才能理解为什么同样 128 kbps,不同编解码器的听感差异巨大。
工程上选编解码器要同时权衡四个维度:码率(带宽成本)、延迟(交互体验)、抗丢包(网络质量)、兼容性(终端覆盖)。Opus 在延迟与码率上全面领先,AAC 在兼容性与硬件解码上占优,MP3 只剩兼容老设备的价值。
本文先建立感知编码的基本模型,再分别拆解 Opus 与 AAC 的架构,然后讨论帧长与延迟的换算关系、码率控制模式、丢包隐藏与前向纠错,最后给出选型、协商与浏览器端(WebCodecs)的落地方法。
目录
- 有损编码的感知模型
- Opus 架构:SILK 与 CELT 的混合
- AAC 家族:LC、HE 与 xHE
- 帧长、码率与算法延迟
- 比特率控制:CBR、VBR 与 CVBR
- 丢包隐藏与前向纠错
- 编解码器选型与协商
- 浏览器中的编解码:WebCodecs
- 质量实测与评估方法
1. 有损编码的感知模型
感知编码的三块基石:
- 临界频带(Critical Band):人耳对频率的分辨率不是线性的,低频分辨率高(约 100 Hz),高频分辨率低(可达 4 kHz)。编码器按临界频带划分频带,而不是均匀划分。
- 频域掩蔽:一个强音会掩蔽邻近频带的弱音。掩蔽阈值随频率距离衰减,典型为 10~25 dB/临界频带。
- 时域掩蔽:强音前后的短时间窗内,弱音也被掩蔽(前掩蔽约 5
20 ms,后掩蔽约 50200 ms)。
编码器的量化噪声不需要"低于信号",只需要低于掩蔽阈值。这就是为什么有损编码能做到 10:1 甚至 20:1 的压缩而听感接近无损。
每频带分配比特数 ≈ f(信号能量 - 掩蔽阈值)
掩蔽阈值高的频带 → 少分配比特甚至不编码
心理声学模型的实现差异(频带划分、掩蔽曲线、时域扩散函数)直接决定了编码器在同等码率下的听感。这也是 AAC 在同码率下普遍优于 MP3 的根本原因——MP3 的模型是第一代,AAC 是第二代。
2. Opus 架构:SILK 与 CELT 的混合
Opus(RFC 6716)是 IETF 标准,2012 年发布,完全免专利费。它的独特之处在于同时包含两种编码模式并按信号动态切换:
- SILK:源自 Skype 的语音编码器,基于线性预测(LPC),面向 8/12/16/24 kHz 的语音,擅长低码率(6~40 kbps)。
- CELT:源自 Xiph 的 Constrained Energy Lapped Transform,基于 MDCT,面向音乐与宽带信号,擅长 32 kbps 以上的高保真。
2.1 模式切换
编码器每帧(2.5~60 ms)决定用 SILK、CELT 还是混合模式(SILK 编低频 + CELT 编高频)。切换由内部信号分析驱动,对调用方透明。
语音(低码率) → SILK
音乐 / 宽带(高码率) → CELT
混合内容(16~32 kbps) → SILK + CELT 混合
2.2 关键参数
| 参数 | 取值范围 | 说明 |
|---|---|---|
| 采样率 | 8/12/16/24/48 kHz | 内部可重采样 |
| 帧长 | 2.5/5/10/20/40/60 ms | 越短延迟越低 |
| 码率 | 6~510 kbps | 单声道 6~256,立体声到 510 |
| 声道 | 1~2 | 支持联合立体声、双声道 |
| 复杂度 | 0~10 | 越高越慢,质量越好 |
# 用 opusenc 编码:48 kHz、20 ms 帧、64 kbps VBR
opusenc --bitrate 64 --framesize 20 --vbr input.wav out.opus
# 强制 CELT 模式(音乐)
opusenc --hard-cbr --bitrate 128 --framesize 20 music.wav out.opus
# 解码
opusdec --force-wav out.opus out.wav
2.3 为什么 WebRTC 强制 Opus
- 延迟最低:2.5 ms 帧长下算法延迟约 5 ms,加上前后处理也只有十几毫秒。
- 码率自适应范围宽:同一编解码器从 6 kbps 到 510 kbps 全覆盖,网络波动时无需换编码器。
- 内置 FEC 与 DTX:前向纠错与静音抑制都是标准的一部分。
- 免专利:WebRTC 的强制编解码(与 VP8/AV1 同属免专利阵营)。
网络拥塞控制如何驱动码率切换,见 webrtc-codec-congestion-control 。
3. AAC 家族:LC、HE 与 xHE
AAC(Advanced Audio Coding,ISO/IEC 13818-7)是 MPEG 标准,1997 年发布,广泛用于流媒体、广播与 Apple 生态。
| 规格 | 全称 | 核心技术 | 典型码率 | 延迟 |
|---|---|---|---|---|
| AAC-LC | Low Complexity | MDCT + 量化 | 96~256 kbps | 中 |
| HE-AAC v1 | High Efficiency | LC + SBR | 32~96 kbps | 中高 |
| HE-AAC v2 | HE-AAC + PS | v1 + 参数立体声 | 16~48 kbps | 中高 |
| AAC-LD | Low Delay | 短窗 MDCT | 64~128 kbps | 低 |
| xHE-AAC | Extended HE | USAC | 12~64 kbps | 中高 |
3.1 SBR 与 PS
**SBR(Spectral Band Replication)**不编码 8 kHz 以上的高频,而是只编码低频并传少量"如何重建高频"的边信息。解码器用低频成分谐波外推生成高频。这能在低码率下保留"明亮感",代价是高频细节是合成的,对某些素材(镲片、弦乐泛音)听感不自然。
**PS(Parametric Stereo)**把立体声降成单声道 + 少量空间参数(ILD、ICLD、ICC),解码时重建立体声像。它在极低码率下效果显著,但立体声分离度会下降。
3.2 AAC 的帧结构
AAC-LC 的标准帧长是 1024 个样本(长窗),外加 2048 点的 MDCT 重叠。在 48 kHz 下:
1024 / 48000 = 21.33 ms(帧长)
加上 MDCT 重叠与编码器前瞻 → 算法延迟约 40~60 ms
AAC-LD 用 512 点窗,算法延迟可压到约 20 ms,代价是低频分辨率下降。这就是为什么实时会议若必须用 AAC 会选 LD 变体。
3.3 专利与许可
AAC 的核心专利多数已过期(最早一批 1997 年申请,2017 年前后到期),但 Via LA 等专利池仍对部分实现收取许可费。这是 WebRTC 选择 Opus 而非 AAC 的原因之一。
4. 帧长、码率与算法延迟
算法延迟(algorithmic delay)由编码器结构决定,与网络延迟无关。它是"采集到可发送"与"收到到可播放"的时间。
总算法延迟 = 编码器前瞻 + 帧长 + 解码器缓冲 + 重采样
| 编解码器 | 帧长 | 算法延迟(编码+解码) |
|---|---|---|
| Opus @ 2.5 ms | 2.5 ms | ~5 ms |
| Opus @ 20 ms | 20 ms | ~26 ms |
| Opus @ 60 ms | 60 ms | ~70 ms |
| AAC-LC | 1024 样本 | |
| AAC-LD | 512 样本 | ~20 ms |
| MP3 | 1152 样本 |
4.1 帧长与码率的权衡
短帧长带来低延迟,但每帧的头部开销与变换效率损失使同码率质量下降:
48 kbps @ 20 ms 帧 → 每帧 960 bit = 120 字节
48 kbps @ 2.5 ms 帧 → 每帧 120 bit = 15 字节
15 字节里还要放帧头与模式信息,留给频谱数据的极少。因此低延迟与高压缩率不可兼得:交互场景用 20 ms 帧,非交互场景用 60 ms 帧换取更好质量。
4.2 端到端延迟中的位置
算法延迟只是端到端延迟的一小部分。以 20 ms 帧的 Opus 为例,一次往返的拆解大致为:
采集缓冲 5 ms
编码 20 ms(帧长)+ 5 ms(前瞻)
网络 30 ms
抖动缓冲 40 ms
解码 5 ms
播放缓冲 10 ms
─────────────────────
合计 约 115 ms
抖动缓冲往往是可优化的最大项,详见 audio-streaming-latency 。
5. 比特率控制:CBR、VBR 与 CVBR
| 模式 | 行为 | 适用 |
|---|---|---|
| CBR | 每帧固定字节数 | 实时传输、固定带宽信道 |
| VBR | 按内容复杂度分配比特 | 离线编码、存储 |
| CVBR | 有上限的 VBR(约束 VBR) | 流媒体、码率上限明确 |
| ABR | 平均码率约束 | 长时间尺度平均 |
5.1 实时场景为什么用 CBR
实时传输中,码率突变会导致发送队列积压或抖动缓冲水位波动。CBR 让每帧大小可预测,网络调度简单。代价是简单段落浪费比特、复杂段落质量下降。
5.2 Opus 的码率控制细节
# 硬 CBR(每帧字节数严格相等)
opusenc --hard-cbr --bitrate 64 in.wav out.opus
# 约束 VBR(允许波动但有上限)
opusenc --vbr --bitrate 96 --comp 10 in.wav out.opus
# 开启 DTX(静音段不发送)
opusenc --dtx in.wav out.opus
--comp 控制复杂度(010),越高越慢但质量越好。移动端通常设 57 平衡功耗。
5.3 码率与质量的拐点
经验数据(48 kHz 立体声,Opus):
| 码率 | 主观质量 |
|---|---|
| 24 kbps | 可懂但明显失真(语音可接受) |
| 48 kbps | 语音良好,音乐一般 |
| 96 kbps | 接近透明(多数素材听不出) |
| 160 kbps | 透明 |
| 256 kbps+ | 边际收益极小 |
AAC-LC 的拐点略高:约 128 kbps 接近透明,192 kbps 透明。HE-AAC 在 48 kbps 下优于 AAC-LC,但在 128 kbps 以上反而不如 LC(SBR 的合成高频成为负担)。低码率用 HE-AAC,高码率用 AAC-LC 是基本原则。
6. 丢包隐藏与前向纠错
丢包对音频的影响远大于视频:视频丢一帧只是画面卡顿,音频丢一帧是"啪"或静音。
6.1 PLC(Packet Loss Concealment)
丢包隐藏不是恢复数据,而是合成一段听起来连续的替代信号:
- 语音:用最近的基频与频谱包络外推,保持音高连续。
- 音乐:重复上一帧的频谱并衰减,或做时域淡出。
Opus 的 PLC 质量在同类中领先,能掩盖 5~10% 的随机丢包而主观上不明显。
6.2 FEC(Forward Error Correction)
Opus 支持带内 FEC:在每个包中携带上一帧的低码率副本。接收端丢包时用副本重建。代价是码率增加约 20~30%。
启用 FEC 后每包结构:
[当前帧数据][上一帧的低码率冗余副本]
6.3 重传与 NACK
WebRTC 默认启用 NACK(Negative Acknowledgement):接收端发现序号空洞就请求重传。重传在 RTT 小于抖动缓冲深度时有效(例如 RTT 40 ms、缓冲 80 ms),否则来不及。FEC 与 NACK 通常同时开启,由拥塞控制器动态决定 FEC 比例。
7. 编解码器选型与协商
7.1 场景矩阵
| 场景 | 首选 | 备选 | 理由 |
|---|---|---|---|
| 实时通话 | Opus 20 ms | G.722、AAC-LD | 低延迟 + 抗丢包 |
| 实时音乐(Jam) | Opus 5~10 ms | — | 延迟优先 |
| 点播流媒体 | AAC-LC 128~256 | Opus、Vorbis | 硬件解码普及 |
| 低码率流媒体 | HE-AAC v2 | Opus | 广播兼容性 |
| 语音助手 | Opus 16 kHz | AMR-WB | 免专利、算力低 |
| 归档 | FLAC | ALAC | 无损 |
7.2 协商机制
WebRTC 用 SDP 协商编解码与参数。Opus 的参数通过 a=fmtp 行传递:
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1;maxaveragebitrate=32000;stereo=0
useinbandfec=1 开启带内 FEC,minptime=10 表示最小打包间隔 10 ms(即 10 ms 帧长),maxaveragebitrate 限制平均码率。SDP 协商的完整流程见 webrtc-sdp-negotiation
。
8. 浏览器中的编解码:WebCodecs
WebCodecs 提供了对编解码器的直接访问,绕开容器与 <audio> 标签。
const decoder = new AudioDecoder({
output(frame) {
// frame: AudioData,可 copyTo 到 Float32Array
const buf = new Float32Array(frame.numberOfFrames);
frame.copyTo(buf, { planeIndex: 0, format: 'f32-planar' });
frame.close(); // 必须显式关闭,否则泄漏
},
error(e) { console.error(e); },
});
const support = await AudioDecoder.isConfigSupported({
codec: 'opus', sampleRate: 48000, numberOfChannels: 2, bitrate: 64000,
});
if (support.supported) {
decoder.configure({
codec: 'opus', sampleRate: 48000, numberOfChannels: 2, bitrate: 64000,
});
}
关键点:
AudioData必须close():它持有底层缓冲,不关闭会迅速耗尽内存。isConfigSupported是异步的:不同浏览器的支持矩阵不同,必须运行时探测。- 编码用
AudioEncoder:可把麦克风采集的数据编码成 Opus 或 AAC,再通过 WebSocket 发送。
把编解码搬进 WASM(例如 ffmpeg.wasm 或 libopus.wasm)是另一种方案,适合需要精确控制编码参数或需要 WebCodecs 不支持的编解码器时,细节见 wasm-media-processing-codecs 。
9. 质量实测与评估方法
9.1 客观指标
- PESQ:语音质量感知评估,输出 MOS-LQO(1~4.5)。适合窄带/宽带语音。
- POLQA:PESQ 的继任者,支持超宽带。
- ViSQOL:基于频谱相似度,适合音乐与通用音频。
- PEAQ:ITU-R BS.1387,面向音乐质量。
这些工具都需要"参考信号 + 处理信号"配对,且对时间对齐敏感。
9.2 主观测试
MUSHRA(ITU-R BS.1534)是音频编解码评估的事实标准:给受试者一个参考信号与多个候选,要求按 0100 打分,其中必须包含一个 3.5 kHz 低通的"锚点"用于校准。至少 1520 名受试者才能得到统计显著的结果。
9.3 快速验证:null test 与频谱对比
# 用 Opus 编码再解码,与原信号做差
ffmpeg -i ref.wav -c:a libopus -b:a 96k tmp.opus
ffmpeg -i tmp.opus -c:a pcm_s24le decoded.wav
ffmpeg -i ref.wav -i decoded.wav -filter_complex \
"[0:a][1:a]amix=inputs=2:weights=1 -1,volumedetect" -f null -
残差电平反映"丢掉了多少信息",但不能直接换算成主观质量——这正是需要 PESQ/MUSHRA 的原因。完整的音频质量测试体系见 audio-quality-testing。
权衡取舍
| 决策点 | 选择 A | 选择 B | 建议 |
|---|---|---|---|
| 编解码器 | Opus | AAC | 实时一律 Opus;点播考虑硬件解码用 AAC |
| 帧长 | 20 ms | 60 ms | 交互 20 ms,非交互 60 ms |
| 码率模式 | CBR | VBR | 实时 CBR,离线 VBR |
| 低码率方案 | HE-AAC | Opus | 广播兼容用 HE-AAC,Web 用 Opus |
| 抗丢包 | FEC | NACK | 两者同时开,比例由拥塞控制决定 |
| 立体声 | 联合立体声 | 双声道 | 低码率用联合/参数立体声 |
| 音频质量 | 96 kbps | 256 kbps | 96 kbps 已接近透明,更高边际收益极小 |
常见坑清单
- 低码率用 AAC-LC:48 kbps 以下 AAC-LC 明显劣于 HE-AAC 与 Opus,应换编码器而非降码率。
- 高码率用 HE-AAC:128 kbps 以上 SBR 的合成高频反成负担,应回退到 AAC-LC。
- 忽略算法延迟:只算网络延迟会低估端到端延迟 30~60 ms,交互场景不可接受。
- 实时场景用 VBR:码率突变导致发送队列积压与抖动缓冲水位波动。
- 忘记 AudioData.close():WebCodecs 中不关闭
AudioData会迅速耗尽内存。 - 假设所有浏览器都支持:必须用
isConfigSupported运行时探测,Safari 的支持矩阵与 Chrome 差异明显。 - 帧长与打包间隔混淆:
minptime是打包间隔,可包含多个帧,不等于帧长。 - 只开 NACK 不开 FEC:RTT 大于抖动缓冲深度时重传来不及,必须配合 FEC。
- 以为 PLC 能恢复数据:丢包隐藏只是合成替代信号,丢包率高于 10% 时主观质量必然下降。
- 用峰值判断质量:编解码前后的峰值差异不能反映主观质量,需 PESQ/MUSHRA 等感知指标。
小结
音频编解码的选型本质是码率、延迟、抗丢包、兼容性四者的取舍。Opus 在延迟与码率上全面领先且免专利,是实时场景的默认选择;AAC 在硬件解码与广播生态上占优,是点播与低码率流媒体的主力。帧长决定算法延迟,码率模式决定传输稳定性,FEC 与 PLC 决定弱网体验。
落地时的顺序建议:先确定延迟预算,再反推帧长与打包间隔;先确定带宽下限,再选择编解码器与码率模式;最后用 PESQ 或 MUSHRA 验证主观质量是否达标。
继续深入建议读 audio-streaming-latency 理解抖动缓冲与端到端延迟的完整拆解,读 webrtc-codec-congestion-control 了解码率如何随网络自适应,读 audio-quality-testing 掌握客观与主观质量评估的完整方法。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。