1. 卡顿现象分类:音频卡顿、视频卡顿、音画不同步、间歇性黑屏/花屏
做 WebRTC 推流排查,第一步不是抓包,也不是看日志。而是先搞清楚——用户到底看到了什么?
我见过太多人一上来就调码率、改编码器,折腾半天发现根本方向就错了。你想想看,音频卡顿和视频卡顿,根因可能完全不同。所以这一章,我们先给卡顿现象分个类。
说白了,卡顿就四种:音频卡顿、视频卡顿、音画不同步、间歇性黑屏/花屏。每种我都踩过坑,下面一个一个说。
核心原则:先定性,再定量。先搞清楚是哪类卡顿,再去找根因。方向错了,越查越远。
1.1 音频卡顿
音频卡顿的表现很直观:声音断断续续,像收音机信号不好。有时候是“滋滋”的杂音,有时候是突然静音几秒又恢复。
我个人习惯,先问用户一个问题:“是声音一直卡,还是偶尔卡一下?”
- 持续卡顿:大概率是网络带宽不够,或者丢包率太高。音频虽然占带宽小,但 WebRTC 的音频编码器(比如 Opus)对丢包很敏感。
- 间歇性卡顿:可能是 CPU 过载,或者音频采集设备出了问题。我在项目中遇到过,某款 USB 麦克风驱动有 bug,每 30 秒会丢一帧音频数据。
还有一个容易被忽略的点——音频缓冲区大小。WebRTC 的音频引擎内部有 jitter buffer,如果 buffer 设置太小,网络抖动一来就卡;设置太大,延迟又高。这是个 trade-off。
避坑指南:我曾经排查过一个线上问题,用户反馈音频卡顿,但网络指标一切正常。最后发现是音频采样率不匹配——发送端用 48kHz,接收端只支持 44.1kHz,导致重采样时丢帧。所以,检查一下 SDP 里的音频参数,往往有惊喜。
1.2 视频卡顿
视频卡顿比音频更复杂。因为视频帧的数据量大,影响因素也多。
视频卡顿通常分两种:
| 类型 | 表现 | 常见原因 |
|---|---|---|
| 帧率低 | 画面像幻灯片,一帧一帧跳 | 编码器性能不足、CPU 满载、网络带宽不够导致帧被丢弃 |
| 帧间隔不均匀 | 画面时而流畅时而卡,不规律 | 网络抖动、发送端 pacing 策略问题、接收端渲染线程卡顿 |
怎么区分?我一般看两个指标:编码帧率和渲染帧率。如果编码帧率正常(比如 30fps),但渲染帧率只有 15fps,那问题大概率在接收端。反过来,编码帧率本身就低,那就是发送端或网络的问题。
嗯,这里要注意:WebRTC 的统计 API(getStats)里,framesEncoded 和 framesDecoded 这两个字段,能帮你快速定位。
// 示例:通过 getStats 获取编码帧率
const stats = await peerConnection.getStats();
stats.forEach(report => {
if (report.type === 'outbound-rtp' && report.kind === 'video') {
console.log('编码帧率:', report.framesEncoded / report.timestamp);
}
if (report.type === 'inbound-rtp' && report.kind === 'video') {
console.log('解码帧率:', report.framesDecoded / report.timestamp);
}
});
注意:不要只看平均值。视频卡顿往往是瞬时的,平均帧率可能看起来正常。我习惯用 1 秒窗口去采样,看帧率的波动曲线。如果波动超过 30%,基本可以判定有卡顿。
1.3 音画不同步
音画不同步,说白了就是嘴型和声音对不上。用户感知非常明显,体验极差。
WebRTC 内部有音视频同步机制(基于 RTP 时间戳和 NTP 时间),但为什么还会不同步?
- 音频和视频的时钟漂移:音频时钟和视频时钟来自不同的硬件,长期运行会产生偏差。
- jitter buffer 处理不一致:音频的 jitter buffer 和视频的 jitter buffer 是独立的,如果网络抖动导致一个被延迟、一个没被延迟,就会错位。
- 采集端延迟差异:音频采集和视频采集的硬件延迟不同。比如某款摄像头有 100ms 的编码延迟,而麦克风只有 20ms,那声音就会比画面快。
我记得有一次排查一个教育场景的卡顿问题,老师说话和板书总是对不上。查了半天,发现是老师的摄像头自带美颜功能,引入了 200ms 的处理延迟。关掉美颜,问题解决。
经验之谈:音画不同步的容忍度,一般音频领先视频 50ms 以内,或者音频落后视频 100ms 以内,用户感知不到。超过这个范围,就需要干预了。WebRTC 的 RTCRtpReceiver 的 getSynchronizationSources 方法可以拿到音视频时间戳的映射关系,用来计算偏移量。
1.4 间歇性黑屏/花屏
这种问题最吓人。用户突然看到黑屏或者满屏马赛克,第一反应就是“软件坏了”。
黑屏和花屏的根因不太一样:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 黑屏 | 关键帧(IDR)丢失、解码器初始化失败、渲染窗口被销毁 | 检查是否收到 IDR 帧、解码器状态、渲染线程是否正常 |
| 花屏 | 非关键帧丢失、网络丢包导致参考帧损坏、编码器/解码器 bug | 检查丢包率、是否启用了 NACK/FEC、编码器配置 |
这里有个关键点:黑屏不一定真的是黑屏。有时候是视频渲染层被其他 UI 元素挡住了,或者渲染窗口的尺寸为 0。我遇到过一例,用户说黑屏,结果发现是 CSS 把 video 元素的 display 设成了 none。
花屏的话,大概率是丢包。WebRTC 默认启用了 NACK(丢包重传),但如果丢包率超过 20%,NACK 也救不回来。这时候需要结合 FEC(前向纠错)来恢复。
警告:间歇性黑屏/花屏,不要只盯着网络看。我曾经排查过一个 case,花屏每 10 分钟出现一次,非常规律。最后发现是编码器的 GOP 设置问题——每 10 分钟强制插入一个 IDR 帧,但 IDR 帧太大,网络瞬间拥塞导致丢包。调整 GOP 大小后解决。
知识体系总览
下面这张图,把四种卡顿现象、它们的表现、以及排查入口串起来了。你可以把它当作排查时的“地图”。
小结
这一章我们只做一件事:给卡顿现象贴标签。音频卡顿、视频卡顿、音画不同步、黑屏/花屏,每种都有不同的表现和排查入口。
下次用户反馈“卡了”,别急着动手。先问一句:“声音卡还是画面卡?是一直卡还是一阵一阵的?声音和画面能对上吗?” 这三个问题问完,排查方向基本就定了。
嗯,记住这张图。后面的章节,我们会针对每一种现象,深入到底层去挖根因。