音视频同步算法
题外话
物理中,音调指乐音的高低,响度指声音的大小强弱,音色指声音的特色,要区分开。
视频:色深、色域、亮度。
人耳的听觉范围是20Hz-20KHz,这个范围内的信号成为音频信号,称为可闻声,而人耳对中频段1-4KHz最敏感;
对于音色来说,在众多因素中影响较大的是声音的频谱分布 (Spectral Envelope) 以及时间包络曲线 (Time Envelope) 。除此之外,平均频率 (Mean Frequencies) 、声音中的噪声 (Noise) 、频谱中心 (Spectral Centroid) 、一些随机成分 (Irregularity Parameters) 和频谱的变化 (Spectral Flux) 等也会对音色造成影响。 [1] [2]
不同的声源之间,最主要的区别便是形状与材质的区别。而形状与材质的不同,则决定了物体振动模态的不同。振动模态不同便导致了其振动产生的频谱不同。因此,频谱分布是区分不同声源的一个重要特征。
音视频播放流程
由于视频播放和音频播放的差异,所以需要将两者进行同步化,否则就会出现音画不同步。
为什么需要音视频同步
声卡和显卡均是以一帧数据来作为播放单位,如果单纯依赖帧率及采样率来进行播放,在理想条件下,应该是同步的,不会出现偏差。
视频按帧率播放,音频按采样率播放,二者没有同步机制,即使一开始音视频是同步的,随着时间的流逝,音视频会渐渐失去同步,并且不同步的现象会随着时间会越来越严重。这是因为:
- 播放时间难以精确控制。音视频解码及渲染的耗时不同,可能造成每一帧输出有一点细微差距,长久累计,不同步便越来越明显。(例如受限于性能,42ms才能输出一帧)
- 音频输出是线性的,而视频输出可能是非线性,从而导致有偏差。
- 媒体流本身音视频有差距。(特别是TS实时流,音视频能播放的第一个帧起点不同)
所以,必须要采用一定的同步策略,不断对音视频的时间差作校正,使图像显示与声音播放总体保持一致。
所以,解决音视频同步问题,引入了时间戳:首先选择一个参考时钟(要求参考时钟上的时间是线性递增的);编码时依据参考时钟上的给每个音视频数据块都打上时间戳;播放时,根据音视频时间戳及参考时钟,来调整播放。所以,视频和音频的同步实际上是一个动态的过程,同步是暂时的,不同步则是常态。以参考时钟为标准,放快了就减慢播放速度;播放快了就加快播放的速度。
音视频中的时间戳
由于编码方式的不同,视频中的画面帧就分为了不同的类别,其中包括:I 帧、P 帧、B 帧:
I 帧(Intra coded frames)图像采用帧I 帧使用帧内压缩,不使用运动补偿,由于 I 帧不依赖其它帧,可以独立解码。I 帧图像的压缩倍数相对较低,周期性出现在图像序列中的,出现频率可由编码器选择。
P 帧(Predicted frames)采用帧间编码方式,即同时利用了空间和时间上的相关性。P 帧图像只采用前向时间预测,可以提高压缩效率和图像质量。P 帧图像中可以包含帧内编码的部分,即 P 帧中的每一个宏块可以是前向预测,也可以是帧内编码。
B 帧(Bi-directional predicted frames)图像采用帧间编码方式,且采用双向时间预测,可以大大提高压缩倍数。也就是其在时间相关性上,还依赖后面的视频帧,也正是由于 B 帧图像采用了后面的帧作为参考,因此造成视频帧的传输顺序和显示顺序是不同的。
时间戳DTS、PTS
- DTS(Decoding Time Stamp):即解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧的数据。
- PTS(Presentation Time Stamp):即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。
当视频流中没有 B 帧时,通常 DTS 和 PTS 的顺序是一致的。但如果有 B 帧时,就回到了我们前面说的问题:解码顺序和播放顺序不一致了,即视频输出是非线性的。
同步策略
时间戳就是PTS,那么参考时钟的选择一般来说有以下三种:
- 将视频同步到音频上:就是以音频的播放速度为基准来同步视频。
- 将音频同步到视频上:就是以视频的播放速度为基准来同步音频。
- 将视频和音频同步外部的时钟上:选择一个外部时钟为基准,视频和音频的播放速度都以该时钟为标准。
当播放源比参考时钟慢,则加快其播放速度,或者丢弃;快了,则延迟播放。
考虑到人对声音的敏感度要强于视频,频繁调节音频会带来较差的观感体验,且音频的播放时钟为线性增长,所以一般会以音频时钟为参考时钟,视频同步到音频上。
阈值
- 无法察觉:音频和视频的时间戳差值在:-100ms ~ +25ms 之间
- 能够察觉:音频滞后了 100ms 以上,或者超前了 25ms 以上
- 无法接受:音频滞后了 185ms 以上,或者超前了 90ms 以上
同步算法
MediaSync | Android Developers (google.cn)
此方法应该可以实现相关的同步,但是改写自己的代码工程量较大,暂时没有依靠此api来实现该功能。
音视频开发—音视频同步算法_基于播放时限的流内同步算法-CSDN博客
直接根据diff的值来决策下一帧要延时的时间。
以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,则得出正在需要延迟显示当前帧的时间。
深入理解Android音视频同步机制(一)概述-CSDN博客
这个博客给出了一系列视频播放器的同步机制:
Code
假设音频是跟系统时间同步,视频向系统时间靠拢。
因为暂时没有办法获得音频相关的时间轴信息,只能获得视频的播放时间轴,但是音频播放是通过audiotrack来播放,它跟系统时间一致,所以我们调整视频。
//延迟渲染
private boolean adjustPlay(long videoTimeUs, long sysTimeMs) {
long diff = videoTimeUs / 1000 - (System.currentTimeMillis() - sysTimeMs);
Log.i(TAG, "The diff is " + diff);
if (diff >= 10 && diff <= 100) {
return true;
}
if (diff > 100) {
try {
DecodeVideoThread.sleep(diff);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
if (diff < 0) {
return false;
}
return true;
}