diff options
-rw-r--r-- | DOCS/man/options.rst | 11 | ||||
-rw-r--r-- | player/command.c | 8 | ||||
-rw-r--r-- | player/core.h | 4 | ||||
-rw-r--r-- | player/loadfile.c | 3 | ||||
-rw-r--r-- | player/misc.c | 92 | ||||
-rw-r--r-- | player/osd.c | 21 | ||||
-rw-r--r-- | player/playloop.c | 30 |
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); } } |