summaryrefslogtreecommitdiffstats
path: root/player/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/client.c')
-rw-r--r--player/client.c1037
1 files changed, 612 insertions, 425 deletions
diff --git a/player/client.c b/player/client.c
index 6a45ec7fb6..5087f89885 100644
--- a/player/client.c
+++ b/player/client.c
@@ -13,14 +13,15 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <stdatomic.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <math.h>
-#include <assert.h>
#include "common/common.h"
#include "common/global.h"
@@ -63,55 +64,64 @@
struct mp_client_api {
struct MPContext *mpctx;
- pthread_mutex_t lock;
+ mp_mutex lock;
// -- protected by lock
struct mpv_handle **clients;
int num_clients;
- uint64_t event_masks; // combined events of all clients, or 0 if unknown
bool shutting_down; // do not allow new clients
bool have_terminator; // a client took over the role of destroying the core
bool terminate_core_thread; // make libmpv core thread exit
+ // This is incremented whenever the clients[] array above changes. This is
+ // used to safely unlock mp_client_api.lock while iterating the list of
+ // clients.
+ uint64_t clients_list_change_ts;
+ int64_t id_alloc;
struct mp_custom_protocol *custom_protocols;
int num_custom_protocols;
struct mpv_render_context *render_context;
- struct mpv_opengl_cb_context *gl_cb_ctx;
};
struct observe_property {
+ // -- immutable
+ struct mpv_handle *owner;
char *name;
int id; // ==mp_get_property_id(name)
uint64_t event_mask; // ==mp_get_property_event_mask(name)
int64_t reply_id;
mpv_format format;
- bool changed; // property change should be signaled to user
- bool need_new_value; // a new value should be retrieved
- bool updating; // a new value is being retrieved
- bool updated; // a new value was successfully retrieved
- bool dead; // property unobserved while retrieving value
- bool new_value_valid, user_value_valid;
- union m_option_value new_value, user_value;
- struct mpv_handle *client;
+ const struct m_option *type;
+ // -- protected by owner->lock
+ size_t refcount;
+ uint64_t change_ts; // logical timestamp incremented on each change
+ uint64_t value_ts; // logical timestamp for value contents
+ bool value_valid;
+ union m_option_value value;
+ uint64_t value_ret_ts; // logical timestamp of value returned to user
+ union m_option_value value_ret;
+ bool waiting_for_hook; // flag for draining old property changes on a hook
};
struct mpv_handle {
- // -- immmutable
+ // -- immutable
char name[MAX_CLIENT_NAME];
struct mp_log *log;
struct MPContext *mpctx;
struct mp_client_api *clients;
+ int64_t id;
// -- not thread-safe
struct mpv_event *cur_event;
struct mpv_event_property cur_property_event;
+ struct observe_property *cur_property;
- pthread_mutex_t lock;
+ mp_mutex lock;
- pthread_mutex_t wakeup_lock;
- pthread_cond_t wakeup;
+ mp_mutex wakeup_lock;
+ mp_cond wakeup;
// -- protected by wakeup_lock
bool need_wakeup;
@@ -123,29 +133,50 @@ struct mpv_handle {
uint64_t event_mask;
bool queued_wakeup;
- int suspend_count;
mpv_event *events; // ringbuffer of max_events entries
int max_events; // allocated number of entries in events
int first_event; // events[first_event] is the first readable event
int num_events; // number of readable events
int reserved_events; // number of entries reserved for replies
+ size_t async_counter; // pending other async events
bool choked; // recovering from queue overflow
+ bool destroying; // pending destruction; no API accesses allowed
+ bool hook_pending; // hook events are returned after draining properties
struct observe_property **properties;
int num_properties;
- int lowest_changed; // attempt at making change processing incremental
- int properties_updating;
+ bool has_pending_properties; // (maybe) new property events (producer side)
+ bool new_property_events; // new property events (consumer side)
+ int cur_property_index; // round-robin for property events (consumer side)
uint64_t property_event_masks; // or-ed together event masks of all properties
+ // This is incremented whenever the properties[] array above changes. This
+ // is used to safely unlock mpv_handle.lock while reading a property. If
+ // the counter didn't change between unlock and relock, then it will assume
+ // the array did not change.
+ uint64_t properties_change_ts;
bool fuzzy_initialized; // see scripting.c wait_loaded()
bool is_weak; // can not keep core alive on its own
struct mp_log_buffer *messages;
+ int messages_level;
};
static bool gen_log_message_event(struct mpv_handle *ctx);
static bool gen_property_change_event(struct mpv_handle *ctx);
-static void notify_property_events(struct mpv_handle *ctx, uint64_t event_mask);
+static void notify_property_events(struct mpv_handle *ctx, int event);
+
+// Must be called with prop->owner->lock held.
+static void prop_unref(struct observe_property *prop)
+{
+ if (!prop)
+ return;
+
+ assert(prop->refcount > 0);
+ prop->refcount -= 1;
+ if (!prop->refcount)
+ talloc_free(prop);
+}
void mp_clients_init(struct MPContext *mpctx)
{
@@ -154,7 +185,7 @@ void mp_clients_init(struct MPContext *mpctx)
.mpctx = mpctx,
};
mpctx->global->client_api = mpctx->clients;
- pthread_mutex_init(&mpctx->clients->lock, NULL);
+ mp_mutex_init(&mpctx->clients->lock);
}
void mp_clients_destroy(struct MPContext *mpctx)
@@ -163,8 +194,6 @@ void mp_clients_destroy(struct MPContext *mpctx)
return;
assert(mpctx->clients->num_clients == 0);
- TA_FREEP(&mpctx->clients->gl_cb_ctx);
-
// The API user is supposed to call mpv_render_context_free(). It's simply
// not allowed not to do this.
if (mpctx->clients->render_context) {
@@ -172,7 +201,7 @@ void mp_clients_destroy(struct MPContext *mpctx)
abort();
}
- pthread_mutex_destroy(&mpctx->clients->lock);
+ mp_mutex_destroy(&mpctx->clients->lock);
talloc_free(mpctx->clients);
mpctx->clients = NULL;
}
@@ -182,45 +211,57 @@ void mp_clients_destroy(struct MPContext *mpctx)
bool mp_clients_all_initialized(struct MPContext *mpctx)
{
bool all_ok = true;
- pthread_mutex_lock(&mpctx->clients->lock);
+ mp_mutex_lock(&mpctx->clients->lock);
for (int n = 0; n < mpctx->clients->num_clients; n++) {
struct mpv_handle *ctx = mpctx->clients->clients[n];
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
all_ok &= ctx->fuzzy_initialized;
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
}
- pthread_mutex_unlock(&mpctx->clients->lock);
+ mp_mutex_unlock(&mpctx->clients->lock);
return all_ok;
}
-static void invalidate_global_event_mask(struct mpv_handle *ctx)
+static struct mpv_handle *find_client_id(struct mp_client_api *clients, int64_t id)
{
- pthread_mutex_lock(&ctx->clients->lock);
- ctx->clients->event_masks = 0;
- pthread_mutex_unlock(&ctx->clients->lock);
+ for (int n = 0; n < clients->num_clients; n++) {
+ if (clients->clients[n]->id == id)
+ return clients->clients[n];
+ }
+ return NULL;
}
static struct mpv_handle *find_client(struct mp_client_api *clients,
const char *name)
{
+ if (name[0] == '@') {
+ char *end;
+ errno = 0;
+ long long int id = strtoll(name + 1, &end, 10);
+ if (errno || end[0])
+ return NULL;
+ return find_client_id(clients, id);
+ }
+
for (int n = 0; n < clients->num_clients; n++) {
if (strcmp(clients->clients[n]->name, name) == 0)
return clients->clients[n];
}
+
return NULL;
}
-bool mp_client_exists(struct MPContext *mpctx, const char *client_name)
+bool mp_client_id_exists(struct MPContext *mpctx, int64_t id)
{
- pthread_mutex_lock(&mpctx->clients->lock);
- bool r = find_client(mpctx->clients, client_name);
- pthread_mutex_unlock(&mpctx->clients->lock);
+ mp_mutex_lock(&mpctx->clients->lock);
+ bool r = find_client_id(mpctx->clients, id);
+ mp_mutex_unlock(&mpctx->clients->lock);
return r;
}
struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name)
{
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
char nname[MAX_CLIENT_NAME];
for (int n = 1; n < 1000; n++) {
@@ -237,7 +278,7 @@ struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name
}
if (!nname[0] || clients->shutting_down) {
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
return NULL;
}
@@ -248,25 +289,26 @@ struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name
.log = mp_log_new(client, clients->mpctx->log, nname),
.mpctx = clients->mpctx,
.clients = clients,
+ .id = ++(clients->id_alloc),
.cur_event = talloc_zero(client, struct mpv_event),
.events = talloc_array(client, mpv_event, num_events),
.max_events = num_events,
.event_mask = (1ULL << INTERNAL_EVENT_BASE) - 1, // exclude internal events
.wakeup_pipe = {-1, -1},
};
- pthread_mutex_init(&client->lock, NULL);
- pthread_mutex_init(&client->wakeup_lock, NULL);
- pthread_cond_init(&client->wakeup, NULL);
+ mp_mutex_init(&client->lock);
+ mp_mutex_init(&client->wakeup_lock);
+ mp_cond_init(&client->wakeup);
snprintf(client->name, sizeof(client->name), "%s", nname);
+ clients->clients_list_change_ts += 1;
MP_TARRAY_APPEND(clients, clients->clients, clients->num_clients, client);
if (clients->num_clients == 1 && !clients->mpctx->is_cli)
client->fuzzy_initialized = true;
- clients->event_masks = 0;
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
mpv_request_event(client, MPV_EVENT_TICK, 0);
@@ -275,9 +317,9 @@ struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name
void mp_client_set_weak(struct mpv_handle *ctx)
{
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
ctx->is_weak = true;
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
}
const char *mpv_client_name(mpv_handle *ctx)
@@ -285,6 +327,11 @@ const char *mpv_client_name(mpv_handle *ctx)
return ctx->name;
}
+int64_t mpv_client_id(mpv_handle *ctx)
+{
+ return ctx->id;
+}
+
struct mp_log *mp_client_get_log(struct mpv_handle *ctx)
{
return ctx->log;
@@ -295,64 +342,43 @@ struct mpv_global *mp_client_get_global(struct mpv_handle *ctx)
return ctx->mpctx->global;
}
-struct MPContext *mp_client_get_core(struct mpv_handle *ctx)
-{
- return ctx->mpctx;
-}
-
-struct MPContext *mp_client_api_get_core(struct mp_client_api *api)
-{
- return api->mpctx;
-}
-
static void wakeup_client(struct mpv_handle *ctx)
{
- pthread_mutex_lock(&ctx->wakeup_lock);
+ mp_mutex_lock(&ctx->wakeup_lock);
if (!ctx->need_wakeup) {
ctx->need_wakeup = true;
- pthread_cond_broadcast(&ctx->wakeup);
+ mp_cond_broadcast(&ctx->wakeup);
if (ctx->wakeup_cb)
ctx->wakeup_cb(ctx->wakeup_cb_ctx);
if (ctx->wakeup_pipe[0] != -1)
(void)write(ctx->wakeup_pipe[1], &(char){0}, 1);
}
- pthread_mutex_unlock(&ctx->wakeup_lock);
+ mp_mutex_unlock(&ctx->wakeup_lock);
}
// Note: the caller has to deal with sporadic wakeups.
static int wait_wakeup(struct mpv_handle *ctx, int64_t end)
{
int r = 0;
- pthread_mutex_unlock(&ctx->lock);
- pthread_mutex_lock(&ctx->wakeup_lock);
- if (!ctx->need_wakeup) {
- struct timespec ts = mp_time_us_to_timespec(end);
- r = pthread_cond_timedwait(&ctx->wakeup, &ctx->wakeup_lock, &ts);
- }
+ mp_mutex_unlock(&ctx->lock);
+ mp_mutex_lock(&ctx->wakeup_lock);
+ if (!ctx->need_wakeup)
+ r = mp_cond_timedwait_until(&ctx->wakeup, &ctx->wakeup_lock, end);
if (r == 0)
ctx->need_wakeup = false;
- pthread_mutex_unlock(&ctx->wakeup_lock);
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_unlock(&ctx->wakeup_lock);
+ mp_mutex_lock(&ctx->lock);
return r;
}
void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d)
{
- pthread_mutex_lock(&ctx->wakeup_lock);
+ mp_mutex_lock(&ctx->wakeup_lock);
ctx->wakeup_cb = cb;
ctx->wakeup_cb_ctx = d;
if (ctx->wakeup_cb)
ctx->wakeup_cb(ctx->wakeup_cb_ctx);
- pthread_mutex_unlock(&ctx->wakeup_lock);
-}
-
-void mpv_suspend(mpv_handle *ctx)
-{
- MP_ERR(ctx, "mpv_suspend() is deprecated and does nothing.\n");
-}
-
-void mpv_resume(mpv_handle *ctx)
-{
+ mp_mutex_unlock(&ctx->wakeup_lock);
}
static void lock_core(mpv_handle *ctx)
@@ -367,10 +393,10 @@ static void unlock_core(mpv_handle *ctx)
void mpv_wait_async_requests(mpv_handle *ctx)
{
- pthread_mutex_lock(&ctx->lock);
- while (ctx->reserved_events || ctx->properties_updating)
+ mp_mutex_lock(&ctx->lock);
+ while (ctx->reserved_events || ctx->async_counter)
wait_wakeup(ctx, INT64_MAX);
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
}
// Send abort signal to all matching work items.
@@ -379,7 +405,7 @@ void mpv_wait_async_requests(mpv_handle *ctx)
static void abort_async(struct MPContext *mpctx, mpv_handle *ctx,
int type, uint64_t id)
{
- pthread_mutex_lock(&mpctx->abort_lock);
+ mp_mutex_lock(&mpctx->abort_lock);
// Destroy all => ensure any newly appearing work is aborted immediately.
if (ctx == NULL)
@@ -394,12 +420,12 @@ static void abort_async(struct MPContext *mpctx, mpv_handle *ctx,
}
}
- pthread_mutex_unlock(&mpctx->abort_lock);
+ mp_mutex_unlock(&mpctx->abort_lock);
}
-static void get_thread(void *ptr)
+static void get_thread_id(void *ptr)
{
- *(pthread_t *)ptr = pthread_self();
+ *(mp_thread_id *)ptr = mp_thread_current_id();
}
static void mp_destroy_client(mpv_handle *ctx, bool terminate)
@@ -410,11 +436,25 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
struct MPContext *mpctx = ctx->mpctx;
struct mp_client_api *clients = ctx->clients;
- MP_VERBOSE(ctx, "Exiting...\n");
+ MP_DBG(ctx, "Exiting...\n");
if (terminate)
mpv_command(ctx, (const char*[]){"quit", NULL});
+ mp_mutex_lock(&ctx->lock);
+
+ ctx->destroying = true;
+
+ for (int n = 0; n < ctx->num_properties; n++)
+ prop_unref(ctx->properties[n]);
+ ctx->num_properties = 0;
+ ctx->properties_change_ts += 1;
+
+ prop_unref(ctx->cur_property);
+ ctx->cur_property = NULL;
+
+ mp_mutex_unlock(&ctx->lock);
+
abort_async(mpctx, ctx, 0, 0);
// reserved_events equals the number of asynchronous requests that weren't
@@ -422,13 +462,14 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
// causes a crash, block until all asynchronous requests were served.
mpv_wait_async_requests(ctx);
- osd_set_external(mpctx->osd, ctx, 0, 0, NULL);
+ osd_set_external_remove_owner(mpctx->osd, ctx);
mp_input_remove_sections_by_owner(mpctx->input, ctx->name);
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
for (int n = 0; n < clients->num_clients; n++) {
if (clients->clients[n] == ctx) {
+ clients->clients_list_change_ts += 1;
MP_TARRAY_REMOVE_AT(clients->clients, clients->num_clients, n);
while (ctx->num_events) {
talloc_free(ctx->events[ctx->first_event].data);
@@ -436,9 +477,9 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
ctx->num_events--;
}
mp_msg_log_buffer_destroy(ctx->messages);
- pthread_cond_destroy(&ctx->wakeup);
- pthread_mutex_destroy(&ctx->wakeup_lock);
- pthread_mutex_destroy(&ctx->lock);
+ mp_cond_destroy(&ctx->wakeup);
+ mp_mutex_destroy(&ctx->wakeup_lock);
+ mp_mutex_destroy(&ctx->lock);
if (ctx->wakeup_pipe[0] != -1) {
close(ctx->wakeup_pipe[0]);
close(ctx->wakeup_pipe[1]);
@@ -470,7 +511,7 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
// mp_hook_test_completion() also relies on this a bit.
mp_wakeup_core(mpctx);
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
// Note that even if num_clients==0, having set have_terminator keeps mpctx
// and the core thread alive.
@@ -481,17 +522,17 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
mpctx->stop_play = PT_QUIT;
mp_dispatch_unlock(mpctx->dispatch);
- pthread_t playthread;
- mp_dispatch_run(mpctx->dispatch, get_thread, &playthread);
+ mp_thread_id playthread;
+ mp_dispatch_run(mpctx->dispatch, get_thread_id, &playthread);
// Ask the core thread to stop.
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
clients->terminate_core_thread = true;
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
mp_wakeup_core(mpctx);
// Blocking wait for all clients and core thread to terminate.
- pthread_join(playthread, NULL);
+ mp_thread_join_id(playthread);
mp_destroy(mpctx);
}
@@ -502,11 +543,6 @@ void mpv_destroy(mpv_handle *ctx)
mp_destroy_client(ctx, false);
}
-void mpv_detach_destroy(mpv_handle *ctx)
-{
- mpv_destroy(ctx);
-}
-
void mpv_terminate_destroy(mpv_handle *ctx)
{
mp_destroy_client(ctx, true);
@@ -521,7 +557,7 @@ void mp_shutdown_clients(struct MPContext *mpctx)
// Forcefully abort async work after 2 seconds of waiting.
double abort_time = mp_time_sec() + 2;
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
// Prevent that new clients can appear.
clients->shutting_down = true;
@@ -530,7 +566,7 @@ void mp_shutdown_clients(struct MPContext *mpctx)
while (clients->num_clients || mpctx->outstanding_async ||
!(mpctx->is_cli || clients->terminate_core_thread))
{
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
double left = abort_time - mp_time_sec();
if (left >= 0) {
@@ -545,26 +581,26 @@ void mp_shutdown_clients(struct MPContext *mpctx)
mp_client_broadcast_event(mpctx, MPV_EVENT_SHUTDOWN, NULL);
mp_wait_events(mpctx);
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
}
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
}
bool mp_is_shutting_down(struct MPContext *mpctx)
{
struct mp_client_api *clients = mpctx->clients;
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
bool res = clients->shutting_down;
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
return res;
}
-static void *core_thread(void *p)
+static MP_THREAD_VOID core_thread(void *p)
{
struct MPContext *mpctx = p;
- mpthread_set_name("mpv core");
+ mp_thread_set_name("core");
while (!mpctx->initialized && mpctx->stop_play != PT_QUIT)
mp_idle(mpctx);
@@ -577,7 +613,7 @@ static void *core_thread(void *p)
// the last mpv_handle.
mp_shutdown_clients(mpctx);
- return NULL;
+ MP_THREAD_RETURN();
}
mpv_handle *mpv_create(void)
@@ -594,8 +630,8 @@ mpv_handle *mpv_create(void)
return NULL;
}
- pthread_t thread;
- if (pthread_create(&thread, NULL, core_thread, mpctx) != 0) {
+ mp_thread thread;
+ if (mp_thread_create(&thread, core_thread, mpctx) != 0) {
ctx->clients->have_terminator = true; // avoid blocking
mpv_terminate_destroy(ctx);
mp_destroy(mpctx);
@@ -648,6 +684,9 @@ static void dup_event_data(struct mpv_event *ev)
ev->data = msg;
break;
}
+ case MPV_EVENT_START_FILE:
+ ev->data = talloc_memdup(NULL, ev->data, sizeof(mpv_event_start_file));
+ break;
case MPV_EVENT_END_FILE:
ev->data = talloc_memdup(NULL, ev->data, sizeof(mpv_event_end_file));
break;
@@ -665,13 +704,13 @@ static void dup_event_data(struct mpv_event *ev)
static int reserve_reply(struct mpv_handle *ctx)
{
int res = MPV_ERROR_EVENT_QUEUE_FULL;
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
if (ctx->reserved_events + ctx->num_events < ctx->max_events && !ctx->choked)
{
ctx->reserved_events++;
res = 0;
}
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
return res;
}
@@ -691,10 +730,10 @@ static int append_event(struct mpv_handle *ctx, struct mpv_event event, bool cop
static int send_event(struct mpv_handle *ctx, struct mpv_event *event, bool copy)
{
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
uint64_t mask = 1ULL << event->event_id;
if (ctx->property_event_masks & mask)
- notify_property_events(ctx, mask);
+ notify_property_events(ctx, event->event_id);
int r;
if (!(ctx->event_mask & mask)) {
r = 0;
@@ -707,7 +746,7 @@ static int send_event(struct mpv_handle *ctx, struct mpv_event *event, bool copy
ctx->choked = true;
}
}
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
return r;
}
@@ -717,43 +756,20 @@ static void send_reply(struct mpv_handle *ctx, uint64_t userdata,
struct mpv_event *event)
{
event->reply_userdata = userdata;
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
// If this fails, reserve_reply() probably wasn't called.
assert(ctx->reserved_events > 0);
ctx->reserved_events--;
if (append_event(ctx, *event, false) < 0)
- abort(); // not reached
- pthread_mutex_unlock(&ctx->lock);
-}
-
-// Return whether there's any client listening to this event.
-// If false is returned, the core doesn't need to send it.
-bool mp_client_event_is_registered(struct MPContext *mpctx, int event)
-{
- struct mp_client_api *clients = mpctx->clients;
-
- pthread_mutex_lock(&clients->lock);
-
- if (!clients->event_masks) { // lazy update
- for (int n = 0; n < clients->num_clients; n++) {
- struct mpv_handle *ctx = clients->clients[n];
- pthread_mutex_lock(&ctx->lock);
- clients->event_masks |= ctx->event_mask | ctx->property_event_masks;
- pthread_mutex_unlock(&ctx->lock);
- }
- }
- bool r = clients->event_masks & (1ULL << event);
-
- pthread_mutex_unlock(&clients->lock);
-
- return r;
+ MP_ASSERT_UNREACHABLE();
+ mp_mutex_unlock(&ctx->lock);
}
void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data)
{
struct mp_client_api *clients = mpctx->clients;
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
for (int n = 0; n < clients->num_clients; n++) {
struct mpv_event event_data = {
@@ -763,7 +779,18 @@ void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data)
send_event(clients->clients[n], &event_data, true);
}
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
+}
+
+// Like mp_client_broadcast_event(), but can be called from any thread.
+// Avoid using this.
+void mp_client_broadcast_event_external(struct mp_client_api *api, int event,
+ void *data)
+{
+ struct MPContext *mpctx = api->mpctx;
+
+ mp_client_broadcast_event(mpctx, event, data);
+ mp_wakeup_core(mpctx);
}
// If client_name == NULL, then broadcast and free the event.
@@ -785,7 +812,7 @@ int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
.reply_userdata = reply_userdata,
};
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
struct mpv_handle *ctx = find_client(clients, client_name);
if (ctx) {
@@ -795,7 +822,7 @@ int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
talloc_free(data);
}
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
return r;
}
@@ -817,6 +844,11 @@ int mp_client_send_event_dup(struct MPContext *mpctx, const char *client_name,
return mp_client_send_event(mpctx, client_name, 0, event, event_data.data);
}
+static const bool deprecated_events[] = {
+ [MPV_EVENT_IDLE] = true,
+ [MPV_EVENT_TICK] = true,
+};
+
int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable)
{
if (!mpv_event_name(event) || enable < 0 || enable > 1)
@@ -824,19 +856,45 @@ int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable)
if (event == MPV_EVENT_SHUTDOWN && !enable)
return MPV_ERROR_INVALID_PARAMETER;
assert(event < (int)INTERNAL_EVENT_BASE); // excluded above; they have no name
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
uint64_t bit = 1ULL << event;
ctx->event_mask = enable ? ctx->event_mask | bit : ctx->event_mask & ~bit;
- pthread_mutex_unlock(&ctx->lock);
- invalidate_global_event_mask(ctx);
+ if (enable && event < MP_ARRAY_SIZE(deprecated_events) &&
+ deprecated_events[event])
+ {
+ MP_WARN(ctx, "The '%s' event is deprecated and will be removed.\n",
+ mpv_event_name(event));
+ }
+ mp_mutex_unlock(&ctx->lock);
return 0;
}
+// Set waiting_for_hook==true for all possibly pending properties.
+static void set_wait_for_hook_flags(mpv_handle *ctx)
+{
+ for (int n = 0; n < ctx->num_properties; n++) {
+ struct observe_property *prop = ctx->properties[n];
+
+ if (prop->value_ret_ts != prop->change_ts)
+ prop->waiting_for_hook = true;
+ }
+}
+
+// Return whether any property still has waiting_for_hook set.
+static bool check_for_for_hook_flags(mpv_handle *ctx)
+{
+ for (int n = 0; n < ctx->num_properties; n++) {
+ if (ctx->properties[n]->waiting_for_hook)
+ return true;
+ }
+ return false;
+}
+
mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
{
mpv_event *event = ctx->cur_event;
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
if (!ctx->fuzzy_initialized)
mp_wakeup_core(ctx->clients->mpctx);
@@ -845,7 +903,7 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
if (timeout < 0)
timeout = 1e20;
- int64_t deadline = mp_add_timeout(mp_time_us(), timeout);
+ int64_t deadline = mp_time_ns_add(mp_time_ns(), timeout);
*event = (mpv_event){0};
talloc_free_children(event);
@@ -859,13 +917,24 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
event->event_id = MPV_EVENT_QUEUE_OVERFLOW;
break;
}
- // This will almost surely lead to a deadlock. (Polling is still ok.)
- if (ctx->suspend_count && timeout > 0) {
- MP_ERR(ctx, "attempting to wait while core is suspended");
- break;
+ struct mpv_event *ev =
+ ctx->num_events ? &ctx->events[ctx->first_event] : NULL;
+ if (ev && ev->event_id == MPV_EVENT_HOOK) {
+ // Give old property notifications priority over hooks. This is a
+ // guarantee given to clients to simplify their logic. New property
+ // changes after this are treated normally, so
+ if (!ctx->hook_pending) {
+ ctx->hook_pending = true;
+ set_wait_for_hook_flags(ctx);
+ }
+ if (check_for_for_hook_flags(ctx)) {
+ ev = NULL; // delay
+ } else {
+ ctx->hook_pending = false;
+ }
}
- if (ctx->num_events) {
- *event = ctx->events[ctx->first_event];
+ if (ev) {
+ *event = *ev;
ctx->first_event = (ctx->first_event + 1) % ctx->max_events;
ctx->num_events--;
talloc_steal(event, event->data);
@@ -883,17 +952,17 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
}
ctx->queued_wakeup = false;
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
return event;
}
void mpv_wakeup(mpv_handle *ctx)
{
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
ctx->queued_wakeup = true;
wakeup_client(ctx);
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
}
// map client API types to internal types
@@ -936,7 +1005,9 @@ static bool conv_node_to_format(void *dst, mpv_format dst_fmt, mpv_node *src)
return true;
}
if (dst_fmt == MPV_FORMAT_INT64 && src->format == MPV_FORMAT_DOUBLE) {
- if (src->u.double_ >= INT64_MIN && src->u.double_ <= INT64_MAX) {
+ if (src->u.double_ > (double)INT64_MIN &&
+ src->u.double_ < (double)INT64_MAX)
+ {
*(int64_t *)dst = src->u.double_;
return true;
}
@@ -953,7 +1024,6 @@ void mpv_free_node_contents(mpv_node *node)
int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format,
void *data)
{
- int flags = ctx->mpctx->initialized ? M_SETOPT_RUNTIME : 0;
const struct m_option *type = get_mp_type(format);
if (!type)
return MPV_ERROR_OPTION_FORMAT;
@@ -964,8 +1034,7 @@ int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format,
data = &tmp;
}
lock_core(ctx);
- int err = m_config_set_option_node(ctx->mpctx->mconfig, bstr0(name),
- data, flags);
+ int err = m_config_set_option_node(ctx->mpctx->mconfig, bstr0(name), data, 0);
unlock_core(ctx);
switch (err) {
case M_OPT_MISSING_PARAM:
@@ -1133,7 +1202,7 @@ static void async_cmd_fn(void *data)
struct async_cmd_request *req = data;
struct mp_cmd *cmd = req->cmd;
- ta_xset_parent(cmd, NULL);
+ ta_set_parent(cmd, NULL);
req->cmd = NULL;
struct mp_abort_entry *abort = NULL;
@@ -1266,6 +1335,12 @@ int mpv_set_property(mpv_handle *ctx, const char *name, mpv_format format,
return req.status;
}
+int mpv_del_property(mpv_handle *ctx, const char *name)
+{
+ const char* args[] = { "del", name, NULL };
+ return mpv_command(ctx, args);
+}
+
int mpv_set_property_string(mpv_handle *ctx, const char *name, const char *data)
{
return mpv_set_property(ctx, name, MPV_FORMAT_STRING, &data);
@@ -1325,7 +1400,7 @@ static void getproperty_fn(void *arg)
struct getproperty_request *req = arg;
const struct m_option *type = get_mp_type_get(req->format);
- union m_option_value xdata = {0};
+ union m_option_value xdata = m_option_value_default;
void *data = req->data ? req->data : &xdata;
int err = -1;
@@ -1450,78 +1525,95 @@ int mpv_get_property_async(mpv_handle *ctx, uint64_t ud, const char *name,
static void property_free(void *p)
{
struct observe_property *prop = p;
- const struct m_option *type = get_mp_type_get(prop->format);
- if (type) {
- m_option_free(type, &prop->new_value);
- m_option_free(type, &prop->user_value);
+
+ assert(prop->refcount == 0);
+
+ if (prop->type) {
+ m_option_free(prop->type, &prop->value);
+ m_option_free(prop->type, &prop->value_ret);
}
}
int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
const char *name, mpv_format format)
{
- if (format != MPV_FORMAT_NONE && !get_mp_type_get(format))
+ const struct m_option *type = get_mp_type_get(format);
+ if (format != MPV_FORMAT_NONE && !type)
return MPV_ERROR_PROPERTY_FORMAT;
// Explicitly disallow this, because it would require a special code path.
if (format == MPV_FORMAT_OSD_STRING)
return MPV_ERROR_PROPERTY_FORMAT;
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
+ assert(!ctx->destroying);
struct observe_property *prop = talloc_ptrtype(ctx, prop);
talloc_set_destructor(prop, property_free);
*prop = (struct observe_property){
- .client = ctx,
+ .owner = ctx,
.name = talloc_strdup(prop, name),
.id = mp_get_property_id(ctx->mpctx, name),
.event_mask = mp_get_property_event_mask(name),
.reply_id = userdata,
.format = format,
- .changed = true,
- .updating = false,
- .updated = false,
+ .type = type,
+ .change_ts = 1, // force initial event
+ .refcount = 1,
+ .value = m_option_value_default,
+ .value_ret = m_option_value_default,
};
+ ctx->properties_change_ts += 1;
MP_TARRAY_APPEND(ctx, ctx->properties, ctx->num_properties, prop);
ctx->property_event_masks |= prop->event_mask;
- ctx->lowest_changed = 0;
- pthread_mutex_unlock(&ctx->lock);
- invalidate_global_event_mask(ctx);
+ ctx->new_property_events = true;
+ ctx->cur_property_index = 0;
+ ctx->has_pending_properties = true;
+ mp_mutex_unlock(&ctx->lock);
+ mp_wakeup_core(ctx->mpctx);
return 0;
}
int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata)
{
- pthread_mutex_lock(&ctx->lock);
- ctx->property_event_masks = 0;
+ mp_mutex_lock(&ctx->lock);
int count = 0;
for (int n = ctx->num_properties - 1; n >= 0; n--) {
struct observe_property *prop = ctx->properties[n];
+ // Perform actual removal of the property lazily to avoid creating
+ // dangling pointers and such.
if (prop->reply_id == userdata) {
- if (prop->updating) {
- prop->dead = true;
- } else {
- // In case mpv_unobserve_property() is called after mpv_wait_event