视频丢一帧,画面可能短暂卡住;语音丢掉一个承载 20 ms 音频的包,则可能吞掉一个关键的辅音片段。接收端还必须按时向扬声器交付 PCM:原包即使最终到了,错过播放时刻也无法补回已经听到的声音。
所以讨论音频丢包恢复,先要问:缺失的音频在播放截止时间前,究竟还有没有机会拿到? 有机会,就争取拿到编码数据;没有机会,就生成一段连续的替代声音。本文以本地 WebRTC 源码提交 c8bca1867efcbc5f52adf58ad4463088ed257aa0(2026-10-22)为基线,说明这些选择如何落到 WebRTC 的音频链路中。
一、丢包造成的并不只是“少了一个 RTP 包”
假设每包承载 20 ms 音频,P2 没有按时到达:
序号缺口不等于永久丢包。 P2 可能只是乱序或延迟;如果它在截止前进入接收缓冲,仍可正常播放。真正影响听感的是截止时仍没有可用音频。短缺口会损伤辅音、爆破音或瞬态;连续缺口会让预测逐渐失真,出现机械音、音量衰减甚至静音。长包虽减少包头开销,丢一包时缺失的时间也更长。
接收端为了等待乱序包,可以提高抖动缓冲的目标延迟;代价是整条通话更慢。缓冲只能把“迟到但仍赶得上”的包变为可用,无法凭空恢复真正丢失的数据。
二、先看 WebRTC 的实际接收链路
NetEQ 不只是一个固定长度的队列。它根据到达情况调整目标延迟,在播放端请求音频时选择正常解码、时间伸缩、丢包隐藏和恢复后的拼接。缓冲偏多时可以加速消耗,偏少时可以预先拉伸;缺帧时执行 Expand,下一段正常音频到来后可能执行 Merge,让接缝不那么突兀。这些操作由 NetEQ 决策逻辑 和 NetEQ 主实现 共同完成。
发送端也能根据网络状况调整 Opus 码率、包时长和 FEC,但音频网络适配器(ANA)的控制策略需要启用并配置。降低码率可能缓解拥塞,却会牺牲编码质量;缩短包时长能缩短单次丢失的声音缺口,却增加每秒的包数与包头开销。这些措施改变的是后续丢包风险和恢复条件,不会把已经丢失的原始音频找回来。Opus 编码器适配层包含相关控制入口。
这里有一个容易误解的区别:libopus 有 PLC,并不表示 WebRTC 默认总是调用 Opus 的 PLC。 在本文源码基线中,AudioDecoderOpusImpl::GeneratePlc 受 WebRTC-Audio-OpusGeneratePlc field trial 控制;未启用时它不产出音频,NetEQ 转用自己的 Expand。启用后,NetEQ 才可能向 Opus 解码器请求 PLC。依据见 Opus 解码器适配层 的 GeneratePlc 和 NetEQ 的 DoCodecPlc。
三、FEC:提前付带宽,换一次及时恢复的机会
Opus 带内 FEC:下一包带上一段语音的低码率冗余
Opus 在合适的编码模式和网络条件下,可以把前一段语音的低码率冗余放进后续包。这种冗余通常称为 SILK LBRR。它不是前一包的字节副本,恢复音质也不必然等同于原包。
图是“一包 20 ms、一包一段语音”的教学例子。要救 A,P2 必须到达、确实携带可用冗余,而且接收端要留出等待时间;不能把“打开 FEC”直接等同于“每次丢包都能恢复”。WebRTC 的 Opus 解码器会在发现 FEC 时,把冗余帧以较早的时间戳交给 NetEQ,再交入当前主帧;NetEQ 按播放时间和帧优先级选用。解析路径可见 ParsePayload 和 DecodeRedundantInternal。
发送端需要协商接收能力,并允许编码器生成 FEC。WebRTC 的 SetFec 允许 Opus 使用带内 FEC,损失估计也会传给编码器;最终是否写入冗余,仍取决于码率、模式和内容。SILK 或 Hybrid 可利用这一路径;CELT-only 不应被假定有同样的 LBRR。参见 RFC 7587 §3.3和 WebRTC Opus 编码器适配层。
RTP RED:复制以前的编码块
RED 是 RFC 2198 定义的冗余负载封装格式,不是一套纠错算法。本文源码中的 AudioEncoderCopyRed 通常把上一份已编码音频连同当前音频装入新包;默认保留一层冗余,配置可改变层数。若原包丢失而后续 RED 包及时到达,可以拿到原先的编码块。相比 Opus LBRR,这种复制可能更接近原包内容,但常常消耗更多带宽,并受包大小、时间戳跨度和协商结果限制。依据见 RED 编码器 和 NetEQ RED 拆包。
前一篇参考资料中讲的 ULPFEC、FlexFEC 属于 RTP 包级保护机制;不能因为 RED、ULPFEC 和 Opus FEC 都带有“冗余”,就认为音频通话会自动同时启用它们。 本文的音频实用路径重点是 Opus 带内 FEC 与协商后的音频 RED。
四、NACK:能拿回原包,但要赶上播放时刻
接收端看到序号缺口后,可通过 RTCP NACK 请求补发;发送端必须还保留原包,并且补发流量没有被限制。这里的耗时可以粗略写为:
恢复耗时 ≈ 发现缺口与反馈等待 + 反馈上行 + 发送端排队 + 补发下行
网络路径近似对称时,两段传播之和常用 RTT 估算,但排队与反馈等待还要另算。如果距播放截止只剩 30 ms,RTT 却有 100 ms,补发包即使最终到达,也难以改善这一刻的声音。RTT 小、缓冲较充足时,NACK 对孤立丢包更有价值;大 RTT 或连续丢包时,反复补发还可能挤占新音频的带宽。
本文基线的音频接收端确实有 NACK 路径:ChannelReceive::OnRtpPacket 调用 NackTracker 找缺口并发送反馈;发送端从历史缓存取包并排队补发。但该路径受协商与 NACK 历史配置控制,NackTracker 的筛选也不等于对每个包精确计算扬声器的播放截止时间。依据见 接收端、NackTracker 和 发送端补发。
NACK 与 FEC 不是必须排成“先等 NACK、再尝试 FEC”的串行流水线。NACK 反馈可以在接收包时发出,冗余帧和补发的原帧则可能先后进入 NetEQ;到播放时刻,谁及时且可用,谁才有机会被采用。
五、PLC:数据来不及,就尽量把声音接起来
PLC(Packet Loss Concealment)不会复原未知的原始语音,而是根据已解码的历史生成替代音频。独立使用 libopus 时,传入空包可触发 Opus PLC:SILK 侧使用语音预测、基音和激励状态;CELT 侧会依据历史波形与频谱状态处理短缺口,较长丢失转向噪声和能量衰减。具体策略因编码模式和连续丢失长度而变。RFC 6716 §4.4描述了 Opus 的丢包隐藏。
WebRTC 默认的 NetEQ Expand 同样从解码后的历史音频外推,并在连续缺口中逐渐衰减;恢复正常帧时通过 Merge 平滑衔接。它不需要额外传输数据,因而是截止前没有可用包时的兜底,但对爆破音、说话人突然转变、音乐瞬态和长时间连续丢包都没有“猜中原声”的保证。参见 Expand 与 Merge。
六、把方案放在同一张表里比较
| 机制 | 获得的内容 | 主要代价 | 更合适的条件 | 主要限制 |
|---|---|---|---|---|
| NetEQ 抖动缓冲 | 迟到、乱序的原包 | 增加播放延迟 | 抖动明显,仍能赶上截止 | 不能修复真正丢失的包 |
| Opus 带内 FEC | 前一段语音的低码率冗余 | 持续带宽,等待后包 | 中等丢包、后包通常能到、可容忍一段等待 | 不保证每包有冗余;连续丢包效果差 |
| 音频 RED | 以前编码块的副本 | 较高的带宽与包长 | 可用带宽较充足,想提高原数据可用率 | 后包也丢或超时就无效 |
| NACK 重传 | 原编码包 | 反馈和补发流量、RTT | RTT 较小、缓冲有余量、偶发丢包 | 过期补发无法改善实时播放 |
| PLC / Expand | 估计的替代 PCM | 无额外网络带宽 | 低延迟要求高,或前面方法都未赶上 | 无法还原原声,连续丢包听感下降 |
这张表比较的是机制,不是某种固定优先级。实际效果受协商、码率、包时长、RTT、抖动分布与播放目标延迟共同影响。FEC、RED 和 NACK 的开销都要算进总发送预算;链路已经拥塞时,盲目叠加冗余可能使新包也丢失。
七、按场景组合,而不是寻找一个万能开关
- 低延迟语音通话、RTT 较大:先控制编码包时长与播放延迟;若带宽允许,评估 Opus 带内 FEC。剩余缺口交给 PLC。不要为了等遥远的重传而无限拉长缓冲。
- 近距离网络、偶发孤立丢包:NACK 可能在截止前拿回原包;仍要观察补发的到达时间,而不只看 NACK 数量。
- 连续突发丢包:依赖下一包的单层冗余经常一起失效。可以评估多层 RED 或其他保护设计,但会增加带宽与等待;同时应检查拥塞和发送码率,避免把网络问题仅当成解码问题。
- 音乐或高质量音频:CELT-only 场景不能照搬 SILK LBRR 的收益预期。若音质比带宽更重要,可评估适当冗余与缓冲;瞬态被 PLC 预测错误时,主观听感仍可能明显下降。
验证时,除了网络丢包率,还要记录 NetEQ 的 concealedSamples、concealmentEvents、fecPacketsReceived、fecPacketsDiscarded、缓冲延迟、NACK 数量,以及补发包是否在截止前真正被解码播放。WebRTC 接收统计映射包含这些接收侧指标。收到冗余包不等于成功用它恢复声音,发出 NACK 也不等于补发赶上了播放。测试应分别注入孤立丢包、连续丢包和纯延迟抖动,并在相同总码率与端到端延迟约束下比较听感;否则多花的带宽或多等的时间会被误认为恢复算法更好。
归根结底,WebRTC 的音频恢复是一场与播放截止时间的赛跑:NetEQ 尽量给原包、冗余和补发创造机会;来不及的部分由 PLC 保住连续性。恢复率、实时性和带宽必须一起看。