summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/options.rst11
-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
7 files changed, 86 insertions, 83 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 775a937c1e..6da1b58b6c 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -314,9 +314,14 @@ Playback Control
the ``a`` timestamp. Seeking past the ``b`` point doesn't loop (this is
intentional).
- If both options are set to ``no`` or unset, looping is disabled.
- Otherwise, the start/end of playback is used if one of the options
- is set to ``no`` or unset.
+ If ``a`` is after ``b``, the behavior is as if the points were given in
+ the right order, and the player will seek to ``b`` after crossing through
+ ``a``. This is different from old behavior, where looping was disabled (and
+ as a bug, looped back to ``a`` on the end of the file).
+
+ If either options are set to ``no`` (or unset), looping is disabled. This
+ is different from old behavior, where an unset ``a`` implied the start of
+ the file, and an unset ``b`` the end of the file.
The loop-points can be adjusted at runtime with the corresponding
properties. See also ``ab-loop`` command.
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);
}
}