mirror of https://github.com/mpv-player/mpv
command: implement A-B loops
Probably needs to be polished a bit more. Also, might require a key binding that can set/clear the loop points in a more intuitive way. For now, something like this can be put into input.conf to use it: ctrl+y set ab-loop-a ${time-pos} # set A ctrl+x set ab-loop-b ${time-pos} # set B ctrl+c set ab-loop-a no # clear (mostly) Fixes #1241.
This commit is contained in:
parent
ed59234378
commit
4bd6c91d9b
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,14 +4534,41 @@ 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,
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct command_ctx *ctx = mpctx->command_ctx;
|
||||
if (event == MPV_EVENT_START_FILE)
|
||||
ctx->last_seek_pts = MP_NOPTS_VALUE;
|
||||
command_event(mpctx, event, arg);
|
||||
|
||||
mp_client_broadcast_event(mpctx, event, arg);
|
||||
}
|
||||
|
|
24
player/osd.c
24
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue