summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-05-27 01:24:22 +0200
committerwm4 <wm4@nowhere>2019-09-19 20:37:05 +0200
commit7a0f112a448df1d0390d53a3169606cad4ee6589 (patch)
tree8e22f7a0d4c1afb9286d1e1fbc21fde35123d97d /player
parent6f9059f85838dccea278402bad7e94cb254af594 (diff)
downloadmpv-7a0f112a448df1d0390d53a3169606cad4ee6589.tar.bz2
mpv-7a0f112a448df1d0390d53a3169606cad4ee6589.tar.xz
player: modify/simplify AB-loop behavior
This changes the behavior of the --ab-loop-a/b options. In addition, it makes it work with backward playback mode. The most obvious change is that the both the A and B point need to be set now before any looping happens. Unlike before, unset points don't implicitly use the start or end of the file. I think the old behavior was a feature that was explicitly added/wanted. Well, it's gone now. This is because of 2 reasons: 1. I never liked this feature, and it always got in my way (as user). 2. It's inherently annoying with backward playback mode. In backward playback mode, the user wants to set A/B in the wrong order. The ab-loop command will first set A, then B, so if you use this command during backward playback, A will be set to a higher timestamps than B. If you switch back to forward playback mode, the loop would stop working. I want the loop to just continue to work, and the chosen solution conflicts with the removed feature. The order issue above _could_ be fixed by also switching the AB-loop user option values around on direction switch. But there are no other instances of option changes magically affecting other options, and doing this would probably lead to unexpected misery (dying from corner cases and such). Another solution is sorting the A/B points by timestamps after copying them from the user options. Then A/B options set in backward mode will work in forward mode. This is the chosen solution. If you sort the points, you don't know anymore whether the unset point is supposed to signify the end or the start of the file. The AB-loop code is slightly better abstracted now, so it should be easy to restore the removed feature. It would still require coming up with a solution for backwards playback, though. A minor change is that if one point is set and the other is unset, I'm rendering both the chapter markers and the marker for the set point. Why? I don't know. My test file had chapters, and I guess I decided this looked better. This commit also fixes some subtle and obvious issues that I already forgot about when I wrote this commit message. It cleans up some minor code duplication and nonsense too. Regarding backward playback, the code uses an unsanitary mix of internal ("transformed") and user timestamps. So the play_dir variable appears more than usual. To mention one unfixed issue: if you set an AB-loop that is completely past the end of the file, it will get stuck in an infinite seeking loop once playback reaches the end of the file. Fixing this reliably seemed annoying, so the fix is "just don't do this". It's not a hard freeze anyway.
Diffstat (limited to 'player')
-rw-r--r--player/command.c8
-rw-r--r--player/core.h4
-rw-r--r--player/loadfile.c3
-rw-r--r--player/misc.c92
-rw-r--r--player/osd.c21
-rw-r--r--player/playloop.c30
6 files changed, 78 insertions, 80 deletions
diff --git a/player/command.c b/player/command.c
index 68ce861c6f..7fa8da8fcb 100644
--- a/player/command.c
+++ b/player/command.c
@@ -3009,7 +3009,6 @@ static int mp_property_ab_loop(void *ctx, struct m_property *prop,
int action, void *arg)
{
struct MPContext *mpctx = ctx;
- struct MPOpts *opts = mpctx->opts;
if (action == M_PROPERTY_KEY_ACTION) {
double val;
if (mp_property_generic_option(mpctx, prop, M_PROPERTY_GET, &val) < 1)
@@ -3019,12 +3018,7 @@ static int mp_property_ab_loop(void *ctx, struct m_property *prop,
}
int r = mp_property_generic_option(mpctx, prop, action, arg);
if (r > 0 && action == M_PROPERTY_SET) {
- mpctx->ab_loop_clip = mpctx->playback_pts < opts->ab_loop[1];
- if (strcmp(prop->name, "ab-loop-b") == 0) {
- if (opts->ab_loop[1] != MP_NOPTS_VALUE &&
- mpctx->playback_pts <= opts->ab_loop[1])
- mpctx->ab_loop_clip = true;
- }
+ update_ab_loop_clip(mpctx);
// Update if visible
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
mp_wakeup_core(mpctx);
diff --git a/player/core.h b/player/core.h
index 0294b77025..c0b3e3d8e0 100644
--- a/player/core.h
+++ b/player/core.h
@@ -551,9 +551,10 @@ void mp_update_logging(struct MPContext *mpctx, bool preinit);
void issue_refresh_seek(struct MPContext *mpctx, enum seek_precision min_prec);
// misc.c
+double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t);
double get_play_end_pts(struct MPContext *mpctx);
double get_play_start_pts(struct MPContext *mpctx);
-double get_ab_loop_start_time(struct MPContext *mpctx);
+bool get_ab_loop_times(struct MPContext *mpctx, double t[2]);
void merge_playlist_files(struct playlist *pl);
void update_vo_playback_state(struct MPContext *mpctx);
void update_window_title(struct MPContext *mpctx, bool force);
@@ -605,6 +606,7 @@ void idle_loop(struct MPContext *mpctx);
int handle_force_window(struct MPContext *mpctx, bool force);
void seek_to_last_frame(struct MPContext *mpctx);
void update_screensaver_state(struct MPContext *mpctx);
+void update_ab_loop_clip(struct MPContext *mpctx);
// scripting.c
struct mp_scripting {
diff --git a/player/loadfile.c b/player/loadfile.c
index 078eac53a6..e1e0c2c794 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -1550,7 +1550,8 @@ static void play_current_file(struct MPContext *mpctx)
}
}
- double play_start_pts = get_play_start_pts(mpctx);
+ // (Not get_play_start_pts(), which would always trigger a seek.)
+ double play_start_pts = rel_time_to_abs(mpctx, opts->play_start);
// Backward playback -> start from end by default.
if (play_start_pts == MP_NOPTS_VALUE && opts->play_dir < 0)
diff --git a/player/misc.c b/player/misc.c
index a01eeef479..a39c8df82b 100644
--- a/player/misc.c
+++ b/player/misc.c
@@ -45,7 +45,7 @@
#include "core.h"
#include "command.h"
-static double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t)
+double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t)
{
double length = get_time_length(mpctx);
// Relative times are an offset to the start of the file.
@@ -75,69 +75,67 @@ static double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t)
return MP_NOPTS_VALUE;
}
-double get_play_end_pts(struct MPContext *mpctx)
+static double get_play_end_pts_setting(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
double end = rel_time_to_abs(mpctx, opts->play_end);
- if (opts->play_length.type) {
+ double length = rel_time_to_abs(mpctx, opts->play_length);
+ if (length != MP_NOPTS_VALUE) {
double start = get_play_start_pts(mpctx);
- if (start == MP_NOPTS_VALUE)
- start = 0;
- double length = rel_time_to_abs(mpctx, opts->play_length);
- if (length != MP_NOPTS_VALUE && (end == MP_NOPTS_VALUE || start + length < end))
+ if (end == MP_NOPTS_VALUE || start + length < end)
end = start + length;
}
- // even though MP_NOPTS_VALUE is currently negative
- // it doesn't necessarily have to remain that way
- double ab_loop_start_time = get_ab_loop_start_time(mpctx);
- if (mpctx->ab_loop_clip && opts->ab_loop[1] != MP_NOPTS_VALUE &&
- (ab_loop_start_time == MP_NOPTS_VALUE || opts->ab_loop[1] > ab_loop_start_time))
- {
- if (end == MP_NOPTS_VALUE || end > opts->ab_loop[1])
- end = opts->ab_loop[1];
+ return end;
+}
+
+// Return absolute timestamp against which currently playing media should be
+// clipped. Returns MP_NOPTS_VALUE if no clipping should happen.
+double get_play_end_pts(struct MPContext *mpctx)
+{
+ double end = get_play_end_pts_setting(mpctx);
+ double ab[2];
+ if (mpctx->ab_loop_clip && get_ab_loop_times(mpctx, ab)) {
+ if (end == MP_NOPTS_VALUE || end > ab[1])
+ end = ab[1];
}
return end;
}
-/**
- * Get the rebased PTS for which playback should start.
- * The order of priority is as follows:
- * 1. --start, if set.
- * 2. The start chapter, if set.
- * 3. MP_NOPTS_VALUE.
- * If unspecified, return MP_NOPTS_VALUE.
- * Does not return zero unless the start time is explicitly set to zero.
- */
+// Get the absolute PTS at which playback should start.
+// Never returns MP_NOPTS_VALUE.
double get_play_start_pts(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- return rel_time_to_abs(mpctx, opts->play_start);
+ double res = rel_time_to_abs(mpctx, opts->play_start);
+ if (res == MP_NOPTS_VALUE) {
+ res = 0;
+ if (!opts->rebase_start_time && mpctx->demuxer)
+ res = mpctx->demuxer->start_time;
+ // Backward playback -> start from end by default.
+ if (mpctx->play_dir < 0 && mpctx->demuxer)
+ res = MPMAX(mpctx->demuxer->duration, 0);
+ }
+ return res;
}
-/**
- * Get the time that an ab-loop seek should seek to.
- * The order of priority is as follows:
- * 1. --ab-loop-a, if set.
- * 2. The Playback Start PTS, if set.
- * 3. MP_NOPTS_VALUE.
- * If unspecified, return MP_NOPTS_VALUE.
- * Does not return zero unless the start time is explicitly set to zero.
- */
-double get_ab_loop_start_time(struct MPContext *mpctx)
+// Get timestamps to use for AB-loop. Returns false iff any of the timestamps
+// are invalid and/or AB-loops are currently disabled, and set t[] to either
+// the user options or NOPTS on best effort basis.
+bool get_ab_loop_times(struct MPContext *mpctx, double t[2])
{
struct MPOpts *opts = mpctx->opts;
- double ab_loop_start_time;
- if (opts->ab_loop[0] != MP_NOPTS_VALUE) {
- ab_loop_start_time = opts->ab_loop[0];
- } else {
- /*
- * There is no check for MP_NOPTS_VALUE here
- * because that's exactly what we want to return
- * if get_play_start_pts comes up empty here.
- */
- ab_loop_start_time = get_play_start_pts(mpctx);
- }
- return ab_loop_start_time;
+ int dir = mpctx->play_dir;
+
+ t[0] = opts->ab_loop[0];
+ t[1] = opts->ab_loop[1];
+
+ if (t[0] == MP_NOPTS_VALUE || t[1] == MP_NOPTS_VALUE || t[0] == t[1])
+ return false;
+
+ if (t[0] * dir > t[1] * dir)
+ MPSWAP(double, t[0], t[1]);
+
+ return true;
}
double get_track_seek_offset(struct MPContext *mpctx, struct track *track)
diff --git a/player/osd.c b/player/osd.c
index 365418e573..a12ccaa8b1 100644
--- a/player/osd.c
+++ b/player/osd.c
@@ -352,27 +352,22 @@ static void update_osd_bar(struct MPContext *mpctx, int type,
void set_osd_bar_chapters(struct MPContext *mpctx, int type)
{
- struct MPOpts *opts = mpctx->opts;
if (mpctx->osd_progbar.type != type)
return;
mpctx->osd_progbar.num_stops = 0;
double len = get_time_length(mpctx);
if (len > 0) {
- if (opts->ab_loop[0] != MP_NOPTS_VALUE ||
- opts->ab_loop[1] != MP_NOPTS_VALUE)
- {
- double ab_loop_start_time = get_ab_loop_start_time(mpctx);
- if (ab_loop_start_time == MP_NOPTS_VALUE)
- ab_loop_start_time = 0;
- MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops,
- mpctx->osd_progbar.num_stops, ab_loop_start_time / len);
- if (opts->ab_loop[1] != MP_NOPTS_VALUE) {
+ // Always render the loop points, even if they're incomplete.
+ double ab[2];
+ bool valid = get_ab_loop_times(mpctx, ab);
+ for (int n = 0; n < 2; n++) {
+ if (ab[n] != MP_NOPTS_VALUE) {
MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops,
- mpctx->osd_progbar.num_stops,
- opts->ab_loop[1] / len);
+ mpctx->osd_progbar.num_stops, ab[n] / len);
}
- } else {
+ }
+ if (!valid) {
int num = get_chapter_count(mpctx);
for (int n = 0; n < num; n++) {
double time = chapter_start_time(mpctx, n);
diff --git a/player/playloop.c b/player/playloop.c
index 3dafb35f3f..0d5e9c1779 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -417,7 +417,7 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
mp_notify(mpctx, MPV_EVENT_SEEK, NULL);
mp_notify(mpctx, MPV_EVENT_TICK, NULL);
- mpctx->ab_loop_clip = mpctx->last_seek_pts < opts->ab_loop[1];
+ update_ab_loop_clip(mpctx);
mpctx->current_seek = seek;
}
@@ -527,10 +527,8 @@ double get_current_pos_ratio(struct MPContext *mpctx, bool use_range)
if (use_range) {
double startpos = get_play_start_pts(mpctx);
double endpos = get_play_end_pts(mpctx);
- if (endpos == MP_NOPTS_VALUE || endpos > MPMAX(0, len))
+ if (endpos > MPMAX(0, len))
endpos = MPMAX(0, len);
- if (startpos == MP_NOPTS_VALUE || startpos < 0)
- startpos = 0;
if (endpos < startpos)
endpos = startpos;
start = startpos;
@@ -614,6 +612,18 @@ int get_chapter_count(struct MPContext *mpctx)
return mpctx->num_chapters;
}
+// If the current playback position (or seek target) falls before the B
+// position, actually make playback loop when reaching the B point. The
+// intention is that you can seek out of the ab-loop range.
+void update_ab_loop_clip(struct MPContext *mpctx)
+{
+ double pts = get_current_time(mpctx);
+ double ab[2];
+ mpctx->ab_loop_clip = pts != MP_NOPTS_VALUE &&
+ get_ab_loop_times(mpctx, ab) &&
+ pts * mpctx->play_dir <= ab[1] * mpctx->play_dir;
+}
+
static void handle_osd_redraw(struct MPContext *mpctx)
{
if (!mpctx->video_out || !mpctx->video_out->config_ok)
@@ -804,17 +814,15 @@ static void handle_loop_file(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- if (mpctx->stop_play == AT_END_OF_FILE &&
- (opts->ab_loop[0] != MP_NOPTS_VALUE || opts->ab_loop[1] != MP_NOPTS_VALUE))
+ double ab[2];
+ if (mpctx->stop_play == AT_END_OF_FILE && get_ab_loop_times(mpctx, ab) &&
+ mpctx->ab_loop_clip)
{
// Assumes execute_queued_seek() happens before next audio/video is
// attempted to be decoded or filtered.
mpctx->stop_play = KEEP_PLAYING;
- double start = get_ab_loop_start_time(mpctx);
- if (start == MP_NOPTS_VALUE)
- start = 0;
mark_seek(mpctx);
- queue_seek(mpctx, MPSEEK_ABSOLUTE, start, MPSEEK_EXACT,
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, ab[0], MPSEEK_EXACT,
MPSEEK_FLAG_NOFLUSH);
}
@@ -1084,7 +1092,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
}
mpctx->playing_msg_shown = true;
mp_wakeup_core(mpctx);
- mpctx->ab_loop_clip = mpctx->playback_pts < opts->ab_loop[1];
+ update_ab_loop_clip(mpctx);
MP_VERBOSE(mpctx, "playback restart complete @ %f\n", mpctx->playback_pts);
}
}