IJKPlayer实现多倍速播放

一、问题描述

官方的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倍是按手机性能到最快速度播放,至此,高倍速问题解决.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容