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 多次!
具体怎么抓?两步走:
- 打开
chrome://webrtc-internals,找到对应的 PeerConnection。 - 在
RTCInboundRTPVideoStream或RTCOutboundRTPVideoStream里找以下字段:
// 接收端统计(看 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 知识体系图:丢包恢复调试全景
下面这张图帮你理清整个调试流程。从抓取数据到分析问题,再到调整策略,每一步都有对应指标。
17.5 实战案例:一次典型的丢包恢复调试
我拿一个真实案例给你拆解。某视频会议产品,用户反馈「画面经常花屏,然后黑屏几秒恢复」。我远程抓了 webrtc-internals 数据:
- NACK 频率:平均 25 次/秒(偏高)
- PLI 频率:平均 2 次/秒(异常高)
- FIR 频率:0 次/秒(正常)
- FEC 恢复率:45%(偏低)
你看,NACK 和 PLI 都高,说明网络丢包已经导致解码器频繁崩溃。FEC 恢复率只有 45%,说明冗余包不够用。
我当时做了两件事:
- 增加 FEC 冗余度:从 10% 提升到 25%。代价是带宽占用多了 15%,但用户带宽足够。
- 缩短 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