VS2017 MFC下GStreamer切换视频从原时间戳续播的首帧闪显问题求解
解决方案
首帧闪显的核心原因是当前执行顺序存在逻辑漏洞:创建新pipeline后直接切PLAYING状态会先触发preroll流程,sink元件会先渲染新视频的第一帧到画布,之后seek请求才会生效,两者的时间差就导致了首帧异常显示。
方案1:状态切换前预seek(最优方案,改动最小)
- 调整pipeline状态切换和seek的执行顺序:创建新pipeline后先不要切PLAYING,先将pipeline设置为
PAUSED状态触发preroll - 收到pipeline的
PAUSED状态通知后,立即发送seek事件,目标时间戳就是上一个视频记录的播放位置,注意seek flag要带上GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT保证立即刷新缓冲 - seek执行完成后再将pipeline切到
PLAYING状态,此时sink渲染的第一帧就是seek目标位置的帧,不会出现首帧闪显 - MFC窗口绑定的视频sink(一般是
d3dvideosink/glimagesink)可以在切换pipeline期间临时设置sink->show-preroll-frame = FALSE,进一步避免preroll帧意外渲染
方案2:双pipeline预加载方案(适合切换速度要求更高的场景)
- 维护两个pipeline实例:当前正在播放的活跃pipeline,以及预加载用的备用pipeline
- 需要切换视频前,先在备用pipeline完成加载、seek到目标时间戳、切到PAUSED状态完成preroll的全部流程
- 切换时直接将显示sink的绑定从活跃pipeline转到备用pipeline,再将旧活跃pipeline销毁/重置为下一次预加载使用,切换过程无缓冲等待,完全不会出现闪显
- 注意要做好两个pipeline的状态同步,避免内存泄漏
额外优化点
- 记录上一个播放位置的时候尽量对齐关键帧,seek的时候可以减少解码等待时间,进一步降低切换延迟
- 如果用的是
playbin组件,可以直接通过playbin的current-position属性读取当前时间戳,不需要手动监听pipeline时钟,精度更高 - MFC窗口在切换视频的间隙可以填充上一帧的画面,不要清空画布,过渡会更顺滑
内容的提问来源于stack exchange,提问作者otter
相关产品推荐
相关产品推荐

