音视频同步

音视频同步简介

声卡和显卡均是以一帧数据来作为播放单位,如果单纯依赖帧率及采样率来进行播放,在理想条件下,应该是同步的,不会出现偏差。

  • 视频:帧率,表示视频一秒显示的帧数。

  • 音频:采样率,表示音频一秒播放的sample数。

一个AAC音频frame每声道1024个sample,一帧播放时长duration=(1024/44100)×1000ms = 23.22ms;一个视频frame播放时长duration=1000ms/24 = 41.67ms。声卡虽然是以音频采样点为播放单位,但通常我们每次往声卡缓冲区送一个音频frame,每送一个音频frame更新一下音频的播放时刻,即每隔一个音频frame时长更新一下音频时钟,实际上ffplay就是这么做的。理想情况下,音视频完全同步,音视频播放过程如下图所示:

但实际情况,往往不同步,原因如下:

  • 一帧的播放时间,难以精准控制。音视频解码及渲染的耗时不同,可能造成每一帧输出有一点细微差距,长久累计,不同步便越来越明显。(例如受限于性能,42ms才能输出一帧)

  • 音频输出是线性的,而视频输出可能是非线性,从而导致有偏差。

  • 媒体流本身音视频有差距。(特别是TS实时流,音视频能播放的第一个帧起点不同)

所以,解决音视频同步问题,引入了时间戳:

首先选择一个参考时钟,时间是线性递增的;编码时依据参考时钟给每个音视频数据块都打上时间戳;播放时,根据音视频时间戳PTS及参考时钟,来调整播放。视频和音频的同步实际上是一个动态的过程,以参考时钟为标准,放快了就减慢播放速度;播放快了就加快播放的速度。
 

1 FFmpeg简易播放器流程图

音视频同步的目的是为了使播放的声音和显示的画面保持一致。

视频按帧播放,图像显示设备每次显示一帧画面,视频播放速度由帧率确定,帧率指示每秒显示多少帧;

音频按采样点播放,声音播放设备每次播放一个采样点,声音播放速度由采样率确定,采样率指示每秒播放多少个采样点。

如果仅仅是视频按帧率播放,音频按采样率播放,二者没有同步机制,即使最初音视频是基本同步的,随着时间的流逝,音视频会逐渐失去同步,并且不同步的现象会越来越严重。

这是因为:一、播放时间难以精确控制,二、异常及误差会随时间累积。所以,必须要采用一定的同步策略,不断对音视频的时间差作校正,使图像显示与声音播放总体保持一致。

音视频同步的方式基本是确定一个时钟(音频时钟、视频时钟、外部时钟)作为主时钟,非主时钟的音频或视频时钟为从时钟。在播放过程中,主时钟作为同步基准,不断判断从时钟与主时钟的差异,调节从时钟,使从时钟追赶(落后时)或等待(超前时)主时钟。

FFmpeg简易播放器流程图

按照主时钟的不同种类,可以将音视频同步模式分为如下三种:

  • 音频同步到视频,视频时钟作为主时钟;
  • 视频同步到音频,音频时钟作为主时钟;
  • 音视频同步到外部时钟,外部时钟作为主时钟;

ffplay默认的同步方式:视频同步到音频。
 

2 I帧/IDR帧/P帧/B帧

I帧:I帧(Intra-codedpicture,帧内编码帧,常称为关键帧)包含一幅完整的图像信息,属于帧内编码图像,不含运动矢量,在解码时不需要参考其他帧图像。因此在I帧图像处可以切换频道,而不会导致图像丢失或无法解码。I帧图像用于阻止误差的累积和扩散。在闭合式GOP中,每个GOP的第一个帧一定是I帧,且当前GOP的数据不会参考前后GOP的数据。

  • IDR帧:IDR帧(InstantaneousDecodingRefreshpicture,即时解码刷新帧)是一种特殊的I帧。当解码器解码到IDR帧时,会将DPB(DecodedPictureBuffer,指前后向参考帧列表)清空,将已解码的数据全部输出或抛弃,然后开始一次全新的解码序列。IDR帧之后的图像不会参考IDR帧之前的图像,因此IDR帧可以阻止视频流中的错误传播,同时IDR帧也是解码器、播放器的一个安全访问点。

  • P帧:P帧(Predictive-codedpicture,预测编码图像帧)是帧间编码帧,利用之前的I帧或P帧进行预测编码。

  • B帧:B帧(Bi-directionallypredictedpicture,双向预测编码图像帧)是帧间编码帧,利用之前和(或)之后的I帧或P帧进行双向预测编码。B帧不可以作为参考帧。B帧具有更高的压缩率,但需要更多的缓冲时间以及更高的CPU占用率,因此B帧适合本地存储以及视频点播,而不适用对实时性要求较高的直播系统。

3 GOP

GOP(Group Of Pictures,图像组)是一组连续的图像,由一个I帧和多个B/P帧组成,是编解码器存取的基本单位。GOP结构常用的两个参数M和N,M指定GOP中两个anchor frame(anchor frame指可被其他帧参考的帧,即I帧或P帧)之间的距离,N指定一个GOP的大小。例如M=3,N=15,GOP结构为:IBBPBBPBBPBBPBB

GOP有两种:闭合式GOP和开放式GOP。

  • 闭合式GOP:闭合式GOP只需要参考本GOP内的图像即可,不需参考前后GOP的数据。这种模式决定了,闭合式GOP的显示顺序总是以I帧开始,以P帧结束;

  • 开放式GOP:开放式GOP中的B帧解码时可能要用到其前一个GOP或后一个GOP的某些帧。码流里面包含B帧的时候才会出现开放式GOP。

在开放式GOP中,普通I帧和IDR帧功能是有差别的,需要明确区分两种帧类型。在闭合式GOP中,普通I帧和IDR帧功能没有差别,可以不作区分。

开放式GOP和闭合式GOP中I帧、P帧、B帧的依赖关系如下图所示:

4 DTS和PTS

DTS(Decoding Time Stamp, 解码时间戳),表示压缩帧的解码时间。 PTS(Presentation Time Stamp, 显示时间戳),表示将压缩帧解码后得到的原始帧的显示时间。 音频中DTS和PTS是相同的。视频中由于B帧需要双向预测,B帧依赖于其前和其后的帧,因此含B帧的视频解码顺序与显示顺序不同,即DTS与PTS不同。当然,不含B帧的视频,其DTS和PTS是相同的。下图以一个开放式GOP示意图为例,说明视频流的解码顺序和显示顺序:

以图中“B[1]”帧为例进行说明,“B[1]”帧解码时需要参考“I[0]”帧和“P[3]”帧,因此“P[3]”帧必须比“B[1]”帧先解码。这就导致了解码顺序和显示顺序的不一致,后显示的帧需要先解码。

5 总结

ffplay音视频同步方式,以audio为参考时钟,video同步到音频:

  • 获取当前要显示的video PTS,减去上一帧视频PTS,则得出上一帧视频应该显示的时长delay;

  • 当前video PTS与参考时钟当前audio PTS比较,得出音视频差距diff;

  • 获取同步阈值sync_threshold,为一帧视频差距,范围为10ms-100ms;

  • diff小于sync_threshold,则认为不需要同步;否则delay+diff值,则是正确纠正delay;

  • 如果超过sync_threshold,且视频落后于音频,那么需要减小delay(FFMAX(0, delay + diff)),让当前帧尽快显示。

  • 如果视频落后超过1秒,且之前10次都快速输出视频帧,那么需要反馈给音频源减慢,同时反馈视频源进行丢帧处理,让视频尽快追上音频。因为这很可能是视频解码跟不上了,再怎么调整delay也没用。

  • 如果超过sync_threshold,且视频快于音频,那么需要加大delay,让当前帧延迟显示。

  • 将delay*2慢慢调整差距,这是为了平缓调整差距,因为直接delay+diff,会让画面画面迟滞。

  • 如果视频前一帧本身显示时间很长,那么直接delay+diff一步调整到位,因为这种情况再慢慢调整也没太大意义。

  • 考虑到渲染的耗时,还需进行调整。frame_timer为一帧显示的系统时间,frame_timer+delay- curr_time,则得出正在需要延迟显示当前帧的时间。

图中,小黑圆圈是代表帧的实际播放时刻,小红圆圈代表帧的理论播放时刻,小绿方块表示当前系统时间(当前时刻),小红方块表示位于不同区间的时间点,则当前时刻处于不同区间时,视频同步策略为:

  • 当前时刻在 T0 位置,则重复播放上一帧,延时 remaining_time 后再播放当前帧;
  • 当前时刻在 T1 位置,则立即播放当前帧;
  • 当前时刻在 T2 位置,则忽略当前帧,立即显示下一帧,加速视频追赶;
     

相关推荐

  1. 【WebRTC---源码篇】(二十五)视频同步

    2024-01-21 01:04:01       46 阅读
  2. 【webrtc】m98: 视频同步 StreamSynchronization

    2024-01-21 01:04:01       17 阅读
  3. 播放器的视频同步问题:ffplay

    2024-01-21 01:04:01       11 阅读
  4. FFmpeg:自实现ijkplayer播放器--11视频同步

    2024-01-21 01:04:01       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-21 01:04:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-21 01:04:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-21 01:04:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-21 01:04:01       20 阅读

热门阅读

  1. 算法训练营Day37(贪心6)

    2024-01-21 01:04:01       44 阅读
  2. 【力扣刷题练习】103. 二叉树的锯齿形层序遍历

    2024-01-21 01:04:01       44 阅读
  3. Webpack5入门到原理3:基本配置

    2024-01-21 01:04:01       35 阅读