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-rtpcandidate-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 知识体系,你看一眼就能明白各个模块之间的关系:

getStats API 网络层统计 candidate-pair RTT / 可用带宽 ICE 连接状态 传输层统计 inbound-rtp outbound-rtp 丢包 / 抖动 / 码率 媒体层统计 track / codec 帧率 / 分辨率 编码质量 / 丢帧 实时监控面板 → 用户体验指标

16.6 避坑指南

我曾经踩过的坑:

  • getStats 在 iframe 中可能被限制: 跨域 iframe 里调用 getStats 会抛异常。解决方案是在主页面中代理调用。
  • 不要频繁调用: 我见过有人用 100ms 间隔轮询,结果 CPU 飙升。1秒一次足够,实时性完全够用。
  • 注意单位: RTT 是秒,抖动也是秒,但丢包率是整数。写代码时一定要乘以 1000 转成毫秒,不然你看到的全是 0.03 这种小数字。
  • candidate-pair 可能有多个: 只有 state === 'succeeded' 的那个才是当前使用的连接对,其他的可以忽略。

16.7 总结

getStats 是 WebRTC 性能分析的基石。你只要抓住三个层次——网络层看 RTT 和带宽,传输层看丢包和抖动,媒体层看帧率和编码质量——就能快速定位问题。

我个人习惯在开发阶段就把监控面板集成进去,上线后配合日志系统持续采集。这样用户反馈问题时,我直接看历史数据就能还原现场,省去了大量沟通成本。

记住一句话:没有数据支撑的优化都是瞎猜。用好 getStats,你的 WebRTC 应用才能跑得稳、看得清、听得真。


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