summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/input.rst3
-rw-r--r--DOCS/man/options.rst6
-rw-r--r--options/m_option.c9
-rw-r--r--options/options.c3
-rw-r--r--options/options.h1
-rw-r--r--player/command.c50
-rw-r--r--player/osd.c24
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);
+ }
}
}
}