17、丢包恢复调试:分析 NACK/PLI/FIR 请求频率,验证 FEC 恢复效果

丢包恢复,说白了就是 WebRTC 的「急救包」。网络环境再差,你也得保证视频能看、声音能听。我做了这么多年实时通信,见过太多「画面卡成 PPT」的案例——最后查下来,全是丢包恢复策略没调好。

今天咱们就深入聊聊 NACK、PLI、FIR 这几个请求的频率怎么分析,以及 FEC 到底有没有用。嗯,这部分内容偏实战,建议你打开 Chrome 的 webrtc-internals 跟着看。

17.1 三种丢包恢复机制的本质区别

先理清概念。WebRTC 里有三种丢包恢复手段:

  • NACK(Negative Acknowledgment):接收端发现某个包丢了,主动告诉发送端「再发一次」。这是最基础的丢包重传。
  • PLI(Picture Loss Indication):视频解码器发现画面坏了,请求一个完整的 I 帧。说白了就是「我解码失败了,给我个关键帧重新开始」。
  • FIR(Full Intra Request):和 PLI 类似,但更「强硬」。FIR 是强制要求对方发 I 帧,通常用于带宽变化或切换场景。

我个人习惯把这三者比作:NACK 是「补货」,PLI 是「重启」,FIR 是「强制重启」。你想想看,如果网络丢包率只有 1%,NACK 就够了;但如果丢包率飙到 20%,PLI 和 FIR 就会频繁出现。

核心观点:NACK 频率高说明网络有随机丢包;PLI/FIR 频率高说明解码器已经扛不住了,画面大概率在频繁花屏。

17.2 如何抓取 NACK/PLI/FIR 请求频率

我在项目中遇到过最头疼的问题——用户反馈「视频偶尔卡一下」,但本地测试完全正常。后来我让用户打开 chrome://webrtc-internals,一看 NACK 请求频率,好家伙,每秒 30 多次!

具体怎么抓?两步走:

  1. 打开 chrome://webrtc-internals,找到对应的 PeerConnection。
  2. RTCInboundRTPVideoStreamRTCOutboundRTPVideoStream 里找以下字段:
// 接收端统计(看 NACK 请求量)
nackCount: 42
pliCount: 3
firCount: 0

// 发送端统计(看 FEC 包发送量)
fecPacketsSent: 128
fecPacketsReceived: 96
fecRecoveryCount: 12

这些数字是累计值。你需要每隔 1 秒采样一次,计算差值,才能得到「每秒请求频率」。我一般写个简单的脚本:

// 伪代码示例
let lastNack = 0;
setInterval(() => {
  let currentNack = stats.nackCount;
  let nackPerSecond = currentNack - lastNack;
  console.log(`NACK 频率: ${nackPerSecond}/s`);
  lastNack = currentNack;
}, 1000);

小技巧:如果 nackCount 每秒超过 10 次,说明网络丢包率已经比较高了。如果 pliCount 每秒超过 1 次,说明解码器在频繁崩溃——这时候光靠 NACK 已经不够了。

17.3 FEC 恢复效果验证

FEC(前向纠错)是另一种思路——我不等你告诉我丢了,我提前多发一些冗余包。接收端就算丢了一部分,也能靠冗余数据恢复出来。

但 FEC 不是万能的。我记得有一次,客户说「我们开了 FEC,为什么还是卡?」我一看配置,FEC 冗余度只有 5%,而丢包率是 15%——这怎么可能恢复得了?

验证 FEC 效果,主要看三个指标:

指标 含义 健康值
fecRecoveryCount FEC 成功恢复的包数 越高越好
fecPacketsLost FEC 也无法恢复的包数 越低越好
fecRecoveryRate 恢复率 = fecRecoveryCount / (fecRecoveryCount + fecPacketsLost) 大于 80% 算合格

怎么算恢复率?看这个公式:

fecRecoveryRate = fecRecoveryCount / (fecRecoveryCount + fecPacketsLost) * 100%

举个例子:如果 fecRecoveryCount 是 100,fecPacketsLost 是 20,那恢复率就是 83.3%。嗯,这个水平还算可以。但如果 fecPacketsLost 超过 50,说明 FEC 冗余度不够,或者丢包太集中了。

注意:FEC 不是免费的。冗余包会占用额外带宽。如果你带宽只有 1Mbps,FEC 冗余度开到 50%,那实际视频码率只剩 500Kbps——画质会明显下降。我曾经踩过这个坑,后来学乖了:动态调整 FEC 冗余度,根据实时丢包率来。

17.4 知识体系图:丢包恢复调试全景

下面这张图帮你理清整个调试流程。从抓取数据到分析问题,再到调整策略,每一步都有对应指标。

丢包恢复调试全景图 数据采集 webrtc-internals 指标分析 NACK/PLI/FIR 频率 FEC 恢复验证 恢复率计算 NACK 分析 • nackCount 每秒差值 • 健康值:< 10次/秒 • 异常:> 30次/秒 PLI/FIR 分析 • pliCount 每秒差值 • 健康值:< 1次/秒 • 异常:频繁触发 FEC 恢复率 • fecRecoveryCount • 健康值:> 80% • 异常:< 50% 调试结论 • NACK 频率高 → 网络随机丢包 → 增加 FEC 冗余度 或 降低码率 • PLI/FIR 频率高 → 解码器崩溃 → 检查 I 帧间隔 或 降低分辨率 • FEC 恢复率低 → 冗余度不足 → 动态调整 FEC 比例

17.5 实战案例:一次典型的丢包恢复调试

我拿一个真实案例给你拆解。某视频会议产品,用户反馈「画面经常花屏,然后黑屏几秒恢复」。我远程抓了 webrtc-internals 数据:

  • NACK 频率:平均 25 次/秒(偏高)
  • PLI 频率:平均 2 次/秒(异常高)
  • FIR 频率:0 次/秒(正常)
  • FEC 恢复率:45%(偏低)

你看,NACK 和 PLI 都高,说明网络丢包已经导致解码器频繁崩溃。FEC 恢复率只有 45%,说明冗余包不够用。

我当时做了两件事:

  1. 增加 FEC 冗余度:从 10% 提升到 25%。代价是带宽占用多了 15%,但用户带宽足够。
  2. 缩短 I 帧间隔:从 2 秒改为 1 秒。这样即使 PLI 触发,也能更快恢复。

调整后,NACK 频率降到 8 次/秒,PLI 降到 0.3 次/秒,FEC 恢复率升到 82%。用户反馈「画面流畅多了」。

避坑指南:我曾经以为 FEC 冗余度越高越好,结果带宽不够,反而导致视频卡顿。后来我学会了「动态调整」——根据实时丢包率和可用带宽,自动计算最优冗余度。WebRTC 的 RTCRtpSender.setParameters() 可以动态改 FEC 配置。

17.6 调试工具与命令速查

最后给你一个速查表,方便你在现场快速定位问题:

场景 关键指标 建议操作
NACK 频率 > 20/s nackCount 差值 检查网络丢包率,考虑开启 FEC
PLI 频率 > 1/s pliCount 差值 缩短 I 帧间隔,或降低编码复杂度
FEC 恢复率 < 60% fecRecoveryRate 增加 FEC 冗余度,或改用 ULPFEC
FIR 频繁触发 firCount 差值 检查带宽估计是否准确,或调整码率

嗯,丢包恢复调试其实没那么玄乎。核心就是盯住 NACK、PLI、FIR 的频率,再结合 FEC 恢复率,基本能定位 90% 的问题。剩下的 10%,可能是编解码器 bug 或者网络中间件搞鬼——那又是另一个故事了。


公众号:蓝海资料掘金营,微信deep3321