summaryrefslogtreecommitdiffstats
path: root/player/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/client.c')
-rw-r--r--player/client.c664
1 files changed, 377 insertions, 287 deletions
diff --git a/player/client.c b/player/client.c
index 14ee8fd7ee..85854e31ff 100644
--- a/player/client.c
+++ b/player/client.c
@@ -13,14 +13,14 @@
* 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"
@@ -39,7 +39,6 @@
#include "options/m_property.h"
#include "options/path.h"
#include "options/parse_configfile.h"
-#include "osdep/atomic.h"
#include "osdep/threads.h"
#include "osdep/timer.h"
#include "osdep/io.h"
@@ -64,7 +63,7 @@
struct mp_client_api {
struct MPContext *mpctx;
- pthread_mutex_t lock;
+ mp_mutex lock;
// -- protected by lock
@@ -77,12 +76,12 @@ struct mp_client_api {
// 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 {
@@ -102,24 +101,26 @@ struct observe_property {
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;
@@ -140,6 +141,7 @@ struct mpv_handle {
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;
@@ -182,7 +184,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)
@@ -191,8 +193,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) {
@@ -200,7 +200,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;
}
@@ -210,38 +210,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 struct mpv_handle *find_client_id(struct mp_client_api *clients, int64_t id)
+{
+ 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++) {
@@ -258,7 +277,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;
}
@@ -269,15 +288,16 @@ 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);
@@ -287,7 +307,7 @@ struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name
if (clients->num_clients == 1 && !clients->mpctx->is_cli)
client->fuzzy_initialized = true;
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
mpv_request_event(client, MPV_EVENT_TICK, 0);
@@ -296,9 +316,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)
@@ -306,6 +326,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;
@@ -318,52 +343,41 @@ struct mpv_global *mp_client_get_global(struct mpv_handle *ctx)
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)
@@ -378,10 +392,10 @@ static void unlock_core(mpv_handle *ctx)
void mpv_wait_async_requests(mpv_handle *ctx)
{
- pthread_mutex_lock(&ctx->lock);
+ 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.
@@ -390,7 +404,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)
@@ -405,12 +419,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)
@@ -426,7 +440,7 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
if (terminate)
mpv_command(ctx, (const char*[]){"quit", NULL});
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
ctx->destroying = true;
@@ -438,7 +452,7 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
prop_unref(ctx->cur_property);
ctx->cur_property = NULL;
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
abort_async(mpctx, ctx, 0, 0);
@@ -450,7 +464,7 @@ static void mp_destroy_client(mpv_handle *ctx, bool terminate)
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) {
@@ -462,9 +476,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]);
@@ -496,7 +510,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.
@@ -507,17 +521,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);
}
@@ -528,11 +542,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);
@@ -547,7 +556,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;
@@ -556,7 +565,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) {
@@ -571,26 +580,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);
@@ -603,7 +612,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)
@@ -620,8 +629,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);
@@ -674,6 +683,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;
@@ -691,13 +703,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;
}
@@ -717,7 +729,7 @@ 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, event->event_id);
@@ -733,7 +745,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;
}
@@ -743,20 +755,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);
+ 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 = {
@@ -766,7 +778,7 @@ 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.
@@ -799,7 +811,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) {
@@ -809,7 +821,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;
}
@@ -831,6 +843,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)
@@ -838,20 +855,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;
- if (enable && event == MPV_EVENT_TICK)
- MP_WARN(ctx, "The 'tick' event is deprecated and will be removed.\n");
- pthread_mutex_unlock(&ctx->lock);
+ 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);
@@ -860,7 +902,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);
@@ -874,8 +916,24 @@ mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
event->event_id = MPV_EVENT_QUEUE_OVERFLOW;
break;
}
- if (ctx->num_events) {
- *event = ctx->events[ctx->first_event];
+ 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 (ev) {
+ *event = *ev;
ctx->first_event = (ctx->first_event + 1) % ctx->max_events;
ctx->num_events--;
talloc_steal(event, event->data);
@@ -893,17 +951,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
@@ -946,7 +1004,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;
}
@@ -1141,7 +1201,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;
@@ -1274,6 +1334,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);
@@ -1333,7 +1399,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;
@@ -1477,7 +1543,7 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
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);
@@ -1491,6 +1557,8 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
.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);
@@ -1498,14 +1566,14 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
ctx->new_property_events = true;
ctx->cur_property_index = 0;
ctx->has_pending_properties = true;
- pthread_mutex_unlock(&ctx->lock);
+ 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);
+ 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];
@@ -1519,10 +1587,34 @@ int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata)
count++;
}
}
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
return count;
}
+static bool property_shared_prefix(const char *a0, const char *b0)
+{
+ bstr a = bstr0(a0);
+ bstr b = bstr0(b0);
+
+ // Treat options and properties as equivalent.
+ bstr_eatstart0(&a, "options/");
+ bstr_eatstart0(&b, "options/");
+
+ // Compare the potentially-common portion
+ if (memcmp(a.start, b.start, MPMIN(a.len, b.len)))
+ return false;
+
+ // If lengths were equal, we're done
+ if (a.len == b.len)
+ return true;
+
+ // Check for a slash in the first non-common byte of the longer string
+ if (a.len > b.len)
+ return a.start[b.len] == '/';
+ else
+ return b.start[a.len] == '/';
+}
+
// Broadcast that a property has changed.
void mp_client_property_change(struct MPContext *mpctx, const char *name)
{
@@ -1530,22 +1622,23 @@ void mp_client_property_change(struct MPContext *mpctx, const char *name)
int id = mp_get_property_id(mpctx, name);
bool any_pending = false;
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
for (int n = 0; n < clients->num_clients; n++) {
struct mpv_handle *client = clients->clients[n];
- pthread_mutex_lock(&client->lock);
+ mp_mutex_lock(&client->lock);
for (int i = 0; i < client->num_properties; i++) {
- if (client->properties[i]->id == id) {
+ if (client->properties[i]->id == id &&
+ property_shared_prefix(name, client->properties[i]->name)) {
client->properties[i]->change_ts += 1;
client->has_pending_properties = true;
any_pending = true;
}
}
- pthread_mutex_unlock(&client->lock);
+ mp_mutex_unlock(&client->lock);
}
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
// If we're inside mp_dispatch_queue_process(), this will cause the playloop
// to be re-run (to get mp_client_send_property_changes() called). If we're
@@ -1588,7 +1681,7 @@ static void send_client_property_changes(struct mpv_handle *ctx)
bool changed = false;
if (prop->format) {
const struct m_option *type = prop->type;
- union m_option_value val = {0};
+ union m_option_value val = m_option_value_default;
struct getproperty_request req = {
.mpctx = ctx->mpctx,
.name = prop->name,
@@ -1602,13 +1695,13 @@ static void send_client_property_changes(struct mpv_handle *ctx)
// or similar things are involved).
prop->refcount += 1; // keep prop alive (esp. prop->name)
ctx->async_counter += 1; // keep ctx alive
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_unlock(&ctx->lock);
getproperty_fn(&req);
- pthread_mutex_lock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
ctx->async_counter -= 1;
prop_unref(prop);
- // Set of observed properties was changed or something similar
+ // Set if observed properties was changed or something similar
// => start over, retry next time.
if (cur_ts != ctx->properties_change_ts || ctx->destroying) {
m_option_free(type, &val);
@@ -1638,10 +1731,16 @@ static void send_client_property_changes(struct mpv_handle *ctx)
changed = true;
}
- if (changed) {
- ctx->new_property_events = true;
- } else if (prop->value_ret_ts == prop->value_ts) {
+ if (prop->waiting_for_hook)
+ ctx->new_property_events = true; // make sure to wakeup
+
+ // Avoid retriggering the change event if the property didn't change,
+ // and the previous value was actually returned to the client.
+ if (!changed && prop->value_ret_ts == prop->value_ts) {
prop->value_ret_ts = prop->change_ts; // no change => no event
+ prop->waiting_for_hook = false;
+ } else {
+ ctx->new_property_events = true;
}
prop->value_ts = prop->change_ts;
@@ -1655,22 +1754,22 @@ void mp_client_send_property_changes(struct MPContext *mpctx)
{
struct mp_client_api *clients = mpctx->clients;
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_lock(&clients->lock);
uint64_t cur_ts = clients->clients_list_change_ts;
for (int n = 0; n < clients->num_clients; n++) {
struct mpv_handle *ctx = clients->clients[n];
- pthread_mutex_lock(&ctx->lock);
- if (!ctx->has_pending_properties) {
- pthread_mutex_unlock(&ctx->lock);
+ mp_mutex_lock(&ctx->lock);
+ if (!ctx->has_pending_properties || ctx->destroying) {
+ mp_mutex_unlock(&ctx->lock);
continue;
}
// Keep ctx->lock locked (unlock order does not matter).
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
send_client_property_changes(ctx);
- pthread_mutex_unlock(&ctx->lock);
- pthread_mutex_lock(&clients->lock);
+ mp_mutex_unlock(&ctx->lock);
+ mp_mutex_lock(&clients->lock);
if (cur_ts != clients->clients_list_change_ts) {
// List changed; need to start over. Do it in the next iteration.
mp_wakeup_core(mpctx);
@@ -1678,7 +1777,7 @@ void mp_client_send_property_changes(struct MPContext *mpctx)
}
}
- pthread_mutex_unlock(&clients->lock);
+ mp_mutex_unlock(&clients->lock);
}
// Set ctx->cur_event to a generated property change event, if there is any
@@ -1690,7 +1789,8 @@ static bool gen_property_change_event(struct mpv_handle *ctx)
while (1) {
if (ctx->cur_property_index >= ctx->num_properties) {
- if (!ctx->new_property_events || !ctx->num_properties)
+ ctx->new_property_events &= ctx->num_properties > 0;
+ if (!ctx->new_property_events)
break;
ctx->new_property_events = false;
ctx->cur_property_index = 0;
@@ -1698,8 +1798,11 @@ static bool gen_property_change_event(struct mpv_handle *ctx)
struct observe_property *prop = ctx->properties[ctx->cur_property_index++];
- if (prop->value_ret_ts != prop->value_ts) {
+ if (prop->value_ts == prop->change_ts && // not a stale value?
+ prop->value_ret_ts != prop->value_ts) // other value than last time?
+ {
prop->value_ret_ts = prop->value_ts;
+ prop->waiting_for_hook = false;
prop_unref(ctx->cur_property);
ctx->cur_property = prop;
prop->refcount += 1;
@@ -1728,7 +1831,7 @@ int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata,
const char *name, int priority)
{
lock_core(ctx);
- mp_hook_add(ctx->mpctx, ctx->name, name, reply_userdata, priority, false);
+ mp_hook_add(ctx->mpctx, ctx->name, ctx->id, name, reply_userdata, priority);
unlock_core(ctx);
return 0;
}
@@ -1736,7 +1839,7 @@ int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata,
int mpv_hook_continue(mpv_handle *ctx, uint64_t id)
{
lock_core(ctx);
- int r = mp_hook_continue(ctx->mpctx, ctx->name, id);
+ int r = mp_hook_continue(ctx->mpctx, ctx->id, id);
unlock_core(ctx);
return r;
}
@@ -1744,7 +1847,7 @@ int mpv_hook_continue(mpv_handle *ctx, uint64_t id)
int mpv_load_config_file(mpv_handle *ctx, const char *filename)
{
lock_core(ctx);
- int r = m_config_parse_config_file(ctx->mpctx->mconfig, filename, NULL, 0);
+ int r = m_config_parse_config_file(ctx->mpctx->mconfig, ctx->mpctx