16、延迟调试:使用 getStats() 的 roundTripTime 和 jitter 指标分析端到端延迟
做 WebRTC 开发,最头疼的问题是什么?
我个人觉得,不是连不上,而是连上了但体验极差。对方说话像卡碟的 CD,画面定格在奇怪的表情上。这种时候,你心里肯定在骂:「到底哪里慢了?」
嗯,今天我们就来彻底解决这个问题。用 getStats() 这把手术刀,精准解剖端到端延迟。
延迟的三个层次:你得知道慢在哪
先理清一个概念。端到端延迟不是单一数字,它由三部分构成:
- 网络延迟:数据包在网络上跑的时间。说白了就是「路况」。
- 抖动(jitter):网络延迟的变化量。路况时好时坏,就是抖动。
- 处理延迟:编解码、渲染、缓冲等本地操作耗时。
我在项目中遇到过好几次,开发人员盯着一个 200ms 的 RTT 猛调,结果问题出在本地渲染缓冲区太大。所以,先搞清楚是哪一层,再动手。
getStats() 的核心指标:RTT 和 Jitter
WebRTC 的 RTCPeerConnection.getStats() 会返回一个 RTCStatsReport 对象。里面藏着大量宝藏。我们重点关注两个:
| 指标 | 含义 | 正常范围 | 异常信号 |
|---|---|---|---|
roundTripTime |
从发送端到接收端再返回的往返时间(秒) | < 0.2s(实时通话) | > 0.5s 明显卡顿 |
jitter |
数据包到达时间的平均偏差(秒) | < 0.03s | > 0.05s 丢包风险高 |
重要提醒:roundTripTime 是往返时间,不是单向延迟。单向延迟 ≈ RTT / 2。但实际场景中,上下行路径可能不对称,所以这个估算仅供参考。
实战:如何拿到这些数据
代码其实很简单。但要注意时机——你需要在连接建立后,定期轮询。
// 假设 pc 是 RTCPeerConnection 实例
async function checkStats() {
const stats = await pc.getStats();
stats.forEach(report => {
// 重点关注 remote-inbound-rtp 类型
if (report.type === 'remote-inbound-rtp' && report.kind === 'video') {
console.log(`RTT: ${report.roundTripTime * 1000} ms`);
console.log(`Jitter: ${report.jitter * 1000} ms`);
}
// 也可以看 outbound-rtp 的 roundTripTime
if (report.type === 'outbound-rtp' && report.kind === 'video') {
console.log(`Outbound RTT: ${report.roundTripTime * 1000} ms`);
}
});
}
// 每 2 秒采样一次
setInterval(checkStats, 2000);
你想想看,为什么用 remote-inbound-rtp?因为这个报告是接收端反馈回来的,包含了真实的网络状况。而 outbound-rtp 的 RTT 是本地估算的,精度稍差。
SVG 流程图:延迟分析决策树
下面这张图,是我在实际调试中总结的决策路径。遇到延迟问题,按这个顺序排查,基本不会漏。
避坑指南:我曾经踩过的三个坑
光知道指标还不够,实际调试时坑很多。我一个个说。
坑一:RTT 突然飙升,但网络没问题
我曾经排查了一整天,发现是服务器端的 TURN 转发配置错了。数据包绕了半个地球才到对端。所以,看到 RTT 高,先确认是不是直连。用 candidatePair 的 localCandidate 和 remoteCandidate 看看 IP 地址,如果出现公网 IP 对公网 IP 但 RTT 还是高,那八成是路由问题。
坑二:jitter 正常,但画面依然卡顿
嗯,这里要注意。jitter 是网络抖动,但卡顿也可能是接收端的渲染瓶颈。我遇到过一例,是客户端的 Canvas 绘制频率跟不上帧率。这时候要看 framesDecoded 和 framesRendered 的差值。如果解码多但渲染少,就是本地性能问题。
技巧:用 jitter 反推缓冲区设置
jitter 值直接决定了你需要多大的抖动缓冲区。公式很简单:bufferSize = jitter * 2(保守估计)。比如 jitter 是 30ms,缓冲区至少设 60ms。太小会丢包,太大会增加延迟。我在项目中一般会动态调整,根据最近 10 秒的 jitter 均值来更新缓冲区大小。
实战案例:一次完整的延迟排查
说个真实案例。有一次线上反馈,某地区的用户视频延迟高达 1.5 秒。我按以下步骤排查:
- 第一步:抓 RTT。getStats 显示 RTT 是 600ms。单向延迟约 300ms。这已经很高了。
- 第二步:看 jitter。jitter 只有 20ms,说明网络很稳定,不是抖动问题。
- 第三步:检查 candidate 类型。发现用的是 TURN 中继,而且中继服务器在另一个国家。
- 第四步:部署本地 TURN 服务器。RTT 降到 80ms,延迟问题解决。
你看,整个过程不到 10 分钟。如果没有 getStats(),你只能靠猜。
总结:记住这三句话
- RTT 高:查网络路径、查 TURN 部署、查带宽。
- jitter 高:增大抖动缓冲区、优化网络质量。
- 两者都正常但卡:查本地编解码、渲染、CPU 负载。
最后说一句。调试延迟不是一锤子买卖。网络环境在变,用户设备在变,你需要持续监控。把 getStats() 的采样结果上报到后台,做成可视化面板,才是长久之计。
好了,这一章就到这里。拿起你的浏览器控制台,试试看吧。