mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
pulse: use metadata to store default source/sink
The metadata is implemented by the session manager and it can decide what to do when the defaults change. It can also choose to save (some of) the metadata to a database. The metadata is also shared between applications so that changes can be picked up immediately.
This commit is contained in:
parent
2991a814cd
commit
ee54cb96aa
5 changed files with 232 additions and 11 deletions
|
|
@ -23,10 +23,12 @@
|
|||
#include <spa/param/props.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <extensions/metadata.h>
|
||||
|
||||
#include <pulse/context.h>
|
||||
#include <pulse/timeval.h>
|
||||
#include <pulse/error.h>
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
|
@ -402,7 +404,7 @@ static struct global *find_node_for_route(pa_context *c, struct global *card, ui
|
|||
spa_list_for_each(n, &c->globals, link) {
|
||||
if (strcmp(n->type, PW_TYPE_INTERFACE_Node) != 0)
|
||||
continue;
|
||||
pw_log_info("%d/%d %d/%d",
|
||||
pw_log_debug("%d/%d %d/%d",
|
||||
n->node_info.device_id, card->id,
|
||||
n->node_info.profile_device_id, device);
|
||||
if (n->node_info.device_id != card->id)
|
||||
|
|
@ -936,6 +938,36 @@ struct global_info client_info = {
|
|||
.destroy = client_destroy,
|
||||
};
|
||||
|
||||
static int metadata_property(void *object,
|
||||
uint32_t subject,
|
||||
const char *key,
|
||||
const char *type,
|
||||
const char *value)
|
||||
{
|
||||
struct global *global = object;
|
||||
return pa_metadata_update(global, subject, key, type, value);
|
||||
}
|
||||
|
||||
static const struct pw_metadata_events metadata_events = {
|
||||
PW_VERSION_METADATA_EVENTS,
|
||||
.property = metadata_property,
|
||||
};
|
||||
|
||||
static void metadata_destroy(void *data)
|
||||
{
|
||||
struct global *global = data;
|
||||
pa_context *c = global->context;
|
||||
if (c->metadata == global)
|
||||
c->metadata = NULL;
|
||||
pw_array_clear(&global->metadata_info.metadata);
|
||||
}
|
||||
|
||||
struct global_info metadata_info = {
|
||||
.version = PW_VERSION_METADATA,
|
||||
.events = &metadata_events,
|
||||
.destroy = metadata_destroy,
|
||||
};
|
||||
|
||||
static void proxy_removed(void *data)
|
||||
{
|
||||
struct global *g = data;
|
||||
|
|
@ -1091,6 +1123,12 @@ static int set_mask(pa_context *c, struct global *g)
|
|||
!f->init)
|
||||
emit_event(c, f, PA_SUBSCRIPTION_EVENT_CHANGE);
|
||||
|
||||
} else if (strcmp(g->type, PW_TYPE_INTERFACE_Metadata) == 0) {
|
||||
if (c->metadata == NULL) {
|
||||
ginfo = &metadata_info;
|
||||
c->metadata = g;
|
||||
}
|
||||
pw_array_init(&g->metadata_info.metadata, 64);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1529,19 +1567,56 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb,
|
|||
return o;
|
||||
}
|
||||
|
||||
struct default_node {
|
||||
uint32_t mask;
|
||||
pa_context_success_cb_t cb;
|
||||
void *userdata;
|
||||
char *name;
|
||||
const char *key;
|
||||
};
|
||||
|
||||
static void do_default_node(pa_operation *o, void *userdata)
|
||||
{
|
||||
struct default_node *d = userdata;
|
||||
pa_context *c = o->context;
|
||||
struct global *g;
|
||||
int error = 0;
|
||||
|
||||
pw_log_debug("%p", c);
|
||||
|
||||
g = pa_context_find_global_by_name(c, d->mask, d->name);
|
||||
if (g == NULL) {
|
||||
error = PA_ERR_NOENTITY;
|
||||
} else if (c->metadata) {
|
||||
pw_metadata_set_property(c->metadata->proxy,
|
||||
PW_ID_CORE, d->key, "text/plain", d->name);
|
||||
} else {
|
||||
error = PA_ERR_NOTIMPLEMENTED;
|
||||
}
|
||||
if (error != 0)
|
||||
pa_context_set_error(c, error);
|
||||
if (d->cb)
|
||||
d->cb(c, error != 0 ? -1 : 1, d->userdata);
|
||||
pa_operation_done(o);
|
||||
pa_xfree(d->name);
|
||||
}
|
||||
|
||||
SPA_EXPORT
|
||||
pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
|
||||
{
|
||||
pa_operation *o;
|
||||
struct success_data *d;
|
||||
struct default_node *d;
|
||||
|
||||
o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data));
|
||||
pa_context_ensure_registry(c);
|
||||
|
||||
o = pa_operation_new(c, NULL, do_default_node, sizeof(*d));
|
||||
d = o->userdata;
|
||||
d->error = PA_ERR_NOTIMPLEMENTED;
|
||||
d->mask = PA_SUBSCRIPTION_MASK_SINK;
|
||||
d->name = pa_xstrdup(name);
|
||||
d->key = "http://pipewire.org/metadata/default-audio-sink",
|
||||
d->cb = cb;
|
||||
d->userdata = userdata;
|
||||
pa_operation_sync(o);
|
||||
pw_log_warn("Not Implemented");
|
||||
|
||||
return o;
|
||||
}
|
||||
|
|
@ -1550,15 +1625,18 @@ SPA_EXPORT
|
|||
pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata)
|
||||
{
|
||||
pa_operation *o;
|
||||
struct success_data *d;
|
||||
struct default_node *d;
|
||||
|
||||
o = pa_operation_new(c, NULL, on_success, sizeof(struct success_data));
|
||||
pa_context_ensure_registry(c);
|
||||
|
||||
o = pa_operation_new(c, NULL, do_default_node, sizeof(*d));
|
||||
d = o->userdata;
|
||||
d->error = PA_ERR_NOTIMPLEMENTED;
|
||||
d->mask = PA_SUBSCRIPTION_MASK_SOURCE;
|
||||
d->name = pa_xstrdup(name);
|
||||
d->key = "http://pipewire.org/metadata/default-audio-source",
|
||||
d->cb = cb;
|
||||
d->userdata = userdata;
|
||||
pa_operation_sync(o);
|
||||
pw_log_warn("Not Implemented");
|
||||
|
||||
return o;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,6 +310,9 @@ struct global {
|
|||
struct {
|
||||
pa_client_info info;
|
||||
} client_info;
|
||||
struct {
|
||||
struct pw_array metadata;
|
||||
} metadata_info;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -350,6 +353,8 @@ struct pa_context {
|
|||
|
||||
int no_fail:1;
|
||||
int disconnect:1;
|
||||
|
||||
struct global *metadata;
|
||||
};
|
||||
|
||||
struct global *pa_context_find_global(pa_context *c, uint32_t id);
|
||||
|
|
@ -474,6 +479,11 @@ pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t cb
|
|||
void pa_operation_done(pa_operation *o);
|
||||
int pa_operation_sync(pa_operation *o);
|
||||
|
||||
int pa_metadata_update(struct global *global, uint32_t subject, const char *key,
|
||||
const char *type, const char *value);
|
||||
int pa_metadata_get(struct global *global, uint32_t subject, const char *key,
|
||||
const char **type, const char **value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1247,13 +1247,25 @@ struct server_data {
|
|||
static const char *get_default_name(pa_context *c, uint32_t mask)
|
||||
{
|
||||
struct global *g;
|
||||
const char *str;
|
||||
const char *str, *name = NULL, *type, *key;
|
||||
|
||||
if (c->metadata) {
|
||||
if (mask & PA_SUBSCRIPTION_MASK_SINK)
|
||||
key = "http://pipewire.org/metadata/default-audio-sink";
|
||||
else if (mask & PA_SUBSCRIPTION_MASK_SOURCE)
|
||||
key = "http://pipewire.org/metadata/default-audio-source";
|
||||
else
|
||||
return NULL;
|
||||
|
||||
if (pa_metadata_get(c->metadata, PW_ID_CORE, key, &type, &name) <= 0)
|
||||
name = NULL;
|
||||
}
|
||||
spa_list_for_each(g, &c->globals, link) {
|
||||
if ((g->mask & mask) != mask)
|
||||
continue;
|
||||
if (g->props != NULL &&
|
||||
(str = pw_properties_get(g->props, PW_KEY_NODE_NAME)) != NULL)
|
||||
(str = pw_properties_get(g->props, PW_KEY_NODE_NAME)) != NULL &&
|
||||
(name == NULL || strcmp(name, str) == 0))
|
||||
return str;
|
||||
}
|
||||
return "unknown";
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ pipewire_pulseaudio_sources = [
|
|||
'json.c',
|
||||
'mainloop.c',
|
||||
'mainloop-signal.c',
|
||||
'metadata.c',
|
||||
'operation.c',
|
||||
'proplist.c',
|
||||
'rtclock.c',
|
||||
|
|
|
|||
120
pipewire-pulseaudio/src/metadata.c
Normal file
120
pipewire-pulseaudio/src/metadata.c
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2020 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <spa/utils/result.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
struct metadata_item {
|
||||
uint32_t subject;
|
||||
char *key;
|
||||
char *type;
|
||||
char *value;
|
||||
};
|
||||
|
||||
void remove_all(struct global *global, uint32_t subject, const char *key)
|
||||
{
|
||||
struct metadata_item *it;
|
||||
for (it = pw_array_first(&global->metadata_info.metadata);
|
||||
pw_array_check(&global->metadata_info.metadata, it);) {
|
||||
if (it->subject == subject &&
|
||||
(key == NULL || it->key == NULL || strcmp(key, it->key) == 0))
|
||||
pw_array_remove(&global->metadata_info.metadata, it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
static struct metadata_item *find_item(struct global *global, uint32_t subject,
|
||||
const char *key)
|
||||
{
|
||||
struct metadata_item *it;
|
||||
pw_array_for_each(it, &global->metadata_info.metadata) {
|
||||
if (it->subject == subject &&
|
||||
(key == NULL || strcmp(key, it->key) == 0))
|
||||
return it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int replace_item(struct metadata_item *it, const char *type, const char *value)
|
||||
{
|
||||
if (it->type == NULL || strcmp(it->type, type) != 0) {
|
||||
free(it->type);
|
||||
it->type = strdup(type);
|
||||
}
|
||||
if (it->value == NULL || strcmp(it->value, value) != 0) {
|
||||
free(it->value);
|
||||
it->value = strdup(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_item(struct global *global, uint32_t subject, const char *key,
|
||||
const char *type, const char *value)
|
||||
{
|
||||
struct metadata_item *it;
|
||||
it = pw_array_add(&global->metadata_info.metadata, sizeof(*it));
|
||||
if (it == NULL)
|
||||
return -errno;
|
||||
it->subject = subject;
|
||||
it->key = strdup(key);
|
||||
it->type = strdup(type);
|
||||
it->value = strdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_metadata_update(struct global *global, uint32_t subject, const char *key,
|
||||
const char *type, const char *value)
|
||||
{
|
||||
struct metadata_item *it;
|
||||
int res = 0;
|
||||
pw_log_info("metadata %p: id:%u key:%s value:%s type:%s",
|
||||
global, subject, key, value, type);
|
||||
|
||||
if (key == NULL || value == NULL) {
|
||||
remove_all(global, subject, key);
|
||||
} else {
|
||||
if (type == NULL)
|
||||
type = "";
|
||||
it = find_item(global, subject, key);
|
||||
if (it == NULL) {
|
||||
res = add_item(global, subject, key, type, value);
|
||||
} else {
|
||||
res = replace_item(it, type, value);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int pa_metadata_get(struct global *global, uint32_t subject, const char *key,
|
||||
const char **type, const char **value)
|
||||
{
|
||||
struct metadata_item *it;
|
||||
it = find_item(global, subject, key);
|
||||
if (it == NULL)
|
||||
return 0;
|
||||
if (type)
|
||||
*type = it->type;
|
||||
if (value)
|
||||
*value = it->value;
|
||||
return 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue