summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--options/m_config.c144
-rw-r--r--options/m_config.h18
-rw-r--r--player/command.c22
-rw-r--r--player/command.h3
-rw-r--r--player/loadfile.c21
-rw-r--r--player/main.c4
-rw-r--r--player/playloop.c16
7 files changed, 142 insertions, 86 deletions
diff --git a/options/m_config.c b/options/m_config.c
index 52a9b2f6d8..a28ae4438b 100644
--- a/options/m_config.c
+++ b/options/m_config.c
@@ -142,6 +142,9 @@ struct m_opt_backup {
void *backup;
};
+static struct m_config_cache *m_config_cache_alloc_internal(void *ta_parent,
+ struct m_config_shadow *shadow,
+ const struct m_sub_options *group);
static void add_sub_group(struct m_config_shadow *shadow, const char *name_prefix,
int parent_group_index, int parent_ptr,
const struct m_sub_options *subopts);
@@ -446,7 +449,7 @@ static void config_destroy(void *p)
struct m_config *config = p;
m_config_restore_backups(config);
- talloc_free(config->data);
+ talloc_free(config->cache);
talloc_free(config->shadow);
}
@@ -460,9 +463,9 @@ struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
config->shadow = m_config_shadow_new(root);
if (root->size) {
- config->data = allocate_option_data(config, config->shadow, 0,
- config->shadow->data);
- config->optstruct = config->data->gdata[0].udata;
+ config->cache =
+ m_config_cache_alloc_internal(config, config->shadow, root);
+ config->optstruct = config->cache->opts;
}
struct opt_iterate_state it;
@@ -475,8 +478,9 @@ struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
.is_hidden = !!it.opt->deprecation_message,
};
- struct m_group_data *gdata =
- config->data ? m_config_gdata(config->data, it.group_index) : NULL;
+ struct m_group_data *gdata = config->cache
+ ? m_config_gdata(config->cache->internal->data, it.group_index)
+ : NULL;
if (gdata && co.opt->offset >= 0)
co.data = gdata->udata + co.opt->offset;
@@ -939,6 +943,67 @@ static int m_config_handle_special_options(struct m_config *config,
return M_OPT_UNKNOWN;
}
+// This notification happens when anyone other than m_config->cache (i.e. not
+// through m_config_set_option_raw() or related) changes any options.
+static void async_change_cb(void *p)
+{
+ struct m_config *config = p;
+
+ void *ptr;
+ while (m_config_cache_get_next_changed(config->cache, &ptr)) {
+ // Regrettable linear search, might degenerate to quadratic.
+ for (int n = 0; n < config->num_opts; n++) {
+ struct m_config_option *co = &config->opts[n];
+ if (co->data == ptr) {
+ if (config->option_change_callback) {
+ config->option_change_callback(
+ config->option_change_callback_ctx, co,
+ config->cache->change_flags, false);
+ }
+ break;
+ }
+ }
+ config->cache->change_flags = 0;
+ }
+}
+
+void m_config_set_update_dispatch_queue(struct m_config *config,
+ struct mp_dispatch_queue *dispatch)
+{
+ m_config_cache_set_dispatch_change_cb(config->cache, dispatch,
+ async_change_cb, config);
+}
+
+// Normally m_config_cache will not send notifications when _we_ change our
+// own stuff. For whatever funny reasons, we need that, though.
+static void force_self_notify_change_opt(struct m_config *config,
+ struct m_config_option *co,
+ bool self_notification)
+{
+ int changed =
+ get_option_change_mask(config->shadow, co->group_index, 0, co->opt);
+
+ if (config->option_change_callback) {
+ config->option_change_callback(config->option_change_callback_ctx, co,
+ changed, self_notification);
+ }
+}
+
+void m_config_notify_change_opt_ptr(struct m_config *config, void *ptr)
+{
+ for (int n = 0; n < config->num_opts; n++) {
+ struct m_config_option *co = &config->opts[n];
+ if (co->data == ptr) {
+ if (m_config_cache_write_opt(config->cache, co->data))
+ force_self_notify_change_opt(config, co, true);
+ return;
+ }
+ }
+ // ptr doesn't point to any config->optstruct field declared in the
+ // option list?
+ assert(false);
+}
+
int m_config_set_option_raw(struct m_config *config,
struct m_config_option *co,
void *data, int flags)
@@ -959,10 +1024,11 @@ int m_config_set_option_raw(struct m_config *config,
if (!co->data)
return flags & M_SETOPT_FROM_CMDLINE ? 0 : M_OPT_UNKNOWN;
- m_option_copy(co->opt, co->data, data);
-
m_config_mark_co_flags(co, flags);
- m_config_notify_change_co(config, co);
+
+ m_option_copy(co->opt, co->data, data);
+ if (m_config_cache_write_opt(config->cache, co->data))
+ force_self_notify_change_opt(config, co, false);
return 0;
}
@@ -1356,11 +1422,10 @@ static void cache_destroy(void *p)
m_config_cache_set_dispatch_change_cb(cache, NULL, NULL, NULL);
}
-struct m_config_cache *m_config_cache_alloc(void *ta_parent,
- struct mpv_global *global,
+static struct m_config_cache *m_config_cache_alloc_internal(void *ta_parent,
+ struct m_config_shadow *shadow,
const struct m_sub_options *group)
{
- struct m_config_shadow *shadow = global->config;
int group_index = -1;
for (int n = 0; n < shadow->num_groups; n++) {
@@ -1397,6 +1462,13 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
return cache;
}
+struct m_config_cache *m_config_cache_alloc(void *ta_parent,
+ struct mpv_global *global,
+ const struct m_sub_options *group)
+{
+ return m_config_cache_alloc_internal(ta_parent, global->config, group);
+}
+
static void update_next_option(struct m_config_cache *cache, void **p_opt)
{
struct config_cache *in = cache->internal;
@@ -1577,54 +1649,6 @@ bool m_config_cache_write_opt(struct m_config_cache *cache, void *ptr)
return changed;
}
-void m_config_notify_change_co(struct m_config *config,
- struct m_config_option *co)
-{
- struct m_config_shadow *shadow = config->shadow;
- assert(co->data);
-
- if (shadow) {
- pthread_mutex_lock(&shadow->lock);
-
- struct m_config_data *data = shadow->data;
- struct m_group_data *gdata = m_config_gdata(data, co->group_index);
- assert(gdata);
-
- gdata->ts = atomic_fetch_add(&shadow->ts, 1) + 1;
-
- m_option_copy(co->opt, gdata->udata + co->opt->offset, co->data);
-
- for (int n = 0; n < shadow->num_listeners; n++) {
- struct config_cache *cache = shadow->listeners[n];
- if (cache->wakeup_cb && m_config_gdata(cache->data, co->group_index))
- cache->wakeup_cb(cache->wakeup_cb_ctx);
- }
-
- pthread_mutex_unlock(&shadow->lock);
- }
-
- int changed = get_option_change_mask(shadow, co->group_index, 0, co->opt);
-
- if (config->option_change_callback) {
- config->option_change_callback(config->option_change_callback_ctx, co,
- changed);
- }
-}
-
-void m_config_notify_change_opt_ptr(struct m_config *config, void *ptr)
-{
- for (int n = 0; n < config->num_opts; n++) {
- struct m_config_option *co = &config->opts[n];
- if (co->data == ptr) {
- m_config_notify_change_co(config, co);
- return;
- }
- }
- // ptr doesn't point to any config->optstruct field declared in the
- // option list?
- assert(false);
-}
-
void m_config_cache_set_wakeup_cb(struct m_config_cache *cache,
void (*cb)(void *ctx), void *cb_ctx)
{
diff --git a/options/m_config.h b/options/m_config.h
index f62bc9670f..25d26e05ee 100644
--- a/options/m_config.h
+++ b/options/m_config.h
@@ -74,8 +74,11 @@ typedef struct m_config {
// Notification after an option was successfully written to.
// Uses flags as set in UPDATE_OPTS_MASK.
+ // self_update==true means the update was caused by a call to
+ // m_config_notify_change_opt_ptr(). If false, it's caused either by
+ // m_config_set_option_*() (and similar) calls or external updates.
void (*option_change_callback)(void *ctx, struct m_config_option *co,
- int flags);
+ int flags, bool self_update);
void *option_change_callback_ctx;
// For the command line parser
@@ -84,7 +87,8 @@ typedef struct m_config {
void *optstruct; // struct mpopts or other
// Private. Non-NULL if data was allocated. m_config_option.data uses it.
- struct m_config_data *data;
+ // API users call m_config_set_update_dispatch_queue() to get async updates.
+ struct m_config_cache *cache;
// Private. Thread-safe shadow memory; only set for the main m_config.
struct m_config_shadow *shadow;
@@ -189,11 +193,7 @@ const char *m_config_get_positional_option(const struct m_config *config, int n)
int m_config_option_requires_param(struct m_config *config, bstr name);
// Notify m_config_cache users that the option has (probably) changed its value.
-void m_config_notify_change_co(struct m_config *config,
- struct m_config_option *co);
-// Like m_config_notify_change_co(), but automatically find the option by its
-// pointer within the global option struct (config->optstruct). In practice,
-// it means it works only on fields in MPContext.opts.
+// This will force a self-notification back to config->option_change_callback.
void m_config_notify_change_opt_ptr(struct m_config *config, void *ptr);
// Return all (visible) option names as NULL terminated string list.
@@ -253,6 +253,10 @@ int m_config_set_profile(struct m_config *config, char *name, int flags);
struct mpv_node m_config_get_profiles(struct m_config *config);
+// Run async option updates here. This will call option_change_callback() on it.
+void m_config_set_update_dispatch_queue(struct m_config *config,
+ struct mp_dispatch_queue *dispatch);
+
// This can be used to create and synchronize per-thread option structs,
// which then can be read without synchronization. No concurrent access to
// the cache itself is allowed.
diff --git a/player/command.c b/player/command.c
index fea26dbdcf..10359e2352 100644
--- a/player/command.c
+++ b/player/command.c
@@ -1889,6 +1889,8 @@ static int property_switch_track(void *ctx, struct m_property *prop,
// not always do what the user means, but keep the complexity low.
mpctx->opts->stream_id[order][type] =
mpctx->opts->stream_id[order][type] == -1 ? -2 : -1;
+ m_config_notify_change_opt_ptr(mpctx->mconfig,
+ &mpctx->opts->stream_id[order][type]);
}
return M_PROPERTY_OK;
}
@@ -3485,11 +3487,7 @@ static const char *const *const mp_event_property_change[] = {
E(MPV_EVENT_FILE_LOADED, "*"),
E(MP_EVENT_CHANGE_ALL, "*"),
E(MPV_EVENT_TRACKS_CHANGED, "track-list"),
- E(MPV_EVENT_TRACK_SWITCHED, "vid", "video", "aid", "audio", "sid", "sub",
- "secondary-sid"),
E(MPV_EVENT_IDLE, "*"),
- E(MPV_EVENT_PAUSE, "pause"),
- E(MPV_EVENT_UNPAUSE, "pause"),
E(MPV_EVENT_TICK, "time-pos", "audio-pts", "stream-pos", "avsync",
"percent-pos", "time-remaining", "playtime-remaining", "playback-time",
"estimated-vf-fps", "drop-frame-count", "vo-drop-frame-count",
@@ -4952,6 +4950,8 @@ static void cmd_track_add(void *p)
print_track_list(mpctx, "Track switched:");
} else {
mpctx->opts->stream_id[0][t->type] = t->user_tid;
+ m_config_notify_change_opt_ptr(mpctx->mconfig,
+ &mpctx->opts->stream_id[0][t->type]);
}
return;
}
@@ -4972,6 +4972,8 @@ static void cmd_track_add(void *p)
mp_switch_track(mpctx, t->type, t, FLAG_MARK_SELECTION);
} else {
mpctx->opts->stream_id[0][t->type] = t->user_tid;
+ m_config_notify_change_opt_ptr(mpctx->mconfig,
+ &mpctx->opts->stream_id[0][t->type]);
}
}
char *title = cmd->args[2].v.s;
@@ -6037,13 +6039,23 @@ static void update_priority(struct MPContext *mpctx)
#endif
}
-void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags)
+void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
+ bool self_update)
{
struct MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
struct command_ctx *cmd = mpctx->command_ctx;
void *opt_ptr = co ? co->data : NULL; // NULL on start
+ if (co)
+ mp_notify_property(mpctx, co->name);
+
+ if (opt_ptr == &opts->pause)
+ mp_notify(mpctx, opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
+
+ if (self_update)
+ return;
+
if (flags & UPDATE_TERM)
mp_update_logging(mpctx, false);
diff --git a/player/command.h b/player/command.h
index 037dec1164..e980f1715e 100644
--- a/player/command.h
+++ b/player/command.h
@@ -78,7 +78,8 @@ void property_print_help(struct MPContext *mpctx);
int mp_property_do(const char* name, int action, void* val,
struct MPContext *mpctx);
-void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags);
+void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags,
+ bool self_update);
void mp_notify(struct MPContext *mpctx, int event, void *arg);
void mp_notify_property(struct MPContext *mpctx, const char *property);
diff --git a/player/loadfile.c b/player/loadfile.c
index 9d515b2012..603b264f60 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -572,8 +572,11 @@ static void check_previous_track_selection(struct MPContext *mpctx)
// defaults are -1 (default selection), or -2 (off) for secondary tracks.
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
for (int i = 0; i < NUM_PTRACKS; i++) {
- if (opts->stream_id[i][t] >= 0)
+ if (opts->stream_id[i][t] >= 0) {
opts->stream_id[i][t] = i == 0 ? -1 : -2;
+ m_config_notify_change_opt_ptr(mpctx->mconfig,
+ &opts->stream_id[i][t]);
+ }
}
}
talloc_free(mpctx->track_layout_hash);
@@ -590,8 +593,11 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
// Mark the current track selection as explicitly user-requested. (This is
// different from auto-selection or disabling a track due to errors.)
- if (flags & FLAG_MARK_SELECTION)
+ if (flags & FLAG_MARK_SELECTION) {
mpctx->opts->stream_id[order][type] = track ? track->user_tid : -2;
+ m_config_notify_change_opt_ptr(mpctx->mconfig,
+ &mpctx->opts->stream_id[order][type]);
+ }
// No decoder should be initialized yet.
if (!mpctx->demuxer)
@@ -1623,8 +1629,10 @@ terminate_playback:
process_hooks(mpctx, "on_unload");
- if (mpctx->step_frames)
+ if (mpctx->step_frames) {
opts->pause = 1;
+ m_config_notify_change_opt_ptr(mpctx->mconfig, &opts->pause);
+ }
close_recorder(mpctx);
@@ -1730,8 +1738,11 @@ struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction,
if (mpctx->opts->shuffle)
playlist_shuffle(mpctx->playlist);
next = mpctx->playlist->first;
- if (next && mpctx->opts->loop_times > 1)
+ if (next && mpctx->opts->loop_times > 1) {
mpctx->opts->loop_times--;
+ m_config_notify_change_opt_ptr(mpctx->mconfig,
+ &mpctx->opts->loop_times);
+ }
} else {
next = mpctx->playlist->last;
// Don't jump to files that would immediately go to next file anyway
@@ -1852,7 +1863,7 @@ void close_recorder_and_error(struct MPContext *mpctx)
close_recorder(mpctx);
talloc_free(mpctx->opts->record_file);
mpctx->opts->record_file = NULL;
- mp_notify_property(mpctx, "record-file");
+ m_config_notify_change_opt_ptr(mpctx->mconfig, &mpctx->opts->record_file);
MP_ERR(mpctx, "Disabling stream recording.\n");
}
diff --git a/player/main.c b/player/main.c
index f4c7348af4..cac012a774 100644
--- a/player/main.c
+++ b/player/main.c
@@ -193,6 +193,7 @@ void mp_destroy(struct MPContext *mpctx)
assert(!mpctx->num_abort_list);
talloc_free(mpctx->abort_list);
pthread_mutex_destroy(&mpctx->abort_lock);
+ talloc_free(mpctx->mconfig); // destroy before dispatch
talloc_free(mpctx);
}
@@ -379,8 +380,9 @@ int mp_initialize(struct MPContext *mpctx, char **options)
mpctx->initialized = true;
mpctx->mconfig->option_change_callback = mp_option_change_callback;
mpctx->mconfig->option_change_callback_ctx = mpctx;
+ m_config_set_update_dispatch_queue(mpctx->mconfig, mpctx->dispatch);
// Run all update handlers.
- mp_option_change_callback(mpctx, NULL, UPDATE_OPTS_MASK);
+ mp_option_change_callback(mpctx, NULL, UPDATE_OPTS_MASK, false);
if (handle_help_options(mpctx))
return 1; // help
diff --git a/player/playloop.c b/player/playloop.c
index 5c83615a86..0e0c09654d 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -144,16 +144,12 @@ void update_core_idle_state(struct MPContext *mpctx)
void set_pause_state(struct MPContext *mpctx, bool user_pause)
{
struct MPOpts *opts = mpctx->opts;
- bool send_update = false;
- if (opts->pause != user_pause)
- send_update = true;
opts->pause = user_pause;
bool internal_paused = opts->pause || mpctx->paused_for_cache;
if (internal_paused != mpctx->paused) {
mpctx->paused = internal_paused;
- send_update = true;
if (mpctx->ao && mpctx->ao_chain) {
if (internal_paused) {
@@ -177,12 +173,15 @@ void set_pause_state(struct MPContext *mpctx, bool user_pause)
} else {
(void)get_relative_time(mpctx); // ignore time that passed during pause
}
+
+ // For some reason, these events are supposed to be sent even if only
+ // the internal pause state changed (and "pause" property didn't)... OK.
+ mp_notify(mpctx, opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
}
update_core_idle_state(mpctx);
- if (send_update)
- mp_notify(mpctx, opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
+ m_config_notify_change_opt_ptr(mpctx->mconfig, &opts->pause);
}
void update_internal_pause_state(struct MPContext *mpctx)
@@ -884,8 +883,10 @@ static void handle_loop_file(struct MPContext *mpctx)
target = ab[0];
prec = MPSEEK_EXACT;
} else if (opts->loop_file) {
- if (opts->loop_file > 0)
+ if (opts->loop_file > 0) {
opts->loop_file--;
+ m_config_notify_change_opt_ptr(mpctx->mconfig, &opts->loop_file);
+ }
target = get_start_time(mpctx, mpctx->play_dir);
}
@@ -1035,6 +1036,7 @@ int handle_force_window(struct MPContext *mpctx, bool force)
err:
mpctx->opts->force_vo = 0;
+ m_config_notify_change_opt_ptr(mpctx->mconfig, &mpctx->opts->force_vo);
uninit_video_out(mpctx);
MP_FATAL(mpctx, "Error opening/initializing the VO window.\n");
return -1;