From 8409e8be49446103ccb3f7fdd7577a8f9e495cc6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 4 Nov 2019 17:27:41 +0100 Subject: [PATCH] endpoint: make endpoints of alsa devices Make one endpoint for each device Make a stream for each node --- src/examples/media-session/alsa-endpoint.c | 186 +++++++++++++++++++++ src/examples/media-session/alsa-monitor.c | 16 ++ src/examples/media-session/media-session.c | 89 +++++++++- src/examples/meson.build | 2 +- 4 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 src/examples/media-session/alsa-endpoint.c diff --git a/src/examples/media-session/alsa-endpoint.c b/src/examples/media-session/alsa-endpoint.c new file mode 100644 index 000000000..c100c9c80 --- /dev/null +++ b/src/examples/media-session/alsa-endpoint.c @@ -0,0 +1,186 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pipewire/pipewire.h" +#include "pipewire/private.h" + +static int client_endpoint_set_id(void *object, uint32_t id) +{ + struct alsa_object *obj = object; + + obj->client_endpoint_info.id = id; + obj->client_endpoint_info.name = (char*)pw_properties_get(obj->props, PW_KEY_DEVICE_DESCRIPTION); + obj->client_endpoint_info.media_class = (char*)pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS); + + pw_client_endpoint_proxy_update(obj->client_endpoint, + PW_CLIENT_ENDPOINT_UPDATE_INFO, + 0, NULL, + &obj->client_endpoint_info); + return 0; +} + +static int client_endpoint_set_session_id(void *object, uint32_t id) +{ + struct alsa_object *obj = object; + obj->client_endpoint_info.session_id = id; + return 0; +} + +static int client_endpoint_set_param(void *object, + uint32_t id, uint32_t flags, const struct spa_pod *param) +{ + return -ENOTSUP; +} + + +static int client_endpoint_stream_set_param(void *object, uint32_t stream_id, + uint32_t id, uint32_t flags, const struct spa_pod *param) +{ + return -ENOTSUP; +} + + +static const struct pw_client_endpoint_proxy_events client_endpoint_events = { + PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS, + .set_id = client_endpoint_set_id, + .set_session_id = client_endpoint_set_session_id, + .set_param = client_endpoint_set_param, + .stream_set_param = client_endpoint_stream_set_param, +}; + +static int setup_alsa_fallback_endpoint(struct alsa_object *obj) +{ + struct alsa_node *n; + + spa_list_for_each(n, &obj->node_list, link) { + n->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; + n->info.id = n->id; + n->info.endpoint_id = obj->client_endpoint_info.id; + n->info.name = (char*)pw_properties_get(n->props, PW_KEY_NODE_DESCRIPTION); + n->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS; + n->info.props = &n->props->dict; + + pw_log_debug("stream %d", n->id); + pw_client_endpoint_proxy_stream_update(obj->client_endpoint, + n->id, + PW_CLIENT_ENDPOINT_STREAM_UPDATE_INFO, + 0, NULL, + &n->info); + } + return 0; +} + +static int setup_alsa_ucm_endpoint(struct alsa_object *obj) +{ + const char *str, *card_name = NULL; + char *name_free = NULL; + int i, res, num_verbs; + const char **verb_list = NULL; + + card_name = pw_properties_get(obj->props, SPA_KEY_API_ALSA_CARD_NAME); + if (card_name == NULL && + (str = pw_properties_get(obj->props, SPA_KEY_API_ALSA_CARD)) != NULL) { + snd_card_get_name(atoi(str), &name_free); + card_name = name_free; + pw_log_debug("got card name %s for index %s", card_name, str); + } + if (card_name == NULL) { + res = -ENOTSUP; + goto exit; + } + + if ((res = snd_use_case_mgr_open(&obj->ucm, card_name)) < 0) { + pw_log_error("can not open UCM for %s: %s", card_name, snd_strerror(res)); + goto exit; + } + + num_verbs = snd_use_case_verb_list(obj->ucm, &verb_list); + if (num_verbs < 0) { + res = num_verbs; + pw_log_error("UCM verb list not found for %s: %s", card_name, snd_strerror(num_verbs)); + goto close_exit; + } + + for (i = 0; i < num_verbs; i++) { + pw_log_debug("verb: %s", verb_list[i]); + } + + obj->use_ucm = true; + + snd_use_case_free_list(verb_list, num_verbs); + + return 0; +close_exit: + snd_use_case_mgr_close(obj->ucm); +exit: + obj->ucm = NULL; + free(name_free); + return res; + +} + +static int setup_alsa_endpoint(struct alsa_object *obj) +{ + struct impl *impl = obj->monitor->impl; + int res; + + obj->client_endpoint = pw_core_proxy_create_object(impl->core_proxy, + "client-endpoint", + PW_TYPE_INTERFACE_ClientEndpoint, + PW_VERSION_CLIENT_ENDPOINT_PROXY, + &obj->props->dict, 0); + + obj->client_endpoint_info.version = PW_VERSION_ENDPOINT_INFO; + obj->client_endpoint_info.name = "name"; + obj->client_endpoint_info.media_class = "media-class"; + + pw_client_endpoint_proxy_add_listener(obj->client_endpoint, + &obj->client_endpoint_listener, + &client_endpoint_events, + obj); + + if ((res = setup_alsa_ucm_endpoint(obj)) < 0) + res = setup_alsa_fallback_endpoint(obj); + + return res; +} diff --git a/src/examples/media-session/alsa-monitor.c b/src/examples/media-session/alsa-monitor.c index ac68a9750..4bcb37d02 100644 --- a/src/examples/media-session/alsa-monitor.c +++ b/src/examples/media-session/alsa-monitor.c @@ -30,6 +30,9 @@ #include "config.h" +#include +#include + #include #include #include @@ -57,6 +60,8 @@ struct alsa_node { struct pw_proxy *proxy; struct spa_node *node; + + struct pw_endpoint_stream_info info; }; struct alsa_object { @@ -79,6 +84,13 @@ struct alsa_object { unsigned int first:1; struct spa_list node_list; + + struct pw_client_endpoint_proxy *client_endpoint; + struct spa_hook client_endpoint_listener; + struct pw_endpoint_info client_endpoint_info; + + unsigned int use_ucm:1; + snd_use_case_mgr_t *ucm; }; static struct alsa_node *alsa_find_node(struct alsa_object *obj, uint32_t id) @@ -415,6 +427,7 @@ static void add_jack_timeout(struct impl *impl) value.tv_nsec = 0; pw_loop_update_timer(main_loop, impl->jack_timeout, &value, NULL, false); } + static void reserve_acquired(void *data, struct rd_device *d) { struct alsa_object *obj = data; @@ -426,6 +439,9 @@ static void reserve_acquired(void *data, struct rd_device *d) remove_jack_timeout(impl); set_jack_profile(impl, 0); set_profile(obj, 1); + + setup_alsa_endpoint(obj); + } static void sync_complete_done(void *data, int seq) diff --git a/src/examples/media-session/media-session.c b/src/examples/media-session/media-session.c index 6eb100b5c..1efe6a101 100644 --- a/src/examples/media-session/media-session.c +++ b/src/examples/media-session/media-session.c @@ -39,6 +39,7 @@ #include "pipewire/pipewire.h" #include "pipewire/private.h" +#include "extensions/session-manager.h" #include @@ -89,6 +90,10 @@ struct impl { struct sm_metadata *metadata; + struct pw_client_session_proxy *client_session; + struct spa_hook client_session_listener; + struct pw_session_info client_session_info; + struct spa_dbus *dbus; struct spa_dbus_connection *dbus_connection; DBusConnection *conn; @@ -192,7 +197,12 @@ struct session { bool need_dsp; }; +struct alsa_object; + +static int setup_alsa_endpoint(struct alsa_object *obj); + #include "alsa-monitor.c" +#include "alsa-endpoint.c" #include "v4l2-monitor.c" #include "bluez-monitor.c" #include "metadata.c" @@ -523,8 +533,8 @@ handle_node(struct impl *impl, uint32_t id, node->type = NODE_TYPE_DEVICE; node->manager = sess; - pw_log_debug(NAME" %p: new session for device node %d %d", impl, id, - need_dsp); + pw_log_debug(NAME" %p: new session for device node %d %d prio:%d", impl, id, + need_dsp, sess->priority); } pw_node_proxy_enum_params((struct pw_node_proxy*)p, 0, SPA_PARAM_EnumFormat, @@ -847,8 +857,8 @@ static int find_session(void *data, struct session *sess) return 0; } - pw_log_debug(NAME " %p: found session '%d' %" PRIu64, impl, - sess->id, plugged); + pw_log_debug(NAME " %p: found session '%d' %"PRIu64" prio:%d", impl, + sess->id, plugged, priority); if (find->sess == NULL || priority > find->priority || @@ -1230,6 +1240,64 @@ static const struct pw_core_proxy_events core_events = { .done = core_done }; +static int client_session_set_id(void *object, uint32_t id) +{ + struct impl *impl = object; + + pw_log_debug("got sesssion id:%d", id); + impl->client_session_info.id = id; + + pw_client_session_proxy_update(impl->client_session, + PW_CLIENT_SESSION_UPDATE_INFO, + 0, NULL, + &impl->client_session_info); + return 0; +} + +static int client_session_set_param(void *object, uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *impl = object; + pw_proxy_error((struct pw_proxy*)impl->client_session, + -ENOTSUP, "Session:SetParam not supported"); + return -ENOTSUP; +} + +static int client_session_link_set_param(void *object, uint32_t link_id, uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *impl = object; + pw_proxy_error((struct pw_proxy*)impl->client_session, + -ENOTSUP, "Session:LinkSetParam not supported"); + return -ENOTSUP; +} + +static int client_session_create_link(void *object, const struct spa_dict *props) +{ + return -ENOTSUP; +} + +static int client_session_destroy_link(void *object, uint32_t link_id) +{ + return -ENOTSUP; +} + +static int client_session_link_request_state(void *object, uint32_t link_id, uint32_t state) +{ + return -ENOTSUP; +} + + +static const struct pw_client_session_proxy_events client_session_events = { + PW_VERSION_CLIENT_SESSION_PROXY_METHODS, + .set_id = client_session_set_id, + .set_param = client_session_set_param, + .link_set_param = client_session_link_set_param, + .create_link = client_session_create_link, + .destroy_link = client_session_destroy_link, + .link_request_state = client_session_link_request_state, +}; + static void start_services(struct impl *impl) { const struct spa_support *support; @@ -1253,6 +1321,18 @@ static void start_services(struct impl *impl) impl->metadata, 0); + impl->client_session = pw_core_proxy_create_object(impl->core_proxy, + "client-session", + PW_TYPE_INTERFACE_ClientSession, + PW_VERSION_CLIENT_SESSION_PROXY, + NULL, 0); + impl->client_session_info.version = PW_VERSION_SESSION_INFO; + + pw_client_session_proxy_add_listener(impl->client_session, + &impl->client_session_listener, + &client_session_events, + impl); + bluez5_start_monitor(impl, &impl->bluez5_monitor); alsa_start_monitor(impl, &impl->alsa_monitor); alsa_start_midi_bridge(impl); @@ -1326,6 +1406,7 @@ int main(int argc, char *argv[]) pw_module_load(impl.core, "libpipewire-module-client-device", NULL, NULL); pw_module_load(impl.core, "libpipewire-module-adapter", NULL, NULL); pw_module_load(impl.core, "libpipewire-module-metadata", NULL, NULL); + pw_module_load(impl.core, "libpipewire-module-session-manager", NULL, NULL); clock_gettime(CLOCK_MONOTONIC, &impl.now); diff --git a/src/examples/meson.build b/src/examples/meson.build index 825fcd103..b710f27e6 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -48,7 +48,7 @@ executable('media-session', 'media-session/media-session.c', c_args : [ '-D_GNU_SOURCE' ], install: false, - dependencies : [dbus_dep, pipewire_dep, mathlib], + dependencies : [dbus_dep, pipewire_dep, alsa_dep, mathlib], ) executable('bluez-session',