From 223cba6e3aa864fc89a5e4ad1b8597dc4486a213 Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Sun, 16 Oct 2011 23:42:53 +0200 Subject: [PATCH] ffplay: reimplement early frame drop This patch reimplements early frame drop, it is now based on the current difference between the master clock and the video clock, and the pts of the current and the last displayed (or skipped) frame. If the frame to be added to the queue is late after decoding, then we drop it early because later we would drop it anyway (unless it is the only frame in the picture queue). The current approach has only one downside that I know of, it does not handle well when the filters are changing significantly the pts of the frames, because we compare pts values from filtered and unfiltered frames. We also start using the pictq_mutex to ensure consistent video_current_pts, video_current_pts_drift, frame_last_pts, frame_last_dropped_pts and frame_last_dropped_pos values. Signed-off-by: Marton Balint --- ffplay.c | 54 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/ffplay.c b/ffplay.c index c998b5d446..81ded546c8 100644 --- a/ffplay.c +++ b/ffplay.c @@ -197,6 +197,8 @@ typedef struct VideoState { double frame_timer; double frame_last_pts; double frame_last_duration; + double frame_last_dropped_pts; + int64_t frame_last_dropped_pos; double video_clock; ///pictq_mutex); } +static void update_video_pts(VideoState *is, double pts, int64_t pos) { + double time = av_gettime() / 1000000.0; + /* update current video pts */ + is->video_current_pts = pts; + is->video_current_pts_drift = is->video_current_pts - time; + is->video_current_pos = pos; + is->frame_last_pts = pts; +} + /* called to display each frame */ static void video_refresh(void *opaque) { VideoState *is = opaque; VideoPicture *vp; + double time; SubPicture *sp, *sp2; if (is->video_st) { retry: if (is->pictq_size == 0) { + SDL_LockMutex(is->pictq_mutex); + if (is->frame_last_dropped_pts != AV_NOPTS_VALUE && is->frame_last_dropped_pts > is->frame_last_pts) { + update_video_pts(is, is->frame_last_dropped_pts, is->frame_last_dropped_pos); + is->frame_last_dropped_pts = AV_NOPTS_VALUE; + } + SDL_UnlockMutex(is->pictq_mutex); //nothing to do, no picture to display in the que } else { - double time= av_gettime()/1000000.0; double last_duration, duration, delay; /* dequeue the picture */ vp = &is->pictq[is->pictq_rindex]; @@ -1133,17 +1150,16 @@ retry: } delay = compute_target_delay(is->frame_last_duration, is); + time= av_gettime()/1000000.0; if(time < is->frame_timer + delay) return; - is->frame_last_pts = vp->pts; if (delay > 0) is->frame_timer += delay * FFMAX(1, floor((time-is->frame_timer) / delay)); - /* update current video pts */ - is->video_current_pts = vp->pts; - is->video_current_pts_drift = is->video_current_pts - time; - is->video_current_pos = vp->pos; + SDL_LockMutex(is->pictq_mutex); + update_video_pts(is, vp->pts, vp->pos); + SDL_UnlockMutex(is->pictq_mutex); if(is->pictq_size > 1) { VideoPicture *nextvp= &is->pictq[(is->pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE]; @@ -1448,17 +1464,20 @@ static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacke SDL_CondWait(is->pictq_cond, is->pictq_mutex); } is->video_current_pos = -1; - SDL_UnlockMutex(is->pictq_mutex); - is->frame_last_pts = AV_NOPTS_VALUE; is->frame_last_duration = 0; is->frame_timer = (double)av_gettime() / 1000000.0; + is->frame_last_dropped_pts = AV_NOPTS_VALUE; + SDL_UnlockMutex(is->pictq_mutex); + return 0; } avcodec_decode_video2(is->video_st->codec, frame, &got_picture, pkt); if (got_picture) { + int ret = 1; + if (decoder_reorder_pts == -1) { *pts = frame->best_effort_timestamp; } else if (decoder_reorder_pts) { @@ -1471,8 +1490,25 @@ static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacke *pts = 0; } - return 1; + if (((is->av_sync_type == AV_SYNC_AUDIO_MASTER && is->audio_st) || is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK) && + (framedrop>0 || (framedrop && is->audio_st))) { + SDL_LockMutex(is->pictq_mutex); + if (is->frame_last_pts != AV_NOPTS_VALUE && *pts) { + double clockdiff = get_video_clock(is) - get_master_clock(is); + double dpts = av_q2d(is->video_st->time_base) * *pts; + double ptsdiff = dpts - is->frame_last_pts; + if (fabs(clockdiff) < AV_NOSYNC_THRESHOLD && + ptsdiff > 0 && ptsdiff < AV_NOSYNC_THRESHOLD && + clockdiff + ptsdiff < 0) { //TODO: Substract approxiamte time of filter + is->frame_last_dropped_pos = pkt->pos; + is->frame_last_dropped_pts = dpts; + ret = 0; + } + } + SDL_UnlockMutex(is->pictq_mutex); + } + return ret; } return 0; }