天天看點

FFmpeg ffplay渲染剖析

代碼堆棧回溯

  ffplayd.exe!video_image_display(VideoState * is) 行 1036 C

  ffplayd.exe!video_display(VideoState * is) 行 1371 C

  ffplayd.exe!video_refresh(void * opaque, double * remaining_time) 行 1690 C

  ffplayd.exe!refresh_loop_wait_event(VideoState * is, SDL_Event * event) 行 3229 C

  ffplayd.exe!event_loop(VideoState * cur_stream) 行 3269 C

  ffplayd.exe!main(int argc, char * * argv) 行 3768 C

調用 SDL_RenderCopyEx(renderer, is->vid_texture, NULL, &rect, 0, NULL, vp->flip_v ? SDL_FLIP_VERTICAL : 0)實作界面的渲染

渲染的時機非常重要

1)refresh_loop_wait_event函數剖析

static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) {

    double remaining_time = 0.0;

   //調用該函數搜集輸入裝置的事件(例如鍵盤輸入,滑鼠移動),然後存放在事件隊列中,等待SDL_PeepEvents擷取事件

    SDL_PumpEvents();

  //SDL_GETEVENT操作指定的SDL_PeepEvents會讀取事件隊列中的事件消息,并且是将讀取的事件移除出隊列

  //while循環!說明了隻要事件隊列有消息,優先執行隊列中的事件,然後才進行視訊的渲染

    while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {

        if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) {

            SDL_ShowCursor(0);

            cursor_hidden = 1;

        }

        if (remaining_time > 0.0)

            av_usleep((int64_t)(remaining_time * 1000000.0));

        remaining_time = REFRESH_RATE;

        if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))

            video_refresh(is, &remaining_time);

        //重新搜尋輸入裝置的事件,如果屏蔽掉該函數的調用,将會導緻鍵盤的輸入無法進行事件隊列,導緻鍵盤輸入無效,例如嘗試按下q停止視訊播放

        SDL_PumpEvents();

    }

}

static void video_refresh(void *opaque, double *remaining_time)

{

    VideoState *is = opaque;

    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 (!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 + rdftspeed < time) {

            video_display(is);

            is->last_vis_time = time;

        *remaining_time = FFMIN(*remaining_time, is->last_vis_time + 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(last_duration, is);

            time= av_gettime_relative()/1000000.0;

            if (time < is->frame_timer + delay) {

                *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);

            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);

                duration = vp_duration(is, vp, nextvp);

                if(!is->step && (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration){

                    is->frame_drops_late++;

                    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) {

                                int i;

                                for (i = 0; i < sp->sub.num_rects; i++) {

                                    AVSubtitleRect *sub_rect = sp->sub.rects[i];

                                    uint8_t *pixels;

                                    int pitch, j;

                                    if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {

                                        for (j = 0; j < sub_rect->h; j++, pixels += pitch)

                                            memset(pixels, 0, sub_rect->w << 2);

                                        SDL_UnlockTexture(is->sub_texture);

                                    }

                                }

                            }

                            frame_queue_next(&is->subpq);

                        } else {

                            break;

                        }

                    }

            frame_queue_next(&is->pictq);

            is->force_refresh = 1;

            if (is->step && !is->paused)

                stream_toggle_pause(is);

display:

        /* display picture */

        if (!display_disable && is->force_refresh && is->show_mode == SHOW_MODE_VIDEO && is->pictq.rindex_shown)

    is->force_refresh = 0;

    if (show_status) {

        static int64_t last_time;

        int64_t cur_time;

        int aqsize, vqsize, sqsize;

        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;

            if (is->subtitle_st)

                sqsize = is->subtitleq.size;

            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;

static void frame_queue_next(FrameQueue *f)

    if (f->keep_last && !f->rindex_shown) {

        f->rindex_shown = 1;

        return;

//釋放已經顯示過的幀

    frame_queue_unref_item(&f->queue[f->rindex]);

//++f->rindex 表示目前幀已經顯示完成,f->rindex指向下一個等待顯示的幀

    if (++f->rindex == f->max_size)

        f->rindex = 0;

    SDL_LockMutex(f->mutex);

//等待顯示的幀數減一

    f->size--;

    SDL_CondSignal(f->cond);

    SDL_UnlockMutex(f->mutex);

/* return the number of undisplayed frames in the queue */

static int frame_queue_nb_remaining(FrameQueue *f)

//f->size是目前隊列中的幀數, f->rindex_shown是已經顯示過的幀數

    return f->size - f->rindex_shown;

/* return last shown position */

static int64_t frame_queue_last_pos(FrameQueue *f)

//說明rindex是上一個顯示的幀,rindex+1就是等待顯示的幀

    Frame *fp = &f->queue[f->rindex];

    if (f->rindex_shown && fp->serial == f->pktq->serial)

        return fp->pos;

    else

        return -1;