diff options
-rw-r--r-- | DOCS/man/input.rst | 3 | ||||
-rw-r--r-- | DOCS/man/options.rst | 6 | ||||
-rw-r--r-- | options/m_option.c | 9 | ||||
-rw-r--r-- | options/options.c | 3 | ||||
-rw-r--r-- | options/options.h | 1 | ||||
-rw-r--r-- | player/command.c | 50 | ||||
-rw-r--r-- | player/osd.c | 24 |
7 files changed, 85 insertions, 11 deletions
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 20c61f38a1..0e9b245ad8 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -778,6 +778,9 @@ Property list "title" MPV_FORMAT_STRING "default" MPV_FORMAT_FLAG +``ab-loop-a``, ``ab-loop-b`` (TW) + Set/get A-B loop points. See corresponding options. + ``angle`` (RW) Current DVD angle. diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index feaeac6b30..39fe8ffa92 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -229,6 +229,12 @@ Playback Control between the two option is that this option performs a seek on loop, instead of reloading the file. +``--ab-loop-a=<time>``, ``--ab-loop-b=<time>`` + Set loop points. If playback passes the ``b`` timestamp, it will seek to + the ``a`` timestamp. Seeking past the ``b`` point doesn't loop (this is + intentional). The loop-points can be adjusted at runtime with the + corresponding properties. + ``--ordered-chapters``, ``--no-ordered-chapters`` Enabled by default. Disable support for Matroska ordered chapters. mpv will not load or diff --git a/options/m_option.c b/options/m_option.c index 97ba467ed4..9ec6ccc427 100644 --- a/options/m_option.c +++ b/options/m_option.c @@ -2180,7 +2180,9 @@ static int parse_time(struct mp_log *log, const m_option_t *opt, if (param.len == 0) return M_OPT_MISSING_PARAM; - if (!parse_timestring(param, &time, 0)) { + if (opt->min == MP_NOPTS_VALUE && bstr_equals0(param, "no")) { + time = MP_NOPTS_VALUE; + } else if (!parse_timestring(param, &time, 0)) { mp_err(log, "Option %.*s: invalid time: '%.*s'\n", BSTR_P(name), BSTR_P(param)); return M_OPT_INVALID; @@ -2193,7 +2195,10 @@ static int parse_time(struct mp_log *log, const m_option_t *opt, static char *pretty_print_time(const m_option_t *opt, const void *val) { - return mp_format_time(*(double *)val, false); + double pts = *(double *)val; + if (pts == MP_NOPTS_VALUE && opt->min == MP_NOPTS_VALUE) + return talloc_strdup(NULL, "no"); // symmetry with parsing + return mp_format_time(pts, false); } const m_option_type_t m_option_type_time = { diff --git a/options/options.c b/options/options.c index ba251f6216..4a4d83210b 100644 --- a/options/options.c +++ b/options/options.c @@ -180,6 +180,9 @@ const m_option_t mp_opts[] = { OPT_REL_TIME("end", play_end, 0), OPT_REL_TIME("length", play_length, 0), + OPT_TIME("ab-loop-a", ab_loop[0], 0, .min = MP_NOPTS_VALUE), + OPT_TIME("ab-loop-b", ab_loop[1], 0, .min = MP_NOPTS_VALUE), + OPT_FLAG("pause", pause, M_OPT_FIXED), OPT_FLAG("keep-open", keep_open, 0), diff --git a/options/options.h b/options/options.h index 9284320f40..63f77b2b3a 100644 --- a/options/options.h +++ b/options/options.h @@ -158,6 +158,7 @@ typedef struct MPOpts { struct m_rel_time play_end; struct m_rel_time play_length; int play_frames; + double ab_loop[2]; double step_sec; int position_resume; int position_save_on_quit; diff --git a/player/command.c b/player/command.c index f6c20a6cb1..803558f7d9 100644 --- a/player/command.c +++ b/player/command.c @@ -74,6 +74,8 @@ struct command_ctx { double last_seek_time; double last_seek_pts; + double prev_pts; + struct cycle_counter *cycle_counters; int num_cycle_counters; @@ -2930,8 +2932,22 @@ static int mp_property_af(void *ctx, struct m_property *prop, return property_filter(prop, action, arg, ctx, STREAM_AUDIO); } +static int mp_property_ab_loop(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + if (action == M_PROPERTY_KEY_ACTION) { + double val; + if (mp_property_generic_option(mpctx, prop, M_PROPERTY_GET, &val) < 1) + return M_PROPERTY_ERROR; + + return property_time(action, arg, val); + } + return mp_property_generic_option(mpctx, prop, action, arg); +} + static int mp_property_version(void *ctx, struct m_property *prop, - int action, void *arg) + int action, void *arg) { return m_property_strdup_ro(action, arg, mpv_version); } @@ -3257,6 +3273,9 @@ static const struct m_property mp_properties[] = { {"video-rotate", video_simple_refresh_property}, + {"ab-loop-a", mp_property_ab_loop}, + {"ab-loop-b", mp_property_ab_loop}, + #define PROPERTY_TV_COLOR(name, type) \ {name, mp_property_tv_color, (void *)(intptr_t)type} PROPERTY_TV_COLOR("tv-brightness", TV_COLOR_BRIGHTNESS), @@ -4515,15 +4534,42 @@ void command_init(struct MPContext *mpctx) mpctx->command_ctx = talloc(NULL, struct command_ctx); *mpctx->command_ctx = (struct command_ctx){ .last_seek_pts = MP_NOPTS_VALUE, + .prev_pts = MP_NOPTS_VALUE, }; } -void mp_notify(struct MPContext *mpctx, int event, void *arg) +static void command_event(struct MPContext *mpctx, int event, void *arg) { struct command_ctx *ctx = mpctx->command_ctx; + struct MPOpts *opts = mpctx->opts; + if (event == MPV_EVENT_START_FILE) ctx->last_seek_pts = MP_NOPTS_VALUE; + if (event == MPV_EVENT_TICK) { + double now = + mpctx->restart_complete ? mpctx->playback_pts : MP_NOPTS_VALUE; + if (now != MP_NOPTS_VALUE && opts->ab_loop[0] != MP_NOPTS_VALUE && + opts->ab_loop[1] != MP_NOPTS_VALUE) + { + if (ctx->prev_pts >= opts->ab_loop[0] && + ctx->prev_pts < opts->ab_loop[1] && + now >= opts->ab_loop[1]) + { + queue_seek(mpctx, MPSEEK_ABSOLUTE, opts->ab_loop[0], 1, false); + } + } + ctx->prev_pts = now; + } + if (event == MPV_EVENT_SEEK) { + ctx->prev_pts = MP_NOPTS_VALUE; + } +} + +void mp_notify(struct MPContext *mpctx, int event, void *arg) +{ + command_event(mpctx, event, arg); + mp_client_broadcast_event(mpctx, event, arg); } diff --git a/player/osd.c b/player/osd.c index 7e099e2d0c..b6a0c1b98d 100644 --- a/player/osd.c +++ b/player/osd.c @@ -333,19 +333,29 @@ static void update_osd_bar(struct MPContext *mpctx, int type, static 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) { - int num = get_chapter_count(mpctx); - for (int n = 0; n < num; n++) { - double time = chapter_start_time(mpctx, n); - if (time >= 0) { - float pos = time / len; - MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops, - mpctx->osd_progbar.num_stops, pos); + if (opts->ab_loop[0] != MP_NOPTS_VALUE && + opts->ab_loop[1] != MP_NOPTS_VALUE) + { + MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops, + mpctx->osd_progbar.num_stops, opts->ab_loop[0] / len); + MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops, + mpctx->osd_progbar.num_stops, opts->ab_loop[1] / len); + } else { + int num = get_chapter_count(mpctx); + for (int n = 0; n < num; n++) { + double time = chapter_start_time(mpctx, n); + if (time >= 0) { + float pos = time / len; + MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops, + mpctx->osd_progbar.num_stops, pos); + } } } } |