11、音频调试:使用 getStats() 分析音频编码类型、采样率、丢包补偿效果

音频调试,说实话,比视频调试更容易被忽视。

画面卡了,大家一眼就能看出来。但音频出问题,往往是「听不清」、「断断续续」、「有杂音」——这些描述很模糊,很难定位。我早期做WebRTC项目时,就吃过这个亏。用户投诉通话质量差,我盯着视频参数调了半天,结果问题出在音频编码上。

今天我们就来聊聊,怎么用 getStats() 这把利器,把音频问题揪出来。

11.1 音频相关的统计指标概览

先看一张图,理清音频调试的核心脉络。

音频 getStats() 调试核心维度 音频流统计 编码类型 codec: opus / PCMU / PCMA 采样率 clockRate: 48000 / 16000 丢包补偿效果 packetsLost / fractionLost 延迟与抖动 jitterBufferDelay / jitterBufferEmittedCount 音量与能量 audioLevel / totalAudioEnergy

音频调试,说白了就是盯着这五个维度:编码类型对不对、采样率够不够、丢包补偿有没有生效、延迟抖动能接受吗、音量是否正常。我们一个一个来拆。

11.2 获取音频统计:inbound-rtp 与 outbound-rtp

音频的统计信息,主要藏在两个地方:

  • inbound-rtp:接收端统计。看对方发过来的音频质量。
  • outbound-rtp:发送端统计。看自己发出去的音频质量。

我习惯先看 inbound,因为用户抱怨的「听不清」,绝大多数是接收端的问题。

获取代码很简单:

const stats = await peerConnection.getStats();
stats.forEach(report => {
  if (report.type === 'inbound-rtp' && report.kind === 'audio') {
    console.log('音频接收统计:', report);
  }
  if (report.type === 'outbound-rtp' && report.kind === 'audio') {
    console.log('音频发送统计:', report);
  }
});
小技巧: 建议每隔 1-2 秒拉一次 stats,然后计算差值。单次快照意义不大,趋势才有价值。

11.3 音频编码类型:codec 字段

编码类型决定了音质和带宽的平衡。WebRTC 最常用的音频编码是 Opus,它支持可变比特率,从 6 kbps 到 510 kbps 都能跑。

在 stats 中,编码信息长这样:

{
  "type": "inbound-rtp",
  "kind": "audio",
  "codecId": "RTCCodec_audio_1"
}

// 再查 codec 对象
{
  "type": "codec",
  "payloadType": 111,
  "mimeType": "audio/opus",
  "clockRate": 48000,
  "channels": 2
}

这里要注意几点:

  • mimeType 如果是 audio/opus,说明用了 Opus。如果是 audio/PCMUaudio/PCMA,那是 G.711,音质差一些,但兼容性好。
  • clockRate 就是采样率。Opus 通常是 48000 Hz,G.711 是 8000 Hz。
  • channels 表示声道数。2 是立体声,1 是单声道。
避坑: 我曾经遇到一个 case,两边都是 Chrome,但音频编码协商成了 PCMU。查了半天,发现是 SDP 中 Opus 的优先级被改低了。如果你发现编码不是 Opus,先检查 SDP 协商过程。

11.4 采样率:clockRate 与实际效果

采样率越高,声音越清晰。但也不是越高越好——高采样率意味着更多带宽。

Opus 支持 8 kHz、12 kHz、16 kHz、24 kHz、48 kHz。WebRTC 默认用 48 kHz,但网络差的时候可能会降级。

怎么看当前实际采样率?

stats.forEach(report => {
  if (report.type === 'codec' && report.mimeType === 'audio/opus') {
    console.log(`当前采样率: ${report.clockRate} Hz`);
  }
});

如果发现采样率掉到了 16000 或 8000,说明网络状况不太好,系统自动降级了。

我的经验: 有一次用户反馈「声音像在水里说话」,我一看 stats,采样率只有 8000 Hz。但网络带宽其实够,问题出在服务器转码时强制降采样了。所以,采样率低不一定是网络问题,也可能是中间设备搞的鬼。

11.5 丢包补偿效果:packetsLost 与 fractionLost

音频丢包比视频丢包更致命。视频丢几帧,人眼可能看不出来。音频丢一个包,耳朵立马能感觉到「卡顿」或「滋滋声」。

WebRTC 的音频丢包补偿(PLC,Packet Loss Concealment)机制,就是尽量掩盖丢包的影响。Opus 内置了 PLC,效果还不错。

在 stats 中,丢包相关的字段有:

字段 说明 正常范围
packetsLost 累计丢包数 持续增长说明有问题
fractionLost 丢包率(小数,0~1) < 0.05 良好,< 0.1 可接受
jitterBufferDelay 抖动缓冲总延迟(秒) 越小越好
jitterBufferEmittedCount 从抖动缓冲输出的音频包数 用于计算平均延迟

怎么判断 PLC 有没有生效?

嗯,这里有个小技巧。如果 packetsLost 在增加,但用户没有明显感觉到卡顿,说明 PLC 起作用了。反之,如果丢包率只有 2%,用户却抱怨断断续续,那可能是 PLC 没生效,或者抖动缓冲设置有问题。

// 计算平均抖动缓冲延迟
stats.forEach(report => {
  if (report.type === 'inbound-rtp' && report.kind === 'audio') {
    const avgDelay = report.jitterBufferDelay / report.jitterBufferEmittedCount;
    console.log(`平均抖动缓冲延迟: ${avgDelay.toFixed(3)} 秒`);
    
    const lossRate = report.fractionLost;
    console.log(`丢包率: ${(lossRate * 100).toFixed(2)}%`);
    
    if (lossRate > 0.1) {
      console.warn('丢包率过高,建议检查网络或启用 FEC');
    }
  }
});
曾经踩过的坑: 有一次丢包率只有 1%,但用户说声音「像机器人」。我查了 stats,发现 jitterBufferDelay 高达 500ms。原来是抖动缓冲设得太大,虽然不丢包,但延迟导致声音变形。后来我把抖动缓冲上限调小了,问题解决。

11.6 音量与能量:audioLevel 与 totalAudioEnergy

音量问题也是音频调试的常客。用户说「听不见」,可能是对方没说话,也可能是音量太小。

WebRTC 提供了两个字段:

  • audioLevel:当前音量级别,0~1 之间。0 是静音,1 是最大。
  • totalAudioEnergy:累计音频能量,用于计算平均音量。

怎么用?

stats.forEach(report => {
  if (report.type === 'inbound-rtp' && report.kind === 'audio') {
    if (report.audioLevel !== undefined) {
      console.log(`当前音量: ${(report.audioLevel * 100).toFixed(1)}%`);
    }
  }
});

如果 audioLevel 一直为 0,说明对方可能静音了,或者麦克风没工作。如果 audioLevel 很低(比如 0.01),说明麦克风增益不够。

实用技巧: 我习惯在调试面板里画一条音量波形。用 audioLevel 的实时值,每 100ms 取一个点,连成线。这样用户说「声音忽大忽小」时,一眼就能看出规律。

11.7 实战:一个完整的音频调试流程

说了这么多,我们来走一遍完整的调试流程。假设用户反馈「通话声音不好」,我会这么做:

  1. 拉 stats:获取 inbound-rtp 和 outbound-rtp 的音频统计。
  2. 看编码:确认是 Opus 还是其他。如果是 G.711,先查 SDP 协商。
  3. 看采样率:如果低于 48000,检查网络或中间设备。
  4. 看丢包:计算丢包率。如果 > 5%,建议开启 FEC(前向纠错)。
  5. 看抖动缓冲:平均延迟是否过大?过大则调整抖动缓冲参数。
  6. 看音量:audioLevel 是否正常?过低则检查麦克风。

这个流程,我用了好几年,基本能覆盖 90% 的音频问题。

核心思路: 音频调试不是看单个指标,而是看指标之间的关联。比如丢包率高但用户没感觉,说明 PLC 好。丢包率低但用户感觉差,说明抖动缓冲或音量有问题。多练几次,你就能凭感觉快速定位了。

好了,音频调试就聊到这里。记住,getStats() 是你的眼睛,但怎么解读数据,靠的是经验。多动手,多踩坑,慢慢就熟了。


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