一、问题描述
官方的ijkplayer播放视频时,最高倍速为2倍,就算把倍速参数设置得再大也没用,但是需求中需要实现4倍、16倍播放,那么ijkplayer就不适用了,故需要修改ffplayer源码来适用我们的需求.
二、ijkplayer解码渲染流程
三、解决问题
根据渲染流程,可以有两个地方去实施丢帧操作,一个是在送到解码器之前按倍速丢帧,一个是渲染时按倍速丢帧,但是考虑到网络流一般是有损压缩,有的视频流会需要参考前一帧,所以在解码前丢帧会出现严重的花屏现象,那就只能在渲染前丢帧;受限于手机的性能,1秒钟大概能解码个100来帧,而16倍按帧率16来算的话一秒需要解码256帧,所以是达不到16倍速的,去打开竞品测试了一下16倍也是没有真正的16倍,也就8倍的样子,所以只能这样解决,丢帧及倍速代码主要修改ff_ffplayer.c文件video_refresh方法,修改代码如下:
static double last_pts = 0;
/* called to display each frame */
static void video_refresh(FFPlayer *opaque, double *remaining_time)
{
FFPlayer *ffp = opaque;
VideoState *is = ffp->is;
double time;
Frame *sp, *sp2;
if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
check_external_clock_speed(is);
if (!ffp->display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) {
time = av_gettime_relative() / 1000000.0;
if (is->force_refresh || is->last_vis_time + ffp->rdftspeed < time) {
video_display2(ffp);
is->last_vis_time = time;
}
*remaining_time = FFMIN(*remaining_time, is->last_vis_time + ffp->rdftspeed - time);
}
if (is->video_st) {
retry:
if (frame_queue_nb_remaining(&is->pictq) == 0) {
// nothing to do, no picture to display in the queue
} else {
double last_duration, duration, delay;
Frame *vp, *lastvp;
/* dequeue the picture */
lastvp = frame_queue_peek_last(&is->pictq);
vp = frame_queue_peek(&is->pictq);
if (vp->serial != is->videoq.serial) {
frame_queue_next(&is->pictq);
goto retry;
}
if (lastvp->serial != vp->serial)
is->frame_timer = av_gettime_relative() / 1000000.0;
if (is->paused)
goto display;
/* compute nominal last_duration */
last_duration = vp_duration(is, lastvp, vp);
delay = compute_target_delay(ffp, last_duration, is);
if (ffp->pf_playback_rate == 4.0) {
delay = delay / (ffp->pf_playback_rate*2.0);
} else if (ffp->pf_playback_rate > 4.0) {
Frame *nextvp = frame_queue_peek_next(&is->pictq);
// av_log(NULL, AV_LOG_DEBUG, "___pts: %f\n", nextvp->pts);
double diff = nextvp->pts - last_pts;
double maxDiff = last_duration*ffp->pf_playback_rate;
// av_log(NULL, AV_LOG_DEBUG, "___time: %lld\n", av_gettime_relative());
if (diff < maxDiff) {
frame_queue_next(&is->pictq);
return;
}
if (nextvp->pts > 0) {
last_pts = nextvp->pts;
}
is->force_refresh = 1;
time= av_gettime_relative()/1000000.0;
if (isnan(is->frame_timer) || time < is->frame_timer)
is->frame_timer = time;
if (time < is->frame_timer + maxDiff) {
*remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
goto display;
}
}
// if(ffp->pf_playback_rate > 2.0) {
// delay = delay / (ffp->pf_playback_rate*2.0);
// }
time= av_gettime_relative()/1000000.0;
// av_log(NULL, AV_LOG_DEBUG, "___delay: %f\n", delay);
// av_log(NULL, AV_LOG_DEBUG, "___time: %f\n", time);
// av_log(NULL, AV_LOG_DEBUG, "___frame_timer: %f ___time:%f\n", is->frame_timer, time);
if (isnan(is->frame_timer) || time < is->frame_timer)
is->frame_timer = time;
if (time < is->frame_timer + delay) {
*remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
goto display;
}
is->frame_timer += delay;
if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
is->frame_timer = time;
SDL_LockMutex(is->pictq.mutex);
if (!isnan(vp->pts))
update_video_pts(is, vp->pts, vp->pos, vp->serial);
SDL_UnlockMutex(is->pictq.mutex);
if (frame_queue_nb_remaining(&is->pictq) > 1) {
Frame *nextvp = frame_queue_peek_next(&is->pictq);
// av_log(NULL, AV_LOG_DEBUG, "___pts: %f\n", nextvp->pts);
duration = vp_duration(is, vp, nextvp);
if(!is->step && (ffp->framedrop > 0 || (ffp->framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration) {
frame_queue_next(&is->pictq);
goto retry;
}
}
if (is->subtitle_st) {
while (frame_queue_nb_remaining(&is->subpq) > 0) {
sp = frame_queue_peek(&is->subpq);
if (frame_queue_nb_remaining(&is->subpq) > 1)
sp2 = frame_queue_peek_next(&is->subpq);
else
sp2 = NULL;
if (sp->serial != is->subtitleq.serial
|| (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
|| (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
{
if (sp->uploaded) {
ffp_notify_msg4(ffp, FFP_MSG_TIMED_TEXT, 0, 0, "", 1);
}
frame_queue_next(&is->subpq);
} else {
break;
}
}
}
frame_queue_next(&is->pictq);
// av_log(NULL, AV_LOG_DEBUG, "frame_queue_next\n");
is->force_refresh = 1;
SDL_LockMutex(ffp->is->play_mutex);
if (is->step) {
is->step = 0;
if (!is->paused)
stream_update_pause_l(ffp);
}
SDL_UnlockMutex(ffp->is->play_mutex);
}
display:
/* display picture */
if (!ffp->display_disable && is->force_refresh && is->show_mode == SHOW_MODE_VIDEO && is->pictq.rindex_shown)
video_display2(ffp);
}
is->force_refresh = 0;
if (ffp->show_status) {
static int64_t last_time;
int64_t cur_time;
int aqsize, vqsize, sqsize __unused;
double av_diff;
cur_time = av_gettime_relative();
if (!last_time || (cur_time - last_time) >= 30000) {
aqsize = 0;
vqsize = 0;
sqsize = 0;
if (is->audio_st)
aqsize = is->audioq.size;
if (is->video_st)
vqsize = is->videoq.size;
#ifdef FFP_MERGE
if (is->subtitle_st)
sqsize = is->subtitleq.size;
#else
sqsize = 0;
#endif
av_diff = 0;
if (is->audio_st && is->video_st)
av_diff = get_clock(&is->audclk) - get_clock(&is->vidclk);
else if (is->video_st)
av_diff = get_master_clock(is) - get_clock(&is->vidclk);
else if (is->audio_st)
av_diff = get_master_clock(is) - get_clock(&is->audclk);
av_log(NULL, AV_LOG_INFO,
"%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64" \r",
get_master_clock(is),
(is->audio_st && is->video_st) ? "A-V" : (is->video_st ? "M-V" : (is->audio_st ? "M-A" : " ")),
av_diff,
is->frame_drops_early + is->frame_drops_late,
aqsize / 1024,
vqsize / 1024,
sqsize,
is->video_st ? is->viddec.avctx->pts_correction_num_faulty_dts : 0,
is->video_st ? is->viddec.avctx->pts_correction_num_faulty_pts : 0);
fflush(stdout);
last_time = cur_time;
}
}
}
修改后能正常4倍播放,16倍是按手机性能到最快速度播放,至此,高倍速问题解决.