16、WebRTC统计API:getStats详解、关键指标监控、性能分析工具
做WebRTC开发,最怕什么?
我最怕用户说「卡」。不是那种彻底连不上的卡,而是画面模糊、声音断断续续、延迟忽高忽低。这种问题最难排查,因为你没法让用户帮你抓日志。
好在WebRTC给了我们一把利器——getStats API。说白了,它就是浏览器内置的「黑匣子」,记录了每一帧、每一个包、每一次抖动的细节。今天我就带你把它吃透。
16.1 getStats 基础用法
先看最基础的调用方式。我个人习惯在 PeerConnection 建立后,每隔1秒拉一次统计数据。
const pc = new RTCPeerConnection(configuration);
// 每隔1秒采集一次统计信息
setInterval(async () => {
const stats = await pc.getStats();
stats.forEach(report => {
console.log(report.type, report);
});
}, 1000);
嗯,这里要注意:getStats 返回的是一个 Map 结构,每个条目是一个 RTCStatsReport 对象。你没法直接拿到「延迟是多少」,得先理解它里面的各种 type。
16.2 核心统计类型详解
我刚开始接触时也被一堆 type 搞晕过。其实核心就这几种:
| type 值 | 含义 | 关键字段 |
|---|---|---|
inbound-rtp |
接收端音视频流 | packetsReceived, bytesReceived, jitter, packetsLost, framesDecoded |
outbound-rtp |
发送端音视频流 | packetsSent, bytesSent, retransmittedPacketsSent, framesEncoded |
remote-inbound-rtp |
远端反馈的接收质量 | roundTripTime, fractionLost |
candidate-pair |
ICE 连接对 | currentRoundTripTime, availableOutgoingBitrate, state |
track |
轨道统计 | framesReceived, framesDropped, totalAudioEnergy |
codec |
编解码器信息 | mimeType, clockRate, channels |
你看,每个 type 关注的点不一样。做性能分析时,我通常把 inbound-rtp 和 candidate-pair 作为第一优先级。
16.3 关键指标监控实战
光看原始数据没用,得提炼出「能反映用户体验」的指标。我在项目中总结了一套黄金指标:
16.3.1 延迟(RTT)
延迟是实时通信的命门。怎么拿?
// 从 remote-inbound-rtp 中获取 RTT
stats.forEach(report => {
if (report.type === 'remote-inbound-rtp') {
const rttMs = report.roundTripTime * 1000;
console.log(`当前 RTT: ${rttMs.toFixed(1)} ms`);
}
});
我曾经遇到一个案例:用户反馈视频延迟严重,但 RTT 只有 30ms。后来发现是发送端帧率设置太低,导致画面更新慢。你看,RTT 低不代表体验好,得综合看。
16.3.2 丢包率
丢包率直接影响画面是否花屏、声音是否断续。
stats.forEach(report => {
if (report.type === 'inbound-rtp' && report.kind === 'video') {
const total = report.packetsReceived + report.packetsLost;
const lossRate = total > 0 ? (report.packetsLost / total) * 100 : 0;
console.log(`视频丢包率: ${lossRate.toFixed(2)}%`);
}
});
丢包率超过 5% 时,画面基本就没法看了。这时候我建议启动 FEC(前向纠错)或者降低码率。
16.3.3 抖动(Jitter)
抖动是网络不稳定的直接体现。WebRTC 内部有抖动缓冲,但如果抖动太大,缓冲也救不了。
stats.forEach(report => {
if (report.type === 'inbound-rtp' && report.kind === 'audio') {
console.log(`音频抖动: ${report.jitter * 1000} ms`);
}
});
音频抖动超过 50ms 时,人耳就能感知到声音「忽快忽慢」。视频抖动超过 100ms 时,画面会出现明显的卡顿感。
16.3.4 可用带宽
这个指标藏在 candidate-pair 里:
stats.forEach(report => {
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
const bw = report.availableOutgoingBitrate;
console.log(`可用上行带宽: ${(bw / 1000).toFixed(0)} kbps`);
}
});
可用带宽是 WebRTC 拥塞控制算法估算出来的。如果它低于当前编码码率,说明网络已经撑不住了,视频会自动降质。
16.4 性能分析工具搭建
光看控制台打印不够,我习惯搭一个实时监控面板。下面是我常用的一个简化版:
核心思路:用 setInterval 轮询 getStats,把关键指标渲染到 DOM 上。注意不要频繁调用,1秒一次足够了。
class StatsMonitor {
constructor(pc, containerId) {
this.pc = pc;
this.container = document.getElementById(containerId);
this.intervalId = null;
}
start() {
this.intervalId = setInterval(async () => {
const stats = await this.pc.getStats();
const metrics = this.parseStats(stats);
this.render(metrics);
}, 1000);
}
parseStats(stats) {
const result = { rtt: 0, lossRate: 0, jitter: 0, bitrate: 0 };
stats.forEach(report => {
if (report.type === 'remote-inbound-rtp') {
result.rtt = report.roundTripTime * 1000;
}
if (report.type === 'inbound-rtp' && report.kind === 'video') {
const total = report.packetsReceived + report.packetsLost;
result.lossRate = total > 0 ? (report.packetsLost / total) * 100 : 0;
result.jitter = report.jitter * 1000;
}
if (report.type === 'candidate-pair' && report.state === 'succeeded') {
result.bitrate = report.availableOutgoingBitrate / 1000;
}
});
return result;
}
render(metrics) {
this.container.innerHTML = `
<div>RTT: ${metrics.rtt.toFixed(0)} ms</div>
<div>丢包率: ${metrics.lossRate.toFixed(1)}%</div>
<div>抖动: ${metrics.jitter.toFixed(0)} ms</div>
<div>可用带宽: ${metrics.bitrate.toFixed(0)} kbps</div>
`;
}
stop() {
clearInterval(this.intervalId);
}
}
小技巧:生产环境中,我还会把历史数据存到数组里,绘制成折线图。这样能直观看到网络波动趋势,比看数字强多了。
16.5 知识体系总览
下面这张图是我梳理的 getStats 知识体系,你看一眼就能明白各个模块之间的关系:
16.6 避坑指南
我曾经踩过的坑:
- getStats 在 iframe 中可能被限制: 跨域 iframe 里调用 getStats 会抛异常。解决方案是在主页面中代理调用。
- 不要频繁调用: 我见过有人用 100ms 间隔轮询,结果 CPU 飙升。1秒一次足够,实时性完全够用。
- 注意单位: RTT 是秒,抖动也是秒,但丢包率是整数。写代码时一定要乘以 1000 转成毫秒,不然你看到的全是 0.03 这种小数字。
- candidate-pair 可能有多个: 只有
state === 'succeeded'的那个才是当前使用的连接对,其他的可以忽略。
16.7 总结
getStats 是 WebRTC 性能分析的基石。你只要抓住三个层次——网络层看 RTT 和带宽,传输层看丢包和抖动,媒体层看帧率和编码质量——就能快速定位问题。
我个人习惯在开发阶段就把监控面板集成进去,上线后配合日志系统持续采集。这样用户反馈问题时,我直接看历史数据就能还原现场,省去了大量沟通成本。
记住一句话:没有数据支撑的优化都是瞎猜。用好 getStats,你的 WebRTC 应用才能跑得稳、看得清、听得真。