summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/en/changes.rst1
-rw-r--r--DOCS/man/en/options.rst5
-rw-r--r--cfg-mplayer.h4
-rw-r--r--command.c90
-rw-r--r--m_option.c12
-rw-r--r--m_option.h4
6 files changed, 90 insertions, 26 deletions
diff --git a/DOCS/man/en/changes.rst b/DOCS/man/en/changes.rst
index ee08aa2a60..e4f331e807 100644
--- a/DOCS/man/en/changes.rst
+++ b/DOCS/man/en/changes.rst
@@ -93,6 +93,7 @@ Command line switches
=================================== ===================================
-nosound --no-audio
-use-filename-title --title="${filename}"
+ -loop 0 --loop=inf
=================================== ===================================
input.conf and slave commands
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst
index 4e8ee56d68..cf622e5c31 100644
--- a/DOCS/man/en/options.rst
+++ b/DOCS/man/en/options.rst
@@ -1142,8 +1142,9 @@
*NOTE*: This option is obsolete now that MPlayer has OpenDML support.
---loop=<number>
- Loops movie playback <number> times. 0 means forever.
+--loop=<number|inf|off>
+ Loops playback <number> times. ``inf`` means forever and ``off`` disables
+ looping.
--mc=<seconds/frame>
Maximum A-V sync correction per frame (in seconds)
diff --git a/cfg-mplayer.h b/cfg-mplayer.h
index d27ee323d3..e433e91955 100644
--- a/cfg-mplayer.h
+++ b/cfg-mplayer.h
@@ -688,7 +688,9 @@ const m_option_t mplayer_opts[]={
{"leak-report", "", CONF_TYPE_PRINT, 0, 0, 0, (void*)1},
OPT_FLAG_CONSTANTS("no-loop", loop_times, 0, 0, -1),
- OPT_INTRANGE("loop", loop_times, 0, -1, 10000),
+ OPT_CHOICE_OR_INT("loop", loop_times, 0, 1, 10000,
+ ({"off", -1}, {"0", -1},
+ {"inf", 0})),
{"playlist", NULL, CONF_TYPE_STRING, CONF_NOCFG | M_OPT_MIN, 1, 0, NULL},
{"shuffle", NULL, CONF_TYPE_FLAG, CONF_NOCFG, 0, 0, NULL},
diff --git a/command.c b/command.c
index 7947f83d6a..8a7f17dfba 100644
--- a/command.c
+++ b/command.c
@@ -105,6 +105,66 @@ static void rescale_input_coordinates(struct MPContext *mpctx, int ix, int iy,
vo->dheight, vo_fs);
}
+static void choice_get_min_max(const struct m_option *opt, int *min, int *max)
+{
+ assert(opt->type == &m_option_type_choice);
+ *min = INT_MAX;
+ *max = INT_MIN;
+ for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++) {
+ *min = FFMIN(*min, alt->value);
+ *max = FFMAX(*max, alt->value);
+ }
+ if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
+ *min = FFMIN(*min, opt->min);
+ *max = FFMAX(*max, opt->max);
+ }
+}
+
+static void check_choice(int dir, int val, bool *found, int *best, int choice)
+{
+ if ((dir == -1 && (!(*found) || choice > (*best)) && choice < val) ||
+ (dir == +1 && (!(*found) || choice < (*best)) && choice > val))
+ {
+ *found = true;
+ *best = choice;
+ }
+}
+
+static int step_choice(const struct m_option *opt, int val, int add, bool wrap)
+{
+ assert(opt->type == &m_option_type_choice);
+ int dir = add > 0 ? +1 : -1;
+ bool found = false;
+ int best = 0; // init. value unused
+
+ if (add == 0)
+ return val;
+
+ if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
+ int newval = val + add;
+ if (val >= opt->min && val <= opt->max &&
+ newval >= opt->min && newval <= opt->max)
+ {
+ found = true;
+ best = newval;
+ } else {
+ check_choice(dir, val, &found, &best, opt->min);
+ check_choice(dir, val, &found, &best, opt->max);
+ }
+ }
+
+ for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++)
+ check_choice(dir, val, &found, &best, alt->value);
+
+ if (!found) {
+ int min, max;
+ choice_get_min_max(opt, &min, &max);
+ best = (dir == -1) ^ wrap ? min : max;
+ }
+
+ return best;
+}
+
static int mp_property_generic_option(struct m_option *prop, int action,
void *arg, MPContext *mpctx)
{
@@ -125,13 +185,9 @@ static int mp_property_generic_option(struct m_option *prop, int action,
return M_PROPERTY_OK;
case M_PROPERTY_STEP_UP:
if (opt->type == &m_option_type_choice) {
+ int add = arg ? (*(int *)arg) : +1;
int v = *(int *) valptr;
- int best = v;
- struct m_opt_choice_alternatives *alt;
- for (alt = opt->priv; alt->name; alt++)
- if ((unsigned) alt->value - v - 1 < (unsigned) best - v - 1)
- best = alt->value;
- *(int *) valptr = best;
+ *(int *) valptr = step_choice(opt, v, add, true);
return M_PROPERTY_OK;
}
break;
@@ -150,20 +206,7 @@ static int mp_property_osdlevel(m_option_t *prop, int action, void *arg,
static int mp_property_loop(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
{
- struct MPOpts *opts = &mpctx->opts;
- switch (action) {
- case M_PROPERTY_PRINT:
- if (!arg)
- return M_PROPERTY_ERROR;
- if (opts->loop_times < 0)
- *(char **)arg = talloc_strdup(NULL, "off");
- else if (opts->loop_times == 0)
- *(char **)arg = talloc_strdup(NULL, "inf");
- else
- break;
- return M_PROPERTY_OK;
- }
- return m_property_int_range(prop, action, arg, &opts->loop_times);
+ return mp_property_generic_option(prop, action, arg, mpctx);
}
/// Playback speed (RW)
@@ -1628,8 +1671,8 @@ static const m_option_t mp_properties[] = {
// General
{ "osdlevel", mp_property_osdlevel, CONF_TYPE_INT,
M_OPT_RANGE, 0, 3, NULL },
- { "loop", mp_property_loop, CONF_TYPE_INT,
- M_OPT_MIN, -1, 0, NULL },
+ { "loop", mp_property_loop, &m_option_type_choice,
+ 0, 0, 0, "loop" },
{ "speed", mp_property_playback_speed, CONF_TYPE_FLOAT,
M_OPT_RANGE, 0.01, 100.0, NULL },
{ "filename", mp_property_filename, CONF_TYPE_STRING,
@@ -2192,7 +2235,8 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
&prop, mpctx)) <= 0)
goto step_prop_err;
if (prop->type == CONF_TYPE_INT ||
- prop->type == CONF_TYPE_FLAG)
+ prop->type == CONF_TYPE_FLAG ||
+ prop->type == &m_option_type_choice)
i = cmd->args[1].v.f, arg = &i;
else if (prop->type == CONF_TYPE_FLOAT)
arg = &cmd->args[1].v.f;
diff --git a/m_option.c b/m_option.c
index d2ab925a70..2f8daa42de 100644
--- a/m_option.c
+++ b/m_option.c
@@ -274,6 +274,14 @@ static int parse_choice(const struct m_option *opt, struct bstr name,
if (!alt->name) {
if (param.len == 0)
return M_OPT_MISSING_PARAM;
+ if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
+ long long val;
+ if (parse_longlong(opt, name, param, &val) == 1) {
+ if (dst)
+ *(int *)dst = val;
+ return 1;
+ }
+ }
mp_msg(MSGT_CFGPARSER, MSGL_ERR,
"Invalid value for option %.*s: %.*s\n",
BSTR_P(name), BSTR_P(param));
@@ -296,6 +304,10 @@ static char *print_choice(const m_option_t *opt, const void *val)
for (alt = opt->priv; alt->name; alt++)
if (alt->value == v)
return talloc_strdup(NULL, alt->name);
+ if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
+ if (v >= opt->min && v <= opt->max)
+ return talloc_asprintf(NULL, "%d", v);
+ }
abort();
}
diff --git a/m_option.h b/m_option.h
index f46b14281c..2b3ab13355 100644
--- a/m_option.h
+++ b/m_option.h
@@ -465,6 +465,10 @@ static inline void m_option_free(const m_option_t *opt, void *dst)
#define OPT_HELPER_REMOVEPAREN(...) __VA_ARGS__
#define OPT_CHOICE(...) OPT_CHOICE_(__VA_ARGS__, .type = &m_option_type_choice)
#define OPT_CHOICE_(optname, varname, flags, choices, ...) OPT_GENERAL(optname, varname, flags, .priv = (void *)&(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}, __VA_ARGS__)
+// Union of choices and an int range. The choice values can be included in the
+// int range, or be completely separate - both works.
+#define OPT_CHOICE_OR_INT(...) OPT_CHOICE_OR_INT_(__VA_ARGS__, .type = &m_option_type_choice)
+#define OPT_CHOICE_OR_INT_(optname, varname, flags, minval, maxval, choices, ...) OPT_GENERAL(optname, varname, (flags) | CONF_RANGE, .min = minval, .max = maxval, .priv = (void *)&(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}, __VA_ARGS__)
#define OPT_TIME(...) OPT_GENERAL(__VA_ARGS__, .type = &m_option_type_time)
// subconf must have the type struct m_sub_options.