summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-01-12 23:49:00 +0100
committerwm4 <wm4@nowhere>2016-01-12 23:49:00 +0100
commite420464ba693a5920d4dc172b3f7e9a0c725e3d4 (patch)
treee149445377e94da3274f7d3f588449ed24ece3c1
parent6fc0fe4426c1b71630d281cbc9e0406f8ad7deee (diff)
downloadmpv-e420464ba693a5920d4dc172b3f7e9a0c725e3d4.tar.bz2
mpv-e420464ba693a5920d4dc172b3f7e9a0c725e3d4.tar.xz
player: simplify backstepping
Basically reimplement it. The old implementation was quite stupid, and was probably done this way because video filtering and output used to be way less decoupled. Now we can reimplement it in a very simple way: when backstepping, seek to current time, but keep the last frame that was supposed to be discarded when reaching the target time. When the seek finishes, prepend the saved frame to the video frame queue. A disadvantage is that the new implementation fails to skip over timeline boundaries (ordered chapters etc.), but this never worked properly anyway. It's possible that this will be fixed some time in the future.
-rw-r--r--player/core.h17
-rw-r--r--player/loadfile.c1
-rw-r--r--player/playloop.c116
-rw-r--r--player/video.c21
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;
}