3、音视频时钟模型:系统时钟、音频时钟、视频时钟的同步机制与主时钟选择策略
做音视频开发,绕不开一个核心问题——时钟。
你想想看,音频和视频是两个完全独立的硬件模块。音频有它的采样率,视频有它的帧率。它们各自按自己的节奏跑,凭什么能对上?
说白了,我们需要一套时钟模型,让它们听同一个指挥。
3.1 三种时钟,各司其职
一个典型的播放器或推流端里,至少存在三种时钟:
| 时钟类型 | 驱动源 | 特点 |
|---|---|---|
| 系统时钟 | 操作系统 CLOCK_MONOTONIC |
单调递增,不受 NTP 调整影响,精度高 |
| 音频时钟 | 音频设备驱动(ALSA / WASAPI / CoreAudio) | 以音频采样率为基准,通常最稳定 |
| 视频时钟 | 视频帧的 PTS / 显示设备刷新率 | 受渲染延迟、丢帧影响,波动较大 |
我刚开始做播放器时,以为直接用系统时间戳就行。结果发现音频和视频的 PTS 根本对不上——因为音频驱动有自己的晶振,系统时钟也有自己的晶振,两个晶振的漂移方向还不一样。
核心认知:三种时钟的精度和稳定性不同。音频时钟最可靠,视频时钟最不可靠,系统时钟居中但最通用。
3.2 主时钟选择策略
既然有三个时钟,总得选一个当老大。这就是主时钟(Master Clock)的概念。
我个人习惯按场景来选:
- 音频为主时钟:最常见。因为人耳对音频抖动的敏感度远高于眼睛。只要音频连续,视频稍微跳一帧,用户感知不强。
- 视频为主时钟:少见。比如屏幕录制场景,视频帧率必须严格对齐显示器刷新率,音频可以适当变速。
- 系统时钟为主时钟:用于纯网络同步场景,比如 WebRTC 的 RTP 时间戳对齐。
嗯,这里要注意:选音频做主时钟,不代表视频完全跟着音频跑。而是说,音频时钟作为参考基准,视频通过重采样或丢帧/插帧来对齐。
我的经验:在 Android 上做低延迟播放时,我遇到过音频时钟突然跳变的情况(驱动 bug)。后来加了一层时钟滤波,用滑动平均来平滑音频时钟的瞬时抖动。效果立竿见影。
3.3 同步机制:谁跟谁对齐?
选好主时钟后,剩下的时钟需要向它看齐。具体怎么做?
3.3.1 音频为主时钟时的同步流程
- 音频回调函数每次写入数据时,记录当前音频时钟值。
- 视频渲染线程拿到视频帧的 PTS,与音频时钟比较。
- 如果视频帧 PTS 落后音频时钟太多,直接丢帧。
- 如果视频帧 PTS 超前音频时钟太多,等待或插入重复帧。
我曾经在项目中遇到一个坑:音频时钟和视频 PTS 的时间基不一样。音频用 44100 Hz 采样率,视频用 90000 Hz 时间基。直接比较数值?那肯定错得离谱。
正确的做法是统一换算成微秒或毫秒:
// 音频时钟:当前播放到第 44100 个采样点
// 采样率 44100 Hz,时间基 1/44100
audio_time_us = audio_clock * 1000000 / 44100;
// 视频 PTS:90000 时间基
video_time_us = video_pts * 1000000 / 90000;
// 现在可以比较了
if (video_time_us < audio_time_us - 20000) {
// 视频落后超过 20ms,丢帧
} else if (video_time_us > audio_time_us + 20000) {
// 视频超前超过 20ms,等待
}
3.3.2 视频为主时钟时的同步流程
这个场景我遇到得不多,但有一次做 VR 头显的渲染优化时用过。VR 对帧率要求极高,音频可以稍微拉伸或压缩来匹配视频。
- 视频渲染完成一帧后,记录当前视频时钟。
- 音频重采样模块根据视频时钟的进度,动态调整播放速度。
- 调整范围通常控制在 ±5% 以内,否则人耳能感知到音调变化。
注意:视频为主时钟时,音频变速处理必须使用高质量重采样算法(如 libsamplerate 的 SRC_SINC_BEST_QUALITY)。简单的线性插值会导致明显的音质劣化。
3.4 时钟模型框架图
下面这张图展示了我常用的三层时钟模型。你可以看到主时钟如何驱动其他模块:
3.5 实际项目中的避坑指南
理论说完了,聊聊我踩过的坑。
我曾经犯过的错:在 iOS 上做直播推流时,我直接用 CACurrentMediaTime() 作为系统时钟。结果发现音频采集回调的时钟和这个系统时钟有固定的偏移。后来改用音频设备的 AudioUnit 时间戳才解决。
几个关键点总结一下:
- 不要混用不同时间基的时钟。统一换算成微秒或纳秒再比较。
- 音频时钟优先。除非你有特殊需求,否则永远让视频去迁就音频。
- 加一层滤波。原始时钟值可能有瞬时抖动,用滑动平均或卡尔曼滤波平滑一下。
- 预留同步容差。±20ms 以内人眼基本感知不到,没必要追求绝对精确。
一个小技巧:调试时把三种时钟的差值打印到日志里。如果发现音频时钟和系统时钟的偏差持续增大,说明硬件晶振漂移严重,需要考虑时钟重同步。
好了,时钟模型这部分就讲到这里。记住一句话:选好主时钟,统一时间基,剩下的就是工程细节了。