音视频同步基础知识—ffplay.c源码分析
作者:罗上文,微信:Loken1,公众号:FFmpeg弦外之音
之前的文章,已经把音频,视频的 解码线程,播放线程通通讲了一遍,现在到了播放器实现最复杂的功能之一,就是音视频同步。
FFplay 支持 3 种同步方式,如下:
1,以音频时钟为主时钟,默认方式。
2,以视频时钟为主时钟。
3,以外部时钟为主时钟。
因为音频流的连续性非常强,例如一秒需要播放 48000 个样本。而视频流通常1秒只需播放 24 帧。而且人的耳朵对音频特别敏感。
所以第一种方式以音频时钟为准是最常用的,其他两种方式几乎没有应用场景。
先普及一下音视频同步的一些基本概念。
1,为什么需要进行音视频同步?
答:由于计算机系统大部分是分时系统,所以当负载过高或者设备性能差的时候,音频播放线程 或者 视频播放线程会卡顿,调度不过来,导致视频画面已经更新了,但是声音还没放出来。这种不同步的差异如果越积越大,就会明显体验不好。典型的场景就是,演讲视频里面的口型跟声音。
2,如何把音频播放 跟 视频播放做到完全同步?
答:无法做到,音频与视频的播放,几乎不能完全同步,不信你可以用 FFplay 播放一个视频试试,控制台输出的 A-V 大多数时间都不是 0 。完全同步的话 A-V 就是 0。
通常我们只需要把音视频不同步的程度,控制在一个合理的阈值内 就可以了。人的感官没有那么敏感的,只要在阈值内,就感受不到不同步了。
虽然不同步是常态,但是感受不到就不算是了。
3,如果只有视频流,没有音频流,如何进行同步?
答:不需要进行同步,只有音频流跟视频流同时播放的时候才需要同步。
由于每一个音频样本就打一个 pts
过于繁琐与麻烦,而且音视频不需要完全同步,控制在阈值内即可。所以大部分音频标准会把多个样本塞进去一个帧,只对一个帧打 PTS 即可。对于 aac
来说,通常一个 AVFrame
里面有 1024 个音频样本。
因此,如果采样率是 48000 每秒,那一帧音频(AVFrame)可以播放 0.021s。
无论是音频还是视频,播放的逻辑都是,在预定的时间(pts)播放对应的 AVFrame
。
还是以 juren-30s.mp4 为例,它的帧率是 1/24,采样率是 1/48000。也就是每隔0.041s 播放一帧视频,每隔 0.021s 播放一帧音频。
因此这个文件的播放过程应该如下图:
上面的流程图是,在下午2点整的时候,播放 juren-30s.mp4
文件,那在 14:00:00
的时候就会播放第一帧视频跟第一帧音频。按照预定的时间 pts,第四帧视频应该在 14:00:00:123
的时刻播放,第 7 帧音频差不多也需要播放,所以算同时播放第 7 帧音频吧。
补充:第7帧音频预定播放时间是 14:00:00:126
,有一点点误差,但是为了方便讲解,我假设第七帧的预定时间是 14:00:00:123
。
假如,计算机突然打开另一个软件,做了大量计算,导致视频播放线程没及时调度过来,从而导致 第四帧视频在 14:00:00:143
的时刻才播放,因此视频流就比预定的时候 14:00:00:123
慢了 0.02s。
那是不是就说明音视频开始不同步了?
不是,视频帧不在预定的时间播放,不代表音视频不同步。因为音频帧也可能不在预定的时间播放,假设音频播放线程也没有及时调度过来,也是在第 14:00:00:143
的时刻才播放第 7 帧音频。
那在 14:00:00:143
这一刻,音视频就是完全同步的,虽然他们没有在预定的时间播放。
再假设一个场景,系统卡顿,导致第四帧视频在 14:00:00:153
的时刻才播放,但是音频播放线程卡顿没那么严重,在 14:00:00:136
的时候已经开始播放第 7 帧了。
对应视频流, 14:00:00:153
比预定的时间 14:00:00:123
慢了 0.03s,那是不是就说明视频比音频慢了 0.03s?
也不是,因为 音频流本来应该在 14:00:00:126
的时刻播放第 7 帧,但到了 14:00:00:136
才播放第 7 帧,音频也慢了 0.01s。
正确的描述是,视频流比预定的时间慢了 0.03s,音频流比预定的时间慢了 0.01s,预定的时间可以对消掉,所以,计算过程如下:
视频pts - 预定时间 = 0.03
音频pts - 预定时间 = 0.01
视频pts - 音频pts = 0.02
现在已经计算出 音频 跟 视频 的时间差,但是以音频为主时钟 跟 与 视频为主时钟又有什么区别呢?无论以哪个为主时钟,时间差都是一样的啊?
答:没错,时间差都是一样的。
以音频为主时钟的逻辑是这样,按 juren-30s.mp4
的 pts 来说,在听到 第 7 帧音频的第一个样本的时候,你的眼睛就应该同时看到第 4 帧视频画面。
第 7 帧音频 跟 第 4 帧视频应该在同一时刻播放,假设音频播放线程卡顿,在 14:00:00:153
的时候才播放第7帧音频,但是视频播放线程无卡顿,那 14:00:00:123
的时候,视频播放线程本来应该去取第四帧视频播放,但是由于第7帧音频还未播放,所以第三帧视频需要继续显示0.03s,等待音频。
这就是以音频为主时钟的逻辑,拉长或者缩短视频帧的播放时长,或者丢弃视频帧。
如果是以视频为主时钟的情况,那在 14:00:00:123
的时候,视频播放线程就正常去取第四帧播放就行,因为确实已经到了第四帧播放的时间点,音频慢了,就缩短音频帧的播放时长就行,不需要视频流慢下来。
以视频为主时钟,就是拉长或者缩短音频帧的播放时长,但是不会丢弃音频帧。音频帧连续性太长,丢帧很容易被耳朵发现。
上面的例子是假设用来说明的,一般音视频不会一下子就差那么多,不同步的程度是一点一滴累加的。当累加的值超过阈值,就需要我们的程序做干预。
感谢 NETINT(镕铭微电子) 赞助《FFmpeg原理》免费版一书的服务器费用,下面是 VPU 产品介绍