一般来说,视频同步指的是视频和音频同步,也就是说播放的声音要和当前显示的画面保持一致。想象以下,看一部电影的时候只看到人物嘴动没有声音传出;或者画面是激烈的战斗场景,而声音不是枪炮声却是人物说话的声音,这是非常差的一种体验。 在视频流和音频流中已包含了其以怎样的速度播放的相关数据,视频的帧率(Frame Rate)指示视频一秒显示的帧数(图像数);音频的采样率(Sample Rate)表示音频一秒播放的样本(Sample)的个数。可以使用以上数据通过简单的计算得到其在某一Frame(Sample)的播放时间,以这样的速度音频和视频各自播放互不影响,在理想条件下,其应该是同步的,不会出现偏差。但,理想条件是什么大家都懂得。如果用上面那种简单的计算方式,慢慢的就会出现音视频不同步的情况。要不是视频播放快了,要么是音频播放快了,很难准确的同步。这就需要一种随着时间会线性增长的量,视频和音频的播放速度都以该量为标准,播放快了就减慢播放速度;播放快了就加快播放的速度。所以呢,视频和音频的同步实际上是一个动态的过程,同步是暂时的,不同步则是常态。以选择的播放速度量为标准,快的等待慢的,慢的则加快速度,是一个你等我赶的过程。
播放速度标准量的的选择一般来说有以下三种:
//extra_delay = repeat_pict / (2 * fps)
//fps = 1 / time_base
//uint64_t pts1 = pfe->pts;
uint64_t pts2 = pfe->best_effort_timestamp;
double extime = pfe->repeat_pict * av_q2d(pFmtCtx->streams[vindex]->time_base) * 0.5;//对齐
double showtime = pts2 * av_q2d(pFmtCtx->streams[vindex]->time_base) + extime;
static double time1 = timeGetTime();//0
static double time2 = 0;
static double preshow = 0;
static double delay = showtime - preshow;
time2 = timeGetTime();
if (time1 > time2 || time1 == 0)
time1 = time2;
if (preshow >= showtime)
preshow = showtime;
if ((time2 - time1) < showtime*1000)//if(time2 - time1 < delay*1000)
SDL_Delay(delay * 1000);
delay = showtime - preshow;
preshow = showtime;
sws_scale(img_ctx, pfe->data, pfe->linesize, 0, vCodecCtx->height, YUV->data, YUV->linesize);
SDL_UpdateTexture(texture, &rect, YUV->data[0], YUV->linesize[0]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
//time1 = timeGetTime();
如果把视频流看做一个数组,那么显示时间戳就像是数组下标,指示该图片帧应该显示的时间。
*pfe显示时间 = pfe->best_effort_timestamp * av_q2d(视频流的时基) + extra_delay
extra_delay = pfe->repeat_pict / (2*fps)
fps = 1 / av_q2d(视频流的时基)
extra_delay = pfe->repeat_pict * 0.5 * av_q2d(视频流的时基)
本例以第一帧的播放时间作为基准,适当延迟后续帧的播放时间,达到(动态)播放的效果。经测试,效果和用暴风影音的几乎一毛一样。
以后再考虑把音视频的解码播放封装成一个类,做一个真正的播放器(到时候应该以音频作为同步的基准)。