1. WebRTC 通话质量指标体系:从指标到体验

做 WebRTC 通话质量监控,第一件事不是调参数,而是搞清楚——到底该看什么?

我见过不少团队,一上来就盯着丢包率猛调 FEC,结果延迟飙到 500ms,用户早挂了电话。说白了,指标之间是互相拉扯的。你得先理解每个指标的含义、怎么测、以及它到底怎么影响用户的感受。

这一章,我们就来拆解 WebRTC 通话质量的核心指标体系。我会结合自己在嵌入式设备上踩过的坑,把延迟、抖动、丢包率、RTT、MOS 分这些概念讲透。

1.1 延迟(Latency)—— 通话的“第一道坎”

延迟,就是说话到对方听到的时间差。你对着麦克风说“喂”,对方 300ms 后才听到,这对话基本就废了。

定义与分类

WebRTC 里的延迟,通常分三段:

  • 采集与编码延迟:麦克风采样 + 编码器压缩。Opus 编码器在 20ms 帧长下,这部分大约 30-50ms。
  • 网络传输延迟:数据包从发送端到接收端的物理时间。取决于带宽、路由、距离。
  • 接收端缓冲与播放延迟:JitterBuffer 为了抗抖动,会故意缓存一些数据。这部分是“人为增加的延迟”。

关键阈值:

  • 0-150ms:优秀,用户几乎无感知。
  • 150-300ms:可接受,但部分用户会感觉“有点慢”。
  • >300ms:糟糕,对话开始出现重叠和等待。

测量方法

WebRTC 内部通过 RTP 时间戳和 NTP 时间来计算延迟。你可以从 RTCPeerConnection.getStats() 中拿到 currentRoundTripTime 和 totalRoundTripTime。但注意,这测的是 RTT,不是端到端延迟。

我在项目中习惯用 发送端打时间戳 + 接收端回显 的方式。在 RTP 扩展头里塞一个毫秒级的时间戳,接收端收到后立即回传,发送端就能算出单程延迟。

// 伪代码:发送端打时间戳
rtpSender.send(rtpPacket.setExtension(
    kTimestampUri, 
    Date.now() & 0xFFFFFFFF
));

// 接收端收到后,通过 RTCP 回传
receiver.onRtcp = function(report) {
    let sendTime = report.getExtension(kTimestampUri);
    let rtt = Date.now() - sendTime;
    // 单程延迟 ≈ rtt / 2
};

注意:嵌入式设备时钟漂移严重。我曾在某 ARM 平台上发现,设备运行 1 小时后,本地时钟偏差达到 50ms。建议用 NTP 同步或 RTP 时间戳做相对测量,别依赖绝对时间。

1.2 抖动(Jitter)—— 网络不稳定的“晴雨表”

抖动,说白了就是延迟的变化量。网络忽快忽慢,数据包到达时间不均匀,这就是抖动。

为什么抖动比延迟更致命?因为延迟是固定的,你可以通过缓冲来适应。但抖动是随机的,缓冲小了会丢包,缓冲大了延迟又上去了。

WebRTC 的抖动计算

WebRTC 使用 Inter-arrival Jitter 算法,定义在 RFC 3550 中。简单说,就是计算相邻两个包到达时间差的方差。

// 简化版抖动计算
let jitter = 0;
let previousDiff = 0;

function updateJitter(arrivalTime, timestamp) {
    let diff = arrivalTime - timestamp;
    jitter += (Math.abs(diff - previousDiff) - jitter) / 16;
    previousDiff = diff;
    return jitter;
}

从 getStats() 中,你可以拿到 jitter 和 jitterBufferDelay。前者是网络抖动,后者是 JitterBuffer 为了消除抖动而引入的延迟。

我的经验:在嵌入式设备上,Wi-Fi 环境下的抖动通常在 10-30ms。如果超过 50ms,建议启用 NACK + FEC 组合。我曾经在一个智能音箱项目里,抖动飙到 80ms,用户反馈“声音像机器人”。后来加了 FEC 冗余,抖动降到 20ms,体验明显改善。

1.3 丢包率(Packet Loss)—— 最直观的“杀手”

丢包率,就是丢失的数据包占总发送包的比例。WebRTC 里,丢包率超过 5%,语音就开始出现断续;超过 10%,基本无法正常通话。

丢包的类型

类型 原因 典型场景
网络拥塞丢包 路由器队列溢出 带宽不足、突发流量
无线信号丢包 Wi-Fi/4G 信号衰减 移动中、穿墙
JitterBuffer 丢包 包到达太晚,被丢弃 高抖动场景

测量与恢复

WebRTC 通过 RTP 序列号来检测丢包。接收端发现序列号不连续,就认为发生了丢包。你可以从 packetsLost 和 packetsReceived 算出丢包率。

恢复手段主要有三种:

  • NACK(否定确认):接收端请求重传丢失的包。延迟增加 1 个 RTT。
  • FEC(前向纠错):发送端发送冗余数据,接收端直接恢复。增加带宽消耗。
  • PLC(丢包隐藏):接收端用算法“猜”丢失的音频数据。Opus 内置 PLC,效果不错。

避坑指南:我曾经在一个低功耗摄像头项目里,为了省带宽把 FEC 关掉了。结果丢包率 3% 时,语音质量就崩了。后来我学乖了——丢包率 < 2% 用 NACK,2%-5% 用 FEC,> 5% 两者同时启用。但注意,FEC 会增加 20%-50% 的带宽,嵌入式设备要算好余量。

1.4 RTT(往返时间)—— 网络质量的“体温计”

RTT 是数据包从发送端到接收端再返回的时间。它反映了网络的“响应速度”。

WebRTC 通过 RTCP Sender Report (SR) 和 Receiver Report (RR) 来计算 RTT。发送端在 SR 中记录发送时间 NTP timestamp,接收端在 RR 中回传 LSR(Last SR timestamp)和 DLSR(Delay since Last SR)。

// RTT 计算公式
RTT = receiveTime - LSR - DLSR

从 getStats() 中,currentRoundTripTime 就是最新的 RTT 值,单位秒。

我的建议:RTT 超过 300ms 时,交互式通话就很吃力了。但如果是单向直播(比如监控摄像头),RTT 500ms 也能接受。关键看场景。我在做智能门铃时,RTT 控制在 200ms 以内,用户按门铃到手机响铃的延迟才勉强能接受。

1.5 MOS 分(Mean Opinion Score)—— 用户感受的“最终裁决”

MOS 分是主观评价,1 分最差,5 分最好。WebRTC 里,我们通常用 POLQA 或 PESQ 算法来客观计算 MOS 分。

但说实话,在嵌入式设备上跑 POLQA 太耗资源了。我一般用 E-Model 来估算 MOS 分,它只需要 R 值,而 R 值可以从延迟、抖动、丢包率推导出来。

// E-Model 简化版
function calculateMos(rValue) {
    if (rValue < 0) return 1;
    if (rValue > 100) return 4.5;
    return 1 + 0.035 * rValue + 
           rValue * (rValue - 60) * (100 - rValue) * 7e-6;
}

// R 值估算(简化)
let rValue = 93.2 - (delayMs / 40) - (jitterMs / 10) - (packetLoss * 2.5);

MOS 分参考:

  • 4.0-4.5:优秀,用户非常满意。
  • 3.5-4.0:良好,用户基本满意。
  • 3.0-3.5:一般,用户能接受但有抱怨。
  • < 3.0:差,用户会挂电话。

1.6 指标体系全景图

下面这张图,是我在项目中用来做质量监控的指标体系框架。它把指标分成了三层:网络层、传输层、体验层。

WebRTC 通话质量指标体系 体验层(用户感知) MOS 分(1-5) 用户满意度 通话可懂度 传输层(WebRTC 内部) 端到端延迟 JitterBuffer 延迟 丢包恢复(NACK/FEC) RTT 网络层(原始指标) 网络延迟 抖动(Jitter) 丢包率 带宽 说明:下层指标直接影响上层表现。网络层抖动大 → 传输层 JitterBuffer 延迟增加 → 体验层 MOS 分下降。

1.7 指标之间的“跷跷板”关系

做质量调优,最难的不是看懂单个指标,而是平衡它们之间的关系。我总结了一个“三角制约”规律:

  • 延迟 vs 丢包:增加 FEC 冗余可以降低丢包,但会占用带宽,增加延迟。
  • 抖动 vs 延迟:增大 JitterBuffer 可以吸收抖动,但会引入额外延迟。
  • 带宽 vs 质量:降低编码码率可以节省带宽,但会降低音质,影响 MOS 分。

我的经验:在嵌入式设备上,我通常先保证延迟不超过 200ms,然后在这个前提下尽量降低丢包率。如果丢包率超过 5%,我会动态调整 FEC 冗余度,而不是一味增大 JitterBuffer。你想想看,JitterBuffer 增大 50ms,用户可能还能忍;但丢包率从 3% 升到 8%,用户直接挂电话。

1.8 本章小结

这一章我们拆解了 WebRTC 通话质量的六个核心指标:延迟、抖动、丢包率、RTT、MOS 分,以及它们之间的关联。记住一句话:指标是工具,体验是目的。不要为了优化一个指标而牺牲整体体验。