在講解EasyPlayerPro快放慢放之前,我們首先要講解下EasyPlayerPro的音視頻同步機制,我們採用視頻同步音頻的方式進行時間戳同步,無音頻情況下視頻自同步;視頻自同步的情況比較簡單這裏就不做描述,下面我們着重講解下音視頻同步存在時的快放慢放並如何保持音視頻同步。
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;
}
}
如上代碼段所示,如果音頻不存在,則視頻的快放慢放則通過修改實時幀率來實現。