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.
This commit is contained in:
Wim Taymans 2020-02-19 18:15:57 +01:00
parent 11086f23ea
commit d3634aca7e
6 changed files with 188 additions and 59 deletions

View file

@ -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;
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);
}
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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;
}