diff --git a/pipewire-pulseaudio/src/context.c b/pipewire-pulseaudio/src/context.c index bf8b630a7..ad2557d63 100644 --- a/pipewire-pulseaudio/src/context.c +++ b/pipewire-pulseaudio/src/context.c @@ -23,10 +23,12 @@ #include #include +#include #include #include #include +#include #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; } diff --git a/pipewire-pulseaudio/src/internal.h b/pipewire-pulseaudio/src/internal.h index 17bbab67c..90df30a14 100644 --- a/pipewire-pulseaudio/src/internal.h +++ b/pipewire-pulseaudio/src/internal.h @@ -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 diff --git a/pipewire-pulseaudio/src/introspect.c b/pipewire-pulseaudio/src/introspect.c index 0c9fe714b..73d2c6ffe 100644 --- a/pipewire-pulseaudio/src/introspect.c +++ b/pipewire-pulseaudio/src/introspect.c @@ -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"; diff --git a/pipewire-pulseaudio/src/meson.build b/pipewire-pulseaudio/src/meson.build index 332824f6d..ebc2f04c2 100644 --- a/pipewire-pulseaudio/src/meson.build +++ b/pipewire-pulseaudio/src/meson.build @@ -13,6 +13,7 @@ pipewire_pulseaudio_sources = [ 'json.c', 'mainloop.c', 'mainloop-signal.c', + 'metadata.c', 'operation.c', 'proplist.c', 'rtclock.c', diff --git a/pipewire-pulseaudio/src/metadata.c b/pipewire-pulseaudio/src/metadata.c new file mode 100644 index 000000000..06e952251 --- /dev/null +++ b/pipewire-pulseaudio/src/metadata.c @@ -0,0 +1,120 @@ +/* PipeWire + * Copyright (C) 2020 Wim Taymans + * + * 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 + +#include + +#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; +}