summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-09-17 20:48:22 +0200
committerwm4 <wm4@nowhere>2016-09-17 20:48:22 +0200
commita3e8ff624c8adf3b18dddea0fdede7b7fa8c4eb1 (patch)
treeb5ed419e0ea57138e584e560c17cecb207d49364 /player
parent2d34171bec55294375e57f8bea86e2dee153d2cc (diff)
downloadmpv-a3e8ff624c8adf3b18dddea0fdede7b7fa8c4eb1.tar.bz2
mpv-a3e8ff624c8adf3b18dddea0fdede7b7fa8c4eb1.tar.xz
options: take care of propertly updating options on runtime changes
All option write accesses are now put through the property interface, which means runtime option value verification and runtime updates are applied. This is done even for command line arguments and config files. This has many subtle and not-so-subtle consequences. The potential for unintended and intended subtle or not-subtle behavior changes is very large. Architecturally, this is us literally jumping through hoops. It really should work the other way around, with options being able to have callbacks for value verification and applying runtime updates. But this would require rewriting the entirety of command.c. This change is more practical, and if anything will at least allow incremental changes. Some options are too incompatible for this to work - these are excluded with an explicit blacklist. This change fixes many issues caused by the mismatch between properties and options. For example, this fixes #3281.
Diffstat (limited to 'player')
-rw-r--r--player/command.c74
-rw-r--r--player/command.h3
-rw-r--r--player/main.c3
3 files changed, 77 insertions, 3 deletions
diff --git a/player/command.c b/player/command.c
index f4e2a0acd3..d791838ef8 100644
--- a/player/command.c
+++ b/player/command.c
@@ -126,6 +126,9 @@ static int edit_filters(struct MPContext *mpctx, struct mp_log *log,
static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
struct m_obj_settings *new_chain);
+static int mp_property_do_silent(const char *name, int action, void *val,
+ struct MPContext *ctx);
+
static void hook_remove(struct MPContext *mpctx, int index)
{
struct command_ctx *cmd = mpctx->command_ctx;
@@ -254,6 +257,64 @@ static char *format_delay(double time)
return talloc_asprintf(NULL, "%d ms", (int)lrint(time * 1000));
}
+// Option-property bridge. This is used so that setting options via various
+// mechanisms (including command line parsing, config files, per-file options)
+// updates state associated with them. For that, they have to go through the
+// property layer. (Ideally, this would be the other way around, and there
+// would be per-option change handlers instead.)
+// Note that the property-option bridge sidesteps this, as we'd get infinite
+// recursion.
+int mp_on_set_option(void *ctx, struct m_config_option *co, void *data, int flags)
+{
+ struct MPContext *mpctx = ctx;
+
+ // These options are too inconsistent as they could be pulled through the
+ // property layer. Ideally we'd remove these inconsistencies in the future,
+ // though the actual problem is compatibility to user-expected behavior.
+ // What matters is whether _write_ access is different - property read
+ // access is not used here.
+ // We're also fine with cases where the property restricts the writable
+ // value range if playback is active, but not otherwise.
+ // OK, restrict during playback: vid, aid, sid, deinterlace, video-aspect,
+ // vf*, af*, chapter
+ // OK, is handled separately: playlist
+ // OK, does not conflict on low level: audio-file, sub-file, external-file
+ static const char *const no_property[] = {
+ "playlist-pos", // checks playlist bounds, "no" choice missing
+ "volume", // restricts to --volume-max
+ "demuxer", "idle", "length", "audio-samplerate", "audio-channels",
+ "audio-format", "fps", "cache", // different semantics
+ NULL
+ };
+
+ for (int n = 0; no_property[n]; n++) {
+ if (strcmp(co->name, no_property[n]) == 0)
+ goto direct_option;
+ }
+
+ // Normalize "vf*" to "vf"
+ const char *name = co->name;
+ bstr bname = bstr0(name);
+ char tmp[50];
+ if (bstr_eatend0(&bname, "*")) {
+ snprintf(tmp, sizeof(name), "%.*s", BSTR_P(bname));
+ name = tmp;
+ }
+
+ int r = mp_property_do_silent(name, M_PROPERTY_SET, data, mpctx);
+ if (r != M_PROPERTY_OK)
+ return M_OPT_INVALID;
+
+ // The flag can't be passed through the property layer correctly.
+ if (flags & M_SETOPT_FROM_CMDLINE)
+ co->is_set_from_cmdline = true;
+
+ return 0;
+
+direct_option:
+ return m_config_set_option_raw_direct(mpctx->mconfig, co, data, flags);
+}
+
// Property-option bridge. (Maps the property to the option with the same name.)
static int mp_property_generic_option_do(void *ctx, struct m_property *prop,
int action, void *arg, bool force)
@@ -277,7 +338,7 @@ static int mp_property_generic_option_do(void *ctx, struct m_property *prop,
m_option_copy(opt->opt, arg, valptr);
return M_PROPERTY_OK;
case M_PROPERTY_SET:
- if (m_config_set_option_raw(mpctx->mconfig, opt, arg, flags) < 0)
+ if (m_config_set_option_raw_direct(mpctx->mconfig, opt, arg, flags) < 0)
return M_PROPERTY_ERROR;
return M_PROPERTY_OK;
}
@@ -4069,13 +4130,20 @@ static bool is_property_set(int action, void *val)
}
}
-int mp_property_do(const char *name, int action, void *val,
- struct MPContext *ctx)
+static int mp_property_do_silent(const char *name, int action, void *val,
+ struct MPContext *ctx)
{
struct command_ctx *cmd = ctx->command_ctx;
int r = m_property_do(ctx->log, cmd->properties, name, action, val, ctx);
if (r == M_PROPERTY_OK && is_property_set(action, val))
mp_notify_property(ctx, (char *)name);
+ return r;
+}
+
+int mp_property_do(const char *name, int action, void *val,
+ struct MPContext *ctx)
+{
+ int r = mp_property_do_silent(name, action, val, ctx);
if (mp_msg_test(ctx->log, MSGL_V) && is_property_set(action, val)) {
struct m_option ot = {0};
void *data = val;
diff --git a/player/command.h b/player/command.h
index c082d8d0d7..33e5b74927 100644
--- a/player/command.h
+++ b/player/command.h
@@ -24,6 +24,7 @@ struct MPContext;
struct mp_cmd;
struct mp_log;
struct mpv_node;
+struct m_config_option;
void command_init(struct MPContext *mpctx);
void command_uninit(struct MPContext *mpctx);
@@ -35,6 +36,8 @@ void property_print_help(struct MPContext *mpctx);
int mp_property_do(const char* name, int action, void* val,
struct MPContext *mpctx);
+int mp_on_set_option(void *ctx, struct m_config_option *co, void *data, int flags);
+
void mp_notify(struct MPContext *mpctx, int event, void *arg);
void mp_notify_property(struct MPContext *mpctx, const char *property);
diff --git a/player/main.c b/player/main.c
index d4c31a4726..12f0191fc6 100644
--- a/player/main.c
+++ b/player/main.c
@@ -356,6 +356,9 @@ struct MPContext *mp_create(void)
mp_input_set_cancel(mpctx->input, mpctx->playback_abort);
+ mpctx->mconfig->option_set_callback = mp_on_set_option;
+ mpctx->mconfig->option_set_callback_cb = mpctx;
+
return mpctx;
}