前端流媒体播放器“音画同步”的解决策略。

1,095 阅读4分钟

为何要实现音画同步

上一篇文章详细介绍了,一个流媒体播放器应该如何实现,这篇文章对该如何解决音画同步问题给出详细的经验和解决方案。

对于在线教育/安防/直播流,等实时性要求较为高,且网络延时不考虑的情况下,要考虑好他的渲染时机和播放时机,不能出现画面出来,声音滞后。

一般来说,人的主观意识感受在200ms-500ms之间是听不出来延后的,当延迟大于500ms以上,就要做音视频同步的兼容了。

服务端丢过来的数据,播放器的外层做抽帧,假设我们的视频流是25fps,两个I帧大概要相差40ms左右,解码时间(DTS)需要做处理,在没有B帧的情况下,可以PTS=BTS。

要考虑倍速播放的场景,在低倍速(4倍速的情况下),很可能出现不同步的情况,具体看下面没做音画同步的情况所示。

没做音画同步的现状

image.png

image.png

这里的到单位都是ms。

视频的数据明显快于音频,这里就要采用下文中的,视频以音频为基准的播放策略。 同步音频到视频,将视频缓存起来。

音画不同步的常见几种场景

  • 音频快于视频。
  • 视频快于音频。
  • 低倍速下(4倍速以下)音频快。
  • 低倍速下(4倍速以下)视频快。

高倍速(4倍速)以上服务端不向播放器侧传输音频数据,这种情况可以不考虑。

DTS和PTS的概念

音频和视频分别有时间戳,音画同步的过程,也就是需要将当前处理的音频和视频时间戳保持在一定的差值内,防止用户感知到音频和视频内容时序上的差异。 DTS、PTS 的概念如下所述:

DTS(Decoding Time Stamp) :即解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧的数据。单位为1/90000 秒。

PTS(Presentation Time Stamp) :即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。单位为1/90000 秒。

作者:littleRabbit
链接:juejin.cn/post/684490…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

如何测试音视频同步

用ffmpeg把一段相声视频输出出来,然后目测声音+嘴型是不是对的上的,再把对应的时间戳打印出来即可。(这个操作很骚。)

第二种方式就是直接让他播完,然后看看音频是否播完。

生产-消费者模型

image.png

需要设计一个缓存的内存最大值,一般1080p,25fps的视频流,一帧大概3M,我们最大可以缓存4096M,也就是4G

image.png

生产者:维护一个yuv数据的队列,需要不断输入yuv帧数据缓存起来。

image.png

消费者:利用一个定时器,当yuv队列存在的时候,不断的拿出第一帧,然后消费第一帧。

image.png

音画同步的解决策略

首先上层将播放器的video标签的dom元素,透传到播放器层,播放器可以通过videoEl拿到当前的实时音频时间(currentTime)。

image.png

当有音频的情况下,且视频快于音频。 可以通过currentTime这个做为视频的渲染时机来渲染画面。可以根据DTS和PTS去判断是否渲染。

以视频的时间戳为基准,同步音频到视频。 如果音频慢了,就加快播放速度,或者丢掉部分的音频帧数据。

以音频的时间戳为基准,同步音频到视频。 视频慢了则加快播放或者丢掉部分的视频帧数据。 视频快了则延迟播放,继续渲染上一帧。

简单来说,就是谁快就等待谁。

音视频共用一个时钟,同步执行,如果说有做倍速播放,更推荐用这种方式去处理,通过维护一个时钟来改变播放速度,并且保持音视频同步。

实际开发中,一般来说都是以音频为基准,从人体的视觉和听觉来说,听觉要远远大于视觉的,从感知能力上。

在渲染的时候,视频帧/音频拿到currentTime的时候同时输出。

graph TD
裸码流 --> 解复用 -->视频帧/音频 --> 渲染/播放 --> 显示器/扬声器
ACC音频 --> 解复用 

image.png

理论上来说,4倍速以下,视频需要跟着音频的时间进度走,高倍速(4倍以上)需要有自己的一套时间处理逻辑。

在高倍速(4倍速)播放场景下,音画同步如何解决。(难点)

4倍以上无法根据声音去定位视频该播放那一帧,需要有自己的视频播放逻辑,要从wasm接口的外层去控制喂入数据的频率。

持续更新。。。。