pts和dts:
pts是录制时产生的时间戳,即显示时间戳,表示这帧数据啥时候播放
dts解码时间戳,音视频同步时一般来说不用关注
音视频同步三种方法:
1、以音频为基准
视频慢了加快或者丢帧
快了则放慢播放
音频丢帧时ffplay会丢视频帧
2、以视频为基准
音频慢了加快或者丢帧
快了则放慢播放
视频丢帧时音频应该加快播放比丢帧效果更好一点
3、以外部时钟为基准
根据外部始终改变播放速度
ffmpeg时间定义:
标准时间 :秒、微秒
自定义时间单位 :a/b秒
aac:pts以1/采样率为单位,1024个采样点为一帧,每个采样点时间是1/44100
第一帧0
第二帧1024*一个采样点时间
第三帧2048*一个采样点
pts1=0
pts2=1024*1/44100=0.232...
pts3=2048*1/44100=0.464399秒
ffmpeg时间单位:
#define AV_TIME_BASE 1000000
#define AV_TIME_BASE_Q 1/1000000
时间基转换公式:
timestamp(ffmpeg内部时间戳)=AV_TIME_BASE*time(秒)
time(秒)=timestamp(ffmpeg内部时间戳)*AV_TIME_BASE_Q
计算时间戳 :
下面是ffplay里面的关于时间单位的一个重要结构体
typedef struct AVRational
{
int num; //分子,如果是aac的话就是1
int den; //分母如果是aac的话就是44100
}AVRational;//这个是基础时间
av_q2d(AVRational a)
{
这里其实可以表示一个时间单位是多少
return (double)a.num/(double)a.den
}
这里算出来的是时间戳:timestamp(秒)=pts*av_q2d(st->time_base)
第二帧pts=2048 所以第二帧时间戳是2048/44100
这里算出来的是一帧的时间长度:st->duration*av_q2d(st->time_base)
st->duration如果是aac的一帧,也即1024,那么aac一帧的间隔是1024个单位,
每个单位的时间是1/44100秒,所以一帧的时常为1024/44100秒,总之1/44100是一个单位(basetime),duration表示的是多少个单位。
同步的机制有分为简单同步和精确同步,在很多自个写的程序中咱们可能简单同步一下就ok,但是在ffplay里面的音视频同步可以说很精确。
为什么通常以音频作为基准:
因为视频随着音频去校准,视频判断时间不对可能掉1-2帧或者加快减慢速度对于人眼来说是分辨不出来的,没有音频掉1-2帧或者变换速度影响来的大,音频可能会明显听出来卡顿,因此我们让音频自然放,视频根据音频来校准。
简单同步:
为简化分析,这边说的同步都是以音频为基准做同步,首先我们让音频正常播放,上面说到了音视频帧的pts,也即显示时间戳,音频播放每一帧都会获取这个时间戳,因此我们将这个时间戳保存起来,比如说音频
第一帧0ms
第二帧30ms
第三帧60ms
每次播放之前都将这个变量存储起来,之后视频即将播放的时候获取到存储的时间是30呢还是60呢,再根据自己的时间戳来对比,因为录制视频的时候时间戳都是对着的,自己的时间戳如果和获取到的音频正在播放的时间戳差的很多的话就应该要作出相应的调整了。
简单同步的话就有一个问题,如果音频的每一帧时间过于长的话,比如采样率很低是8k的时候
第一帧0ms
第二帧128ms
第三帧256ms
当我的视频获取时间戳的时候刚好音频播放到255ms,但其实第三帧还没播放,返回的数据还是128ms,但实际只有1ms就进入第三帧了,因此这会再根据128来做对比判断就不会那么精确啦。
ffplay同步:
ffplay的同步机制引进了一个系统时钟,其实他把你解码流失的时间也记录下来了,能够精确定位到你这会已经播放到多少ms了(包括播放到一帧的中间时间段),而不是大概的刚开始播放帧的时间戳。
音频走到第一帧时,记录一下当前pts比如等于30ms,同时还会记录一个系统时间比如00::00,意思就是,我在系统时间是00:00的时候记录了需要播放音频的第一帧是30ms,那么好我视频来了,视频这会读取到音频的pts此时还记录在20ms,但是视频读取音频时间的时候系统时间已经是00:10了,所以这时的音频播放其实不是30ms了,而是40ms了就是用:
00:10 减去 00:00 加上 30ms 等于 30ms,这会视频应该和40ms对比而不是30(使用简单同步的方法就是对比30ms的),其实视频已经开始10ms了。那大致的意思就是这样啦~详细同步原理请看ffplay源码~网上资料一大把哈~
一张图片给大家自己体会~