mirror of https://github.com/mpv-player/mpv
player: more option/property consistency fixes
Some properties had a different type from their equivalent options (such as mute, volume, deinterlace, edition). This wasn't really sane, as raw option values should be always within their bounds. On the other hand, these properties use a different type to reflect runtime limits (such as range of available editions), or simply to improve the "UI" (you don't want to cycle throuhg the completely useless "auto" value when cycling the "mute" property). Handle this by making them always return the option type, but also allowing them to provide a "constricted" type, which is used for UI purposes. All M_PROPERTY_GET_CONSTRICTED_TYPE changes are related to this. One consequence is that you can set the volume property to arbitrary high values just like with the --volume option, but using the "add" command it still restricts it to the --volume-max range. Also deprecate --chapter, as it is grossly incompatible to the chapter property. We pondered renaming it to --chapters, or introducing a more powerful --range option, but concluded that --start --end is actually enough. These changes appear to take care of the last gross property/option incompatibilities, although there might still be a few lurking.
This commit is contained in:
parent
3ecc6d0a79
commit
2415b69572
|
@ -64,6 +64,8 @@ Interface changes
|
|||
- deprecate "--vo=direct3d_shaders" - use "--vo=direct3d" instead.
|
||||
Change "--vo=direct3d" to always use shaders by default.
|
||||
- deprecate --playlist-pos option, renamed to --playlist-start
|
||||
- deprecate the --chapter option, as it is redundant with --start/--end,
|
||||
and conflicts with the semantics of the "chapter" property
|
||||
- incompatible change to cdda:// protocol options: the part after cdda://
|
||||
now always sets the device, not the span or speed to be played. No
|
||||
separating extra "/" is needed. The hidden --cdda-device options is also
|
||||
|
|
|
@ -2079,12 +2079,14 @@ caveats with some properties (due to historical reasons):
|
|||
``deinterlace``
|
||||
While video is active, this behaves differently from the option. It will
|
||||
never return the ``auto`` value (but the state as observed by the video
|
||||
chain). You cannot set ``auto`` either.
|
||||
|
||||
Option changes at runtime are affected by this as well.
|
||||
chain). If you set ``auto``, the property will set this as the option value,
|
||||
and will return the actual video chain state as observed instead of auto.
|
||||
|
||||
``video-aspect``
|
||||
While video is active, always returns the effective aspect ratio.
|
||||
While video is active, always returns the effective aspect ratio. Setting
|
||||
a special value (like ``no``, values ``<= 0``) will make the property
|
||||
set this as option, and return whatever actual aspect was derived from the
|
||||
option setting.
|
||||
|
||||
``brightness`` (and other color options)
|
||||
If ``--vo=xv`` is used, these properties may return the adapter's current
|
||||
|
@ -2103,19 +2105,10 @@ caveats with some properties (due to historical reasons):
|
|||
|
||||
Option changes at runtime are affected by this as well.
|
||||
|
||||
``chapter``
|
||||
While playback is *not* active, the property behaves like the option, and
|
||||
you can set a chapter range. While playback is active, you can set only
|
||||
the current chapter (to which the player will seek immediately).
|
||||
|
||||
Option changes at runtime are affected by this as well.
|
||||
|
||||
``volume``
|
||||
When set as option, the maximum (set by ``--volume-max``) is not checked,
|
||||
while when set as property, the maximum is enforced.
|
||||
|
||||
``mute``
|
||||
The option has a deprecated ``auto`` value, which is equal to ``no``.
|
||||
``edition``
|
||||
While a file is loaded, the property will always return the effective
|
||||
edition, and setting the ``auto`` value will show somewhat strange behavior
|
||||
(the property eventually switching to whatever is the default edition).
|
||||
|
||||
``playlist``
|
||||
The property is read-only and returns the current internal playlist. The
|
||||
|
@ -2138,7 +2131,7 @@ caveats with some properties (due to historical reasons):
|
|||
Strictly speaking, option access via API (e.g. ``mpv_set_option_string()``)
|
||||
has the same problem, and it's only a difference between CLI/API.
|
||||
|
||||
``demuxer``, ``idle``, ``length``, ``audio-samplerate``, ``audio-channels``, ``audio-format``, ``fps``, ``cache``, ``playlist-pos``
|
||||
``demuxer``, ``idle``, ``length``, ``audio-samplerate``, ``audio-channels``, ``audio-format``, ``fps``, ``cache``, ``playlist-pos``, ``chapter``
|
||||
These behave completely different as property, but are deprecated (newer
|
||||
aliases which don't conflict have been added). After the deprecation period
|
||||
they will be changed to the proper option behavior.
|
||||
|
|
|
@ -87,9 +87,6 @@ struct m_opt_backup {
|
|||
void *backup;
|
||||
};
|
||||
|
||||
static struct m_config_option *m_config_get_co_raw(const struct m_config *config,
|
||||
struct bstr name);
|
||||
|
||||
static int parse_include(struct m_config *config, struct bstr param, bool set,
|
||||
int flags)
|
||||
{
|
||||
|
@ -556,8 +553,8 @@ static void m_config_add_option(struct m_config *config,
|
|||
MP_TARRAY_APPEND(config, config->opts, config->num_opts, co);
|
||||
}
|
||||
|
||||
static struct m_config_option *m_config_get_co_raw(const struct m_config *config,
|
||||
struct bstr name)
|
||||
struct m_config_option *m_config_get_co_raw(const struct m_config *config,
|
||||
struct bstr name)
|
||||
{
|
||||
if (!name.len)
|
||||
return NULL;
|
||||
|
|
|
@ -190,6 +190,8 @@ int m_config_set_option_node(struct m_config *config, bstr name,
|
|||
int m_config_parse_suboptions(struct m_config *config, char *name,
|
||||
char *subopts);
|
||||
|
||||
struct m_config_option *m_config_get_co_raw(const struct m_config *config,
|
||||
struct bstr name);
|
||||
struct m_config_option *m_config_get_co(const struct m_config *config,
|
||||
struct bstr name);
|
||||
|
||||
|
|
|
@ -115,6 +115,10 @@ int m_property_do(struct mp_log *log, const struct m_property *prop_list,
|
|||
M_PROPERTY_NOT_IMPLEMENTED)
|
||||
return r;
|
||||
// Fallback to m_option
|
||||
r = do_action(prop_list, name, M_PROPERTY_GET_CONSTRICTED_TYPE, &opt, ctx);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
assert(opt.type);
|
||||
if (!opt.type->add)
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
|
||||
|
@ -124,6 +128,13 @@ int m_property_do(struct mp_log *log, const struct m_property *prop_list,
|
|||
m_option_free(&opt, &val);
|
||||
return r;
|
||||
}
|
||||
case M_PROPERTY_GET_CONSTRICTED_TYPE: {
|
||||
if ((r = do_action(prop_list, name, action, arg, ctx)) >= 0)
|
||||
return r;
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, arg, ctx)) >= 0)
|
||||
return r;
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
case M_PROPERTY_SET: {
|
||||
return do_action(prop_list, name, M_PROPERTY_SET, arg, ctx);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,12 @@ enum mp_property_action {
|
|||
// arg: char**
|
||||
M_PROPERTY_PRINT,
|
||||
|
||||
// Like M_PROPERTY_GET_TYPE, but get a type that is compatible to the real
|
||||
// type, but reflect practical limits, such as runtime-available values.
|
||||
// This is mostly used for "UI" related things.
|
||||
// (Example: volume property.)
|
||||
M_PROPERTY_GET_CONSTRICTED_TYPE,
|
||||
|
||||
// Switch the property up/down by a given value.
|
||||
// If unimplemented, the property wrapper uses the property type as
|
||||
// fallback.
|
||||
|
|
|
@ -296,7 +296,8 @@ const m_option_t mp_opts[] = {
|
|||
#if HAVE_DVDREAD || HAVE_DVDNAV
|
||||
OPT_SUBSTRUCT("", dvd_opts, dvd_conf, 0),
|
||||
#endif /* HAVE_DVDREAD */
|
||||
OPT_INTPAIR("chapter", chapterrange, 0),
|
||||
OPT_INTPAIR("chapter", chapterrange, 0, .deprecation_message = "instead of "
|
||||
"--chapter=A-B use --start=#A --end=#B+1"),
|
||||
OPT_CHOICE_OR_INT("edition", edition_id, 0, 0, 8190,
|
||||
({"auto", -1})),
|
||||
#if HAVE_LIBBLURAY
|
||||
|
|
|
@ -279,10 +279,11 @@ int mp_on_set_option(void *ctx, struct m_config_option *co, void *data, int flag
|
|||
// vf*, af*, chapter
|
||||
// OK, is handled separately: playlist
|
||||
// OK, does not conflict on low level: audio-file, sub-file, external-file
|
||||
// OK, different value ranges, but happens to work for now: volume, edition
|
||||
// All the other properties are deprecated in their current form.
|
||||
static const char *const no_property[] = {
|
||||
"volume", // restricts to --volume-max
|
||||
"demuxer", "idle", "length", "audio-samplerate", "audio-channels",
|
||||
"audio-format", "fps", "cache", "playlist-pos", // different semantics
|
||||
"audio-format", "fps", "cache", "playlist-pos", "chapter",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -300,9 +301,19 @@ int mp_on_set_option(void *ctx, struct m_config_option *co, void *data, int flag
|
|||
name = tmp;
|
||||
}
|
||||
|
||||
int r = mp_property_do_silent(name, M_PROPERTY_SET, data, mpctx);
|
||||
struct m_option type = {0};
|
||||
|
||||
int r = mp_property_do_silent(name, M_PROPERTY_GET_TYPE, &type, mpctx);
|
||||
if (r == M_PROPERTY_UNKNOWN)
|
||||
goto direct_option; // not mapped as property
|
||||
if (r != M_PROPERTY_OK)
|
||||
return M_OPT_INVALID; // shouldn't happen
|
||||
|
||||
assert(type.type == co->opt->type);
|
||||
assert(type.max == co->opt->max);
|
||||
assert(type.min == co->opt->min);
|
||||
|
||||
r = mp_property_do_silent(name, M_PROPERTY_SET, data, mpctx);
|
||||
if (r != M_PROPERTY_OK)
|
||||
return M_OPT_INVALID;
|
||||
|
||||
|
@ -873,7 +884,7 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
|
|||
{
|
||||
MPContext *mpctx = ctx;
|
||||
if (!mpctx->playback_initialized)
|
||||
return mp_property_generic_option(mpctx, prop, action, arg);
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
int chapter = get_current_chapter(mpctx);
|
||||
int num = get_chapter_count(mpctx);
|
||||
|
@ -993,14 +1004,9 @@ static int mp_property_edition(void *ctx, struct m_property *prop,
|
|||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
if (!mpctx->playback_initialized)
|
||||
return mp_property_generic_option(mpctx, prop, action, arg);
|
||||
|
||||
struct demuxer *demuxer = mpctx->demuxer;
|
||||
if (!demuxer)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
if (demuxer->num_editions <= 0)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
if (!mpctx->playback_initialized || !demuxer || demuxer->num_editions <= 0)
|
||||
return mp_property_generic_option(mpctx, prop, action, arg);
|
||||
|
||||
int edition = demuxer->edition;
|
||||
|
||||
|
@ -1014,22 +1020,18 @@ static int mp_property_edition(void *ctx, struct m_property *prop,
|
|||
mpctx->opts->edition_id = edition;
|
||||
if (!mpctx->stop_play)
|
||||
mpctx->stop_play = PT_RELOAD_FILE;
|
||||
mp_wakeup_core(mpctx);;
|
||||
mp_wakeup_core(mpctx);
|
||||
break; // make it accessible to the demuxer via option change notify
|
||||
}
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
case M_PROPERTY_GET_TYPE: {
|
||||
struct m_option opt = {
|
||||
.type = CONF_TYPE_INT,
|
||||
.flags = CONF_RANGE,
|
||||
.min = 0,
|
||||
.max = demuxer->num_editions - 1,
|
||||
};
|
||||
*(struct m_option *)arg = opt;
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_GET_CONSTRICTED_TYPE: {
|
||||
int r = mp_property_generic_option(mpctx, prop, M_PROPERTY_GET_TYPE, arg);
|
||||
((struct m_option *)arg)->max = demuxer->num_editions - 1;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
return mp_property_generic_option(mpctx, prop, action, arg);
|
||||
}
|
||||
|
||||
static int get_edition_entry(int item, int action, void *arg, void *ctx)
|
||||
|
@ -1677,7 +1679,7 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
|
|||
struct MPOpts *opts = mpctx->opts;
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET_TYPE:
|
||||
case M_PROPERTY_GET_CONSTRICTED_TYPE:
|
||||
*(struct m_option *)arg = (struct m_option){
|
||||
.type = CONF_TYPE_FLOAT,
|
||||
.flags = M_OPT_RANGE,
|
||||
|
@ -1705,7 +1707,7 @@ static int mp_property_mute(void *ctx, struct m_property *prop,
|
|||
{
|
||||
MPContext *mpctx = ctx;
|
||||
|
||||
if (action == M_PROPERTY_GET_TYPE) {
|
||||
if (action == M_PROPERTY_GET_CONSTRICTED_TYPE) {
|
||||
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
|
@ -2373,14 +2375,14 @@ static int mp_property_deinterlace(void *ctx, struct m_property *prop,
|
|||
case M_PROPERTY_GET:
|
||||
*(int *)arg = get_deinterlacing(mpctx) > 0;
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_GET_TYPE:
|
||||
case M_PROPERTY_GET_CONSTRICTED_TYPE:
|
||||
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_SET:
|
||||
set_deinterlacing(mpctx, *(int *)arg);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
return mp_property_generic_option(mpctx, prop, action, arg);
|
||||
}
|
||||
|
||||
static int video_simple_refresh_property(void *ctx, struct m_property *prop,
|
||||
|
@ -4315,7 +4317,7 @@ static void show_property_osd(MPContext *mpctx, const char *name, int osd_mode)
|
|||
}
|
||||
|
||||
struct m_option prop = {0};
|
||||
mp_property_do(name, M_PROPERTY_GET_TYPE, &prop, mpctx);
|
||||
mp_property_do(name, M_PROPERTY_GET_CONSTRICTED_TYPE, &prop, mpctx);
|
||||
if ((osd_mode & MP_ON_OSD_BAR) && (prop.flags & CONF_RANGE) == CONF_RANGE) {
|
||||
if (prop.type == CONF_TYPE_INT) {
|
||||
int n = prop.min;
|
||||
|
@ -4668,7 +4670,7 @@ static int mp_property_multiply(char *property, double f, struct MPContext *mpct
|
|||
struct m_option opt = {0};
|
||||
int r;
|
||||
|
||||
r = mp_property_do(property, M_PROPERTY_GET_TYPE, &opt, mpctx);
|
||||
r = mp_property_do(property, M_PROPERTY_GET_CONSTRICTED_TYPE, &opt, mpctx);
|
||||
if (r != M_PROPERTY_OK)
|
||||
return r;
|
||||
assert(opt.type);
|
||||
|
@ -5615,7 +5617,8 @@ extern const struct m_sub_options gl_video_conf;
|
|||
|
||||
void mp_notify_property(struct MPContext *mpctx, const char *property)
|
||||
{
|
||||
struct m_config_option *co = m_config_get_co(mpctx->mconfig, bstr0(property));
|
||||
struct m_config_option *co =
|
||||
m_config_get_co_raw(mpctx->mconfig, bstr0(property));
|
||||
if (co) {
|
||||
if (m_config_is_in_group(mpctx->mconfig, &gl_video_conf, co)) {
|
||||
if (mpctx->video_out)
|
||||
|
|
|
@ -575,7 +575,7 @@ void uninit_video_chain(struct MPContext *mpctx);
|
|||
double calc_average_frame_duration(struct MPContext *mpctx);
|
||||
int init_video_decoder(struct MPContext *mpctx, struct track *track);
|
||||
int get_deinterlacing(struct MPContext *mpctx);
|
||||
void set_deinterlacing(struct MPContext *mpctx, bool enable);
|
||||
void set_deinterlacing(struct MPContext *mpctx, int opt_val);
|
||||
|
||||
// Values of MPOpts.softvol
|
||||
enum {
|
||||
|
|
|
@ -264,14 +264,16 @@ int get_deinterlacing(struct MPContext *mpctx)
|
|||
return enabled;
|
||||
}
|
||||
|
||||
void set_deinterlacing(struct MPContext *mpctx, bool enable)
|
||||
void set_deinterlacing(struct MPContext *mpctx, int opt_val)
|
||||
{
|
||||
if (enable == (get_deinterlacing(mpctx) > 0))
|
||||
if ((opt_val < 0 && mpctx->opts->deinterlace == opt_val) ||
|
||||
(opt_val == (get_deinterlacing(mpctx) > 0)))
|
||||
return;
|
||||
|
||||
mpctx->opts->deinterlace = enable;
|
||||
mpctx->opts->deinterlace = opt_val;
|
||||
recreate_auto_filters(mpctx);
|
||||
mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
|
||||
if (opt_val >= 0)
|
||||
mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
|
||||
}
|
||||
|
||||
static void recreate_video_filters(struct MPContext *mpctx)
|
||||
|
|
Loading…
Reference in New Issue