mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
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:
parent
11086f23ea
commit
d3634aca7e
6 changed files with 188 additions and 59 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue