diff options
Diffstat (limited to 'player/video.c')
-rw-r--r-- | player/video.c | 92 |
1 files changed, 59 insertions, 33 deletions
diff --git a/player/video.c b/player/video.c index 42c184a1b3..da93203b7a 100644 --- a/player/video.c +++ b/player/video.c @@ -195,8 +195,9 @@ void reset_video_state(struct MPContext *mpctx) if (mpctx->video_out) vo_seek_reset(mpctx->video_out); - mp_image_unrefp(&mpctx->next_frame[0]); - mp_image_unrefp(&mpctx->next_frame[1]); + for (int n = 0; n < mpctx->num_next_frames; n++) + mp_image_unrefp(&mpctx->next_frames[n]); + mpctx->num_next_frames = 0; mp_image_unrefp(&mpctx->saved_frame); mpctx->delay = 0; @@ -541,19 +542,15 @@ static void adjust_sync(struct MPContext *mpctx, double v_pts, double frame_time mpctx->total_avsync_change += change; } -// Move the frame in next_frame[1] to next_frame[0]. This makes the frame -// "known" to the playback logic. A frame in next_frame[0] is either "known" or -// NULL, so the moving must always be done by this function. -static void shift_new_frame(struct MPContext *mpctx) +// Make the frame at position 0 "known" to the playback logic. This must happen +// only once for each frame, so this function has to be called carefully. +// Generally, if position 0 gets a new frame, this must be called. +static void handle_new_frame(struct MPContext *mpctx) { - if (mpctx->next_frame[0] || !mpctx->next_frame[1]) - return; - - mpctx->next_frame[0] = mpctx->next_frame[1]; - mpctx->next_frame[1] = NULL; + assert(mpctx->num_next_frames >= 1); double frame_time = 0; - double pts = mpctx->next_frame[0]->pts; + double pts = mpctx->next_frames[0]->pts; if (mpctx->video_pts != MP_NOPTS_VALUE) { frame_time = pts - mpctx->video_pts; double tolerance = 15; @@ -585,33 +582,45 @@ static void shift_new_frame(struct MPContext *mpctx) MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time); } +static int get_req_frames(struct MPContext *mpctx, bool eof) +{ + struct MPOpts *opts = mpctx->opts; + + // On EOF, drain all frames. + // On the first frame, output a new frame as quickly as possible. + if (eof || mpctx->video_pts == MP_NOPTS_VALUE) + return 1; + + int req = 1 + vo_get_num_future_frames(mpctx->video_out); + if (opts->frame_dropping & 1) + req = MPMAX(req, 2); + return req; +} + // Whether it's fine to call add_new_frame() now. static bool needs_new_frame(struct MPContext *mpctx) { - return !mpctx->next_frame[1]; + return mpctx->num_next_frames < get_req_frames(mpctx, false); } -// Queue a frame to mpctx->next_frame[]. Call only if needs_new_frame() signals ok. +// Queue a frame to mpctx->next_frames[]. Call only if needs_new_frame() signals ok. static void add_new_frame(struct MPContext *mpctx, struct mp_image *frame) { assert(needs_new_frame(mpctx)); assert(frame); - mpctx->next_frame[1] = frame; - shift_new_frame(mpctx); + mpctx->next_frames[mpctx->num_next_frames++] = frame; + if (mpctx->num_next_frames == 1) + handle_new_frame(mpctx); } // Enough video filtered already to push one frame to the VO? // Set eof to true if no new frames are to be expected. static bool have_new_frame(struct MPContext *mpctx, bool eof) { - bool need_2nd = !!(mpctx->opts->frame_dropping & 1) // we need the duration - && mpctx->video_pts != MP_NOPTS_VALUE // ...except for the 1st frame - && !eof; // on EOF, drain the remaining frames - - return mpctx->next_frame[0] && (!need_2nd || mpctx->next_frame[1]); + return mpctx->num_next_frames >= get_req_frames(mpctx, eof); } -// Fill mpctx->next_frame[] with a newly filtered or decoded image. +// Fill mpctx->next_frames[] with a newly filtered or decoded image. // returns VD_* code static int video_output_image(struct MPContext *mpctx, double endpts) { @@ -620,13 +629,15 @@ static int video_output_image(struct MPContext *mpctx, double endpts) if (mpctx->d_video->header->attached_picture) { if (vo_has_frame(mpctx->video_out)) return VD_EOF; - if (mpctx->next_frame[0]) + if (mpctx->num_next_frames >= 1) return VD_NEW_FRAME; int r = video_decode_and_filter(mpctx); video_filter(mpctx, true); // force EOF filtering (avoid decoding more) - mpctx->next_frame[0] = vf_read_output_frame(mpctx->d_video->vfilter); - if (mpctx->next_frame[0]) - mpctx->next_frame[0]->pts = MP_NOPTS_VALUE; + mpctx->next_frames[0] = vf_read_output_frame(mpctx->d_video->vfilter); + if (mpctx->next_frames[0]) { + mpctx->next_frames[0]->pts = MP_NOPTS_VALUE; + mpctx->num_next_frames = 1; + } return r <= 0 ? VD_EOF : VD_PROGRESS; } @@ -802,7 +813,7 @@ void write_video(struct MPContext *mpctx, double endpts) } // Filter output is different from VO input? - struct mp_image_params p = mpctx->next_frame[0]->params; + struct mp_image_params p = mpctx->next_frames[0]->params; if (!vo->params || !mp_image_params_equal(&p, vo->params)) { // Changing config deletes the current frame; wait until it's finished. if (vo_still_displaying(vo)) @@ -839,8 +850,11 @@ void write_video(struct MPContext *mpctx, double endpts) int64_t duration = -1; double diff = -1; - double vpts0 = mpctx->next_frame[0] ? mpctx->next_frame[0]->pts : MP_NOPTS_VALUE; - double vpts1 = mpctx->next_frame[1] ? mpctx->next_frame[1]->pts : MP_NOPTS_VALUE; + assert(mpctx->num_next_frames >= 1); + double vpts0 = mpctx->next_frames[0]->pts; + double vpts1 = MP_NOPTS_VALUE; + if (mpctx->num_next_frames >= 2) + vpts1 = mpctx->next_frames[1]->pts; if (vpts0 != MP_NOPTS_VALUE && vpts1 != MP_NOPTS_VALUE) diff = vpts1 - vpts0; if (diff < 0 && mpctx->d_video->fps > 0) @@ -855,7 +869,7 @@ void write_video(struct MPContext *mpctx, double endpts) duration = MPCLAMP(diff, 0, 10) * 1e6; } - mpctx->video_pts = mpctx->next_frame[0]->pts; + mpctx->video_pts = mpctx->next_frames[0]->pts; mpctx->last_vo_pts = mpctx->video_pts; mpctx->playback_pts = mpctx->video_pts; @@ -865,10 +879,22 @@ void write_video(struct MPContext *mpctx, double endpts) update_osd_msg(mpctx); update_subtitles(mpctx); - vo_queue_frame(vo, mpctx->next_frame[0], pts, duration); - mpctx->next_frame[0] = NULL; + assert(mpctx->num_next_frames >= 1); + struct mp_image *frames[VO_MAX_FUTURE_FRAMES + 2] = {0}; + frames[0] = mpctx->next_frames[0]; + for (int n = 0; n < mpctx->num_next_frames - 1; n++) + mpctx->next_frames[n] = mpctx->next_frames[n + 1]; + mpctx->num_next_frames -= 1; + for (int n = 0; n < mpctx->num_next_frames && n < VO_MAX_FUTURE_FRAMES; n++) { + frames[n + 1] = mp_image_new_ref(mpctx->next_frames[n]); + if (!frames[n + 1]) + break; // OOM + } + vo_queue_frame(vo, frames, pts, duration); - shift_new_frame(mpctx); + // The frames were shifted down; "initialize" the new first entry. + if (mpctx->num_next_frames >= 1) + handle_new_frame(mpctx); mpctx->shown_vframes++; if (mpctx->video_status < STATUS_PLAYING) { |