diff options
-rw-r--r-- | player/core.h | 17 | ||||
-rw-r--r-- | player/loadfile.c | 1 | ||||
-rw-r--r-- | player/playloop.c | 116 | ||||
-rw-r--r-- | player/video.c | 21 |
4 files changed, 38 insertions, 117 deletions
diff --git a/player/core.h b/player/core.h index 7098b3bd46..5c5466a3ae 100644 --- a/player/core.h +++ b/player/core.h @@ -66,6 +66,7 @@ enum seek_type { MPSEEK_RELATIVE, MPSEEK_ABSOLUTE, MPSEEK_FACTOR, + MPSEEK_BACKSTEP, }; enum seek_precision { @@ -259,9 +260,10 @@ typedef struct MPContext { struct vo *video_out; // next_frame[0] is the next frame, next_frame[1] the one after that. - struct mp_image *next_frames[VO_MAX_REQ_FRAMES]; + // The +1 is for adding 1 additional frame in backstep mode. + struct mp_image *next_frames[VO_MAX_REQ_FRAMES + 1]; int num_next_frames; - struct mp_image *saved_frame; // for hrseek_lastframe + struct mp_image *saved_frame; // for hrseek_lastframe and hrseek_backstep enum playback_status video_status, audio_status; bool restart_complete; @@ -285,6 +287,7 @@ typedef struct MPContext { bool hrseek_active; // skip all data until hrseek_pts bool hrseek_framedrop; // allow decoder to drop frames before hrseek_pts bool hrseek_lastframe; // drop everything until last frame reached + bool hrseek_backstep; // go to frame before seek target double hrseek_pts; // AV sync: the next frame should be shown when the audio out has this // much (in seconds) buffered data left. Increased when more data is @@ -320,15 +323,7 @@ typedef struct MPContext { int last_chapter; - // History of video frames timestamps that were queued in the VO - // This includes even skipped frames during hr-seek - double vo_pts_history_pts[MAX_NUM_VO_PTS]; - // Whether the PTS at vo_pts_history[n] is after a seek reset - uint64_t vo_pts_history_seek[MAX_NUM_VO_PTS]; - uint64_t vo_pts_history_seek_ts; - uint64_t backstep_start_seek_ts; - bool backstep_active; - // Past timestamps etc. (stupidly duplicated with vo_pts_history). + // Past timestamps etc. // The newest frame is at index 0. struct frame_info *past_frames; int num_past_frames; diff --git a/player/loadfile.c b/player/loadfile.c index cdf3d5b0f8..ee555ed421 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -1044,7 +1044,6 @@ static void play_current_file(struct MPContext *mpctx) mpctx->paused = false; mpctx->paused_for_cache = false; mpctx->playing_msg_shown = false; - mpctx->backstep_active = false; mpctx->max_frames = -1; mpctx->video_speed = mpctx->audio_speed = opts->playback_speed; mpctx->speed_factor_a = mpctx->speed_factor_v = 1.0; diff --git a/player/playloop.c b/player/playloop.c index 4d3e70b4f7..5226103733 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -133,9 +133,8 @@ void add_step_frame(struct MPContext *mpctx, int dir) mpctx->step_frames += 1; unpause_player(mpctx); } else if (dir < 0) { - if (!mpctx->backstep_active && !mpctx->hrseek_active) { - mpctx->backstep_active = true; - mpctx->backstep_start_seek_ts = mpctx->vo_pts_history_seek_ts; + if (!mpctx->hrseek_backstep || !mpctx->hrseek_active) { + queue_seek(mpctx, MPSEEK_BACKSTEP, 0, MPSEEK_VERY_EXACT, true); pause_player(mpctx); } } @@ -151,6 +150,7 @@ void reset_playback_state(struct MPContext *mpctx) mpctx->hrseek_active = false; mpctx->hrseek_framedrop = false; mpctx->hrseek_lastframe = false; + mpctx->hrseek_backstep = false; mpctx->playback_pts = MP_NOPTS_VALUE; mpctx->last_seek_pts = MP_NOPTS_VALUE; mpctx->cache_wait_time = 0; @@ -167,7 +167,6 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, bool timeline_fallthrough) { struct MPOpts *opts = mpctx->opts; - uint64_t prev_seek_ts = mpctx->vo_pts_history_seek_ts; int prev_step = mpctx->step_frames; if (!mpctx->demuxer) @@ -192,11 +191,20 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, double target_time = MP_NOPTS_VALUE; int direction = 0; + bool backstep = false; switch (seek.type) { case MPSEEK_ABSOLUTE: target_time = seek.amount; break; + case MPSEEK_BACKSTEP: + seek.type = MPSEEK_ABSOLUTE; + seek.amount = get_current_time(mpctx); + if (seek.amount == MP_NOPTS_VALUE) + seek.amount = 0; + target_time = seek.amount; + backstep = true; + break; case MPSEEK_RELATIVE: direction = seek.amount > 0 ? 1 : -1; double cur = get_current_time(mpctx); @@ -272,14 +280,8 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, reset_playback_state(mpctx); - if (timeline_fallthrough) { - // Important if video reinit happens. - mpctx->vo_pts_history_seek_ts = prev_seek_ts; + if (timeline_fallthrough) mpctx->step_frames = prev_step; - } else { - mpctx->vo_pts_history_seek_ts++; - mpctx->backstep_active = false; - } /* Use the target time as "current position" for further relative * seeks etc until a new video frame has been decoded */ @@ -294,11 +296,13 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, if (hr_seek || mpctx->timeline) { mpctx->hrseek_active = true; mpctx->hrseek_framedrop = !hr_seek_very_exact && opts->hr_seek_framedrop; + mpctx->hrseek_backstep = backstep; mpctx->hrseek_pts = hr_seek ? seek.amount : mpctx->timeline[mpctx->timeline_part].start; - MP_VERBOSE(mpctx, "hr-seek, skipping to %f%s\n", mpctx->hrseek_pts, - mpctx->hrseek_framedrop ? "" : " (no framedrop)"); + MP_VERBOSE(mpctx, "hr-seek, skipping to %f%s%s\n", mpctx->hrseek_pts, + mpctx->hrseek_framedrop ? "" : " (no framedrop)", + mpctx->hrseek_backstep ? " (backstep)" : ""); } mpctx->start_timestamp = mp_time_sec(); @@ -334,6 +338,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, return; case MPSEEK_ABSOLUTE: case MPSEEK_FACTOR: + case MPSEEK_BACKSTEP: *seek = (struct seek_params) { .type = type, .amount = amount, @@ -670,89 +675,6 @@ static void handle_vo_events(struct MPContext *mpctx) mp_notify(mpctx, MP_EVENT_WIN_STATE, NULL); } -void add_frame_pts(struct MPContext *mpctx, double pts) -{ - if (pts == MP_NOPTS_VALUE || mpctx->hrseek_framedrop) { - mpctx->vo_pts_history_seek_ts++; // mark discontinuity - return; - } - if (mpctx->vo_pts_history_pts[0] == pts) // may be called multiple times - return; - for (int n = MAX_NUM_VO_PTS - 1; n >= 1; n--) { - mpctx->vo_pts_history_seek[n] = mpctx->vo_pts_history_seek[n - 1]; - mpctx->vo_pts_history_pts[n] = mpctx->vo_pts_history_pts[n - 1]; - } - mpctx->vo_pts_history_seek[0] = mpctx->vo_pts_history_seek_ts; - mpctx->vo_pts_history_pts[0] = pts; -} - -static double find_previous_pts(struct MPContext *mpctx, double pts) -{ - for (int n = 0; n < MAX_NUM_VO_PTS - 1; n++) { - if (pts == mpctx->vo_pts_history_pts[n] && - mpctx->vo_pts_history_seek[n] != 0 && - mpctx->vo_pts_history_seek[n] == mpctx->vo_pts_history_seek[n + 1]) - { - return mpctx->vo_pts_history_pts[n + 1]; - } - } - return MP_NOPTS_VALUE; -} - -static double get_last_frame_pts(struct MPContext *mpctx) -{ - if (mpctx->vo_pts_history_seek[0] == mpctx->vo_pts_history_seek_ts) - return mpctx->vo_pts_history_pts[0]; - return MP_NOPTS_VALUE; -} - -static void handle_backstep(struct MPContext *mpctx) -{ - if (!mpctx->backstep_active) - return; - - double current_pts = mpctx->last_vo_pts; - mpctx->backstep_active = false; - if (mpctx->d_video && current_pts != MP_NOPTS_VALUE) { - double seek_pts = find_previous_pts(mpctx, current_pts); - if (seek_pts != MP_NOPTS_VALUE) { - queue_seek(mpctx, MPSEEK_ABSOLUTE, seek_pts, MPSEEK_VERY_EXACT, true); - } else { - double last = get_last_frame_pts(mpctx); - if (last != MP_NOPTS_VALUE && last >= current_pts && - mpctx->backstep_start_seek_ts != mpctx->vo_pts_history_seek_ts) - { - MP_ERR(mpctx, "Backstep failed.\n"); - queue_seek(mpctx, MPSEEK_ABSOLUTE, current_pts, - MPSEEK_VERY_EXACT, true); - } else if (!mpctx->hrseek_active) { - MP_VERBOSE(mpctx, "Start backstep indexing.\n"); - // Force it to index the video up until current_pts. - // The whole point is getting frames _before_ that PTS, - // so apply an arbitrary offset. (In theory the offset - // has to be large enough to reach the previous frame.) - mp_seek(mpctx, (struct seek_params){ - .type = MPSEEK_ABSOLUTE, - .amount = current_pts - 1.0, - }, false); - // Don't leave hr-seek mode. If all goes right, hr-seek - // mode is cancelled as soon as the frame before - // current_pts is found during hr-seeking. - // Note that current_pts should be part of the index, - // otherwise we can't find the previous frame, so set the - // seek target an arbitrary amount of time after it. - if (mpctx->hrseek_active) { - mpctx->hrseek_pts = current_pts + 10.0; - mpctx->hrseek_framedrop = false; - mpctx->backstep_active = true; - } - } else { - mpctx->backstep_active = true; - } - } - } -} - static void handle_sstep(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; @@ -1061,8 +983,6 @@ void run_playloop(struct MPContext *mpctx) mp_process_input(mpctx); - handle_backstep(mpctx); - handle_chapter_change(mpctx); handle_force_window(mpctx, false); diff --git a/player/video.c b/player/video.c index 28c6ac4d5c..33f3b4b970 100644 --- a/player/video.c +++ b/player/video.c @@ -310,7 +310,6 @@ int reinit_video_chain(struct MPContext *mpctx) vo_set_paused(mpctx->video_out, mpctx->paused); mpctx->sync_audio_to_video = !sh->attached_picture; - mpctx->vo_pts_history_seek_ts++; // If we switch on video again, ensure audio position matches up. if (mpctx->d_audio) @@ -603,7 +602,7 @@ static int get_req_frames(struct MPContext *mpctx, bool eof) return mpctx->opts->video_sync == VS_DEFAULT ? 1 : 2; int req = vo_get_num_req_frames(mpctx->video_out); - return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames)); + return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames) - 1); } // Whether it's fine to call add_new_frame() now. @@ -615,7 +614,7 @@ static bool needs_new_frame(struct MPContext *mpctx) // 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(mpctx->num_next_frames < MP_ARRAY_SIZE(mpctx->next_frames)); assert(frame); mpctx->next_frames[mpctx->num_next_frames++] = frame; if (mpctx->num_next_frames == 1) @@ -662,9 +661,6 @@ static int video_output_image(struct MPContext *mpctx, double endpts) return r; // error struct mp_image *img = vf_read_output_frame(mpctx->d_video->vfilter); if (img) { - // Always add these; they make backstepping after seeking faster. - add_frame_pts(mpctx, img->pts); - if (endpts != MP_NOPTS_VALUE && img->pts >= endpts) { r = VD_EOF; } else if (mpctx->max_frames == 0) { @@ -672,8 +668,19 @@ static int video_output_image(struct MPContext *mpctx, double endpts) } else if (hrseek && mpctx->hrseek_lastframe) { mp_image_setrefp(&mpctx->saved_frame, img); } else if (hrseek && img->pts < mpctx->hrseek_pts - .005) { - /* just skip */ + /* just skip - but save if backstep active */ + if (mpctx->hrseek_backstep) + mp_image_setrefp(&mpctx->saved_frame, img); } else { + if (mpctx->hrseek_backstep) { + if (mpctx->saved_frame) { + add_new_frame(mpctx, mpctx->saved_frame); + mpctx->saved_frame = NULL; + } else { + MP_WARN(mpctx, "Backstep failed.\n"); + } + mpctx->hrseek_backstep = false; + } add_new_frame(mpctx, img); img = NULL; } |