From d3634aca7ebf1a33022f8cac22836a4b1df3c079 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 19 Feb 2020 18:15:57 +0100 Subject: [PATCH] media-session: rework device reservation Monitor the device reservation objects and mark the device available. Don't select nodes from devices that are not available. Acquire the device reservation when a device starts. Release the device reservation when we suspend the device again. --- src/examples/media-session/alsa-monitor.c | 156 +++++++++++++++------ src/examples/media-session/media-session.h | 18 ++- src/examples/media-session/policy-node.c | 6 + src/examples/media-session/reserve.c | 56 ++++++-- src/examples/media-session/reserve.h | 8 +- src/examples/media-session/suspend-node.c | 3 + 6 files changed, 188 insertions(+), 59 deletions(-) diff --git a/src/examples/media-session/alsa-monitor.c b/src/examples/media-session/alsa-monitor.c index d346bcd9c..d8b682de1 100644 --- a/src/examples/media-session/alsa-monitor.c +++ b/src/examples/media-session/alsa-monitor.c @@ -67,6 +67,7 @@ struct node { struct spa_node *node; struct sm_node *snode; + unsigned int acquired:1; }; struct device { @@ -81,6 +82,7 @@ struct device { int priority; int profile; + int pending_profile; struct pw_properties *props; @@ -91,6 +93,8 @@ struct device { struct sm_device *sdevice; struct spa_hook listener; + uint32_t n_acquired; + unsigned int first:1; unsigned int appeared:1; struct spa_list node_list; @@ -138,6 +142,46 @@ static void alsa_update_node(struct device *device, struct node *node, pw_properties_update(node->props, info->props); } +static int node_acquire(void *data) +{ + struct node *node = data; + struct device *device = node->device; + + pw_log_debug("acquire %u", node->id); + + if (node->acquired) + return 0; + + node->acquired = true; + + if (device && device->n_acquired++ == 0) + rd_device_acquire(device->reserve); + return 0; +} + +static int node_release(void *data) +{ + struct node *node = data; + struct device *device = node->device; + + pw_log_debug("release %u", node->id); + + if (!node->acquired) + return 0; + + node->acquired = false; + + if (device && --device->n_acquired == 0) + rd_device_release(device->reserve); + return 0; +} + +static const struct sm_object_methods node_methods = { + SM_VERSION_OBJECT_METHODS, + .acquire = node_acquire, + .release = node_release, +}; + static struct node *alsa_create_node(struct device *device, uint32_t id, const struct spa_device_object_info *info) { @@ -242,6 +286,8 @@ static struct node *alsa_create_node(struct device *device, uint32_t id, goto clean_node; } + node->snode->obj.methods = SPA_CALLBACKS_INIT(&node_methods, node); + spa_list_append(&device->node_list, &node->link); return node; @@ -398,6 +444,23 @@ static int update_device_props(struct device *device) return 1; } +static void set_profile(struct device *device, int index) +{ + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + pw_log_debug("%p: set profile %d id:%d", device, index, device->device_id); + + if (device->device_id != 0) { + device->profile = index; + spa_device_set_param(device->device, + SPA_PARAM_Profile, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_index, SPA_POD_Int(index))); + } +} + static void set_jack_profile(struct impl *impl, int index) { char buf[1024]; @@ -413,23 +476,6 @@ static void set_jack_profile(struct impl *impl, int index) SPA_PARAM_PROFILE_index, SPA_POD_Int(index))); } -static void set_profile(struct device *device, int index) -{ - char buf[1024]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); - - pw_log_debug("%p: set profile %d id:%d", device, index, device->device_id); - - device->profile = index; - if (device->device_id != 0) { - spa_device_set_param(device->device, - SPA_PARAM_Profile, 0, - spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_index, SPA_POD_Int(index))); - } -} - static void remove_jack_timeout(struct impl *impl) { struct pw_loop *main_loop = impl->session->loop; @@ -463,19 +509,18 @@ static void add_jack_timeout(struct impl *impl) static void reserve_acquired(void *data, struct rd_device *d) { struct device *device = data; - struct impl *impl = device->impl; - pw_log_debug("%p: reserve acquired", device); + pw_log_debug("%p: reserve acquired %d", device, device->n_acquired); - remove_jack_timeout(impl); - set_jack_profile(impl, 0); - set_profile(device, 1); + device->sdevice->locked = false; + + if (device->n_acquired == 0) + rd_device_release(device->reserve); } static void sync_complete_done(void *data, int seq) { struct device *device = data; - struct impl *impl = device->impl; pw_log_debug("%d %d", device->seq, seq); if (seq != device->seq) @@ -486,8 +531,6 @@ static void sync_complete_done(void *data, int seq) if (device->reserve) rd_device_complete_release(device->reserve, true); - - add_jack_timeout(impl); } static void sync_destroy(void *data) @@ -506,11 +549,9 @@ static const struct pw_proxy_events sync_complete_release = { static void reserve_release(void *data, struct rd_device *d, int forced) { struct device *device = data; - struct impl *impl = device->impl; pw_log_debug("%p: reserve release", device); - remove_jack_timeout(impl); set_profile(device, 0); if (device->seq == 0) @@ -520,9 +561,41 @@ static void reserve_release(void *data, struct rd_device *d, int forced) device->seq = pw_proxy_sync(device->sdevice->obj.proxy, 0); } +static void reserve_busy(void *data, struct rd_device *d, const char *name, int32_t prio) +{ + struct device *device = data; + struct impl *impl = device->impl; + + pw_log_debug("%p: reserve busy %s", device, name); + device->sdevice->locked = true; + + if (strcmp(name, "jack") == 0) { + add_jack_timeout(impl); + } else { + remove_jack_timeout(impl); + } +} + +static void reserve_available(void *data, struct rd_device *d, const char *name) +{ + struct device *device = data; + struct impl *impl = device->impl; + + pw_log_debug("%p: reserve available %s", device, name); + device->sdevice->locked = false; + + remove_jack_timeout(impl); + if (strcmp(name, "jack") == 0) { + set_jack_profile(impl, 0); + } + +} + static const struct rd_device_callbacks reserve_callbacks = { .acquired = reserve_acquired, .release = reserve_release, + .busy = reserve_busy, + .available = reserve_available, }; static void device_destroy(void *data) @@ -542,18 +615,17 @@ static void device_update(void *data) pw_log_debug("device %p appeared %d %d", device, device->appeared, device->profile); - if (device->appeared) - return; + if (!device->appeared) { + device->device_id = device->sdevice->obj.id; + device->appeared = true; - device->device_id = device->sdevice->obj.id; - device->appeared = true; - - spa_device_add_listener(device->device, - &device->device_listener, - &alsa_device_events, device); - - set_profile(device, device->profile); - sm_object_sync_update(&device->sdevice->obj); + spa_device_add_listener(device->device, + &device->device_listener, + &alsa_device_events, device); + sm_object_sync_update(&device->sdevice->obj); + } + if (device->pending_profile != device->profile && !device->sdevice->locked) + set_profile(device, device->pending_profile); } static const struct sm_object_events device_events = { @@ -634,14 +706,12 @@ static struct device *alsa_create_device(struct impl *impl, uint32_t id, } else { rd_device_set_application_device_name(device->reserve, spa_dict_lookup(info->props, SPA_KEY_API_ALSA_PATH)); + + rd_device_acquire(device->reserve); } device->priority -= atol(card) * 64; } - - /* no device reservation, activate device right now */ - if (device->reserve == NULL) - set_profile(device, 1); - + device->pending_profile = 1; device->first = true; spa_list_init(&device->node_list); spa_list_append(&impl->device_list, &device->link); diff --git a/src/examples/media-session/media-session.h b/src/examples/media-session/media-session.h index 0286e61a7..06cd5a892 100644 --- a/src/examples/media-session/media-session.h +++ b/src/examples/media-session/media-session.h @@ -45,6 +45,14 @@ struct sm_object_events { void (*destroy) (void *data); }; +struct sm_object_methods { +#define SM_VERSION_OBJECT_METHODS 0 + uint32_t version; + + int (*acquire) (void *data); + int (*release) (void *data); +}; + struct sm_object { uint32_t id; const char *type; @@ -71,12 +79,20 @@ struct sm_object { struct spa_hook handle_listener; struct spa_hook_list hooks; + struct spa_callbacks methods; + struct spa_list data; }; int sm_object_add_listener(struct sm_object *obj, struct spa_hook *listener, const struct sm_object_events *events, void *data); +#define sm_object_call(o,...) spa_callbacks_call(&(o)->methods, struct sm_object_methods, __VA_ARGS__) +#define sm_object_call_res(o,...) spa_callbacks_call_res(&(o)->methods, struct sm_object_methods, 0, __VA_ARGS__) + +#define sm_object_acquire(o) sm_object_call(o, acquire, 0) +#define sm_object_release(o) sm_object_call(o, release, 0) + struct sm_param { uint32_t id; struct spa_list link; /**< link in param_list */ @@ -104,6 +120,7 @@ struct sm_device { struct sm_object obj; unsigned int subscribe:1; /**< if we subscribed to param changes */ + unsigned int locked:1; /**< if the device is locked by someone else right now */ #define SM_DEVICE_CHANGE_MASK_INFO (SM_OBJECT_CHANGE_MASK_LAST<<0) #define SM_DEVICE_CHANGE_MASK_PARAMS (SM_OBJECT_CHANGE_MASK_LAST<<1) @@ -114,7 +131,6 @@ struct sm_device { struct spa_list node_list; }; - struct sm_node { struct sm_object obj; diff --git a/src/examples/media-session/policy-node.c b/src/examples/media-session/policy-node.c index 8542be683..d65804d13 100644 --- a/src/examples/media-session/policy-node.c +++ b/src/examples/media-session/policy-node.c @@ -334,6 +334,7 @@ static int find_node(void *data, struct node *node) struct impl *impl = find->impl; int priority = 0; uint64_t plugged = 0; + struct sm_device *device = node->obj->device; pw_log_debug(NAME " %p: looking at node '%d' enabled:%d state:%d peer:%p exclusive:%d", impl, node->id, node->enabled, node->obj->info->state, node->peer, node->exclusive); @@ -341,6 +342,11 @@ static int find_node(void *data, struct node *node) if (!node->enabled || node->type == NODE_TYPE_UNKNOWN) return 0; + if (device && device->locked) { + pw_log_debug(".. device locked"); + return 0; + } + if (node->direction == find->target->direction) { pw_log_debug(".. same direction"); return 0; diff --git a/src/examples/media-session/reserve.c b/src/examples/media-session/reserve.c index d92938900..92ba40490 100644 --- a/src/examples/media-session/reserve.c +++ b/src/examples/media-session/reserve.c @@ -297,6 +297,28 @@ static DBusHandlerResult filter_handler(DBusConnection *c, DBusMessage *m, void d->registered = false; } } + if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) { + const char *old, *new; + if (!dbus_message_get_args( m, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) + goto invalid; + + if (strcmp(name, d->service_name) != 0) + goto invalid; + + pw_log_debug(NAME" %p: changed %s: %s -> %s", d, name, old, new); + + if (old == NULL || *old == 0) { + if (d->callbacks->busy) + d->callbacks->busy(d->data, d, new, 0); + } else { + if (d->callbacks->available) + d->callbacks->available(d->data, d, old); + } + } invalid: dbus_error_free(&error); @@ -308,7 +330,6 @@ rd_device_new(DBusConnection *connection, const char *device_name, const char *a int32_t priority, const struct rd_device_callbacks *callbacks, void *data) { struct rd_device *d; - DBusError error; int res; d = calloc(1, sizeof(struct rd_device)); @@ -333,8 +354,6 @@ rd_device_new(DBusConnection *connection, const char *device_name, const char *a goto error_free; } - dbus_error_init(&error); - if (!dbus_connection_add_filter(d->connection, filter_handler, d, @@ -348,19 +367,13 @@ rd_device_new(DBusConnection *connection, const char *device_name, const char *a dbus_bus_add_match(d->connection, "type='signal',sender='org.freedesktop.DBus'," "interface='org.freedesktop.DBus',member='NameAcquired'", NULL); - - if ((res = dbus_bus_request_name(d->connection, - d->service_name, - (d->priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0), - &error)) < 0) { - dbus_error_free(&error); - res = -EIO; - goto error_free; - } + dbus_bus_add_match(d->connection, + "type='signal',sender='org.freedesktop.DBus'," + "interface='org.freedesktop.DBus',member='NameOwnerChanged'", NULL); dbus_connection_ref(d->connection); - pw_log_debug(NAME"%p: new device %s: res %d", d, device_name, res); + pw_log_debug(NAME"%p: new device %s", d, device_name); return d; @@ -372,6 +385,23 @@ error_free: return NULL; } +int rd_device_acquire(struct rd_device *d) +{ + int res; + DBusError error; + + dbus_error_init(&error); + + if ((res = dbus_bus_request_name(d->connection, + d->service_name, + (d->priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0), + &error)) < 0) { + dbus_error_free(&error); + res = -EBUSY; + } + return res; +} + int rd_device_request_release(struct rd_device *d) { DBusMessage *m = NULL; diff --git a/src/examples/media-session/reserve.h b/src/examples/media-session/reserve.h index a334667d6..c31e2d0c3 100644 --- a/src/examples/media-session/reserve.h +++ b/src/examples/media-session/reserve.h @@ -35,10 +35,14 @@ extern "C" { struct rd_device; struct rd_device_callbacks { - /** the device is acquired */ + /** the device is acquired by us */ void (*acquired) (void *data, struct rd_device *d); /** request a release of the device */ void (*release) (void *data, struct rd_device *d, int forced); + /** the device is busy by someone else */ + void (*busy) (void *data, struct rd_device *d, const char *name, int32_t priority); + /** the device is made available by someone else */ + void (*available) (void *data, struct rd_device *d, const char *name); }; /* create a new device and start watching */ @@ -53,7 +57,7 @@ rd_device_new(DBusConnection *connection, /**< Bus to watch */ void *data); /** try to acquire the device */ -int rd_device_request_acquire(struct rd_device *d); +int rd_device_acquire(struct rd_device *d); /** request the owner to release the device */ int rd_device_request_release(struct rd_device *d); diff --git a/src/examples/media-session/suspend-node.c b/src/examples/media-session/suspend-node.c index cbabcdf81..8d02c5ac3 100644 --- a/src/examples/media-session/suspend-node.c +++ b/src/examples/media-session/suspend-node.c @@ -92,6 +92,8 @@ static void idle_timeout(void *data, uint64_t expirations) remove_idle_timeout(node); pw_node_send_command((struct pw_node*)node->obj->obj.proxy, cmd); + + sm_object_release(&node->obj->obj); } static void add_idle_timeout(struct node *node) @@ -118,6 +120,7 @@ static int on_node_idle(struct impl *impl, struct node *node) static int on_node_running(struct impl *impl, struct node *node) { pw_log_debug(NAME" %p: node %d running", impl, node->id); + sm_object_acquire(&node->obj->obj); remove_idle_timeout(node); return 0; }