NetEq 的六种操作 · 每 10ms 的抉择
每次声卡要音频,NetEq 从六种操作里选一个:Normal、Acceleration、Preemptive expand、Expand、Merge、Comfort noise。这一章依据 g3doc 讲清这六种操作各是什么、DecisionLogic 怎么根据缓冲状态选择,以及它们如何合力把混乱的网络变成流畅的声音。
NetEq 的六种操作 · 每 10ms 的抉择
NetEq 每次 GetAudio,都要凭当前状态(缓冲量 vs 目标、包到没到)做一个抉择:这 10ms 的音频,怎么产出?g3doc 列出六种操作。这一章把它们讲全——它们是 NetEq 全部戏法的清单。
六种操作
NetEq 的 g3doc 明确列出 GetAudio 输出的六种可能:
NetEq 的六种操作,覆盖了“缓冲多、缓冲少、有包、没包、静音”所有情形:
| 操作 | 什么时候 | 做什么 |
|---|---|---|
| Normal(正常) | 缓冲 ≈ 目标、有包 | 正常解码一个包播放 |
| Acceleration(加速) | 缓冲 > 目标(攒多了) | 时间压缩播得略快,消耗缓冲、降延迟 |
| Preemptive expand(抢先扩展 / 减速) | 缓冲 < 目标(快空了) | 时间拉伸播得略慢,攒缓冲、防断音 |
| Expand(扩展 / 丢包隐藏) | 下一个包没到 / 丢了 | 凭历史编造一段填充(PLC) |
| Merge(拼接) | Expand 之后包又来了 | 把编造的填充和真实解码音频平滑拼接 |
| Comfort noise(舒适噪声,CNG) | 对端静音(DTX) | 生成柔和的背景噪声填静默 |
前三种(Normal/Accelerate/Preemptive)是缓冲量的常规调节——根据缓冲 vs 目标微调播放速度。后三种(Expand/Merge/CNG)是应对特殊情况——丢包、丢包后恢复、静音。六种合起来,NetEq 对任何输入情形都有对策,保证声卡每次都能拿到一段音频。
舒适噪声:为什么静音不能真静音
六种里 Comfort noise 值得单说,它和一个反直觉的事实有关:
对端静音时,不能真的播“绝对安静”——那会让人以为通话断了。NetEq 生成柔和的“舒适噪声”填补静默,维持“线路还在”的感觉。 配合发送侧的 Opus DTX:你不说话时,发送端停发或只发极少的噪声参数省带宽;接收端的 NetEq 收到“进入静音”的信号,就本地生成一段和你环境本底噪声相似的柔和噪声播放。于是听者感觉“对方没说话,但线路是通的、有环境底噪”,而非“死一般寂静让人怀疑掉线”。
绝对的安静反而让人不安——这是通信心理学的一个洞察。老式电话在你不说话时也有轻微“嘶嘶”底噪,正是这个作用。NetEq 的舒适噪声把这个体验数字化地重建了,还顺便省了静音期的带宽。一个小细节,体现了 WebRTC 对“听感体验”的深究。
DecisionLogic:谁来选
选哪种操作,由 DecisionLogic(modules/audio_coding/neteq/decision_logic.cc)拍板。每次 GetAudio,它综合:
- 当前缓冲量 vs 目标延迟(决定 Normal/Accelerate/Preemptive)。
- 下一个包在不在(不在 → Expand)。
- 是不是刚丢包隐藏完、包又来了(→ Merge)。
- 是不是静音期(→ CNG)。
DecisionLogic 是 NetEq 的中枢神经——每 10ms 做一次这个抉择,把延迟管理、丢包应对、静音处理统一到一个决策点。
小结与下一章
NetEq 每次 GetAudio 从六种操作选一个:Normal(正常解码)、Acceleration(加速追缓冲)、Preemptive expand(减速攒缓冲)、Expand(丢包隐藏)、Merge(隐藏后拼接)、Comfort noise(静音填柔和噪声,因绝对安静让人不安)。前三管缓冲调节、后三管特殊情况。DecisionLogic 每 10ms 综合缓冲状态、包在不在、是否静音,拍板选哪种。
六种里最见功力的两类:丢包隐藏(Expand/Merge) 和 时间伸缩(Accelerate/Preemptive)。下一章先看丢包隐藏——怎么“编造”出以假乱真的音频。👉 NetEq 丢包隐藏。