4. 音画同步的参考模型:发送端同步模型、接收端同步模型、缓冲区管理与抖动消除

音画同步这件事,说白了就是让音频和视频在时间轴上对齐。但网络是个不靠谱的家伙,它会抖动、会延迟、甚至会丢包。所以我们需要一套模型来管理这一切。

我个人习惯把同步模型拆成三块来看:发送端怎么打时间戳、接收端怎么对齐播放、缓冲区怎么吸收抖动。这三块缺一不可。你想想看,如果发送端的时间戳就错了,接收端再怎么努力也白搭。

4.1 发送端同步模型

发送端的核心任务只有一个:给每一帧音频和视频打上准确的时间戳。这个时间戳不是本地时钟的随意读数,而是基于同一个参考时钟的。

在 WebRTC 里,这个参考时钟通常是 NTP 时间。音频和视频采集时,各自记录下当前的 NTP 时间,然后把这个时间戳塞进 RTP 包头的 timestamp 字段里。

关键点:音频和视频的时间戳必须来自同一个时钟源。如果音频用声卡时钟,视频用摄像头时钟,那这两个时钟的漂移率不同,时间戳就会越走越偏。

我在项目中遇到过一个问题:某款 Android 手机的音频驱动和视频驱动使用了不同的晶振,导致音频时间戳比视频时间戳每小时快 3 秒。用户开视频会议半小时后,口型就对不上了。后来我们强制让两个采集线程都从系统 NTP 获取时间戳,才解决了这个问题。

发送端的时间戳模型可以用下面这张图来表示:

发送端时间戳同步模型 音频采集 声卡驱动 视频采集 摄像头驱动 NTP 参考时钟 统一时间源 音频时间戳 RTP timestamp 视频时间戳 RTP timestamp 音频和视频的时间戳必须来自同一个 NTP 时钟源,否则时间戳会逐渐漂移

4.2 接收端同步模型

接收端的工作就复杂多了。它要接收音频和视频的 RTP 包,从包里提取时间戳,然后决定什么时候播放。

接收端同步的核心是 SRTP 时间戳映射。音频和视频的 RTP 时间戳单位不同——音频通常是 48kHz 采样率,视频通常是 90kHz 时钟频率。所以接收端不能直接比较这两个数值,得先把它们映射到同一个时间轴上。

WebRTC 的做法是:利用 RTCP 的 Sender Report(SR)包。SR 包里同时包含了 RTP 时间戳和对应的 NTP 时间。接收端收到 SR 后,就能建立 RTP 时间戳到 NTP 时间的映射关系。

小技巧:我建议你在接收端维护一个映射表,记录最近收到的 SR 包信息。当音频和视频的 RTP 时间戳需要比较时,先查表转换成 NTP 时间,再做差值计算。这样能避免单位不一致的问题。

接收端同步的流程大致如下:

  1. 接收 RTP 包:分别从音频和视频流中接收数据包。
  2. 提取时间戳:从 RTP 包头拿到 timestamp 字段。
  3. 映射到 NTP 时间:利用 SR 包中的映射关系,把 RTP timestamp 转换成 NTP 时间。
  4. 计算差值:比较音频和视频的 NTP 时间差。
  5. 调整播放:如果差值超过阈值,就调整音频或视频的播放速度。

4.3 缓冲区管理与抖动消除

网络抖动是音画同步的头号敌人。什么叫抖动?就是数据包到达的时间间隔不均匀。有时候 10ms 来一个包,有时候 200ms 才来一个。如果不处理,播放端就会一卡一卡的。

解决抖动的方法就是加缓冲区。把收到的数据先存起来,等缓冲区积累到一定量后再开始播放。这样网络抖动就被缓冲区吸收了。

但缓冲区不是越大越好。缓冲区大了,延迟就大了。视频会议里延迟超过 300ms 用户就能感觉到,超过 500ms 对话就会变得很不自然。

缓冲区类型 作用 典型大小 风险
音频抖动缓冲区 (JitterBuffer) 吸收音频包到达抖动 20-120ms 过大导致延迟,过小导致丢音
视频抖动缓冲区 (JitterBuffer) 吸收视频包到达抖动 30-200ms 过大导致延迟,过小导致卡顿
解码后缓冲区 (DecodeBuffer) 对齐音频和视频的播放时间 0-100ms 用于音画同步微调

注意:我曾经在一个项目中把音频抖动缓冲区设成了 500ms,结果用户反馈说话有回声。为什么?因为缓冲区太大,本地播放的音频延迟了,用户听到自己的声音从对方那边传回来,形成了回声。后来我把缓冲区降到 80ms,问题就解决了。

抖动消除的算法有很多种,WebRTC 用的是 NetEQ(音频)和 JitterBuffer(视频)。NetEQ 会根据网络状态动态调整缓冲区大小,同时还能做丢包补偿。视频 JitterBuffer 则更关注帧的完整性,一帧没到齐不会送去解码。

缓冲区管理的核心逻辑可以用下面这张图来表示:

接收端缓冲区管理与抖动消除流程 网络 RTP 包 音频 JitterBuffer NetEQ 动态调整 视频 JitterBuffer 帧完整性检查 解码器 解码后缓冲区 播放输出 音画同步控制器 比较时间戳差值,调整播放速度

嗯,这里要注意一个细节:解码后缓冲区的作用。音频和视频解码后,它们的播放时间可能还是对不齐。为什么?因为解码本身也有延迟,而且音频和视频的解码延迟不一样。所以我们需要一个解码后缓冲区,让音频或视频等一等对方,直到时间戳对齐了再播放。

核心原则:抖动缓冲区解决的是网络抖动问题,解码后缓冲区解决的是音画同步问题。两个缓冲区各司其职,不要混为一谈。

我曾经在一个项目中看到有人把抖动缓冲区和解码后缓冲区合并了,结果网络一抖,音画同步也跟着乱。后来我强制拆成两个独立的缓冲区,各自做各自的事,问题就清晰多了。

最后总结一下这三个模型的关系:

  • 发送端同步模型:保证时间戳准确,这是基础。
  • 接收端同步模型:把时间戳映射到统一时间轴,计算差值。
  • 缓冲区管理与抖动消除:吸收网络抖动,对齐播放时间。

这三层缺一不可。发送端时间戳错了,后面再怎么调也没用。接收端映射错了,差值计算就不准。缓冲区管理不好,播放就会卡顿或延迟。只有三层都做好,音画同步才能真正稳定。