summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--misc/dispatch.c56
-rw-r--r--misc/dispatch.h4
-rw-r--r--options/m_config.c91
-rw-r--r--options/m_config.h31
-rw-r--r--options/m_option.h2
-rw-r--r--options/options.c16
-rw-r--r--player/command.c8
-rw-r--r--video/out/opengl/video.c1
-rw-r--r--video/out/vo.c36
-rw-r--r--video/out/vo.h1
10 files changed, 223 insertions, 23 deletions
diff --git a/misc/dispatch.c b/misc/dispatch.c
index 086896ba79..e625f196dc 100644
--- a/misc/dispatch.c
+++ b/misc/dispatch.c
@@ -59,6 +59,7 @@ struct mp_dispatch_item {
mp_dispatch_fn fn;
void *fn_data;
bool asynchronous;
+ bool mergeable;
bool completed;
struct mp_dispatch_item *next;
};
@@ -113,12 +114,25 @@ static void mp_dispatch_append(struct mp_dispatch_queue *queue,
struct mp_dispatch_item *item)
{
pthread_mutex_lock(&queue->lock);
+ if (item->mergeable) {
+ for (struct mp_dispatch_item *cur = queue->head; cur; cur = cur->next) {
+ if (cur->mergeable && cur->fn == item->fn &&
+ cur->fn_data == item->fn_data)
+ {
+ talloc_free(item);
+ pthread_mutex_unlock(&queue->lock);
+ return;
+ }
+ }
+ }
+
if (queue->tail) {
queue->tail->next = item;
} else {
queue->head = item;
}
queue->tail = item;
+
// Wake up the main thread; note that other threads might wait on this
// condition for reasons, so broadcast the condition.
pthread_cond_broadcast(&queue->cond);
@@ -127,6 +141,7 @@ static void mp_dispatch_append(struct mp_dispatch_queue *queue,
if (!queue->wakeup_fn)
queue->interrupted = true;
pthread_mutex_unlock(&queue->lock);
+
if (queue->wakeup_fn)
queue->wakeup_fn(queue->wakeup_ctx);
}
@@ -165,6 +180,47 @@ void mp_dispatch_enqueue_autofree(struct mp_dispatch_queue *queue,
mp_dispatch_append(queue, item);
}
+// Like mp_dispatch_enqueue(), but
+void mp_dispatch_enqueue_notify(struct mp_dispatch_queue *queue,
+ mp_dispatch_fn fn, void *fn_data)
+{
+ struct mp_dispatch_item *item = talloc_ptrtype(NULL, item);
+ *item = (struct mp_dispatch_item){
+ .fn = fn,
+ .fn_data = fn_data,
+ .mergeable = true,
+ .asynchronous = true,
+ };
+ mp_dispatch_append(queue, item);
+}
+
+// Remove already queued item. Only items enqueued with the following functions
+// can be canceled:
+// - mp_dispatch_enqueue()
+// - mp_dispatch_enqueue_notify()
+// Items which were enqueued, and which are currently executing, can not be
+// canceled anymore. This function is mostly for being called from the same
+// context as mp_dispatch_queue_process(), where the "currently executing" case
+// can be excluded.
+void mp_dispatch_cancel_fn(struct mp_dispatch_queue *queue,
+ mp_dispatch_fn fn, void *fn_data)
+{
+ pthread_mutex_lock(&queue->lock);
+ struct mp_dispatch_item **pcur = &queue->head;
+ queue->tail = NULL;
+ while (*pcur) {
+ struct mp_dispatch_item *cur = *pcur;
+ if (cur->fn == fn && cur->fn_data == fn_data) {
+ *pcur = cur->next;
+ talloc_free(cur);
+ } else {
+ queue->tail = cur;
+ pcur = &cur->next;
+ }
+ }
+ pthread_mutex_unlock(&queue->lock);
+}
+
// Run fn(fn_data) on the target thread synchronously. This function enqueues
// the callback and waits until the target thread is done doing this.
// This is redundant to calling the function inside mp_dispatch_[un]lock(),
diff --git a/misc/dispatch.h b/misc/dispatch.h
index a762e47cd2..d850437934 100644
--- a/misc/dispatch.h
+++ b/misc/dispatch.h
@@ -12,6 +12,10 @@ void mp_dispatch_enqueue(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_enqueue_autofree(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
+void mp_dispatch_enqueue_notify(struct mp_dispatch_queue *queue,
+ mp_dispatch_fn fn, void *fn_data);
+void mp_dispatch_cancel_fn(struct mp_dispatch_queue *queue,
+ mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_run(struct mp_dispatch_queue *queue,
mp_dispatch_fn fn, void *fn_data);
void mp_dispatch_queue_process(struct mp_dispatch_queue *queue, double timeout);
diff --git a/options/m_config.c b/options/m_config.c
index 378bed25ad..24f4b83c45 100644
--- a/options/m_config.c
+++ b/options/m_config.c
@@ -39,6 +39,7 @@
#include "common/global.h"
#include "common/msg.h"
#include "common/msg_control.h"
+#include "misc/dispatch.h"
#include "misc/node.h"
#include "osdep/atomic.h"
@@ -57,6 +58,8 @@ struct m_config_shadow {
pthread_mutex_t lock;
struct m_config *root;
char *data;
+ struct m_config_cache **listeners;
+ int num_listeners;
};
// Represents a sub-struct (OPT_SUBSTRUCT()).
@@ -156,8 +159,11 @@ static void config_destroy(void *p)
m_option_free(co->opt, config->shadow->data + co->shadow_offset);
}
- if (config->shadow)
+ if (config->shadow) {
+ // must all have been unregistered
+ assert(config->shadow->num_listeners == 0);
pthread_mutex_destroy(&config->shadow->lock);
+ }
}
struct m_config *m_config_new(void *talloc_ctx, struct mp_log *log,
@@ -1190,6 +1196,16 @@ static bool is_group_included(struct m_config *config, int group, int parent)
return false;
}
+static void cache_destroy(void *p)
+{
+ struct m_config_cache *cache = p;
+
+ // (technically speaking, being able to call them both without anything
+ // breaking is a feature provided by these functions)
+ m_config_cache_set_wakeup_cb(cache, NULL, NULL);
+ 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,
const struct m_sub_options *group)
@@ -1198,6 +1214,7 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
struct m_config *root = shadow->root;
struct m_config_cache *cache = talloc_zero(ta_parent, struct m_config_cache);
+ talloc_set_destructor(cache, cache_destroy);
cache->shadow = shadow;
cache->shadow_config = m_config_new(cache, mp_null_log, root->size,
root->defaults, root->options);
@@ -1292,12 +1309,84 @@ void m_config_notify_change_co(struct m_config *config,
group = g->parent_group;
}
+ if (shadow) {
+ pthread_mutex_lock(&shadow->lock);
+ for (int n = 0; n < shadow->num_listeners; n++) {
+ struct m_config_cache *cache = shadow->listeners[n];
+ if (cache->wakeup_cb)
+ cache->wakeup_cb(cache->wakeup_cb_ctx);
+ }
+ pthread_mutex_unlock(&shadow->lock);
+ }
+
if (config->option_change_callback) {
config->option_change_callback(config->option_change_callback_ctx, co,
changed);
}
}
+void m_config_cache_set_wakeup_cb(struct m_config_cache *cache,
+ void (*cb)(void *ctx), void *cb_ctx)
+{
+ struct m_config_shadow *shadow = cache->shadow;
+
+ pthread_mutex_lock(&shadow->lock);
+ if (cache->in_list) {
+ for (int n = 0; n < shadow->num_listeners; n++) {
+ if (shadow->listeners[n] == cache)
+ MP_TARRAY_REMOVE_AT(shadow->listeners, shadow->num_listeners, n);
+ }
+ if (!shadow->num_listeners) {
+ talloc_free(shadow->listeners);
+ shadow->listeners = NULL;
+ }
+ }
+ if (cb) {
+ MP_TARRAY_APPEND(NULL, shadow->listeners, shadow->num_listeners, cache);
+ cache->in_list = true;
+ cache->wakeup_cb = cb;
+ cache->wakeup_cb_ctx = cb_ctx;
+ }
+ pthread_mutex_unlock(&shadow->lock);
+}
+
+static void dispatch_notify(void *p)
+{
+ struct m_config_cache *cache = p;
+
+ assert(cache->wakeup_dispatch_queue);
+ mp_dispatch_enqueue_notify(cache->wakeup_dispatch_queue,
+ cache->wakeup_dispatch_cb,
+ cache->wakeup_dispatch_cb_ctx);
+}
+
+void m_config_cache_set_dispatch_change_cb(struct m_config_cache *cache,
+ struct mp_dispatch_queue *dispatch,
+ void (*cb)(void *ctx), void *cb_ctx)
+{
+ // Remove the old one is tricky. Firts make sure no new notifications will
+ // come.
+ m_config_cache_set_wakeup_cb(cache, NULL, NULL);
+ // Remove any pending notifications (assume we're on the same thread as
+ // any potential mp_dispatch_queue_process() callers).
+ if (cache->wakeup_dispatch_queue) {
+ mp_dispatch_cancel_fn(cache->wakeup_dispatch_queue,
+ cache->wakeup_dispatch_cb,
+ cache->wakeup_dispatch_cb_ctx);
+ }
+
+ cache->wakeup_dispatch_queue = NULL;
+ cache->wakeup_dispatch_cb = NULL;
+ cache->wakeup_dispatch_cb_ctx = NULL;
+
+ if (cb) {
+ cache->wakeup_dispatch_queue = dispatch;
+ cache->wakeup_dispatch_cb = cb;
+ cache->wakeup_dispatch_cb_ctx = cb_ctx;
+ m_config_cache_set_wakeup_cb(cache, dispatch_notify, cache);
+ }
+}
+
bool m_config_is_in_group(struct m_config *config,
const struct m_sub_options *group,
struct m_config_option *co)
diff --git a/options/m_config.h b/options/m_config.h
index 65145c093b..fc32ca5bf0 100644
--- a/options/m_config.h
+++ b/options/m_config.h
@@ -275,9 +275,20 @@ struct m_config_cache {
struct m_config *shadow_config;
long long ts;
int group;
+ bool in_list;
+ // --- Implicitly synchronized by setting/unsetting wakeup_cb.
+ struct mp_dispatch_queue *wakeup_dispatch_queue;
+ void (*wakeup_dispatch_cb)(void *ctx);
+ void *wakeup_dispatch_cb_ctx;
+ // --- Protected by shadow->lock
+ void (*wakeup_cb)(void *ctx);
+ void *wakeup_cb_ctx;
};
// Create a mirror copy from the global options.
+// Keep in mind that a m_config_cache object is not thread-safe; it merely
+// provides thread-safe access to the global options. All API functions for
+// the same m_config_cache object must synchronized, unless otherwise noted.
// ta_parent: parent for the returned allocation
// global: option data source
// group: the option group to return. This can be NULL for the global option
@@ -287,6 +298,22 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
struct mpv_global *global,
const struct m_sub_options *group);
+// If any of the options in the group possibly changes, call this callback. The
+// callback must not actually access the cache or anything option related.
+// Instead, it must wake up the thread that normally accesses the cache.
+void m_config_cache_set_wakeup_cb(struct m_config_cache *cache,
+ void (*cb)(void *ctx), void *cb_ctx);
+
+// If any of the options in the group change, call this callback on the given
+// dispatch queue. This is higher level than m_config_cache_set_wakeup_cb(),
+// and you can do anything you want in the callback (assuming the dispatch
+// queue is processed in the same thread that accesses m_config_cache API).
+// To ensure clean shutdown, you must destroy the m_config_cache (or unset the
+// callback) before the dispatch queue is destroyed.
+void m_config_cache_set_dispatch_change_cb(struct m_config_cache *cache,
+ struct mp_dispatch_queue *dispatch,
+ void (*cb)(void *ctx), void *cb_ctx);
+
// Update the options in cache->opts to current global values. Return whether
// there was an update notification at all (which may or may not indicate that
// some options have changed).
@@ -295,13 +322,13 @@ struct m_config_cache *m_config_cache_alloc(void *ta_parent,
bool m_config_cache_update(struct m_config_cache *cache);
// Like m_config_cache_alloc(), but return the struct (m_config_cache->opts)
-// directly, with no way to update the config.
+// directly, with no way to update the config. Basically this returns a copy
+// with a snapshot of the current option values.
// Warning: does currently not set the child as its own talloc root, which
// means the only way to free the struct is by freeing ta_parent.
void *mp_get_config_group(void *ta_parent, struct mpv_global *global,
const struct m_sub_options *group);
-
// Read a single global option in a thread-safe way. For multiple options,
// use m_config_cache. The option must exist and match the provided type (the
// type is used as a sanity check only). Performs semi-expensive lookup.
diff --git a/options/m_option.h b/options/m_option.h
index 5feed14542..cfc5f6bb15 100644
--- a/options/m_option.h
+++ b/options/m_option.h
@@ -398,8 +398,6 @@ struct m_option {
// certain groups of options.
#define UPDATE_OPT_FIRST (1 << 7)
#define UPDATE_TERM (1 << 7) // terminal options
-#define UPDATE_RENDERER (1 << 8) // mainly vo_opengl options
-#define UPDATE_VIDEOPOS (1 << 9) // video position (panscan etc.)
#define UPDATE_OSD (1 << 10) // related to OSD rendering
#define UPDATE_BUILTIN_SCRIPTS (1 << 11) // osc/ytdl
#define UPDATE_IMGPAR (1 << 12) // video image params overrides
diff --git a/options/options.c b/options/options.c
index 2bd7bf869d..b35dd9b265 100644
--- a/options/options.c
+++ b/options/options.c
@@ -146,20 +146,20 @@ static const m_option_t mp_vo_opt_list[] = {
OPT_FLAG("fullscreen", fullscreen, 0),
OPT_ALIAS("fs", "fullscreen"),
OPT_FLAG("native-keyrepeat", native_keyrepeat, 0),
- OPT_FLOATRANGE("panscan", panscan, UPDATE_VIDEOPOS, 0.0, 1.0),
- OPT_FLOATRANGE("video-zoom", zoom, UPDATE_VIDEOPOS, -20.0, 20.0),
- OPT_FLOATRANGE("video-pan-x", pan_x, UPDATE_VIDEOPOS, -3.0, 3.0),
- OPT_FLOATRANGE("video-pan-y", pan_y, UPDATE_VIDEOPOS, -3.0, 3.0),
- OPT_FLOATRANGE("video-align-x", align_x, UPDATE_VIDEOPOS, -1.0, 1.0),
- OPT_FLOATRANGE("video-align-y", align_y, UPDATE_VIDEOPOS, -1.0, 1.0),
- OPT_CHOICE("video-unscaled", unscaled, UPDATE_VIDEOPOS,
+ OPT_FLOATRANGE("panscan", panscan, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("video-zoom", zoom, 0, -20.0, 20.0),
+ OPT_FLOATRANGE("video-pan-x", pan_x, 0, -3.0, 3.0),
+ OPT_FLOATRANGE("video-pan-y", pan_y, 0, -3.0, 3.0),
+ OPT_FLOATRANGE("video-align-x", align_x, 0, -1.0, 1.0),
+ OPT_FLOATRANGE("video-align-y", align_y, 0, -1.0, 1.0),
+ OPT_CHOICE("video-unscaled", unscaled, 0,
({"no", 0}, {"yes", 1}, {"downscale-big", 2})),
OPT_INT64("wid", WinID, 0),
OPT_CHOICE_OR_INT("screen", screen_id, 0, 0, 32,
({"default", -1})),
OPT_CHOICE_OR_INT("fs-screen", fsscreen_id, 0, 0, 32,
({"all", -2}, {"current", -1})),
- OPT_FLAG("keepaspect", keepaspect, UPDATE_VIDEOPOS),
+ OPT_FLAG("keepaspect", keepaspect, 0),
OPT_FLAG("keepaspect-window", keepaspect_window, 0),
OPT_FLAG("hidpi-window-scale", hidpi_window_scale, 0),
OPT_FLAG("native-fs", native_fs, 0),
diff --git a/player/command.c b/player/command.c
index 51b025cad4..80c0c970fa 100644
--- a/player/command.c
+++ b/player/command.c
@@ -5854,14 +5854,6 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags)
if (flags & UPDATE_TERM)
mp_update_logging(mpctx, false);
- if (mpctx->video_out) {
- if (flags & UPDATE_VIDEOPOS)
- vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
-
- if (flags & UPDATE_RENDERER)
- vo_control(mpctx->video_out, VOCTRL_UPDATE_RENDER_OPTS, NULL);
- }
-
if (flags & UPDATE_OSD) {
osd_changed(mpctx->osd);
for (int n = 0; n < NUM_PTRACKS; n++) {
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index bb3730022a..bbf88e2747 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -414,7 +414,6 @@ const struct m_sub_options gl_video_conf = {
},
.size = sizeof(struct gl_video_opts),
.defaults = &gl_video_opts_def,
- .change_flags = UPDATE_RENDERER,
};
static void uninit_rendering(struct gl_video *p);
diff --git a/video/out/vo.c b/video/out/vo.c
index e52495e195..217baac632 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -167,6 +167,8 @@ struct vo_internal {
int opt_framedrop;
};
+extern const struct m_sub_options gl_video_conf;
+
static void forget_frames(struct vo *vo);
static void *vo_thread(void *ptr);
@@ -210,10 +212,33 @@ static void dispatch_wakeup_cb(void *ptr)
vo_wakeup(vo);
}
+static void update_opts(void *p)
+{
+ struct vo *vo = p;
+
+ if (m_config_cache_update(vo->opts_cache)) {
+ // "Legacy" update of video position related options.
+ if (vo->driver->control)
+ vo->driver->control(vo, VOCTRL_SET_PANSCAN, NULL);
+ }
+
+ if (vo->gl_opts_cache && m_config_cache_update(vo->gl_opts_cache))
+ {
+ // "Legacy" update of video GL renderer related options.
+ if (vo->driver->control)
+ vo->driver->control(vo, VOCTRL_UPDATE_RENDER_OPTS, NULL);
+ }
+}
+
// Does not include thread- and VO uninit.
static void dealloc_vo(struct vo *vo)
{
forget_frames(vo); // implicitly synchronized
+
+ // These must be free'd before vo->in->dispatch.
+ talloc_free(vo->opts_cache);
+ talloc_free(vo->gl_opts_cache);
+
pthread_mutex_destroy(&vo->in->lock);
pthread_cond_destroy(&vo->in->wakeup);
talloc_free(vo);
@@ -254,9 +279,18 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
pthread_mutex_init(&vo->in->lock, NULL);
pthread_cond_init(&vo->in->wakeup, NULL);
- vo->opts_cache = m_config_cache_alloc(vo, global, &vo_sub_opts);
+ vo->opts_cache = m_config_cache_alloc(NULL, global, &vo_sub_opts);
vo->opts = vo->opts_cache->opts;
+ m_config_cache_set_dispatch_change_cb(vo->opts_cache, vo->in->dispatch,
+ update_opts, vo);
+
+#if HAVE_GL
+ vo->gl_opts_cache = m_config_cache_alloc(NULL, global, &gl_video_conf);
+ m_config_cache_set_dispatch_change_cb(vo->gl_opts_cache, vo->in->dispatch,
+ update_opts, vo);
+#endif
+
mp_input_set_mouse_transform(vo->input_ctx, NULL, NULL);
if (vo->driver->encode != !!vo->encode_lavc_ctx)
goto error;
diff --git a/video/out/vo.h b/video/out/vo.h
index 82ec284219..c3145d5a5d 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -400,6 +400,7 @@ struct vo {
struct m_config_cache *opts_cache; // cache for ->opts
struct mp_vo_opts *opts;
+ struct m_config_cache *gl_opts_cache;
bool want_redraw; // redraw as soon as possible