8.2.2 音画同步:主时钟的三种策略
8.2.1 的第三问停在岔路口:音画两条时钟必然漂移,必须钦定一条为主。候选者有三——音频主时钟、视频主时钟、外部时钟。这不是口味问题,三条路通向三套完全不同的追随机制。ffplay 参考实现把三套都造了出来,还附赠一条回退链 [2]:
回退链的朝向已经泄露了答案的排序:音频在,则音频为主——这也是 ffplay 的出厂默认 [2]。为什么是它?看完三套机制各自的本钱,答案会自己浮出来。
视频做从时钟:丢与重的粗粒度艺术
视频追随主时钟的手段,糙得像裁缝只用剪刀——要么丢,要么重,没有中间态。ffplay 的判决围绕一个偏差值展开:diff = 视频时钟 - 主时钟,再配一条随帧间隔浮动的同步阈值带,钳制在 40ms 到 100ms 之间 [2]:
- 视频落后(偏差负向超阈):把显示间隔压短,加速追帧——
delay = max(0, delay + diff),落后狠了可以压到零,连珠上屏 - 视频超前且本帧间隔本就偏大:顺势延长本帧显示,把超前量消化掉
- 视频超前但间隔正常:直接双倍间隔,同一帧重放一轮
- 追也追不上:队列里已有下一帧候场,而当前时刻连本帧的应显结束点都过了——丢弃本帧,认栽赶路 [2]
粗粒度不等于粗糙。注意丢帧的判决条件:不是 "晚了一律丢",而是 "下一帧已在候场才丢"——库存见底时宁可迟到,也不能让画面断供。这套丢重逻辑在 8.3 的实测里会交出成绩单:三百帧的测试片上,音频主时钟把偏差摁在 ±40ms 以内,代价是 83 帧候补被丢弃——看得见的丢帧,换听不出的同步。
音频做从时钟:采样级微调的细针脚
换成视频做主、音频追随,画风骤变。音频绝不丢帧——人耳对声音断续的敏感度远超人眼对画面丢帧,砍一段采样,听众立刻皱眉。ffplay 的做法是微调每帧的采样数 [2]:
- 偏差先做一阶平滑,取最近 20 次的均值——拒绝被单次抖动带偏
- 校正量按
偏差 × 采样率折算成采样数,但钳制在原采样数的 ±10% 以内——每次只挪一小步 - 偏差一旦达到 10 秒量级,判定为 PTS 异常,放弃校正并清空统计——这不是漂移,是数据坏了
粗剪对细针:视频的单位是帧(33ms 起步),音频的单位是采样(48kHz 下约 0.02ms)。视频只能大步腾挪,音频可以无声无息地把偏差揉碎在千分之一秒的粒度里。两相对照,默认策略的理由一目了然——让粗粒度的那侧去追随细粒度的那侧,追随动作才不可能被察觉。视频丢一帧,33ms 的空白藏在视觉暂留里;音频丢一帧,耳朵当场报警。工业界让视频迁就音频,不是偏爱,是生理。
外部时钟:谁都不信,只信墙钟
第三条路:音画都不做主,共同对齐一台外部墙钟。流媒体直播是它的主场——7.4 的低延迟直播里,音频可能稀疏甚至缺席,主时钟无人可任。
外部时钟自身也要防漂移,ffplay 给它配的舵是缓冲水位反馈调速 [2]:任一队列入队包数跌到 2 帧以下,判定供料不足,播放速度微降;水位回升,速度恢复。调速边界钳在 0.900 到 1.010 之间,步进千分之一——减速明显(断供是急症,得下猛药),加速克制(观众对变调敏感,对慢半拍迟钝)。不对称的调速区间,写的是同一份对人性的体谅。
小结:同步即政治
回看三种策略:主时钟是音画世界的宪政设计——谁做主,决定了谁的体验被优先保护,谁承担全部追随成本。音频主时钟保护听觉、视频主时钟保护视觉、外部时钟保护直播的时效,各守一种价值排序。主时钟解决了 "对齐谁",但还有半边问题悬着:帧从哪里来、何时来,全看网络脸色。下一节,抖动缓冲——播放器与网络和解的艺术。