用户手里的倍速是一条档位滑杆,引擎里接住的却不是一档参数:四倍是一条线,线两边各有一套跑法。四倍以内每帧都解,音频变速不变调,画面靠丢帧追节奏;四倍以上解码已经追不上播放速度,整条链换成 I 帧模式——非关键帧在进包处就丢掉,音频整条退出,进度条换判据。这篇讲两边的跑法,以及从一边切到另一边时撞上的时序问题。
倍速在四倍分成两套跑法:四倍以内全量解码、音频变速不变调,四倍以上只解关键帧、音频整条退出。这篇讲分界从哪来、I 帧模式在进包处与渲染端各做什么,以及穿越四倍时那一下怎么接。
四倍以内,视频照常解码,显示时长按倍速缩放,落后了丢帧追赶;音频照常出声,只是时间轴被压缩。这一半的机制——变速在交给设备之前完成、时钟怎么走——音频渲染与音画同步两篇写过,这里不展开。
四倍这条线不是拍出来的。解码是这条链上最重的一段,全负荷跑也就实时四倍上下:两倍到四倍之间,解码线程连时间片都不让出,能解就立刻解下一包;再往上把每帧都解出来已经追不上,只能不解非关键帧。代码里那句注释写的就是这件事:超过四倍后只解码 I 帧,相反可以慢下来了。所以分界两侧是两条策略——一侧全速解、靠渲染端丢帧控节奏,另一侧干脆不解,把解码压力降下来。
丢弃的落点也只留一处:解码侧那段丢帧的分支在代码里是注释掉的,实际丢弃全发生在进包处。两处都丢,等于两个地方各扣各的,节奏反而乱。
恰好四倍仍按全量处理,丢弃只对超过四倍生效。
开了只解 I 帧的开关、倍速又超过四倍,这两个条件合成一个真值,三处共用:进包处、渲染节奏、音频解码线程的超时豁免。共用是刻意的——音频解码线程在 I 帧模式期间要持续重置超时,因为音频包被丢掉、无包可解是预期;要是那边的判据单独写成「倍速大于四」,开关关掉、音频包正常流动时,豁免还在生效,真实的解码故障就被吞掉了。

进包处丢两类:视频的非关键帧直接丢,音频包也直接丢。配置帧照常放行——降回四倍以内时,新解码器要靠它立刻接上。录制不受影响:录制拿的是完整原始流,丢帧只是播放策略。
音频为什么是整条丢,而不是静音?静音的话音频管线还在按原速消费,包队列很快堆满,入队阻塞之后解封装就喂不进视频包,视频的 I 帧管线反而被饿死;反过来消费更快时,解封装又会狂读,进度条乱闪。在进包处丢掉,解封装只被视频 I 帧管线按倍速自节流,音频侧不需要任何节流参数。代价是同步也没了对象——四倍以上音频不渲染,音画同步一并关掉,降回来再恢复。
I 帧模式下,每一帧只显示「自己的时长除以倍速」这么久:两秒一个关键帧的片源,八倍速就是每帧两百五十毫秒。普通模式的三件事在这里都要改:关键帧之间本来就隔得远,不把长间隔判成时间戳跳变;帧本来就少,不做丢帧;没有音频时钟可追,渲染节奏只按自己的缩放时长走。
进度条也换了判据,取渲染到哪,不取读到哪。四倍以上磁盘或网络会超前读,视频队列里能缓存上百个关键帧,相当于几十秒到几分钟的内容,拿读取位置当进度,进度条会说谎。
上面都是播放侧的策略。还有一类流本身就是纯关键帧:一些切片、监看类的流,服务端只发 I 帧。这种流不用倍速就会撞上,引擎在源侧自动识别——连续收到时间戳不同的关键帧就进入 I 帧模式,收到 P、B 帧自动退出。另一条是预判:只有真有倍速语义的源(服务端能按倍速切流的)才在倍速下预判进入;对包流不随倍速变化的本地与点播源,预判只会白静音、打乱时钟。
两个方向切换,坑都在切换那一下,而且对称。

升速(四倍以内进到四倍以上):门闸只拦新包,包队列里已经积压的几十秒 P、B 旧包要靠解码排干才能真进入 I 帧模式,这个排干是秒级到几十秒——点了八倍速,画面还在慢慢放一阵,然后突然开始跳。修法是把它当成一次 seek:可重新定位的源,直接重定位到当前渲染位置,冲掉旧包,重启后的包全部经过已经打开的丢弃。四倍以上内部调速(八倍调六倍)本来没有积压,不走这条路。
降速(回到四倍以内)是另一个方向:四倍以上音频包被丢、解封装又超前读,音频恢复的位置和视频渲染位置对不上,进度条跟着音频时钟前后跳。修法一样,重定位到渲染位置;这一半音画同步那篇讲过。不可重定位的源(纯直播)没有这两条路,误差只能靠缓冲和后续同步慢慢收敛。
还有一处不起眼的滞后:变速插件的播放态一旦标定,回到一倍速并不立刻复位。半倍、两倍期间攒下的音视频解码轴发散要等队列排空才收敛,在那之前对齐看门狗会把这段发散误报成异常,所以这个闩一直留到换片或关闭才解。
体验上这道线很好认:四倍以上声音本来就不渲染,穿过去是「有声音和没声音」的区别,不是音量变小;变调只发生在四倍以内、且变速插件缺席的降级路径上。
引擎 avox 在 GitHub 开源,仓库 xxxzhou/avox。
系列《从零做一个全平台播放器》按施工顺序发,本篇是 6.2;已发各篇收在这里:https://cloud.tencent.com/developer/column/109457
上一篇:6.1 seek 先挑入口,要准就多解一段 https://cloud.tencent.com/developer/article/2781763
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。