From e1ef01c8609f780d5b218ab959e1290b28e98b5c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 28 Nov 2019 11:13:53 +0100 Subject: alsa-endpoint: move to separate service Let the session manager monitor device objects as well. Make the alsa-endpoint monitor a separate service instead of letting the alsa-monitor call it directly. This means that it listens for device objects and then tries to configure the endpoints when the device profile is set to active. This does not work yet because we can't link the nodes to the device yet because there is no way to know what the global id is of the device we created. Make sure implementations of objects run in a separate remote connection because the main remote connection might block while waiting for a return value from the implementation. Trigger an object update after all object info is collected. We do this by triggering a roundtrip after receiving the info event. When we get the reply, we can assume all info is flushed. This includes the parameters that we received. --- src/examples/media-session/alsa-endpoint.c | 175 ++++++++++++++------ src/examples/media-session/alsa-monitor.c | 199 +++++++++++------------ src/examples/media-session/media-session.c | 240 ++++++++++++++++++++-------- src/examples/media-session/media-session.h | 24 ++- src/examples/media-session/stream-monitor.c | 2 +- src/examples/meson.build | 1 + 6 files changed, 416 insertions(+), 225 deletions(-) diff --git a/src/examples/media-session/alsa-endpoint.c b/src/examples/media-session/alsa-endpoint.c index 3e45a609..5cd0a652 100644 --- a/src/examples/media-session/alsa-endpoint.c +++ b/src/examples/media-session/alsa-endpoint.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -43,16 +44,21 @@ #include #include "pipewire/pipewire.h" +#include -#undef NAME -#define NAME "alsa-endpoint" +#include "media-session.h" + +#define NAME "alsa-endpoint" +#define SESSION_KEY "alsa-endpoint" struct endpoint { struct spa_list link; + struct impl *impl; + struct pw_properties *props; - struct alsa_node *obj; + struct node *node; struct spa_hook listener; struct pw_client_endpoint_proxy *client_endpoint; @@ -82,6 +88,31 @@ struct stream { unsigned int active:1; }; +struct node { + struct spa_list link; + + struct impl *impl; + struct sm_node *node; + + struct endpoint *endpoint; +}; + +struct device { + struct spa_list link; + + struct impl *impl; + uint32_t id; + struct sm_device *device; + struct spa_hook listener; + + struct spa_list endpoint_list; +}; + +struct impl { + struct sm_media_session *session; + struct spa_hook listener; +}; + static int client_endpoint_set_id(void *object, uint32_t id) { struct endpoint *endpoint = object; @@ -100,9 +131,9 @@ static int client_endpoint_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { struct endpoint *endpoint = object; - struct impl *impl = endpoint->obj->impl; + struct impl *impl = endpoint->impl; pw_log_debug(NAME " %p: endpoint %p set param %d", impl, endpoint, id); - return pw_node_proxy_set_param((struct pw_node_proxy*)endpoint->obj->proxy, + return pw_node_proxy_set_param((struct pw_node_proxy*)endpoint->node->node->obj.proxy, id, flags, param); } @@ -137,7 +168,7 @@ static int stream_set_active(struct endpoint *endpoint, struct stream *stream, b if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) spa_debug_pod(2, NULL, param); - pw_node_proxy_set_param((struct pw_node_proxy*)endpoint->obj->proxy, + pw_node_proxy_set_param((struct pw_node_proxy*)endpoint->node->node->obj.proxy, SPA_PARAM_PortConfig, 0, param); } stream->active = active; @@ -147,7 +178,7 @@ static int stream_set_active(struct endpoint *endpoint, struct stream *stream, b static int client_endpoint_create_link(void *object, const struct spa_dict *props) { struct endpoint *endpoint = object; - struct impl *impl = endpoint->obj->impl; + struct impl *impl = endpoint->impl; struct pw_properties *p; int res; @@ -177,12 +208,12 @@ static int client_endpoint_create_link(void *object, const struct spa_dict *prop goto exit; } - pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->obj->info->id); + pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->node->node->info->id); pw_properties_setf(p, PW_KEY_LINK_OUTPUT_PORT, "-1"); pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)obj->proxy, &p->dict); } else { - pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->obj->info->id); + pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->node->node->info->id); pw_properties_setf(p, PW_KEY_LINK_INPUT_PORT, "-1"); sm_media_session_create_links(impl->session, &p->dict); @@ -253,7 +284,7 @@ static void update_params(void *data) uint32_t n_params; const struct spa_pod **params; struct endpoint *endpoint = data; - struct sm_node *node = endpoint->obj->snode; + struct sm_node *node = endpoint->node->node; struct sm_param *p; pw_log_debug(NAME" %p: endpoint", endpoint); @@ -278,13 +309,13 @@ static void update_params(void *data) &endpoint->info); } -static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *monitor); +static struct endpoint *make_endpoint(struct node *node, struct endpoint *monitor); static void object_update(void *data) { struct endpoint *endpoint = data; - struct impl *impl = endpoint->obj->impl; - struct sm_node *node = endpoint->obj->snode; + struct impl *impl = endpoint->impl; + struct sm_node *node = endpoint->node->node; pw_log_debug(NAME" %p: endpoint %p", impl, endpoint); @@ -305,7 +336,7 @@ static void complete_endpoint(void *data) pw_log_debug("endpoint %p: complete", endpoint); - spa_list_for_each(p, &endpoint->obj->snode->param_list, link) { + spa_list_for_each(p, &endpoint->node->node->param_list, link) { struct spa_audio_info info = { 0, }; if (p->id != SPA_PARAM_EnumFormat) @@ -340,7 +371,7 @@ static void complete_endpoint(void *data) struct endpoint *monitor; /* make monitor for sinks */ - monitor = make_endpoint(endpoint->obj, endpoint); + monitor = make_endpoint(endpoint->node, endpoint); if (monitor == NULL) return; @@ -348,33 +379,34 @@ static void complete_endpoint(void *data) } stream_set_active(endpoint, stream, true); - sm_object_add_listener(&endpoint->obj->snode->obj, &endpoint->listener, &object_events, endpoint); + sm_object_add_listener(&endpoint->node->node->obj, &endpoint->listener, &object_events, endpoint); } -static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *monitor) +static struct endpoint *make_endpoint(struct node *node, struct endpoint *monitor) { - struct impl *impl = obj->impl; + struct impl *impl = node->impl; struct pw_properties *props; struct endpoint *endpoint; struct pw_proxy *proxy; const char *str, *media_class = NULL, *name = NULL; uint32_t subscribe[4], n_subscribe = 0; + struct pw_properties *pr = node->node->obj.props; props = pw_properties_new(NULL, NULL); if (props == NULL) return NULL; - if (obj->props) { - if ((media_class = pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS)) != NULL) { + if (pr) { + if ((media_class = pw_properties_get(pr, PW_KEY_MEDIA_CLASS)) != NULL) { if (monitor != NULL) { pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Source"); } else { pw_properties_set(props, PW_KEY_MEDIA_CLASS, media_class); } } - if ((str = pw_properties_get(obj->props, PW_KEY_PRIORITY_SESSION)) != NULL) + if ((str = pw_properties_get(pr, PW_KEY_PRIORITY_SESSION)) != NULL) pw_properties_set(props, PW_KEY_PRIORITY_SESSION, str); - if ((name = pw_properties_get(obj->props, PW_KEY_NODE_DESCRIPTION)) != NULL) { + if ((name = pw_properties_get(pr, PW_KEY_NODE_DESCRIPTION)) != NULL) { if (monitor != NULL) { pw_properties_setf(props, PW_KEY_ENDPOINT_NAME, "Monitor of %s", monitor->info.name); pw_properties_setf(props, PW_KEY_ENDPOINT_MONITOR, "%d", monitor->info.id); @@ -382,9 +414,7 @@ static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *mo pw_properties_set(props, PW_KEY_ENDPOINT_NAME, name); } } - } - if (obj->object && obj->object->props) { - if ((str = pw_properties_get(obj->object->props, PW_KEY_DEVICE_ICON_NAME)) != NULL) + if ((str = pw_properties_get(pr, PW_KEY_DEVICE_ICON_NAME)) != NULL) pw_properties_set(props, PW_KEY_ENDPOINT_ICON_NAME, str); } @@ -399,7 +429,7 @@ static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *mo } endpoint = pw_proxy_get_user_data(proxy); - endpoint->obj = obj; + endpoint->node = node; endpoint->monitor = monitor; endpoint->props = props; endpoint->client_endpoint = (struct pw_client_endpoint_proxy *) proxy; @@ -407,7 +437,7 @@ static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *mo endpoint->info.name = (char*)pw_properties_get(endpoint->props, PW_KEY_ENDPOINT_NAME); endpoint->info.media_class = (char*)pw_properties_get(endpoint->props, PW_KEY_MEDIA_CLASS); endpoint->info.session_id = impl->session->session->obj.id; - endpoint->info.direction = monitor != NULL ? PW_DIRECTION_OUTPUT : obj->direction; + endpoint->info.direction = monitor != NULL ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT; endpoint->info.flags = 0; endpoint->info.change_mask = PW_ENDPOINT_CHANGE_MASK_STREAMS | @@ -422,7 +452,7 @@ static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *mo endpoint->info.n_params = 2; spa_list_init(&endpoint->stream_list); - pw_log_debug(NAME" %p: new endpoint %p for alsa node %p", impl, endpoint, obj); + pw_log_debug(NAME" %p: new endpoint %p for alsa node %p", impl, endpoint, node); pw_client_endpoint_proxy_add_listener(endpoint->client_endpoint, &endpoint->client_endpoint_listener, @@ -433,8 +463,8 @@ static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *mo subscribe[n_subscribe++] = SPA_PARAM_Props; subscribe[n_subscribe++] = SPA_PARAM_PropInfo; pw_log_debug(NAME" %p: endpoint %p proxy %p subscribe %d params", impl, - endpoint, obj->proxy, n_subscribe); - pw_node_proxy_subscribe_params((struct pw_node_proxy*)obj->proxy, + endpoint, node->node->obj.proxy, n_subscribe); + pw_node_proxy_subscribe_params((struct pw_node_proxy*)node->node->obj.proxy, subscribe, n_subscribe); if (monitor == NULL) @@ -444,15 +474,24 @@ static struct endpoint *make_endpoint(struct alsa_node *obj, struct endpoint *mo } /** fallback, one stream for each node */ -static int setup_alsa_fallback_endpoint(struct alsa_object *obj) +static int setup_alsa_fallback_endpoint(struct device *device) { - struct alsa_node *n; + struct impl *impl = device->impl; + struct sm_node *n; + struct sm_device *d = device->device; - spa_list_for_each(n, &obj->node_list, link) { - struct endpoint *endpoint; + pw_log_debug(NAME" %p: device %p fallback", impl, d); - endpoint = make_endpoint(n, NULL); - if (endpoint == NULL) + spa_list_for_each(n, &d->node_list, link) { + struct node *node; + + pw_log_debug(NAME" %p: device %p has node %p", impl, d, n); + + node = sm_object_add_data(&n->obj, SESSION_KEY, sizeof(struct node)); + node->node = n; + node->impl = impl; + node->endpoint = make_endpoint(node, NULL); + if (node->endpoint == NULL) return -errno; } return 0; @@ -462,22 +501,24 @@ static int setup_alsa_fallback_endpoint(struct alsa_object *obj) * * We create 1 stream for each verb + modifier combination */ -static int setup_alsa_ucm_endpoint(struct alsa_object *obj) +static int setup_alsa_ucm_endpoint(struct device *device) { const char *str, *card_name = NULL; char *name_free = NULL; int i, res, num_verbs; const char **verb_list = NULL; + struct spa_dict *props = device->device->info->props; snd_use_case_mgr_t *ucm; - card_name = pw_properties_get(obj->props, SPA_KEY_API_ALSA_CARD_NAME); + card_name = spa_dict_lookup(props, SPA_KEY_API_ALSA_CARD_NAME); if (card_name == NULL && - (str = pw_properties_get(obj->props, SPA_KEY_API_ALSA_CARD)) != NULL) { + (str = spa_dict_lookup(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) { + pw_log_error("can't get card name for index %s", str); res = -ENOTSUP; goto exit; } @@ -509,24 +550,67 @@ exit: return res; } -static int setup_alsa_endpoint(struct alsa_object *obj) +static int setup_alsa_endpoint(struct device *device) { int res; - if ((res = setup_alsa_ucm_endpoint(obj)) < 0) - res = setup_alsa_fallback_endpoint(obj); + if ((res = setup_alsa_ucm_endpoint(device)) < 0) + res = setup_alsa_fallback_endpoint(device); return res; } -#if 0 +static void device_update(void *data) +{ + struct device *device = data; + struct impl *impl = device->impl; + + pw_log_debug(NAME" %p: device %p %08x %08x", impl, device, + device->device->obj.avail, device->device->obj.changed); + if (!(device->device->obj.avail & SM_DEVICE_CHANGE_MASK_INFO)) + return; + + if (device->device->obj.changed & SM_DEVICE_CHANGE_MASK_PARAMS) { + setup_alsa_endpoint(device); + } +} + +static const struct sm_object_events device_events = { + SM_VERSION_OBJECT_EVENTS, + .update = device_update +}; + static int handle_device(struct impl *impl, struct sm_object *obj) { + const char *media_class, *str; + struct device *device; + + if (obj->props == NULL) + return 0; + + media_class = pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS); + str = pw_properties_get(obj->props, PW_KEY_DEVICE_API); + + pw_log_debug(NAME" %p: device "PW_KEY_MEDIA_CLASS":%s api:%s", impl, media_class, str); + + if (strstr(media_class, "Audio/") != media_class) + return 0; + if (strcmp(str, "alsa") != 0) + return 0; + + device = sm_object_add_data(obj, SESSION_KEY, sizeof(struct device)); + device->impl = impl; + device->id = obj->id; + device->device = (struct sm_device*)obj; + pw_log_debug(NAME" %p: found alsa device %d media_class %s", impl, obj->id, media_class); + + sm_object_add_listener(obj, &device->listener, &device_events, device); + return 0; } -static void session_update(void *data, struct sm_object *object) +static void session_create(void *data, struct sm_object *object) { struct impl *impl = data; int res; @@ -559,7 +643,7 @@ static void session_remove(void *data, struct sm_object *object) static const struct sm_media_session_events session_events = { SM_VERSION_MEDIA_SESSION_EVENTS, - .update = session_update, + .create = session_create, .remove = session_remove, }; @@ -581,4 +665,3 @@ int sm_alsa_endpoint_stop(void *data) { return 0; } -#endif diff --git a/src/examples/media-session/alsa-monitor.c b/src/examples/media-session/alsa-monitor.c index 3b1c93c6..1cb8d919 100644 --- a/src/examples/media-session/alsa-monitor.c +++ b/src/examples/media-session/alsa-monitor.c @@ -52,10 +52,10 @@ #define DEFAULT_JACK_SECONDS 1 -struct alsa_node { +struct node { struct impl *impl; enum pw_direction direction; - struct alsa_object *object; + struct device *object; struct spa_list link; uint32_t id; @@ -64,13 +64,9 @@ struct alsa_node { struct spa_node *node; struct sm_node *snode; - - struct pw_proxy *proxy; - struct spa_hook listener; - struct pw_node_info *info; }; -struct alsa_object { +struct device { struct impl *impl; struct spa_list link; uint32_t id; @@ -102,20 +98,18 @@ struct impl { struct spa_device *monitor; struct spa_hook listener; - struct spa_list object_list; + struct spa_list device_list; struct spa_source *jack_timeout; struct pw_proxy *jack_device; }; -#include "alsa-endpoint.c" - #undef NAME #define NAME "alsa-monitor" -static struct alsa_node *alsa_find_node(struct alsa_object *obj, uint32_t id) +static struct node *alsa_find_node(struct device *obj, uint32_t id) { - struct alsa_node *node; + struct node *node; spa_list_for_each(node, &obj->node_list, link) { if (node->id == id) @@ -124,7 +118,7 @@ static struct alsa_node *alsa_find_node(struct alsa_object *obj, uint32_t id) return NULL; } -static void alsa_update_node(struct alsa_object *obj, struct alsa_node *node, +static void alsa_update_node(struct device *obj, struct node *node, const struct spa_device_object_info *info) { pw_log_debug("update node %u", node->id); @@ -135,22 +129,10 @@ static void alsa_update_node(struct alsa_object *obj, struct alsa_node *node, pw_properties_update(node->props, info->props); } -static void node_event_info(void *object, const struct pw_node_info *info) -{ - struct alsa_node *node = object; - pw_log_debug("node info %d", info->id); - node->info = pw_node_info_update(node->info, info); -} - -static const struct pw_node_proxy_events node_events = { - PW_VERSION_NODE_PROXY_EVENTS, - .info = node_event_info, -}; - -static struct alsa_node *alsa_create_node(struct alsa_object *obj, uint32_t id, +static struct node *alsa_create_node(struct device *obj, uint32_t id, const struct spa_device_object_info *info) { - struct alsa_node *node; + struct node *node; struct impl *impl = obj->impl; int res; const char *dev, *subdev, *stream; @@ -248,13 +230,10 @@ static struct alsa_node *alsa_create_node(struct alsa_object *obj, uint32_t id, "adapter", &node->props->dict, 0); - node->proxy = node->snode->obj.proxy; - if (node->proxy == NULL) { + if (node->snode == NULL) { res = -errno; goto clean_node; } - pw_proxy_add_object_listener(node->proxy, &node->listener, &node_events, node); - spa_list_append(&obj->node_list, &node->link); return node; @@ -267,17 +246,17 @@ exit: return NULL; } -static void alsa_remove_node(struct alsa_object *obj, struct alsa_node *node) +static void alsa_remove_node(struct device *obj, struct node *node) { pw_log_debug("remove node %u", node->id); spa_list_remove(&node->link); - pw_proxy_destroy(node->proxy); + sm_object_destroy(&node->snode->obj); free(node); } static void alsa_device_info(void *data, const struct spa_device_info *info) { - struct alsa_object *obj = data; + struct device *obj = data; const char *str; if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) @@ -292,8 +271,8 @@ static void alsa_device_info(void *data, const struct spa_device_info *info) static void alsa_device_object_info(void *data, uint32_t id, const struct spa_device_object_info *info) { - struct alsa_object *obj = data; - struct alsa_node *node; + struct device *obj = data; + struct node *node; node = alsa_find_node(obj, id); @@ -316,38 +295,38 @@ static const struct spa_device_events alsa_device_events = { .object_info = alsa_device_object_info }; -static struct alsa_object *alsa_find_object(struct impl *impl, uint32_t id) +static struct device *alsa_find_device(struct impl *impl, uint32_t id) { - struct alsa_object *obj; + struct device *obj; - spa_list_for_each(obj, &impl->object_list, link) { + spa_list_for_each(obj, &impl->device_list, link) { if (obj->id == id) return obj; } return NULL; } -static void alsa_update_object(struct impl *impl, struct alsa_object *obj, +static void alsa_update_device(struct impl *impl, struct device *device, const struct spa_device_object_info *info) { - pw_log_debug("update object %u", obj->id); + pw_log_debug("update device %u", device->id); if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) spa_debug_dict(0, info->props); - pw_properties_update(obj->props, info->props); + pw_properties_update(device->props, info->props); } -static int update_device_props(struct alsa_object *obj) +static int update_device_props(struct device *device) { - struct pw_properties *p = obj->props; + struct pw_properties *p = device->props; const char *s, *d; char temp[32]; if ((s = pw_properties_get(p, SPA_KEY_DEVICE_NAME)) == NULL) { if ((s = pw_properties_get(p, SPA_KEY_DEVICE_BUS_ID)) == NULL) { if ((s = pw_properties_get(p, SPA_KEY_DEVICE_BUS_PATH)) == NULL) { - snprintf(temp, sizeof(temp), "%d", obj->id); + snprintf(temp, sizeof(temp), "%d", device->id); s = temp; } } @@ -429,11 +408,11 @@ static void set_jack_profile(struct impl *impl, int index) SPA_PARAM_PROFILE_index, SPA_POD_Int(index))); } -static void set_profile(struct alsa_object *obj, 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)); - spa_device_set_param(obj->device, + spa_device_set_param(device->device, SPA_PARAM_Profile, 0, spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, @@ -472,31 +451,31 @@ static void add_jack_timeout(struct impl *impl) static void reserve_acquired(void *data, struct rd_device *d) { - struct alsa_object *obj = data; - struct impl *impl = obj->impl; + struct device *device = data; + struct impl *impl = device->impl; - pw_log_debug("%p: reserve acquired", obj); + pw_log_debug("%p: reserve acquired", device); remove_jack_timeout(impl); set_jack_profile(impl, 0); - set_profile(obj, 1); + set_profile(device, 1); - setup_alsa_endpoint(obj); +// setup_alsa_endpoint(device); } static void sync_complete_done(void *data, int seq) { - struct alsa_object *obj = data; - struct impl *impl = obj->impl; + struct device *device = data; + struct impl *impl = device->impl; - pw_log_debug("%d %d", obj->seq, seq); - if (seq != obj->seq) + pw_log_debug("%d %d", device->seq, seq); + if (seq != device->seq) return; - spa_hook_remove(&obj->sync_listener); - obj->seq = 0; + spa_hook_remove(&device->sync_listener); + device->seq = 0; - rd_device_complete_release(obj->reserve, true); + rd_device_complete_release(device->reserve, true); add_jack_timeout(impl); } @@ -508,19 +487,19 @@ static const struct pw_proxy_events sync_complete_release = { static void reserve_release(void *data, struct rd_device *d, int forced) { - struct alsa_object *obj = data; - struct impl *impl = obj->impl; + struct device *device = data; + struct impl *impl = device->impl; - pw_log_debug("%p: reserve release", obj); + pw_log_debug("%p: reserve release", device); remove_jack_timeout(impl); - set_profile(obj, 0); + set_profile(device, 0); - if (obj->seq == 0) - pw_proxy_add_listener(obj->proxy, - &obj->sync_listener, - &sync_complete_release, obj); - obj->seq = pw_proxy_sync(obj->proxy, 0); + if (device->seq == 0) + pw_proxy_add_listener(device->proxy, + &device->sync_listener, + &sync_complete_release, device); + device->seq = pw_proxy_sync(device->proxy, 0); } static const struct rd_device_callbacks reserve_callbacks = { @@ -528,17 +507,17 @@ static const struct rd_device_callbacks reserve_callbacks = { .release = reserve_release, }; -static struct alsa_object *alsa_create_object(struct impl *impl, uint32_t id, +static struct device *alsa_create_device(struct impl *impl, uint32_t id, const struct spa_device_object_info *info) { struct pw_core *core = impl->session->core; - struct alsa_object *obj; + struct device *device; struct spa_handle *handle; int res; void *iface; const char *card; - pw_log_debug("new object %u", id); + pw_log_debug("new device %u", id); if (info->type != SPA_TYPE_INTERFACE_Device) { errno = EINVAL; @@ -559,64 +538,64 @@ static struct alsa_object *alsa_create_object(struct impl *impl, uint32_t id, goto unload_handle; } - obj = calloc(1, sizeof(*obj)); - if (obj == NULL) { + device = calloc(1, sizeof(*device)); + if (device == NULL) { res = -errno; goto unload_handle; } - obj->impl = impl; - obj->id = id; - obj->handle = handle; - obj->device = iface; - obj->props = pw_properties_new_dict(info->props); - obj->priority = 1000; - update_device_props(obj); + device->impl = impl; + device->id = id; + device->handle = handle; + device->device = iface; + device->props = pw_properties_new_dict(info->props); + device->priority = 1000; + update_device_props(device); - obj->proxy = sm_media_session_export(impl->session, + device->proxy = sm_media_session_export(impl->session, info->type, - pw_properties_copy(obj->props), - obj->device, 0); - if (obj->proxy == NULL) { + pw_properties_copy(device->props), + device->device, 0); + if (device->proxy == NULL) { res = -errno; - goto clean_object; + goto clean_device; } if ((card = spa_dict_lookup(info->props, SPA_KEY_API_ALSA_CARD)) != NULL) { const char *reserve; - pw_properties_setf(obj->props, "api.dbus.ReserveDevice1", "Audio%s", card); - reserve = pw_properties_get(obj->props, "api.dbus.ReserveDevice1"); + pw_properties_setf(device->props, "api.dbus.ReserveDevice1", "Audio%s", card); + reserve = pw_properties_get(device->props, "api.dbus.ReserveDevice1"); - obj->reserve = rd_device_new(impl->conn, reserve, + device->reserve = rd_device_new(impl->conn, reserve, "PipeWire", 10, - &reserve_callbacks, obj); + &reserve_callbacks, device); - if (obj->reserve == NULL) { + if (device->reserve == NULL) { pw_log_warn("can't create device reserve for %s: %m", reserve); } else { - rd_device_set_application_device_name(obj->reserve, + rd_device_set_application_device_name(device->reserve, spa_dict_lookup(info->props, SPA_KEY_API_ALSA_PATH)); } - obj->priority -= atol(card) * 64; + device->priority -= atol(card) * 64; } /* no device reservation, activate device right now */ - if (obj->reserve == NULL) - set_profile(obj, 1); + if (device->reserve == NULL) + set_profile(device, 1); - obj->first = true; - spa_list_init(&obj->node_list); + device->first = true; + spa_list_init(&device->node_list); - spa_device_add_listener(obj->device, - &obj->device_listener, &alsa_device_events, obj); + spa_device_add_listener(device->device, + &device->device_listener, &alsa_device_events, device); - spa_list_append(&impl->object_list, &obj->link); + spa_list_append(&impl->device_list, &device->link); - return obj; + return device; -clean_object: - free(obj); +clean_device: + free(device); unload_handle: pw_unload_spa_handle(handle); exit: @@ -624,9 +603,9 @@ exit: return NULL; } -static void alsa_remove_object(struct impl *impl, struct alsa_object *obj) +static void alsa_remove_device(struct impl *impl, struct device *obj) { - pw_log_debug("remove object %u", obj->id); + pw_log_debug("remove device %u", obj->id); spa_list_remove(&obj->link); spa_hook_remove(&obj->device_listener); if (obj->seq != 0) @@ -642,19 +621,19 @@ static void alsa_udev_object_info(void *data, uint32_t id, const struct spa_device_object_info *info) { struct impl *impl = data; - struct alsa_object *obj; + struct device *obj; - obj = alsa_find_object(impl, id); + obj = alsa_find_device(impl, id); if (info == NULL) { if (obj == NULL) return; - alsa_remove_object(impl, obj); + alsa_remove_device(impl, obj); } else if (obj == NULL) { - if ((obj = alsa_create_object(impl, id, info)) == NULL) + if ((obj = alsa_create_device(impl, id, info)) == NULL) return; } else { - alsa_update_object(impl, obj, info); + alsa_update_device(impl, obj, info); } } @@ -718,7 +697,7 @@ void *sm_alsa_monitor_start(struct sm_media_session *session) goto out_unload; } impl->monitor = iface; - spa_list_init(&impl->object_list); + spa_list_init(&impl->device_list); spa_device_add_listener(impl->monitor, &impl->listener, &alsa_udev_events, impl); if ((res = alsa_start_jack_device(impl)) < 0) diff --git a/src/examples/media-session/media-session.c b/src/examples/media-session/media-session.c index 0eaa6896..f55f8340 100644 --- a/src/examples/media-session/media-session.c +++ b/src/examples/media-session/media-session.c @@ -63,6 +63,7 @@ void * sm_alsa_midi_start(struct sm_media_session *sess); void * sm_v4l2_monitor_start(struct sm_media_session *sess); void * sm_bluez5_monitor_start(struct sm_media_session *sess); void * sm_alsa_monitor_start(struct sm_media_session *sess); +void * sm_alsa_endpoint_start(struct sm_media_session *sess); int sm_policy_ep_start(struct sm_media_session *sess); /** user data to add to an object */ @@ -92,11 +93,11 @@ struct impl { struct pw_remote *monitor_remote; struct spa_hook monitor_listener; + struct pw_core_proxy *monitor_core; struct pw_remote *policy_remote; struct spa_hook policy_listener; - - struct pw_core_proxy *core_proxy; + struct pw_core_proxy *policy_core; struct spa_hook core_listener; struct pw_registry_proxy *registry_proxy; @@ -215,33 +216,10 @@ int sm_object_remove_data(struct sm_object *obj, const char *id) return 0; } -/** - * Clients - */ -static void client_event_info(void *object, const struct pw_client_info *info) -{ - struct sm_client *client = object; - struct impl *impl = SPA_CONTAINER_OF(client->obj.session, struct impl, this); - - pw_log_debug(NAME" %p: client %d info", impl, client->obj.id); - client->info = pw_client_info_update(client->info, info); - - client->obj.avail |= SM_CLIENT_CHANGE_MASK_INFO; - client->obj.changed |= SM_CLIENT_CHANGE_MASK_INFO; - sm_object_emit_update(&client->obj); - client->obj.changed = 0; -} - -static const struct pw_client_proxy_events client_events = { - PW_VERSION_CLIENT_PROXY_EVENTS, - .info = client_event_info, -}; - -static void client_destroy(void *object) +int sm_object_destroy(struct sm_object *obj) { - struct sm_client *client = object; - if (client->info) - pw_client_info_free(client->info); + pw_proxy_destroy(obj->proxy); + return 0; } static struct param *add_param(struct spa_list *param_list, @@ -285,6 +263,94 @@ static uint32_t clear_params(struct spa_list *param_list, uint32_t id) return count; } +/** + * Clients + */ +static void client_event_info(void *object, const struct pw_client_info *info) +{ + struct sm_client *client = object; + struct impl *impl = SPA_CONTAINER_OF(client->obj.session, struct impl, this); + + pw_log_debug(NAME" %p: client %d info", impl, client->obj.id); + client->info = pw_client_info_update(client->info, info); + + client->obj.avail |= SM_CLIENT_CHANGE_MASK_INFO; + client->obj.changed |= SM_CLIENT_CHANGE_MASK_INFO; + pw_proxy_sync(client->obj.proxy, 1); +} + +static const struct pw_client_proxy_events client_events = { + PW_VERSION_CLIENT_PROXY_EVENTS, + .info = client_event_info, +}; + +static void client_destroy(void *object) +{ + struct sm_client *client = object; + if (client->info) + pw_client_info_free(client->info); +} + +/** + * Device + */ +static void device_event_info(void *object, const struct pw_device_info *info) +{ + struct sm_device *device = object; + struct impl *impl = SPA_CONTAINER_OF(device->obj.session, struct impl, this); + + pw_log_debug(NAME" %p: device %d info", impl, device->obj.id); + device->info = pw_device_info_update(device->info, info); + + device->obj.avail |= SM_DEVICE_CHANGE_MASK_INFO; + device->obj.changed |= SM_DEVICE_CHANGE_MASK_INFO; + + if (info->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { + pw_device_proxy_enum_params((struct pw_device_proxy*)device->obj.proxy, + 1, SPA_PARAM_Profile, 0, UINT32_MAX, NULL); + } + pw_proxy_sync(device->obj.proxy, 1); +} + +static void device_event_param(void *object, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct sm_device *device = object; + struct impl *impl = SPA_CONTAINER_OF(device->obj.session, struct impl, this); + + pw_log_debug(NAME" %p: device %p param %d index:%d", impl, device, id, index); + device->n_params -= clear_params(&device->param_list, id); + + if (add_param(&device->param_list, id, param) != NULL) + device->n_params++; + + device->obj.avail |= SM_DEVICE_CHANGE_MASK_PARAMS; + device->obj.changed |= SM_DEVICE_CHANGE_MASK_PARAMS; +} + +static const struct pw_device_proxy_events device_events = { + PW_VERSION_DEVICE_PROXY_EVENTS, + .info = device_event_info, + .param = device_event_param, +}; + +static void device_destroy(void *object) +{ + struct sm_device *device = object; + struct sm_node *node; + + spa_list_consume(node, &device->node_list, link) { + node->device = NULL; + spa_list_remove(&node->link); + } + clear_params(&device->param_list, SPA_ID_INVALID); + device->n_params = 0; + + if (device->info) + pw_device_info_free(device->info); +} + /** * Node */ @@ -305,8 +371,6 @@ static void node_event_info(void *object, const struct pw_node_info *info) node->obj.avail |= SM_NODE_CHANGE_MASK_INFO; node->obj.changed |= SM_NODE_CHANGE_MASK_INFO; - sm_object_emit_update(&node->obj); - node->obj.changed = 0; if (info->change_mask & PW_NODE_CHANGE_MASK_PARAMS && (node->obj.mask & SM_NODE_CHANGE_MASK_PARAMS) && @@ -332,6 +396,8 @@ static void node_event_info(void *object, const struct pw_node_info *info) node->subscribe = true; } } + node->last_id = SPA_ID_INVALID; + pw_proxy_sync(node->obj.proxy, 1); } static void node_event_param(void *object, int seq, @@ -342,15 +408,18 @@ static void node_event_param(void *object, int seq, struct impl *impl = SPA_CONTAINER_OF(node->obj.session, struct impl, this); pw_log_debug(NAME" %p: node %p param %d index:%d", impl, node, id, index); - node->n_params -= clear_params(&node->param_list, id); + + if (node->last_id != id) { + pw_log_debug(NAME" %p: node %p clear param %d", impl, node, id); + node->n_params -= clear_params(&node->param_list, id); + node->last_id = id; + } if (add_param(&node->param_list, id, param) != NULL) node->n_params++; node->obj.avail |= SM_NODE_CHANGE_MASK_PARAMS; node->obj.changed |= SM_NODE_CHANGE_MASK_PARAMS; - sm_object_emit_update(&node->obj); - node->obj.changed = 0; } static const struct pw_node_proxy_events node_events = { @@ -371,6 +440,10 @@ static void node_destroy(void *object) clear_params(&node->param_list, SPA_ID_INVALID); node->n_params = 0; + if (node->device) { + spa_list_remove(&node->link); + node->device->obj.changed |= SM_DEVICE_CHANGE_MASK_NODES; + } if (node->info) pw_node_info_free(node->info); } @@ -388,8 +461,7 @@ static void port_event_info(void *object, const struct pw_port_info *info) port->obj.avail |= SM_PORT_CHANGE_MASK_INFO; port->obj.changed |= SM_PORT_CHANGE_MASK_INFO; - sm_object_emit_update(&port->obj); - port->obj.changed = 0; + pw_proxy_sync(port->obj.proxy, 1); } static const struct pw_port_proxy_events port_events = { @@ -432,8 +504,7 @@ static void session_event_info(void *object, const struct pw_session_info *info) sess->obj.avail |= SM_SESSION_CHANGE_MASK_INFO; sess->obj.changed |= SM_SESSION_CHANGE_MASK_INFO; - sm_object_emit_update(&sess->obj); - sess->obj.changed = 0; + pw_proxy_sync(sess->obj.proxy, 1); } static const struct pw_session_proxy_events session_events = { @@ -489,8 +560,7 @@ static void endpoint_event_info(void *object, const struct pw_endpoint_info *inf endpoint->obj.avail |= SM_ENDPOINT_CHANGE_MASK_INFO; endpoint->obj.changed |= SM_ENDPOINT_CHANGE_MASK_INFO; - sm_object_emit_update(&endpoint->obj); - endpoint->obj.changed = 0; + pw_proxy_sync(endpoint->obj.proxy, 1); } static const struct pw_endpoint_proxy_events endpoint_events = { @@ -539,8 +609,7 @@ static void endpoint_stream_event_info(void *object, const struct pw_endpoint_st stream->obj.avail |= SM_ENDPOINT_CHANGE_MASK_INFO; stream->obj.changed |= SM_ENDPOINT_CHANGE_MASK_INFO; - sm_object_emit_update(&stream->obj); - stream->obj.changed = 0; + pw_proxy_sync(stream->obj.proxy, 1); } static const struct pw_endpoint_stream_proxy_events endpoint_stream_events = { @@ -584,8 +653,7 @@ static void endpoint_link_event_info(void *object, const struct pw_endpoint_link link->obj.avail |= SM_ENDPOINT_LINK_CHANGE_MASK_INFO; link->obj.changed |= SM_ENDPOINT_LINK_CHANGE_MASK_INFO; - sm_object_emit_update(&link->obj); - link->obj.changed = 0; + pw_proxy_sync(link->obj.proxy, 1); } static const struct pw_endpoint_link_proxy_events endpoint_link_events = { @@ -615,7 +683,7 @@ static void endpoint_link_destroy(void *object) * Proxy */ static void -destroy_proxy (void *data) +destroy_proxy(void *data) { struct sm_object *obj = data; struct impl *impl = SPA_CONTAINER_OF(obj->session, struct impl, this); @@ -626,9 +694,21 @@ destroy_proxy (void *data) obj->destroy(obj); } +static void done_proxy(void *data, int seq) +{ + struct sm_object *obj = data; + pw_log_debug("done %p proxy %p avail:%08x update:%08x", obj, + obj->proxy, obj->avail, obj->changed); + if (obj->changed) { + sm_object_emit_update(obj); + } + obj->changed = 0; +} + static const struct pw_proxy_events proxy_events = { PW_VERSION_PROXY_EVENTS, .destroy = destroy_proxy, + .done = done_proxy, }; static void @@ -656,6 +736,13 @@ init_object(struct impl *impl, struct sm_object *obj, uint32_t id, user_data_size = sizeof(struct sm_client); break; + case PW_TYPE_INTERFACE_Device: + events = &device_events; + client_version = PW_VERSION_DEVICE_PROXY; + destroy = (pw_destroy_t) device_destroy; + user_data_size = sizeof(struct sm_device); + break; + case PW_TYPE_INTERFACE_Node: events = &node_events; client_version = PW_VERSION_NODE_PROXY; @@ -728,11 +815,29 @@ init_object(struct impl *impl, struct sm_object *obj, uint32_t id, pw_proxy_add_listener(proxy, &obj->proxy_listener, &proxy_events, obj); switch (type) { + case PW_TYPE_INTERFACE_Device: + { + struct sm_device *device = (struct sm_device*) obj; + spa_list_init(&device->node_list); + spa_list_init(&device->param_list); + break; + } + case PW_TYPE_INTERFACE_Node: { struct sm_node *node = (struct sm_node*) obj; spa_list_init(&node->port_list); spa_list_init(&node->param_list); + + if (props) { + if ((str = spa_dict_lookup(props, PW_KEY_DEVICE_ID)) != NULL) + node->device = find_object(impl, atoi(str)); + pw_log_debug(NAME" %p: node %d parent device %s", impl, id, str); + if (node->device) { + spa_list_append(&node->device->node_list, &node->link); + node->device->obj.changed |= SM_DEVICE_CHANGE_MASK_NODES; + } + } break; } case PW_TYPE_INTERFACE_Port: @@ -796,6 +901,7 @@ init_object(struct impl *impl, struct sm_object *obj, uint32_t id, break; } + pw_log_debug(NAME" %p: created new object %p id:%d", impl, obj, obj->id); sm_media_session_emit_create(impl, obj); pw_proxy_add_object_listener(proxy, &obj->object_listener, events, obj); @@ -856,8 +962,8 @@ struct sm_object *sm_media_session_find_object(struct sm_media_session *sess, ui int sm_media_session_schedule_rescan(struct sm_media_session *sess) { struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this); - if (impl->core_proxy) - impl->rescan_seq = pw_core_proxy_sync(impl->core_proxy, 0, impl->last_seq); + if (impl->policy_core) + impl->rescan_seq = pw_core_proxy_sync(impl->policy_core, 0, impl->last_seq); return impl->rescan_seq; } @@ -874,7 +980,7 @@ int sm_media_session_sync(struct sm_media_session *sess, spa_list_append(&impl->sync_list, &sync->link); sync->callback = callback; sync->data = data; - sync->seq = pw_core_proxy_sync(impl->core_proxy, 0, impl->last_seq); + sync->seq = pw_core_proxy_sync(impl->policy_core, 0, impl->last_seq); return sync->seq; } @@ -890,7 +996,7 @@ int sm_media_session_roundtrip(struct sm_media_session *sess) struct pw_loop *loop = impl->this.loop; int done, res; - if (impl->core_proxy == NULL) + if (impl->policy_core == NULL) return -EIO; done = 0; @@ -948,7 +1054,7 @@ struct pw_proxy *sm_media_session_create_object(struct sm_media_session *sess, const struct spa_dict *props, size_t user_data_size) { struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this); - return pw_core_proxy_create_object(impl->core_proxy, + return pw_core_proxy_create_object(impl->policy_core, factory_name, type, version, props, user_data_size); } @@ -962,7 +1068,7 @@ struct sm_node *sm_media_session_create_node(struct sm_media_session *sess, pw_log_debug(NAME " %p: node '%s'", impl, factory_name); - proxy = pw_core_proxy_create_object(impl->core_proxy, + proxy = pw_core_proxy_create_object(impl->policy_core, factory_name, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE_PROXY, @@ -1042,7 +1148,7 @@ static int link_nodes(struct impl *impl, struct endpoint_link *link, pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", outport->obj.id); pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", inport->obj.id); - p = pw_core_proxy_create_object(impl->core_proxy, + p = pw_core_proxy_create_object(impl->policy_core, "link-factory", PW_TYPE_INTERFACE_Link, PW_VERSION_LINK_PROXY, @@ -1195,6 +1301,7 @@ static int client_session_set_id(void *object, uint32_t id) sm_alsa_midi_start(&impl->this); sm_bluez5_monitor_start(&impl->this); sm_alsa_monitor_start(&impl->this); + sm_alsa_endpoint_start(&impl->this); sm_v4l2_monitor_start(&impl->this); sm_stream_monitor_start(&impl->this); return 0; @@ -1233,7 +1340,7 @@ static const struct pw_client_session_proxy_events client_session_events = { static int start_session(struct impl *impl) { - impl->client_session = pw_core_proxy_create_object(impl->core_proxy, + impl->client_session = pw_core_proxy_create_object(impl->monitor_core, "client-session", PW_TYPE_INTERFACE_ClientSession, PW_VERSION_CLIENT_SESSION_PROXY, @@ -1271,11 +1378,6 @@ static void core_done(void *data, uint32_t id, int seq) } } -static const struct pw_core_proxy_events core_events = { - PW_VERSION_CORE_EVENTS, - .done = core_done -}; - static void on_monitor_state_changed(void *_data, enum pw_remote_state old, enum pw_remote_state state, const char *error) { @@ -1289,15 +1391,7 @@ static void on_monitor_state_changed(void *_data, enum pw_remote_state old, case PW_REMOTE_STATE_CONNECTED: pw_log_info(NAME" %p: connected", impl); - impl->core_proxy = pw_remote_get_core_proxy(impl->monitor_remote); - pw_core_proxy_add_listener(impl->core_proxy, - &impl->core_listener, - &core_events, impl); - impl->registry_proxy = pw_core_proxy_get_registry(impl->core_proxy, - PW_VERSION_REGISTRY_PROXY, 0); - pw_registry_proxy_add_listener(impl->registry_proxy, - &impl->registry_listener, - ®istry_events, impl); + impl->monitor_core = pw_remote_get_core_proxy(impl->monitor_remote); start_session(impl); break; @@ -1317,6 +1411,11 @@ static const struct pw_remote_events monitor_remote_events = { .state_changed = on_monitor_state_changed, }; +static const struct pw_core_proxy_events core_events = { + PW_VERSION_CORE_EVENTS, + .done = core_done +}; + static void on_policy_state_changed(void *_data, enum pw_remote_state old, enum pw_remote_state state, const char *error) { @@ -1330,6 +1429,15 @@ static void on_policy_state_changed(void *_data, enum pw_remote_state old, case PW_REMOTE_STATE_CONNECTED: pw_log_info(NAME" %p: connected", impl); + impl->policy_core = pw_remote_get_core_proxy(impl->policy_remote); + pw_core_proxy_add_listener(impl->policy_core, + &impl->core_listener, + &core_events, impl); + impl->registry_proxy = pw_core_proxy_get_registry(impl->policy_core, + PW_VERSION_REGISTRY_PROXY, 0); + pw_registry_proxy_add_listener(impl->registry_proxy, + &impl->registry_listener, + ®istry_events, impl); start_policy(impl); break; diff --git a/src/examples/media-session/media-session.h b/src/examples/media-session/media-session.h index e7369bb2..84de6078 100644 --- a/src/examples/media-session/media-session.h +++ b/src/examples/media-session/media-session.h @@ -79,6 +79,8 @@ void *sm_object_add_data(struct sm_object *obj, const char *id, size_t size); void *sm_object_get_data(struct sm_object *obj, const char *id); int sm_object_remove_data(struct sm_object *obj, const char *id); +int sm_object_destroy(struct sm_object *obj); + struct sm_client { struct sm_object obj; @@ -87,14 +89,32 @@ struct sm_client { struct pw_client_info *info; }; +struct sm_device { + struct sm_object obj; + + unsigned int subscribe:1; /**< if we subscribed to param changes */ + +#define SM_DEVICE_CHANGE_MASK_INFO (SM_OBJECT_CHANGE_MASK_LAST<<0) +#define SM_DEVICE_CHANGE_MASK_PARAMS (SM_OBJECT_CHANGE_MASK_LAST<<1) +#define SM_DEVICE_CHANGE_MASK_NODES (SM_OBJECT_CHANGE_MASK_LAST<<2) + uint32_t n_params; + struct spa_list param_list; /**< list of sm_param */ + struct pw_device_info *info; + struct spa_list node_list; +}; + + struct sm_node { struct sm_object obj; + struct sm_device *device; /**< optional device */ + struct spa_list link; /**< link in device node_list */ unsigned int subscribe:1; /**< if we subscribed to param changes */ + uint32_t last_id; #define SM_NODE_CHANGE_MASK_INFO (SM_OBJECT_CHANGE_MASK_LAST<<0) -#define SM_NODE_CHANGE_MASK_PORTS (SM_OBJECT_CHANGE_MASK_LAST<<1) -#define SM_NODE_CHANGE_MASK_PARAMS (SM_OBJECT_CHANGE_MASK_LAST<<2) +#define SM_NODE_CHANGE_MASK_PARAMS (SM_OBJECT_CHANGE_MASK_LAST<<1) +#define SM_NODE_CHANGE_MASK_PORTS (SM_OBJECT_CHANGE_MASK_LAST<<2) uint32_t n_params; struct spa_list param_list; /**< list of sm_param */ struct pw_node_info *info; diff --git a/src/examples/media-session/stream-monitor.c b/src/examples/media-session/stream-monitor.c index ed957d8c..01a5a171 100644 --- a/src/examples/media-session/stream-monitor.c +++ b/src/examples/media-session/stream-monitor.c @@ -406,7 +406,7 @@ static struct endpoint *make_endpoint(struct node *node) subscribe[n_subscribe++] = SPA_PARAM_Props; subscribe[n_subscribe++] = SPA_PARAM_PropInfo; pw_log_debug(NAME" %p: node %p proxy %p subscribe %d params", impl, - node, node->obj->obj.proxy, n_subscribe); + node->obj, node->obj->obj.proxy, n_subscribe); pw_node_proxy_subscribe_params((struct pw_node_proxy*)node->obj->obj.proxy, subscribe, n_subscribe); diff --git a/src/examples/meson.build b/src/examples/meson.build index f95f3c5f..86ca8618 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -47,6 +47,7 @@ executable('export-spa-device', executable('media-session', 'media-session/alsa-midi.c', 'media-session/alsa-monitor.c', + 'media-session/alsa-endpoint.c', 'media-session/bluez-monitor.c', 'media-session/media-session.c', 'media-session/metadata.c', -- cgit v1.2.3