diff --git a/player/client.c b/player/client.c index 49cb866edb..bccf2d9605 100644 --- a/player/client.c +++ b/player/client.c @@ -63,6 +63,8 @@ struct mp_client_api { struct observe_property { 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 @@ -111,13 +113,15 @@ struct mpv_handle { struct observe_property **properties; int num_properties; - int lowest_changed; + int lowest_changed; // attempt at making change processing incremental int properties_updating; + uint64_t property_event_masks; // or-ed together event masks of all properties struct mp_log_buffer *messages; }; static bool gen_property_change_event(struct mpv_handle *ctx); +static void notify_property_events(struct mpv_handle *ctx, uint64_t event_mask); void mp_clients_init(struct MPContext *mpctx) { @@ -188,13 +192,15 @@ struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name .cur_event = talloc_zero(client, struct mpv_event), .events = talloc_array(client, mpv_event, num_events), .max_events = num_events, - .event_mask = ((uint64_t)-1) & ~(1ULL << MPV_EVENT_TICK), + .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); + mpv_request_event(client, MPV_EVENT_TICK, 0); + MP_TARRAY_APPEND(clients, clients->clients, clients->num_clients, client); pthread_mutex_unlock(&clients->lock); @@ -429,7 +435,10 @@ static int append_event(struct mpv_handle *ctx, struct mpv_event *event) static int send_event(struct mpv_handle *ctx, struct mpv_event *event) { pthread_mutex_lock(&ctx->lock); - if (!(ctx->event_mask & (1ULL << event->event_id))) { + uint64_t mask = 1ULL << event->event_id; + if (ctx->property_event_masks & mask) + notify_property_events(ctx, mask); + if (!(ctx->event_mask & mask)) { pthread_mutex_unlock(&ctx->lock); return 0; } @@ -541,8 +550,9 @@ int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable) { if (!mpv_event_name(event) || enable < 0 || enable > 1) return MPV_ERROR_INVALID_PARAMETER; + assert(event < INTERNAL_EVENT_BASE); // excluded above; they have no name pthread_mutex_lock(&ctx->lock); - uint64_t bit = 1LLU << event; + uint64_t bit = 1ULL << event; ctx->event_mask = enable ? ctx->event_mask | bit : ctx->event_mask & ~bit; pthread_mutex_unlock(&ctx->lock); return 0; @@ -1120,12 +1130,15 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata, *prop = (struct observe_property){ .client = ctx, .name = talloc_strdup(prop, name), + .id = mp_get_property_id(name), + .event_mask = mp_get_property_event_mask(name), .reply_id = userdata, .format = format, .changed = true, .need_new_value = true, }; 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); return 0; @@ -1134,6 +1147,7 @@ int mpv_observe_property(mpv_handle *ctx, uint64_t userdata, int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata) { pthread_mutex_lock(&ctx->lock); + ctx->property_event_masks = 0; int count = 0; for (int n = ctx->num_properties - 1; n >= 0; n--) { struct observe_property *prop = ctx->properties[n]; @@ -1150,52 +1164,38 @@ int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata) MP_TARRAY_REMOVE_AT(ctx->properties, ctx->num_properties, n); count++; } + if (!prop->dead) + ctx->property_event_masks |= prop->event_mask; } ctx->lowest_changed = 0; pthread_mutex_unlock(&ctx->lock); return count; } -static int prefix_len(const char *p) +static void mark_property_changed(struct mpv_handle *client, int index) { - const char *end = strchr(p, '/'); - return end ? end - p : strlen(p); + struct observe_property *prop = client->properties[index]; + if (!prop->changed && !prop->need_new_value) { + prop->changed = true; + prop->need_new_value = prop->format != 0; + client->lowest_changed = MPMIN(client->lowest_changed, index); + } } -static bool match_property(const char *a, const char *b) -{ - if (strcmp(b, "*") == 0) - return true; - int len_a = prefix_len(a); - int len_b = prefix_len(b); - return strncmp(a, b, MPMIN(len_a, len_b)) == 0; -} - -// Broadcast that properties have changed. -void mp_client_property_change(struct MPContext *mpctx, const char *const *list) +// Broadcast that a property has changed. +void mp_client_property_change(struct MPContext *mpctx, const char *name) { struct mp_client_api *clients = mpctx->clients; + int id = mp_get_property_id(name); pthread_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); - - client->lowest_changed = client->num_properties; for (int i = 0; i < client->num_properties; i++) { - struct observe_property *prop = client->properties[i]; - if (!prop->changed && !prop->need_new_value) { - for (int x = 0; list && list[x]; x++) { - if (match_property(prop->name, list[x])) { - prop->changed = true; - prop->need_new_value = prop->format != 0; - break; - } - } - } - if ((prop->changed || prop->updating) && i < client->lowest_changed) - client->lowest_changed = i; + if (client->properties[i]->id == id) + mark_property_changed(client, i); } if (client->lowest_changed < client->num_properties) wakeup_client(client); @@ -1205,6 +1205,18 @@ void mp_client_property_change(struct MPContext *mpctx, const char *const *list) pthread_mutex_unlock(&clients->lock); } +// Mark properties as changed in reaction to specific events. +// Called with ctx->lock held. +static void notify_property_events(struct mpv_handle *ctx, uint64_t event_mask) +{ + for (int i = 0; i < ctx->num_properties; i++) { + if (ctx->properties[i]->event_mask & event_mask) + mark_property_changed(ctx, i); + } + if (ctx->lowest_changed < ctx->num_properties) + wakeup_client(ctx); +} + static void update_prop(void *p) { struct observe_property *prop = p; diff --git a/player/client.h b/player/client.h index af94778a55..b329ae6d7b 100644 --- a/player/client.h +++ b/player/client.h @@ -17,7 +17,7 @@ int mp_clients_num(struct MPContext *mpctx); void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data); int mp_client_send_event(struct MPContext *mpctx, const char *client_name, int event, void *data); -void mp_client_property_change(struct MPContext *mpctx, const char *const *list); +void mp_client_property_change(struct MPContext *mpctx, const char *name); struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name); struct mp_log *mp_client_get_log(struct mpv_handle *ctx); diff --git a/player/command.c b/player/command.c index df44df5f69..df59e34714 100644 --- a/player/command.c +++ b/player/command.c @@ -2834,6 +2834,46 @@ static const char *const *const mp_event_property_change[] = { }; #undef E +static int prefix_len(const char *p) +{ + const char *end = strchr(p, '/'); + return end ? end - p : strlen(p); +} + +static bool match_property(const char *a, const char *b) +{ + if (strcmp(a, "*") == 0) + return true; + int len_a = prefix_len(a); + int len_b = prefix_len(b); + return strncmp(a, b, MPMIN(len_a, len_b)) == 0; +} + +// Return a bitset of events which change the property. +uint64_t mp_get_property_event_mask(const char *name) +{ + uint64_t mask = 0; + for (int n = 0; n < MP_ARRAY_SIZE(mp_event_property_change); n++) { + const char *const *const list = mp_event_property_change[n]; + for (int i = 0; list && list[i]; i++) { + if (match_property(list[i], name)) + mask |= 1ULL << n; + } + } + return mask; +} + +// Return an ID for the property. It might not be unique, but is good enough +// for property change handling. Return -1 if property unknown. +int mp_get_property_id(const char *name) +{ + for (int n = 0; mp_properties[n].name; n++) { + if (match_property(mp_properties[n].name, name)) + return n; + } + return -1; +} + static bool is_property_set(int action, void *val) { switch (action) { @@ -3921,13 +3961,10 @@ void mp_notify(struct MPContext *mpctx, int event, void *arg) if (event == MPV_EVENT_START_FILE) ctx->last_seek_pts = MP_NOPTS_VALUE; - if (event < INTERNAL_EVENT_BASE) - mp_client_broadcast_event(mpctx, event, arg); - if (event >= 0 && event < MP_ARRAY_SIZE(mp_event_property_change)) - mp_client_property_change(mpctx, mp_event_property_change[event]); + mp_client_broadcast_event(mpctx, event, arg); } void mp_notify_property(struct MPContext *mpctx, const char *property) { - mp_client_property_change(mpctx, (const char*[]){property, NULL}); + mp_client_property_change(mpctx, property); } diff --git a/player/command.h b/player/command.h index 2f9770b714..6e3312b806 100644 --- a/player/command.h +++ b/player/command.h @@ -36,6 +36,10 @@ int mp_property_do(const char* name, int action, void* val, void mp_notify(struct MPContext *mpctx, int event, void *arg); void mp_notify_property(struct MPContext *mpctx, const char *property); +int mp_get_property_id(const char *name); +uint64_t mp_get_property_event_mask(const char *name); + +// Must start with the first unused positive value in enum mpv_event_id #define INTERNAL_EVENT_BASE 24 #define MP_EVENT_CACHE_UPDATE (INTERNAL_EVENT_BASE + 0)