WebRTC 音视频管道概览

做音画同步,你得先搞清楚音视频数据是怎么在 WebRTC 里流动的。说白了,就是音频和视频各自走了一条什么样的路,最后在 RTP 层怎么被打包、怎么带上时间戳。我刚开始接触 WebRTC 时,也踩过不少坑——比如音频采集的采样率没对齐,导致远端播放声音变调。所以这一章,咱们把管道从头到尾捋一遍。

音频采集与编码流程

音频从麦克风到网络,大致经过这么几步:

  1. 采集:浏览器通过 getUserMedia 拿到麦克风数据,通常是 16kHz 或 48kHz 的 PCM 裸流。
  2. 预处理:回声消除(AEC)、降噪(NS)、自动增益控制(AGC)——这些都在 AudioProcessing 模块里完成。
  3. 编码:Opus 或 PCMU/PCMA 编码器把 PCM 数据压缩成编码帧。
  4. 封装成 RTP:每个编码帧被塞进 RTP 包,带上时间戳和序列号。

关键点:音频的 RTP 时间戳增量是固定的。比如 48kHz 采样率下,每 20ms 一帧,时间戳增量就是 960。这个值在接收端用来计算播放节奏。

我在项目中遇到过一个问题:某款 Android 手机采集的采样率是 16kHz,但编码器配置成了 48kHz,结果时间戳计算全乱套了。嗯,这里要注意——采集和编码的采样率必须一致。

视频采集与编码流程

视频管道比音频复杂一些,因为它涉及帧率控制、分辨率适配、码率调节。流程如下:

  1. 采集:摄像头输出 YUV 或 NV12 格式的原始帧。
  2. 预处理:缩放、旋转、裁剪——这些由 VideoAdapter 和 VideoFrameProcessor 处理。
  3. 编码:VP8、VP9、H264 或 AV1 编码器压缩视频帧。
  4. 封装成 RTP:一帧可能被拆成多个 RTP 包(特别是大尺寸帧),每个包携带相同的时间戳。
环节 常见问题 影响
采集 帧率不稳定(30fps 掉到 15fps) 时间戳间隔不均匀,接收端播放卡顿
编码 编码器丢帧(CPU 过载) RTP 时间戳出现跳变
封装 NALU 分片顺序错乱 解码失败或花屏

我的习惯:在编码器输出回调里打日志,记录每一帧的采集时间、编码时间、RTP 时间戳。这样排查音画不同步时,能快速定位是采集端还是编码端的问题。

RTP 打包与时间戳机制

RTP 时间戳是音画同步的核心。音频和视频各自维护一个时间轴,接收端通过这两个时间轴的关系来对齐播放。

音频 RTP 时间戳

  • 基于采样率递增。比如 48kHz 采样率,每 20ms 一包,时间戳加 960。
  • 音频的 RTP 时间戳是连续的,不会跳变(除非有丢包或静音检测)。

视频 RTP 时间戳

  • 基于 90kHz 时钟。为什么是 90kHz?因为它是 24fps、30fps、60fps 的公约数,方便计算。
  • 每帧的时间戳 = 采集时刻的 90kHz 时钟值。比如 30fps 下,两帧间隔 3000(90kHz / 30)。

我曾经踩过的坑:视频编码器内部会重新打时间戳,导致 RTP 时间戳和实际采集时间对不上。后来我在采集端用 webrtc::Timestamp 记录原始时间,编码后再做一次映射,才把问题解决。

音视频管道整体流程

下面这张图展示了音频和视频从采集到 RTP 输出的完整路径。你可以看到,两条管道在 RTP 层汇合,但时间戳各自独立。

WebRTC 音视频管道流程图 WebRTC 音视频管道流程图 音频采集 (麦克风) 音频预处理 (AEC/NS/AGC) Opus 编码 RTP 打包 视频采集 (摄像头) 视频预处理 (缩放/裁剪) VP8/H264 编码 RTP 打包 时间戳机制 音频:基于采样率递增(如 48kHz 下每 20ms 加 960) 视频:基于 90kHz 时钟(如 30fps 下每帧加 3000) 常见问题与排查方向 1. 音频时间戳跳变 → 检查采样率配置和 AEC 模块 2. 视频时间戳不均匀 → 检查编码器帧率控制和 CPU 负载 3. 音视频时间戳基准不一致 → 检查 NTP 对齐和 RTP 扩展头

时间戳对齐的关键点

你想想看,音频和视频的时间戳是各自独立的,接收端怎么知道哪一帧音频对应哪一帧视频?答案是:通过 RTP 时间戳和 NTP 时间的映射。

  • 发送端:在 RTCP SR(Sender Report)里,把 RTP 时间戳和 NTP 时间绑定在一起。
  • 接收端:根据 SR 里的映射关系,把音频和视频的时间戳统一到同一个时间轴上。

核心公式:音画同步误差 = |(音频播放时间 - 音频参考时间) - (视频播放时间 - 视频参考时间)|。当这个误差超过 100ms 时,人眼就能感知到不同步。

我个人习惯在接收端维护一个缓冲区,音频和视频各自有一个播放队列。每次播放前,计算当前音频帧和视频帧的时间戳差值,如果超过阈值,就调整播放速度或丢帧。说白了,这就是音画同步的底层逻辑。

避坑指南

我曾经遇到的一个典型问题:某次线上会议,用户反馈声音和画面差了一秒多。排查后发现,视频编码器在 CPU 过载时自动跳帧,但 RTP 时间戳还是按原始帧率递增,导致接收端以为视频还在正常播放。解决方案是在编码器跳帧时,同步调整 RTP 时间戳的生成逻辑。

嗯,这里要注意:RTP 时间戳反映的是采集时间,而不是编码时间。如果编码延迟很大,时间戳和实际播放时间就会脱节。所以,我建议在采集端就打好时间戳,编码和打包过程中不要随意修改。