一、音视频同步原理
如果简单的按照音频的采样率与视频的帧率去播放,由于机器运行速度,解码效率等种种造成时间差异的因素影响,很难同步,音视频时间差将会呈现线性增长。所以要做音视频的同步,有三种方式:
参考一个外部时钟,将音频与视频同步至此时间。我首先想到这种方式,但是并不好,由于某些生物学的原理,人对声音的变化比较敏感,但是对视觉变化不太敏感。所以频繁的去调整声音的播放会有些刺耳或者杂音吧影响用户体验。(ps:顺便科普生物学知识,自我感觉好高大上_)。
所以,原理就是以音频时间为基准,判断视频快了还是慢了,从而调整视频速度。其实是一个动态的追赶与等待的过程。
二、一些概念
音视频中都有DTS与PTS。
DTS ,Decoding Time Stamp,解码时间戳,告诉解码器packet的解码顺序。
PTS ,Presentation Time Stamp,显示时间戳,指示从packet中解码出来的数据的显示顺序。
音频中二者是相同的,但是视频由于B帧(双向预测)的存在,会造成解码顺序与显示顺序并不相同,也就是视频中DTS与PTS不一定相同。
时间基 :看FFmpeg源码
AVRational time_base;
typedef struct AVRational{
int num;
个人理解,其实就是ffmpeg中的用分数表示时间单位,num为分子,den为分母。并且ffmpeg提供了计算方法:
static inline double av_q2d(AVRational a){
return a.num / (double) a.den;
}
所以 视频中某帧的显示时间 计算方式为(单位为妙):
time = pts * av_q2d(time_base);
三、同步代码
1、 音频部分
clock 为音频的播放时长(从开始到当前的时间)
if (packet->pts != AV_NOPTS_VALUE) {
audio->clock = av_q2d(audio->time_base) * packet->pts;
}
然后加上此packet中数据需要播放的时间
double time = datalen/((double) 44100 *2 * 2);
audio->clock = audio->clock +time;
datalen为数据长度。采样率为44100,采样位数为16,通道数为2。所以 数据长度 / 每秒字节数。
ps:此处计算方式不是很完美,有很多问题,回头研究在再补上。
四、 视频部分
先定义几个值:
double last_play
,play
, last_delay
,delay
,audio_clock
,diff
,sync_threshold
,start_time
,pts
,actual_delay
start_time = av_gettime() / 1000000.0;
if ((pts = av_frame_get_best_effort_timestamp(frame)) == AV_NOPTS_VALUE) {
pts = 0;
}
play = pts * av_q2d(vedio->time_base);
play = vedio->synchronize(frame, play);
delay = play - last_play;
if (delay <&#61; 0 || delay > 1) {
delay &#61; last_delay;
}
audio_clock &#61; vedio->audio->clock;
last_delay &#61; delay;
last_play &#61; play;
diff &#61; vedio->clock - audio_clock;
sync_threshold &#61; (delay > 0.01 ? 0.01 : delay);
if (fabs(diff) <10) {
if (diff <&#61; -sync_threshold) {
delay &#61; 0;
} else if (diff >&#61; sync_threshold) {
delay &#61; 2 * delay;
}
}
start_time &#43;&#61; delay;
actual_delay &#61; start_time - av_gettime() / 1000000.0;
if (actual_delay <0.01) {
actual_delay &#61; 0.01;
}
av_usleep(actual_delay * 1000000.0 &#43; 6000);
纠正play &#xff08;播放时间&#xff09;的方法 repeat_pict / (2 * fps) 是ffmpeg注释里教的
synchronize(AVFrame *frame, double play) {
if (play !&#61; 0)
clock&#61;play;
else
play &#61; clock;
double repeat_pict &#61; frame->repeat_pict;
double frame_delay &#61; av_q2d(codec->time_base);
double fps &#61; 1 / frame_delay;
double extra_delay &#61; repeat_pict / (2 * fps);
double delay &#61; extra_delay &#43; frame_delay;
clock &#43;&#61; delay;
return play;
}
原创作者&#xff1a;Learner_9a9e
原文链接&#xff1a;https://www.jianshu.com/p/3578e794f6b5
校验&#xff1a;逆流的鱼yuiop