問題描述
當(dāng)解碼較快的時(shí)候ffmpeg中時(shí)間戳同步,并不是解碼后立馬顯示視頻幀,這樣看著是倍速的效果。如何還原實(shí)際的播放速率?
解決方案
為了解決在解碼后視頻播放還原原來每幀播放的時(shí)刻點(diǎn)。我們需要在解碼較快的情況下對(duì)幀顯示加一定的時(shí)間延時(shí)ffmpeg中時(shí)間戳同步,這個(gè)延時(shí)策略就是計(jì)算出
延時(shí)調(diào)整時(shí)間 =(當(dāng)前幀時(shí)間戳 - 上一幀時(shí)間戳)- (當(dāng)前機(jī)器準(zhǔn)顯示時(shí)間 - 上一幀顯示機(jī)器時(shí)間)
延時(shí)調(diào)整時(shí)間 有可能為負(fù)值則丟棄。如果為正值,可根據(jù)時(shí)長(zhǎng)做一定調(diào)整,畢竟送幀顯示也是耗時(shí)操作。
demo示例:
void dispThread(void *arg)
{
Input *input = (Input *)arg;
static const double interval = 1000000.0 / input->fps;
double q2d = av_q2d(input->tb) * 1000000.0;
int64_t pre_pts = 0;
int64_t frame_pre_pts = AV_NOPTS_VALUE;
AVFrame *pending_frm = NULL;
while (!input->is_req_exit()) {
int64_t cur_pts;
int remaining_time = 10000;
double duration = interval;
AVFrame *frm;
if (pending_frm) {
frm = pending_frm;
} else {
frm = input->PopFrame();
if (!frm) {
msleep(10);
continue;
}
// printf("pop frame pts: %ld\n", frm->pts);
}
static auto delete_func = [](AVFrame * f) {
av_frame_free(&f);
};
cur_pts = av_gettime_relative();
if (frame_pre_pts != AV_NOPTS_VALUE)
duration = (frm->pts - frame_pre_pts) * q2d;
int countdown = (pre_pts == 0) ? 0 : (int)(duration - (cur_pts - pre_pts));
remaining_time = std::min<int>(remaining_time, countdown);
// printf("countdown: %d, remaining_time: %d us\n",
// countdown, remaining_time);
if (input->realtime) {
countdown = 0;
remaining_time = 0;
}
if (countdown <= 0) {
frame_pre_pts = frm->pts;
pre_pts = cur_pts;
if (frm == pending_frm)
pending_frm = NULL;
push_frame(input->join, input->slice_idx,
std::shared_ptr<AVFrame>(frm, delete_func));
} else {
pending_frm = frm;
}
if (remaining_time > 0)
{
printf("countdown: %d, remaining_time: %d us\n",
countdown, remaining_time);
std::this_thread::sleep_for(std::chrono::microseconds(remaining_time));
}
}
if (pending_frm)
av_frame_free(&pending_frm);
}
如果是直播的情況下我們不做延時(shí)。
大致的流程如下
取得解碼視頻幀記錄當(dāng)前機(jī)器時(shí)間計(jì)算當(dāng)前準(zhǔn)顯示時(shí)間與上次顯示時(shí)間差值d1計(jì)算當(dāng)前幀時(shí)間戳與上次顯示時(shí)間戳差值d2計(jì)算延時(shí)時(shí)間 d2 - d1延時(shí)時(shí)間大于0則進(jìn)行sleep延時(shí)并保存當(dāng)前幀在下一次循環(huán)送顯
以上步驟為解決方案。請(qǐng)參考。