在讲解SkeyeExPlayer快放慢放之前,我们首先要讲解下SkeyeExPlayer的音视频同步机制,我们采用视频同步音频的方式进行时间戳同步,无音频情况下视频自同步;视频自同步的情况比较简单这里就不做描述,下面我们着重讲解下音视频同步存在时的快放慢放并如何保持音视频同步。
1.音视频同步原理
首先,音频解码后获取到的PCM原始数据使用waveout直接进行播放,并记录当前播放时间戳,音频在任何时候都是正常播放,不做同步操作;
然后,视频时间戳来同步音频时间戳:
DWORD tickcur = GetTickCount();
int tickdiff = tickcur - c->ticklast;
int64_t avdiff = apts - vpts - c->tickavdiff;
c->ticklast = tickcur;
if (apts == -1 && vpts != -1&&c->play_speed>0) //无音频情况做特殊处理
{
c->tickframe = 100000/(c->framerate*c->play_speed);
if (c->play_speed != 100)//倍速播放时变速加快
{
if (tickdiff - c->tickframe > 2) c->ticksleep-=2;
if (tickdiff - c->tickframe < -2) c->ticksleep+=2;
}
}
{
if (tickdiff - c->tickframe > 2) c->ticksleep--;
if (tickdiff - c->tickframe < -2) c->ticksleep++;
}
if (apts != -1 && vpts != -1) {
if (avdiff > 5) c->ticksleep-=2;
if (avdiff <-5) c->ticksleep+=2;
}
if (c->ticksleep < 0) c->ticksleep = 0;
if (c->ticksleep > 0)
Sleep(c->ticksleep);
同步大致流程如上段代码所示,首先,计算音频时间戳和视频时间戳以及当前时间戳和上一次时间戳的差值,然后,根据帧率计算当前帧和上一帧的差值,从而得出视频帧显示的时间是快于音频还是慢于音频,而在下面进行调整,进而实现音视频的动态东同步:
if (apts != -1 && vpts != -1)
{
if (avdiff > 5) c->ticksleep-=2;
if (avdiff <-5) c->ticksleep+=2;
}
2.快放慢放实现
从上面的同步原理我们了解到,音频快的时候视频为了同步也跟着快,反之亦然;所以,为了实现快放慢放,我们可以通过调整音频播放的快慢即可实现:
int sample_rate = 44100 * 100 / player_speed_cur;
player->swr_context = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, sample_rate,
player->chan_layout, player->sample_fmt, player->sample_rate, 0, NULL);
swr_init(player->swr_context);
而音频的快放慢放则通过修改音频的采样率即可实现;
3.单视频的快放慢放
上文已经说到,单视频的情况下,则不能通过视频同步音频的逻辑来处理视频播放的逻辑,而只能通过视频的帧率来进行播放快慢的调整:
if (apts == -1 && vpts != -1&&c->play_speed>0) //无音频情况做特殊处理
{
c->tickframe = 100000/(c->framerate*c->play_speed);
if (c->play_speed != 100)//倍速播放时变速加快
{
if (tickdiff - c->tickframe > 2) c->ticksleep-=2;
if (tickdiff - c->tickframe < -2) c->ticksleep+=2;
}
}
如上代码段所示,如果音频不存在,则视频的快放慢放则通过修改实时帧率来实现。