From 3f3dfbc67e2dc41569365321f5d81a9c4ae8d37d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 13 Nov 2019 09:38:40 +0100 Subject: [PATCH] More session manager work pulseaudio card is mapped to device pulseaudio sink/source is mapped to an endpoint prepare to map streams to card profiles Add Route param to implement the endpoint routing later (ports) Create an alsa endpoint for each device Create one stream for each endpoint (Playback/Capture) Implement create_link on the endpoint. The idea is to call create link on the peer endpoint to complete the link. Remove create_link on the session. Add stream-monitor to turn pw_stream nodes into endpoints Add a policy manager that tries to link endpoints Use enum pw_direction for the endpoint direction. We can use the media_class to determine if this is a pw_stream or not but it should not really matter, you can link any output to any input. Add autoconnect property for endpoints to make the policy connect. --- pipewire-pulseaudio/src/context.c | 220 ++-- pipewire-pulseaudio/src/internal.h | 11 +- pipewire-pulseaudio/src/introspect.c | 109 +- pipewire-pulseaudio/src/stream.c | 2 +- spa/include/spa/param/param.h | 20 + spa/include/spa/param/type-info.h | 24 + spa/include/spa/utils/type-info.h | 1 + spa/include/spa/utils/type.h | 1 + src/examples/media-session/alsa-endpoint.c | 266 +++-- src/examples/media-session/alsa-monitor.c | 25 +- src/examples/media-session/media-session.c | 4 +- src/examples/media-session/monitor.c | 31 +- src/examples/media-session/policy-ep.c | 1003 +++++++++++++++++ src/examples/media-session/policy.c | 10 +- src/examples/media-session/stream-monitor.c | 756 +++++++++++++ src/examples/meson.build | 2 + .../session-manager/impl-interfaces.h | 19 +- src/extensions/session-manager/interfaces.h | 18 +- src/extensions/session-manager/introspect.h | 9 +- src/extensions/session-manager/keys.h | 2 + .../module-session-manager/client-endpoint.c | 1 - .../module-session-manager/client-endpoint.h | 2 + .../module-session-manager/endpoint-link.c | 13 - src/modules/module-session-manager/endpoint.c | 16 + .../module-session-manager/protocol-native.c | 187 ++- src/modules/module-session-manager/session.c | 13 - src/tools/pipewire-cli.c | 12 +- 27 files changed, 2338 insertions(+), 439 deletions(-) create mode 100644 src/examples/media-session/policy-ep.c create mode 100644 src/examples/media-session/stream-monitor.c diff --git a/pipewire-pulseaudio/src/context.c b/pipewire-pulseaudio/src/context.c index 2b9b3fc90..aee46935e 100644 --- a/pipewire-pulseaudio/src/context.c +++ b/pipewire-pulseaudio/src/context.c @@ -150,21 +150,21 @@ struct global *pa_context_find_linked(pa_context *c, uint32_t idx) struct global *g, *f; spa_list_for_each(g, &c->globals, link) { - uint32_t src_node_id, dst_node_id; + uint32_t src_endpoint_id, dst_endpoint_id; if (g->type != PW_TYPE_INTERFACE_Link) continue; - src_node_id = g->link_info.src->stream_info.endpoint_id; - dst_node_id = g->link_info.dst->stream_info.endpoint_id; + src_endpoint_id = g->link_info.src->stream_info.endpoint_id; + dst_endpoint_id = g->link_info.dst->stream_info.endpoint_id; pw_log_debug("context %p: %p %d %d %d", c, g, idx, - src_node_id, dst_node_id); + src_endpoint_id, dst_endpoint_id); - if (src_node_id == idx) - f = pa_context_find_global(c, dst_node_id); - else if (dst_node_id == idx) - f = pa_context_find_global(c, src_node_id); + if (src_endpoint_id == idx) + f = pa_context_find_global(c, dst_endpoint_id); + else if (dst_endpoint_id == idx) + f = pa_context_find_global(c, src_endpoint_id); else continue; @@ -185,67 +185,65 @@ static void emit_event(pa_context *c, struct global *g, pa_subscription_event_ty c->subscribe_userdata); if (g->mask == (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE)) { - pw_log_debug("context %p: obj %d: emit %d:%d", c, g->stream_info.monitor, + pw_log_debug("context %p: obj %d: emit %d:%d", c, g->endpoint_info.monitor, event, PA_SUBSCRIPTION_EVENT_SOURCE); c->subscribe_callback(c, event | PA_SUBSCRIPTION_EVENT_SOURCE, - g->stream_info.monitor, + g->endpoint_info.monitor, c->subscribe_userdata); } } } -static void update_endpoint_props(struct global *g) +static void update_device_props(struct global *g) { pa_card_info *i = &g->card_info.info; const char *s; - if ((s = pa_proplist_gets(i->proplist, PW_KEY_ENDPOINT_ICON_NAME))) + if ((s = pa_proplist_gets(i->proplist, PW_KEY_DEVICE_ICON_NAME))) pa_proplist_sets(i->proplist, PA_PROP_DEVICE_ICON_NAME, s); } -static void endpoint_event_info(void *object, const struct pw_endpoint_info *update) +static void device_event_info(void *object, const struct pw_device_info *update) { struct global *g = object; pa_card_info *i = &g->card_info.info; const char *str; uint32_t n; - struct pw_endpoint_info *info = g->info; + struct pw_device_info *info; + + pw_log_debug("global %p: id:%d change-mask:%08lx", g, g->id, update->change_mask); + info = g->info = pw_device_info_update(g->info, update); i->index = g->id; + i->name = info->props ? + spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME) : "unknown"; + str = info->props ? spa_dict_lookup(info->props, PW_KEY_MODULE_ID) : NULL; + i->owner_module = str ? (unsigned)atoi(str) : SPA_ID_INVALID; + if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS) { + i->driver = info->props ? + spa_dict_lookup(info->props, PW_KEY_DEVICE_API) : NULL; - pw_log_debug("global %p: id:%d change-mask:%08x", g, g->id, update->change_mask); - if (!info) { - info = g->info = calloc(1, sizeof(*info)); - info->id = update->id; - info->name = update->name ? strdup(update->name) : NULL; - info->media_class = update->media_class ? strdup(update->media_class) : NULL; - info->direction = update->direction; - info->flags = update->flags; + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else { + i->proplist = pa_proplist_new_dict(info->props); + } + update_device_props(g); } info->change_mask = update->change_mask; - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) - info->n_streams = update->n_streams; - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) - info->session_id = update->session_id; - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { - info->n_params = update->n_params; - free(info->params); - info->params = malloc(info->n_params * sizeof(struct spa_param_info)); - memcpy(info->params, update->params, - info->n_params * sizeof(struct spa_param_info)); - + if (update->change_mask & PW_DEVICE_CHANGE_MASK_PARAMS) { for (n = 0; n < info->n_params; n++) { if (!(info->params[n].flags & SPA_PARAM_INFO_READ)) continue; switch (info->params[n].id) { case SPA_PARAM_EnumProfile: - pw_endpoint_proxy_enum_params((struct pw_endpoint_proxy*)g->proxy, + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, 0, SPA_PARAM_EnumProfile, 0, -1, NULL); break; case SPA_PARAM_Profile: - pw_endpoint_proxy_enum_params((struct pw_endpoint_proxy*)g->proxy, + pw_device_proxy_enum_params((struct pw_device_proxy*)g->proxy, 0, SPA_PARAM_Profile, 0, -1, NULL); break; default: @@ -253,31 +251,10 @@ static void endpoint_event_info(void *object, const struct pw_endpoint_info *upd } } } - if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { - if (info->props) - pw_properties_free ((struct pw_properties *)info->props); - info->props = - (struct spa_dict *) pw_properties_new_dict (update->props); - - i->name = info->props ? - spa_dict_lookup(info->props, PW_KEY_ENDPOINT_NAME) : "unknown"; - str = info->props ? spa_dict_lookup(info->props, PW_KEY_MODULE_ID) : NULL; - i->owner_module = str ? (unsigned)atoi(str) : SPA_ID_INVALID; - i->driver = info->props ? - spa_dict_lookup(info->props, PW_KEY_DEVICE_API) : "unknown"; - - if (i->proplist) - pa_proplist_update_dict(i->proplist, info->props); - else { - i->proplist = pa_proplist_new_dict(info->props); - } - update_endpoint_props(g); - } - g->pending_seq = pw_proxy_sync(g->proxy, 0); } -static void endpoint_event_param(void *object, int seq, +static void device_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { @@ -327,13 +304,13 @@ static void endpoint_event_param(void *object, int seq, } } -static const struct pw_endpoint_proxy_events endpoint_events = { +static const struct pw_device_proxy_events device_events = { PW_VERSION_ENDPOINT_PROXY_EVENTS, - .info = endpoint_event_info, - .param = endpoint_event_param, + .info = device_event_info, + .param = device_event_param, }; -static void endpoint_destroy(void *data) +static void device_destroy(void *data) { struct global *global = data; struct param *p; @@ -344,33 +321,32 @@ static void endpoint_destroy(void *data) spa_list_remove(&p->link); free(p); } - if (global->info) { - struct pw_endpoint_info *info = global->info; - free(info->name); - free(info->media_class); - free(info->params); - if (info->props) - pw_properties_free ((struct pw_properties *)info->props); - free(info); - } + if (global->info) + pw_device_info_free(global->info); } -static void stream_event_info(void *object, const struct pw_endpoint_stream_info *update) +static void endpoint_event_info(void *object, const struct pw_endpoint_info *update) { struct global *g = object; - struct pw_endpoint_stream_info *info = g->info; + struct pw_endpoint_info *info = g->info; uint32_t i; pw_log_debug("update %d %08x", g->id, update->change_mask); if (info == NULL) { info = g->info = calloc(1, sizeof(*info)); info->id = update->id; - info->endpoint_id = update->endpoint_id; info->name = update->name ? strdup(update->name) : NULL; + info->media_class = update->media_class ? strdup(update->media_class) : NULL; + info->direction = update->direction; + info->flags = update->flags; } info->change_mask = update->change_mask; - if (update->change_mask & PW_NODE_CHANGE_MASK_PARAMS && !g->subscribed) { + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) + info->n_streams = update->n_streams; + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) + info->session_id = update->session_id; + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS && !g->subscribed) { uint32_t subscribed[32], n_subscribed = 0; info->n_params = update->n_params; @@ -381,7 +357,7 @@ static void stream_event_info(void *object, const struct pw_endpoint_stream_info for (i = 0; i < info->n_params; i++) { switch (info->params[i].id) { - case SPA_PARAM_EnumFormat: + case SPA_PARAM_EnumRoute: case SPA_PARAM_Props: subscribed[n_subscribed++] = info->params[i].id; break; @@ -390,16 +366,31 @@ static void stream_event_info(void *object, const struct pw_endpoint_stream_info } } if (n_subscribed > 0) { - pw_endpoint_stream_proxy_subscribe_params((struct pw_endpoint_stream_proxy*)g->proxy, + pw_endpoint_proxy_subscribe_params((struct pw_endpoint_proxy*)g->proxy, subscribed, n_subscribed); g->subscribed = true; } } - if (update->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) { + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { if (info->props) pw_properties_free ((struct pw_properties *)info->props); info->props = (struct spa_dict *) pw_properties_new_dict (update->props); + +#if 0 + i->name = info->props ? + spa_dict_lookup(info->props, PW_KEY_ENDPOINT_NAME) : "unknown"; + str = info->props ? spa_dict_lookup(info->props, PW_KEY_MODULE_ID) : NULL; + i->owner_module = str ? (unsigned)atoi(str) : SPA_ID_INVALID; + i->driver = info->props ? + spa_dict_lookup(info->props, PW_KEY_DEVICE_API) : "unknown"; + + if (i->proplist) + pa_proplist_update_dict(i->proplist, info->props); + else { + i->proplist = pa_proplist_new_dict(info->props); + } +#endif } g->pending_seq = pw_proxy_sync(g->proxy, 0); } @@ -412,22 +403,22 @@ static void parse_props(struct global *g, const struct spa_pod *param) SPA_POD_OBJECT_FOREACH(obj, prop) { switch (prop->key) { case SPA_PROP_volume: - spa_pod_get_float(&prop->value, &g->stream_info.volume); + spa_pod_get_float(&prop->value, &g->endpoint_info.volume); break; case SPA_PROP_mute: - spa_pod_get_bool(&prop->value, &g->stream_info.mute); + spa_pod_get_bool(&prop->value, &g->endpoint_info.mute); break; case SPA_PROP_channelVolumes: { uint32_t n_vals; n_vals = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - g->stream_info.channel_volumes, SPA_AUDIO_MAX_CHANNELS); + g->endpoint_info.channel_volumes, SPA_AUDIO_MAX_CHANNELS); - if (n_vals != g->stream_info.n_channel_volumes) { + if (n_vals != g->endpoint_info.n_channel_volumes) { emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_REMOVE); emit_event(g->context, g, PA_SUBSCRIPTION_EVENT_NEW); - g->stream_info.n_channel_volumes = n_vals; + g->endpoint_info.n_channel_volumes = n_vals; } break; } @@ -437,7 +428,12 @@ static void parse_props(struct global *g, const struct spa_pod *param) } } -static void stream_event_param(void *object, int seq, +/* routing information on the endpoint is mapped to sink/source ports. */ +static void parse_route(struct global *g, const struct spa_pod *param) +{ +} + +static void endpoint_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) { @@ -448,22 +444,25 @@ static void stream_event_param(void *object, int seq, case SPA_PARAM_Props: parse_props(g, param); break; + case SPA_PARAM_EnumRoute: + parse_route(g, param); + break; default: break; } } -static const struct pw_endpoint_stream_proxy_events stream_events = { +static const struct pw_endpoint_proxy_events endpoint_events = { PW_VERSION_NODE_PROXY_EVENTS, - .info = stream_event_info, - .param = stream_event_param, + .info = endpoint_event_info, + .param = endpoint_event_param, }; -static void stream_destroy(void *data) +static void endpoint_destroy(void *data) { struct global *global = data; if (global->info) { - struct pw_endpoint_stream_info *info = global->info; + struct pw_endpoint_info *info = global->info; free(info->name); free(info->params); if (info->props) @@ -588,7 +587,7 @@ static int set_mask(pa_context *c, struct global *g) uint32_t client_version; switch (g->type) { - case PW_TYPE_INTERFACE_Endpoint: + case PW_TYPE_INTERFACE_Device: if (g->props == NULL) return 0; if ((str = pw_properties_get(g->props, PW_KEY_MEDIA_CLASS)) == NULL) @@ -596,17 +595,18 @@ static int set_mask(pa_context *c, struct global *g) if (strcmp(str, "Audio/Device") != 0) return 0; + /* devices are turned into card objects */ pw_log_debug("found card %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_CARD; g->event = PA_SUBSCRIPTION_EVENT_CARD; - events = &endpoint_events; - client_version = PW_VERSION_ENDPOINT_PROXY; - destroy = endpoint_destroy; + events = &device_events; + client_version = PW_VERSION_DEVICE_PROXY; + destroy = device_destroy; spa_list_init(&g->card_info.profiles); break; - case PW_TYPE_INTERFACE_EndpointStream: + case PW_TYPE_INTERFACE_Endpoint: if (g->props == NULL) return 0; @@ -614,15 +614,16 @@ static int set_mask(pa_context *c, struct global *g) g->priority_session = pw_properties_parse_int(str); if ((str = pw_properties_get(g->props, PW_KEY_MEDIA_CLASS)) == NULL) { - pw_log_warn("node %d without "PW_KEY_MEDIA_CLASS, g->id); + pw_log_warn("endpoint %d without "PW_KEY_MEDIA_CLASS, g->id); return 0; } + /* endpoints get transformed into sink/source or sink_input/source_output */ if (strcmp(str, "Audio/Sink") == 0) { pw_log_debug("found sink %d", g->id); g->mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; g->event = PA_SUBSCRIPTION_EVENT_SINK; - g->stream_info.monitor = g->id | PA_IDX_FLAG_DSP; + g->endpoint_info.monitor = g->id | PA_IDX_FLAG_DSP; } else if (strcmp(str, "Audio/Source") == 0) { pw_log_debug("found source %d", g->id); @@ -641,15 +642,28 @@ static int set_mask(pa_context *c, struct global *g) } if ((str = pw_properties_get(g->props, PW_KEY_CLIENT_ID)) != NULL) - g->stream_info.client_id = atoi(str); - if ((str = pw_properties_get(g->props, PW_KEY_ENDPOINT_ID)) != NULL) - g->stream_info.endpoint_id = atoi(str); + g->endpoint_info.client_id = atoi(str); + if ((str = pw_properties_get(g->props, PW_KEY_DEVICE_ID)) != NULL) + g->endpoint_info.device_id = atoi(str); - events = &stream_events; - client_version = PW_VERSION_ENDPOINT_STREAM_PROXY; - destroy = stream_destroy; - g->stream_info.volume = 1.0; - g->stream_info.mute = false; + events = &endpoint_events; + client_version = PW_VERSION_ENDPOINT_PROXY; + destroy = endpoint_destroy; + g->endpoint_info.volume = 1.0; + g->endpoint_info.mute = false; + break; + + case PW_TYPE_INTERFACE_EndpointStream: + if (g->props == NULL) + return 0; + + if ((str = pw_properties_get(g->props, PW_KEY_ENDPOINT_ID)) == NULL) { + pw_log_warn("endpoint stream %d without "PW_KEY_ENDPOINT_ID, g->id); + return 0; + } + + /* streams get transformed into profiles on the device */ + pw_log_debug("found endpoint stream %d", g->id); break; case PW_TYPE_INTERFACE_Module: diff --git a/pipewire-pulseaudio/src/internal.h b/pipewire-pulseaudio/src/internal.h index f392eb764..74873616c 100644 --- a/pipewire-pulseaudio/src/internal.h +++ b/pipewire-pulseaudio/src/internal.h @@ -250,7 +250,10 @@ struct global { struct global *src; struct global *dst; } link_info; - /* for endpoint stream */ + struct { + uint32_t endpoint_id; + } stream_info; + /* for endpoints */ struct { uint32_t client_id; uint32_t monitor; @@ -258,9 +261,9 @@ struct global { bool mute; uint32_t n_channel_volumes; float channel_volumes[SPA_AUDIO_MAX_CHANNELS]; - uint32_t endpoint_id; - } stream_info; - /* for endpoints */ + uint32_t device_id; + } endpoint_info; + /* for devices */ struct { struct spa_list profiles; uint32_t n_profiles; diff --git a/pipewire-pulseaudio/src/introspect.c b/pipewire-pulseaudio/src/introspect.c index d20bf8534..64ea2b542 100644 --- a/pipewire-pulseaudio/src/introspect.c +++ b/pipewire-pulseaudio/src/introspect.c @@ -90,7 +90,7 @@ static int wait_globals(pa_context *c, pa_subscription_mask_t mask, pa_operation static void sink_callback(struct sink_data *d) { struct global *g = d->global; - struct pw_endpoint_stream_info *info = g->info; + struct pw_endpoint_info *info = g->info; const char *str; uint32_t n; pa_sink_info i; @@ -98,30 +98,30 @@ static void sink_callback(struct sink_data *d) pa_format_info *ip[1]; spa_zero(i); - if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_NAME))) + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_ENDPOINT_NAME))) i.name = str; else i.name = "unknown"; - pw_log_debug("sink %d %s monitor %d", g->id, i.name, g->stream_info.monitor); + pw_log_debug("sink %d %s monitor %d", g->id, i.name, g->endpoint_info.monitor); i.index = g->id; - if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION))) + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_ENDPOINT_NAME))) i.description = str; else i.description = "unknown"; i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - if (g->stream_info.n_channel_volumes) - i.sample_spec.channels = g->stream_info.n_channel_volumes; + if (g->endpoint_info.n_channel_volumes) + i.sample_spec.channels = g->endpoint_info.n_channel_volumes; else i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.owner_module = 0; i.volume.channels = i.sample_spec.channels; for (n = 0; n < i.volume.channels; n++) - i.volume.values[n] = g->stream_info.volume * g->stream_info.channel_volumes[n] * PA_VOLUME_NORM; - i.mute = g->stream_info.mute; - i.monitor_source = g->stream_info.monitor; + i.volume.values[n] = g->endpoint_info.volume * g->endpoint_info.channel_volumes[n] * PA_VOLUME_NORM; + i.mute = g->endpoint_info.mute; + i.monitor_source = g->endpoint_info.monitor; i.monitor_source_name = "unknown"; i.latency = 0; i.driver = "PipeWire"; @@ -283,7 +283,7 @@ static void set_stream_volume(pa_context *c, pa_stream *s, const pa_cvolume *vol } } -static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *volume, bool mute) +static void set_endpoint_volume(pa_context *c, struct global *g, const pa_cvolume *volume, bool mute) { char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); @@ -297,22 +297,22 @@ static void set_node_volume(pa_context *c, struct global *g, const pa_cvolume *v vols = channel_volumes; n_channel_volumes = volume->channels; - if (n_channel_volumes == g->stream_info.n_channel_volumes && - memcmp(g->stream_info.channel_volumes, vols, n_channel_volumes * sizeof(float)) == 0 && - mute == g->stream_info.mute) + if (n_channel_volumes == g->endpoint_info.n_channel_volumes && + memcmp(g->endpoint_info.channel_volumes, vols, n_channel_volumes * sizeof(float)) == 0 && + mute == g->endpoint_info.mute) return; - memcpy(g->stream_info.channel_volumes, vols, n_channel_volumes * sizeof(float)); - g->stream_info.n_channel_volumes = n_channel_volumes; + memcpy(g->endpoint_info.channel_volumes, vols, n_channel_volumes * sizeof(float)); + g->endpoint_info.n_channel_volumes = n_channel_volumes; } else { - n_channel_volumes = g->stream_info.n_channel_volumes; - vols = g->stream_info.channel_volumes; - if (mute == g->stream_info.mute) + n_channel_volumes = g->endpoint_info.n_channel_volumes; + vols = g->endpoint_info.channel_volumes; + if (mute == g->endpoint_info.mute) return; } - g->stream_info.mute = mute; + g->endpoint_info.mute = mute; - pw_node_proxy_set_param((struct pw_node_proxy*)g->proxy, + pw_endpoint_proxy_set_param((struct pw_endpoint_proxy*)g->proxy, SPA_PARAM_Props, 0, spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, @@ -345,7 +345,7 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - set_node_volume(c, g, volume, g->stream_info.mute); + set_endpoint_volume(c, g, volume, g->endpoint_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -374,7 +374,7 @@ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - set_node_volume(c, g, volume, g->stream_info.mute); + set_endpoint_volume(c, g, volume, g->endpoint_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -404,7 +404,7 @@ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int if (!(g->mask & PA_SUBSCRIPTION_MASK_SINK)) return NULL; - set_node_volume(c, g, NULL, mute); + set_endpoint_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -432,7 +432,7 @@ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SINK, name)) == NULL) return NULL; - set_node_volume(c, g, NULL, mute); + set_endpoint_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -534,7 +534,7 @@ static pa_source_state_t node_state_to_source(enum pw_node_state s) static void source_callback(struct source_data *d) { struct global *g = d->global; - struct pw_endpoint_stream_info *info = g->info; + struct pw_endpoint_info *info = g->info; const char *str; uint32_t n; pa_source_info i; @@ -546,31 +546,31 @@ static void source_callback(struct source_data *d) PA_SOURCE_DECIBEL_VOLUME; spa_zero(i); - if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_NAME))) + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_ENDPOINT_NAME))) i.name = str; else i.name = "unknown"; i.index = g->id; - if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION))) + if (info->props && (str = spa_dict_lookup(info->props, PW_KEY_ENDPOINT_NAME))) i.description = str; else i.description = "unknown"; i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - if (g->stream_info.n_channel_volumes) - i.sample_spec.channels = g->stream_info.n_channel_volumes; + if (g->endpoint_info.n_channel_volumes) + i.sample_spec.channels = g->endpoint_info.n_channel_volumes; else i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); i.owner_module = 0; i.volume.channels = i.sample_spec.channels; for (n = 0; n < i.volume.channels; n++) - i.volume.values[n] = g->stream_info.volume * g->stream_info.channel_volumes[n] * PA_VOLUME_NORM; - i.mute = g->stream_info.mute; + i.volume.values[n] = g->endpoint_info.volume * g->endpoint_info.channel_volumes[n] * PA_VOLUME_NORM; + i.mute = g->endpoint_info.mute; if (g->mask & PA_SUBSCRIPTION_MASK_SINK) { i.monitor_of_sink = g->id; i.monitor_of_sink_name = "unknown"; - i.index = g->stream_info.monitor; + i.index = g->endpoint_info.monitor; } else { i.monitor_of_sink = PA_INVALID_INDEX; i.monitor_of_sink_name = NULL; @@ -729,7 +729,7 @@ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - set_node_volume(c, g, volume, g->stream_info.mute); + set_endpoint_volume(c, g, volume, g->endpoint_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -758,7 +758,7 @@ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *na if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - set_node_volume(c, g, volume, g->stream_info.mute); + set_endpoint_volume(c, g, volume, g->endpoint_info.mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -788,7 +788,7 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i if (!(g->mask & PA_SUBSCRIPTION_MASK_SOURCE)) return NULL; - set_node_volume(c, g, NULL, mute); + set_endpoint_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -816,7 +816,7 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name if ((g = pa_context_find_global_by_name(c, PA_SUBSCRIPTION_MASK_SOURCE, name)) == NULL) return NULL; - set_node_volume(c, g, NULL, mute); + set_endpoint_volume(c, g, NULL, mute); o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1481,7 +1481,7 @@ struct sink_input_data { static void sink_input_callback(struct sink_input_data *d) { struct global *g = d->global, *cl; - struct pw_endpoint_stream_info *info = g->info; + struct pw_endpoint_info *info = g->info; const char *name; uint32_t n; pa_sink_input_info i; @@ -1496,19 +1496,19 @@ static void sink_input_callback(struct sink_input_data *d) if (info->props) { if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL && - (name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) == NULL) + (name = spa_dict_lookup(info->props, PW_KEY_ENDPOINT_NAME)) == NULL) name = "unknown"; } else name = "unknown"; - cl = pa_context_find_global(d->context, g->stream_info.client_id); + cl = pa_context_find_global(d->context, g->endpoint_info.client_id); spa_zero(i); i.index = g->id; i.name = name; i.owner_module = PA_INVALID_INDEX; - i.client = g->stream_info.client_id; + i.client = g->endpoint_info.client_id; if (s) { i.sink = s->device_index; } @@ -1529,7 +1529,7 @@ static void sink_input_callback(struct sink_input_data *d) else { i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = g->stream_info.n_channel_volumes; + i.sample_spec.channels = g->endpoint_info.n_channel_volumes; if (i.sample_spec.channels == 0) i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); @@ -1540,9 +1540,9 @@ static void sink_input_callback(struct sink_input_data *d) pa_cvolume_init(&i.volume); i.volume.channels = i.sample_spec.channels; for (n = 0; n < i.volume.channels; n++) - i.volume.values[n] = g->stream_info.volume * g->stream_info.channel_volumes[n] * PA_VOLUME_NORM; + i.volume.values[n] = g->endpoint_info.volume * g->endpoint_info.channel_volumes[n] * PA_VOLUME_NORM; - i.mute = g->stream_info.mute; + i.mute = g->endpoint_info.mute; i.buffer_usec = 0; i.sink_usec = 0; i.resample_method = "PipeWire resampler"; @@ -1697,7 +1697,7 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons set_stream_volume(c, s, volume, s->mute); } else if (g) { - set_node_volume(c, g, volume, g->stream_info.mute); + set_endpoint_volume(c, g, volume, g->endpoint_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1729,7 +1729,7 @@ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mu set_stream_volume(c, s, NULL, mute); } else if (g) { - set_node_volume(c, g, NULL, mute); + set_endpoint_volume(c, g, NULL, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -1780,7 +1780,7 @@ struct source_output_data { static void source_output_callback(struct source_output_data *d) { struct global *g = d->global, *l, *cl; - struct pw_endpoint_stream_info *info = g->info; + struct pw_endpoint_info *info = g->info; const char *name = NULL; uint32_t n; pa_source_output_info i; @@ -1793,7 +1793,8 @@ static void source_output_callback(struct source_output_data *d) s = find_stream(d->context, g->id); - if (info->props) { + name = info->name; + if (name == NULL && info->props) { if ((name = spa_dict_lookup(info->props, PW_KEY_MEDIA_NAME)) == NULL && (name = spa_dict_lookup(info->props, PW_KEY_APP_NAME)) == NULL && (name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) == NULL) @@ -1802,13 +1803,13 @@ static void source_output_callback(struct source_output_data *d) if (name == NULL) name = "unknown"; - cl = pa_context_find_global(d->context, g->stream_info.client_id); + cl = pa_context_find_global(d->context, g->endpoint_info.client_id); spa_zero(i); i.index = g->id; i.name = name ? name : "Unknown"; i.owner_module = PA_INVALID_INDEX; - i.client = g->stream_info.client_id; + i.client = g->endpoint_info.client_id; if (s) { i.source = s->device_index; } @@ -1828,7 +1829,7 @@ static void source_output_callback(struct source_output_data *d) else { i.sample_spec.format = PA_SAMPLE_S16LE; i.sample_spec.rate = 44100; - i.sample_spec.channels = g->stream_info.n_channel_volumes; + i.sample_spec.channels = g->endpoint_info.n_channel_volumes; if (i.sample_spec.channels == 0) i.sample_spec.channels = 2; pa_channel_map_init_auto(&i.channel_map, i.sample_spec.channels, PA_CHANNEL_MAP_OSS); @@ -1839,9 +1840,9 @@ static void source_output_callback(struct source_output_data *d) pa_cvolume_init(&i.volume); i.volume.channels = i.sample_spec.channels; for (n = 0; n < i.volume.channels; n++) - i.volume.values[n] = g->stream_info.volume * g->stream_info.channel_volumes[n] * PA_VOLUME_NORM; + i.volume.values[n] = g->endpoint_info.volume * g->endpoint_info.channel_volumes[n] * PA_VOLUME_NORM; - i.mute = g->stream_info.mute; + i.mute = g->endpoint_info.mute; i.buffer_usec = 0; i.source_usec = 0; i.resample_method = "PipeWire resampler"; @@ -1991,7 +1992,7 @@ pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, c set_stream_volume(c, s, volume, s->mute); } else if (g) { - set_node_volume(c, g, volume, g->stream_info.mute); + set_endpoint_volume(c, g, volume, g->endpoint_info.mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; @@ -2021,7 +2022,7 @@ pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int set_stream_volume(c, s, NULL, mute); } else if (g) { - set_node_volume(c, g, NULL, mute); + set_endpoint_volume(c, g, NULL, mute); } o = pa_operation_new(c, NULL, on_success, sizeof(struct success_ack)); d = o->userdata; diff --git a/pipewire-pulseaudio/src/stream.c b/pipewire-pulseaudio/src/stream.c index b7c268f58..49f125b57 100644 --- a/pipewire-pulseaudio/src/stream.c +++ b/pipewire-pulseaudio/src/stream.c @@ -200,7 +200,7 @@ static void configure_device(pa_stream *s) else { if (s->direction == PA_STREAM_RECORD) { if (g->mask == (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE)) - s->device_index = g->stream_info.monitor; + s->device_index = g->endpoint_info.monitor; else s->device_index = g->id; } diff --git a/spa/include/spa/param/param.h b/spa/include/spa/param/param.h index 04d1dfbc0..039c19d67 100644 --- a/spa/include/spa/param/param.h +++ b/spa/include/spa/param/param.h @@ -45,6 +45,8 @@ enum spa_param_type { SPA_PARAM_Profile, /**< profile configuration as SPA_TYPE_OBJECT_ParamProfile */ SPA_PARAM_EnumPortConfig, /**< port configuration enumeration as SPA_TYPE_OBJECT_ParamPortConfig */ SPA_PARAM_PortConfig, /**< port configuration as SPA_TYPE_OBJECT_ParamPortConfig */ + SPA_PARAM_EnumRoute, /**< routing enumeration as SPA_TYPE_OBJECT_ParamRoute */ + SPA_PARAM_Route, /**< routing configuration as SPA_TYPE_OBJECT_ParamRoute */ }; /** information about a parameter */ @@ -112,6 +114,24 @@ enum spa_param_port_config { SPA_PARAM_PORT_CONFIG_format, /**< (Object) format filter */ }; +enum spa_param_route_availability { + SPA_PARAM_ROUTE_AVAILABILITY_unknown, /**< unknown if route is available */ + SPA_PARAM_ROUTE_AVAILABILITY_no, /**< route is not available */ + SPA_PARAM_ROUTE_AVAILABILITY_yes, /**< route is available */ +}; + +/** properties for SPA_TYPE_OBJECT_ParamRoute */ +enum spa_param_route { + SPA_PARAM_ROUTE_START, + SPA_PARAM_ROUTE_index, /**< index of the routing destination (Int) */ + SPA_PARAM_ROUTE_name, /**< name of the routing destination (String) */ + SPA_PARAM_ROUTE_description, /**< description of the destination (String) */ + SPA_PARAM_ROUTE_priority, /**< priority of the destination (Int) */ + SPA_PARAM_ROUTE_available, /**< availability of the destination + * (Id enum spa_param_route_availability) */ +}; + + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/spa/include/spa/param/type-info.h b/spa/include/spa/param/type-info.h index d7908c16b..1c0c24c38 100644 --- a/spa/include/spa/param/type-info.h +++ b/spa/include/spa/param/type-info.h @@ -297,6 +297,30 @@ static const struct spa_type_info spa_type_param_port_config[] = { { 0, 0, NULL, NULL }, }; +#define SPA_TYPE_INFO_ParamRouteAvailability SPA_TYPE_INFO_ENUM_BASE "ParamRouteAvailability" +#define SPA_TYPE_INFO_PARAM_ROUTE_AVAILABILITY_BASE SPA_TYPE_INFO_ParamRouteAvailability ":" + +static const struct spa_type_info spa_type_param_route_availability[] = { + { SPA_PARAM_ROUTE_AVAILABILITY_unknown, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_AVAILABILITY_BASE "unknown", NULL }, + { SPA_PARAM_ROUTE_AVAILABILITY_no, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_AVAILABILITY_BASE "no", NULL }, + { SPA_PARAM_ROUTE_AVAILABILITY_yes, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_AVAILABILITY_BASE "yes", NULL }, + { 0, 0, NULL, NULL }, +}; + + +#define SPA_TYPE_INFO_PARAM_Route SPA_TYPE_INFO_PARAM_BASE "Route" +#define SPA_TYPE_INFO_PARAM_ROUTE_BASE SPA_TYPE_INFO_PARAM_Route ":" + +static const struct spa_type_info spa_type_param_route[] = { + { SPA_PARAM_ROUTE_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE, spa_type_param, }, + { SPA_PARAM_ROUTE_index, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "index", NULL, }, + { SPA_PARAM_ROUTE_name, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_ROUTE_BASE "name", NULL, }, + { SPA_PARAM_ROUTE_description, SPA_TYPE_String, SPA_TYPE_INFO_PARAM_ROUTE_BASE "description", NULL, }, + { SPA_PARAM_ROUTE_priority, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ROUTE_BASE "priority", NULL, }, + { SPA_PARAM_ROUTE_available, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_ROUTE_BASE "available", spa_type_param_route_availability, }, + { 0, 0, NULL, NULL }, +}; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/spa/include/spa/utils/type-info.h b/spa/include/spa/utils/type-info.h index 738c35eac..441e0eab6 100644 --- a/spa/include/spa/utils/type-info.h +++ b/spa/include/spa/utils/type-info.h @@ -130,6 +130,7 @@ static const struct spa_type_info spa_types[] = { { SPA_TYPE_OBJECT_ParamIO, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_IO, spa_type_param_io }, { SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Profile, spa_type_param_profile }, { SPA_TYPE_OBJECT_ParamPortConfig, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_PortConfig, spa_type_param_port_config }, + { SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Route, spa_type_param_route }, { 0, 0, NULL, NULL } }; diff --git a/spa/include/spa/utils/type.h b/spa/include/spa/utils/type.h index 3e9b4aa72..9131bd790 100644 --- a/spa/include/spa/utils/type.h +++ b/spa/include/spa/utils/type.h @@ -102,6 +102,7 @@ enum { SPA_TYPE_OBJECT_ParamIO, SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_OBJECT_ParamPortConfig, + SPA_TYPE_OBJECT_ParamRoute, SPA_TYPE_OBJECT_LAST, /**< not part of ABI */ /* vendor extensions */ diff --git a/src/examples/media-session/alsa-endpoint.c b/src/examples/media-session/alsa-endpoint.c index 1495af845..a6dcaaeee 100644 --- a/src/examples/media-session/alsa-endpoint.c +++ b/src/examples/media-session/alsa-endpoint.c @@ -48,16 +48,21 @@ struct endpoint { struct spa_list link; struct pw_properties *props; - struct alsa_object *obj; + + struct alsa_node *obj; + struct spa_hook listener; struct pw_client_endpoint_proxy *client_endpoint; struct spa_hook client_endpoint_listener; - struct pw_endpoint_info client_endpoint_info; + struct pw_endpoint_info info; unsigned int use_ucm:1; snd_use_case_mgr_t *ucm; struct spa_list stream_list; + struct spa_audio_info format; + + unsigned int active:1; }; struct stream { @@ -65,28 +70,25 @@ struct stream { struct pw_properties *props; struct pw_endpoint_stream_info info; + + unsigned int active:1; }; static int client_endpoint_set_id(void *object, uint32_t id) { struct endpoint *endpoint = object; - struct alsa_object *obj = endpoint->obj; - - endpoint->client_endpoint_info.id = id; - endpoint->client_endpoint_info.name = (char*)pw_properties_get(endpoint->props, PW_KEY_ENDPOINT_NAME); - endpoint->client_endpoint_info.media_class = (char*)pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS); - + endpoint->info.id = id; pw_client_endpoint_proxy_update(endpoint->client_endpoint, PW_CLIENT_ENDPOINT_UPDATE_INFO, 0, NULL, - &endpoint->client_endpoint_info); + &endpoint->info); return 0; } static int client_endpoint_set_session_id(void *object, uint32_t id) { struct endpoint *endpoint = object; - endpoint->client_endpoint_info.session_id = id; + endpoint->info.session_id = id; return 0; } @@ -103,6 +105,56 @@ static int client_endpoint_stream_set_param(void *object, uint32_t stream_id, return -ENOTSUP; } +static int client_endpoint_create_link(void *object, const struct spa_dict *props) +{ + struct endpoint *endpoint = object; + struct impl *impl = endpoint->obj->monitor->impl; + struct pw_properties *p; + char buf[1024]; + struct spa_pod_builder b = { 0, }; + struct spa_pod *param; + + p = pw_properties_new_dict(props); + + if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { + pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->obj->info->id); + pw_properties_setf(p, PW_KEY_LINK_OUTPUT_PORT, "-1"); + } else { + pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->obj->info->id); + pw_properties_setf(p, PW_KEY_LINK_INPUT_PORT, "-1"); + } + + if (!endpoint->active) { + endpoint->format.info.raw.rate = 48000; + + spa_pod_builder_init(&b, buf, sizeof(buf)); + param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &endpoint->format.info.raw); + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, + SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(endpoint->info.direction), + SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), + SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_Bool(true), + SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); + + 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, + SPA_PARAM_PortConfig, 0, param); + + endpoint->active = true; + } + + pw_core_proxy_create_object(impl->core_proxy, + "link-factory", + PW_TYPE_INTERFACE_Link, + PW_VERSION_LINK_PROXY, + &p->dict, 0); + + pw_properties_free(p); + + return 0; +} static const struct pw_client_endpoint_proxy_events client_endpoint_events = { PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS, @@ -110,17 +162,133 @@ static const struct pw_client_endpoint_proxy_events client_endpoint_events = { .set_session_id = client_endpoint_set_session_id, .set_param = client_endpoint_set_param, .stream_set_param = client_endpoint_stream_set_param, + .create_link = client_endpoint_create_link, }; -/** fallback, one stream for each node */ -static int setup_alsa_fallback_endpoint(struct endpoint *endpoint) +static void node_event_param(void *object, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct endpoint *endpoint = object; + struct alsa_node *n = endpoint->obj; + struct impl *impl = n->monitor->impl; + struct spa_audio_info info = { 0, }; + + pw_log_debug(NAME" %p: param for node %d, %d", impl, n->info->id, id); + + if (id != SPA_PARAM_EnumFormat) + goto error; + + if (spa_format_parse(param, &info.media_type, &info.media_subtype) < 0) + goto error; + + if (info.media_type != SPA_MEDIA_TYPE_audio || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return; + + spa_pod_object_fixate((struct spa_pod_object*)param); + if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) + spa_debug_pod(2, NULL, param); + + if (spa_format_audio_raw_parse(param, &info.info.raw) < 0) + goto error; + + if (endpoint->format.info.raw.channels < info.info.raw.channels) + endpoint->format = info; + return; + + error: + pw_log_warn("unhandled param:"); + if (pw_log_level_enabled(SPA_LOG_LEVEL_WARN)) + spa_debug_pod(2, NULL, param); + return; +} + +static const struct pw_node_proxy_events endpoint_node_events = { + PW_VERSION_NODE_PROXY_EVENTS, + .param = node_event_param, +}; + +static struct endpoint *make_endpoint(struct alsa_node *obj) +{ + struct impl *impl = obj->monitor->impl; + struct pw_properties *props; + struct endpoint *endpoint; + struct pw_proxy *proxy; + const char *str, *media_class = NULL, *name = NULL; + + 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) + pw_properties_set(props, PW_KEY_MEDIA_CLASS, media_class); + if ((str = pw_properties_get(obj->props, 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) + 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) + pw_properties_set(props, PW_KEY_ENDPOINT_ICON_NAME, str); + } + + proxy = pw_core_proxy_create_object(impl->core_proxy, + "client-endpoint", + PW_TYPE_INTERFACE_ClientEndpoint, + PW_VERSION_CLIENT_ENDPOINT_PROXY, + &props->dict, sizeof(*endpoint)); + if (proxy == NULL) { + pw_properties_free(props); + return NULL; + } + + endpoint = pw_proxy_get_user_data(proxy); + endpoint->obj = obj; + endpoint->props = props; + endpoint->client_endpoint = (struct pw_client_endpoint_proxy *) proxy; + endpoint->info.version = PW_VERSION_ENDPOINT_INFO; + endpoint->info.name = (char*)pw_properties_get(endpoint->props, PW_KEY_ENDPOINT_NAME); + endpoint->info.media_class = (char*)pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS); + endpoint->info.session_id = impl->client_session_info.id; + endpoint->info.direction = obj->direction; + endpoint->info.flags = 0; + endpoint->info.change_mask = + PW_ENDPOINT_CHANGE_MASK_STREAMS | + PW_ENDPOINT_CHANGE_MASK_SESSION | + PW_ENDPOINT_CHANGE_MASK_PROPS; + endpoint->info.n_streams = 1; + endpoint->info.props = &endpoint->props->dict; + spa_list_init(&endpoint->stream_list); + + pw_client_endpoint_proxy_add_listener(endpoint->client_endpoint, + &endpoint->client_endpoint_listener, + &client_endpoint_events, + endpoint); + + pw_proxy_add_object_listener(obj->proxy, &endpoint->listener, &endpoint_node_events, endpoint); + + pw_node_proxy_enum_params((struct pw_node_proxy*)obj->proxy, + 0, SPA_PARAM_EnumFormat, + 0, -1, NULL); + + return endpoint; +} + +/** fallback, one stream for each node */ +static int setup_alsa_fallback_endpoint(struct alsa_object *obj) { - struct alsa_object *obj = endpoint->obj; struct alsa_node *n; const char *str; spa_list_for_each(n, &obj->node_list, link) { struct stream *s; + struct endpoint *endpoint; + + endpoint = make_endpoint(n); + if (endpoint == NULL) + return -errno; s = calloc(1, sizeof(*s)); if (s == NULL) @@ -133,15 +301,15 @@ static int setup_alsa_fallback_endpoint(struct endpoint *endpoint) pw_properties_set(s->props, PW_KEY_MEDIA_CLASS, str); if ((str = pw_properties_get(n->props, PW_KEY_PRIORITY_SESSION)) != NULL) pw_properties_set(s->props, PW_KEY_PRIORITY_SESSION, str); - if ((str = pw_properties_get(n->props, PW_KEY_NODE_NAME)) != NULL) - pw_properties_set(s->props, PW_KEY_STREAM_NAME, str); - if ((str = pw_properties_get(n->props, PW_KEY_NODE_DESCRIPTION)) != NULL) - pw_properties_set(s->props, PW_KEY_STREAM_DESCRIPTION, str); + if (n->direction == PW_DIRECTION_OUTPUT) + pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Playback"); + else + pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Capture"); s->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; s->info.id = n->id; - s->info.endpoint_id = endpoint->client_endpoint_info.id; - s->info.name = (char*)pw_properties_get(s->props, PW_KEY_STREAM_DESCRIPTION); + s->info.endpoint_id = endpoint->info.id; + s->info.name = (char*)pw_properties_get(s->props, PW_KEY_STREAM_NAME); s->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS; s->info.props = &s->props->dict; @@ -159,13 +327,13 @@ static int setup_alsa_fallback_endpoint(struct endpoint *endpoint) * * We create 1 stream for each verb + modifier combination */ -static int setup_alsa_ucm_endpoint(struct endpoint *endpoint) +static int setup_alsa_ucm_endpoint(struct alsa_object *obj) { - struct alsa_object *obj = endpoint->obj; const char *str, *card_name = NULL; char *name_free = NULL; int i, res, num_verbs; const char **verb_list = NULL; + snd_use_case_mgr_t *ucm; card_name = pw_properties_get(obj->props, SPA_KEY_API_ALSA_CARD_NAME); if (card_name == NULL && @@ -179,12 +347,12 @@ static int setup_alsa_ucm_endpoint(struct endpoint *endpoint) goto exit; } - if ((res = snd_use_case_mgr_open(&endpoint->ucm, card_name)) < 0) { + if ((res = snd_use_case_mgr_open(&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(endpoint->ucm, &verb_list); + num_verbs = snd_use_case_verb_list(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)); @@ -195,15 +363,13 @@ static int setup_alsa_ucm_endpoint(struct endpoint *endpoint) pw_log_debug("verb: %s", verb_list[i]); } - endpoint->use_ucm = true; - snd_use_case_free_list(verb_list, num_verbs); - return 0; + res = -ENOTSUP; + close_exit: - snd_use_case_mgr_close(endpoint->ucm); + snd_use_case_mgr_close(ucm); exit: - endpoint->ucm = NULL; free(name_free); return res; @@ -211,50 +377,10 @@ exit: static int setup_alsa_endpoint(struct alsa_object *obj) { - struct impl *impl = obj->monitor->impl; int res; - struct pw_proxy *proxy; - struct endpoint *endpoint; - struct pw_properties *props; - const char *str; - props = pw_properties_new(NULL, NULL); - if (props == NULL) - return -errno; - - if ((str = pw_properties_get(obj->props, PW_KEY_MEDIA_CLASS)) != NULL) - pw_properties_set(props, PW_KEY_MEDIA_CLASS, str); - if ((str = pw_properties_get(obj->props, PW_KEY_DEVICE_DESCRIPTION)) != NULL) - pw_properties_set(props, PW_KEY_ENDPOINT_NAME, str); - if ((str = pw_properties_get(obj->props, PW_KEY_DEVICE_ICON_NAME)) != NULL) - pw_properties_set(props, PW_KEY_ENDPOINT_ICON_NAME, str); - - proxy = pw_core_proxy_create_object(impl->core_proxy, - "client-endpoint", - PW_TYPE_INTERFACE_ClientEndpoint, - PW_VERSION_CLIENT_ENDPOINT_PROXY, - &props->dict, sizeof(*endpoint)); - if (proxy == NULL) { - pw_properties_free(props); - return -errno; - } - - endpoint = pw_proxy_get_user_data(proxy); - endpoint->obj = obj; - endpoint->props = props; - endpoint->client_endpoint = (struct pw_client_endpoint_proxy *) proxy; - endpoint->client_endpoint_info.version = PW_VERSION_ENDPOINT_INFO; - endpoint->client_endpoint_info.name = "name"; - endpoint->client_endpoint_info.media_class = "media-class"; - spa_list_init(&endpoint->stream_list); - - pw_client_endpoint_proxy_add_listener(endpoint->client_endpoint, - &endpoint->client_endpoint_listener, - &client_endpoint_events, - endpoint); - - if ((res = setup_alsa_ucm_endpoint(endpoint)) < 0) - res = setup_alsa_fallback_endpoint(endpoint); + 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 0fe67f8a3..6d9b80f29 100644 --- a/src/examples/media-session/alsa-monitor.c +++ b/src/examples/media-session/alsa-monitor.c @@ -52,14 +52,18 @@ struct alsa_object; struct alsa_node { struct monitor *monitor; + enum pw_direction direction; struct alsa_object *object; struct spa_list link; uint32_t id; struct pw_properties *props; - struct pw_proxy *proxy; struct spa_node *node; + + struct pw_proxy *proxy; + struct spa_hook listener; + struct pw_node_info *info; }; struct alsa_object { @@ -106,6 +110,17 @@ 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; + 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, const struct spa_device_object_info *info) { @@ -142,6 +157,11 @@ static struct alsa_node *alsa_create_node(struct alsa_object *obj, uint32_t id, if ((stream = pw_properties_get(node->props, SPA_KEY_API_ALSA_PCM_STREAM)) == NULL) stream = "unknown"; + if (!strcmp(stream, "capture")) + node->direction = PW_DIRECTION_OUTPUT; + else + node->direction = PW_DIRECTION_INPUT; + if (obj->first) { if (atol(dev) != 0) obj->priority -= 256; @@ -160,7 +180,7 @@ static struct alsa_node *alsa_create_node(struct alsa_object *obj, uint32_t id, } if (pw_properties_get(node->props, SPA_KEY_MEDIA_CLASS) == NULL) { - if (!strcmp(stream, "capture")) + if (node->direction == PW_DIRECTION_OUTPUT) pw_properties_setf(node->props, SPA_KEY_MEDIA_CLASS, "Audio/Source"); else pw_properties_setf(node->props, SPA_KEY_MEDIA_CLASS, "Audio/Sink"); @@ -209,6 +229,7 @@ static struct alsa_node *alsa_create_node(struct alsa_object *obj, uint32_t id, 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); diff --git a/src/examples/media-session/media-session.c b/src/examples/media-session/media-session.c index cc59168af..5b7893bb1 100644 --- a/src/examples/media-session/media-session.c +++ b/src/examples/media-session/media-session.c @@ -47,6 +47,7 @@ int sm_monitor_start(struct pw_remote *remote); int sm_policy_start(struct pw_remote *remote); +int sm_policy_ep_start(struct pw_remote *remote); struct impl { struct pw_main_loop *loop; @@ -115,7 +116,8 @@ int main(int argc, char *argv[]) pw_module_load(impl.core, "libpipewire-module-session-manager", NULL, NULL); sm_monitor_start(impl.monitor_remote); - sm_policy_start(impl.policy_remote); +// sm_policy_start(impl.policy_remote); + sm_policy_ep_start(impl.policy_remote); if ((res = pw_remote_connect(impl.monitor_remote)) < 0) return res; diff --git a/src/examples/media-session/monitor.c b/src/examples/media-session/monitor.c index 3dec28a43..44f0995ee 100644 --- a/src/examples/media-session/monitor.c +++ b/src/examples/media-session/monitor.c @@ -47,6 +47,8 @@ #define DEFAULT_IDLE_SECONDS 3 +void * sm_stream_monitor_start(struct pw_remote *remote, uint32_t session_id); + struct impl; struct monitor { @@ -87,6 +89,11 @@ struct impl { struct spa_source *jack_timeout; struct pw_proxy *jack_device; + + struct pw_registry_proxy *registry_proxy; + struct spa_hook registry_listener; + + }; struct alsa_object; @@ -110,6 +117,13 @@ static int client_session_set_id(void *object, uint32_t id) PW_CLIENT_SESSION_UPDATE_INFO, 0, NULL, &impl->client_session_info); + + bluez5_start_monitor(impl, &impl->bluez5_monitor); + alsa_start_monitor(impl, &impl->alsa_monitor); + alsa_start_midi_bridge(impl); + alsa_start_jack_device(impl); + v4l2_start_monitor(impl, &impl->v4l2_monitor); + sm_stream_monitor_start(impl->remote, id); return 0; } @@ -131,16 +145,6 @@ static int client_session_link_set_param(void *object, uint32_t link_id, uint32_ 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; @@ -152,8 +156,6 @@ static const struct pw_client_session_proxy_events client_session_events = { .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, }; @@ -192,11 +194,6 @@ static void start_services(struct impl *impl) &client_session_events, impl); - bluez5_start_monitor(impl, &impl->bluez5_monitor); - alsa_start_monitor(impl, &impl->alsa_monitor); - alsa_start_midi_bridge(impl); - alsa_start_jack_device(impl); - v4l2_start_monitor(impl, &impl->v4l2_monitor); } static void on_state_changed(void *_data, enum pw_remote_state old, enum pw_remote_state state, const char *error) diff --git a/src/examples/media-session/policy-ep.c b/src/examples/media-session/policy-ep.c new file mode 100644 index 000000000..cc3d44077 --- /dev/null +++ b/src/examples/media-session/policy-ep.c @@ -0,0 +1,1003 @@ +/* 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 "pipewire/pipewire.h" +#include "pipewire/private.h" +#include "extensions/session-manager.h" + +#define NAME "policy-ep" + +#define DEFAULT_CHANNELS 2 +#define DEFAULT_SAMPLERATE 48000 + +#define DEFAULT_IDLE_SECONDS 3 + +struct impl; + +struct impl { + struct timespec now; + + struct pw_core *core; + struct pw_remote *remote; + struct spa_hook remote_listener; + + struct pw_core_proxy *core_proxy; + struct spa_hook core_listener; + + struct pw_registry_proxy *registry_proxy; + struct spa_hook registry_listener; + + struct pw_map globals; + + struct spa_list client_list; + struct spa_list endpoint_list; + struct spa_list session_list; + int seq; +}; + +struct object { + struct impl *impl; + uint32_t id; + uint32_t type; + struct pw_proxy *proxy; + struct spa_hook listener; +}; + +struct client { + struct object obj; + + struct spa_list l; + + struct spa_hook listener; + struct pw_client_info *info; +}; + +struct session { + struct object obj; + + struct spa_list l; + + struct spa_hook listener; + struct pw_session_info *info; +}; + +struct endpoint { + struct object obj; + + struct spa_list l; + + struct spa_hook listener; + struct pw_endpoint_info *info; + + struct endpoint *peer; + struct session *session; + + uint32_t client_id; + int32_t priority; + + struct spa_list stream_list; + + enum pw_direction direction; +#define ENDPOINT_TYPE_UNKNOWN 0 +#define ENDPOINT_TYPE_STREAM 1 +#define ENDPOINT_TYPE_DEVICE 2 + uint32_t type; + char *media; + + uint32_t media_type; + uint32_t media_subtype; + struct spa_audio_info_raw format; + + uint64_t plugged; + unsigned int exclusive:1; + unsigned int enabled:1; + unsigned int busy:1; +}; + +struct stream { + struct object obj; + + struct spa_list l; + enum pw_direction direction; + struct pw_endpoint_stream_info *info; + struct endpoint *endpoint; +#define STREAM_FLAG_NONE 0 +#define STREAM_FLAG_DSP (1<<0) +#define STREAM_FLAG_SKIP (1<<1) + uint32_t flags; + + struct spa_hook listener; +}; + +struct link { + struct object obj; + struct stream *out; + struct stream *in; +}; + +static void add_object(struct impl *impl, struct object *obj) +{ + size_t size = pw_map_get_size(&impl->globals); + while (obj->id > size) + pw_map_insert_at(&impl->globals, size++, NULL); + pw_map_insert_at(&impl->globals, obj->id, obj); +} + +static void remove_object(struct impl *impl, struct object *obj) +{ + pw_map_insert_at(&impl->globals, obj->id, NULL); +} + +static void *find_object(struct impl *impl, uint32_t id) +{ + void *obj; + if ((obj = pw_map_lookup(&impl->globals, id)) != NULL) + return obj; + return NULL; +} + +static void schedule_rescan(struct impl *impl) +{ + if (impl->core_proxy) + impl->seq = pw_core_proxy_sync(impl->core_proxy, 0, impl->seq); +} + +static void endpoint_event_info(void *object, const struct pw_endpoint_info *update) +{ + struct endpoint *e = object; + struct impl *impl = e->obj.impl; + struct pw_endpoint_info *info = e->info; + const char *str; + + pw_log_debug(NAME" %p: info for endpoint %d type %d", impl, e->obj.id, e->type); + + if (info == NULL && update) { + info = e->info = calloc(1, sizeof(*info)); + info->id = update->id; + info->name = update->name ? strdup(update->name) : NULL; + info->media_class = update->media_class ? strdup(update->media_class) : NULL; + info->direction = update->direction; + info->flags = update->flags; + } + info->change_mask = update->change_mask; + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) { + info->session_id = update->session_id; + e->session = find_object(impl, info->session_id); + } + if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { + if (info->props) + pw_properties_free ((struct pw_properties *)info->props); + info->props = (struct spa_dict *) pw_properties_new_dict (update->props); + if ((str = spa_dict_lookup(info->props, PW_KEY_PRIORITY_SESSION)) != NULL) + e->priority = pw_properties_parse_int(str); + } + e->enabled = true; +} + +static void endpoint_event_param(void *object, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct endpoint *e = object; + struct impl *impl = e->obj.impl; + pw_log_debug(NAME" %p: param for endpoint %d, %d", impl, e->obj.id, id); +} + +static const struct pw_endpoint_proxy_events endpoint_events = { + PW_VERSION_ENDPOINT_PROXY_EVENTS, + .info = endpoint_event_info, + .param = endpoint_event_param, +}; + +static void endpoint_proxy_destroy(void *data) +{ + struct endpoint *e = data; + struct impl *impl = e->obj.impl; + struct stream *s, *t; + + pw_log_debug(NAME " %p: proxy destroy endpoint %d", impl, e->obj.id); + + spa_list_remove(&e->l); + + spa_list_for_each_safe(s, t, &e->stream_list, l) { + spa_list_remove(&s->l); + s->endpoint = NULL; + } + free(e->media); +} + +static const struct pw_proxy_events endpoint_proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = endpoint_proxy_destroy, +}; + +static int +handle_endpoint(struct impl *impl, uint32_t id, + uint32_t type, const struct spa_dict *props) +{ + const char *str, *media_class; + enum pw_direction direction; + struct pw_proxy *p; + struct endpoint *ep; + uint32_t client_id = SPA_ID_INVALID; + + if (props) { + if ((str = spa_dict_lookup(props, PW_KEY_CLIENT_ID)) != NULL) + client_id = atoi(str); + } + + p = pw_registry_proxy_bind(impl->registry_proxy, + id, type, PW_VERSION_ENDPOINT_PROXY, + sizeof(struct endpoint)); + + ep = pw_proxy_get_user_data(p); + ep->obj.impl = impl; + ep->obj.id = id; + ep->obj.type = type; + ep->obj.proxy = p; + ep->client_id = client_id; + spa_list_init(&ep->stream_list); + pw_proxy_add_listener(p, &ep->obj.listener, &endpoint_proxy_events, ep); + pw_proxy_add_object_listener(p, &ep->listener, &endpoint_events, ep); + add_object(impl, &ep->obj); + spa_list_append(&impl->endpoint_list, &ep->l); + ep->type = ENDPOINT_TYPE_UNKNOWN; + + media_class = props ? spa_dict_lookup(props, PW_KEY_MEDIA_CLASS) : NULL; + + pw_log_debug(NAME" %p: endpoint "PW_KEY_MEDIA_CLASS" %s", impl, media_class); + + if (media_class == NULL) + return 0; + + if (strstr(media_class, "Stream/") == media_class) { + media_class += strlen("Stream/"); + + if (strstr(media_class, "Output/") == media_class) { + direction = PW_DIRECTION_OUTPUT; + media_class += strlen("Output/"); + } + else if (strstr(media_class, "Input/") == media_class) { + direction = PW_DIRECTION_INPUT; + media_class += strlen("Input/"); + } + else + return 0; + + ep->direction = direction; + ep->type = ENDPOINT_TYPE_STREAM; + ep->media = strdup(media_class); + pw_log_debug(NAME "%p: endpoint %d is stream %s", impl, id, ep->media); + } + else { + if (strstr(media_class, "Audio/") == media_class) { + media_class += strlen("Audio/"); + } + else if (strstr(media_class, "Video/") == media_class) { + media_class += strlen("Video/"); + } + else + return 0; + + if (strcmp(media_class, "Sink") == 0) + direction = PW_DIRECTION_OUTPUT; + else if (strcmp(media_class, "Source") == 0) + direction = PW_DIRECTION_INPUT; + else + return 0; + + ep->direction = direction; + ep->type = ENDPOINT_TYPE_DEVICE; + + pw_log_debug(NAME" %p: endpoint %d prio:%d", impl, id, ep->priority); + } + return 1; +} + +static void stream_event_info(void *object, const struct pw_endpoint_stream_info *info) +{ + struct stream *s = object; + pw_log_debug(NAME" %p: info for stream %d", s->obj.impl, s->obj.id); +} + +static void stream_event_param(void *object, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct stream *s = object; + struct endpoint *ep = s->endpoint; + struct spa_audio_info_raw info = { 0, }; + + pw_log_debug(NAME" %p: param for stream %d", s->obj.impl, s->obj.id); + + if (ep == NULL) + return; + + if (id != SPA_PARAM_EnumFormat) + return; + + if (spa_format_parse(param, &ep->media_type, &ep->media_subtype) < 0) + return; + + if (ep->media_type != SPA_MEDIA_TYPE_audio || + ep->media_subtype != SPA_MEDIA_SUBTYPE_raw) + return; + + spa_pod_fixate((struct spa_pod*)param); + + if (spa_format_audio_raw_parse(param, &info) < 0) + return; + + if (info.channels > ep->format.channels) + ep->format = info; +} + +static const struct pw_endpoint_stream_proxy_events stream_events = { + PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS, + .info = stream_event_info, + .param = stream_event_param, +}; + +static void stream_proxy_destroy(void *data) +{ + struct stream *s = data; + + pw_log_debug(NAME " %p: proxy destroy stream %d", s->obj.impl, s->obj.id); + + if (s->endpoint) { + spa_list_remove(&s->l); + s->endpoint = NULL; + } +} + +static const struct pw_proxy_events stream_proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = stream_proxy_destroy, +}; + +static int +handle_stream(struct impl *impl, uint32_t id, uint32_t type, + const struct spa_dict *props) +{ + struct stream *s; + struct pw_proxy *p; + struct endpoint *ep; + const char *str; + uint32_t endpoint_id; + + if (props == NULL || (str = spa_dict_lookup(props, PW_KEY_ENDPOINT_ID)) == NULL) + return -EINVAL; + + endpoint_id = atoi(str); + + if ((ep = find_object(impl, endpoint_id)) == NULL) + return -ESRCH; + + p = pw_registry_proxy_bind(impl->registry_proxy, + id, type, PW_VERSION_ENDPOINT_STREAM_PROXY, + sizeof(struct stream)); + + s = pw_proxy_get_user_data(p); + s->obj.impl = impl; + s->obj.id = id; + s->obj.type = type; + s->obj.proxy = p; + s->endpoint = ep; + s->direction = ep->direction; + + pw_proxy_add_listener(p, &s->obj.listener, &stream_proxy_events, s); + pw_proxy_add_object_listener(p, &s->listener, &stream_events, s); + add_object(impl, &s->obj); + + spa_list_append(&ep->stream_list, &s->l); + + pw_log_debug(NAME" %p: new stream %d for endpoint %d type %d %08x", impl, id, endpoint_id, + ep->type, s->flags); + + if (ep->type == ENDPOINT_TYPE_DEVICE) { + pw_endpoint_stream_proxy_enum_params((struct pw_endpoint_stream_proxy*)p, + 0, SPA_PARAM_EnumFormat, + 0, -1, NULL); + } + return 0; +} + +static void client_event_info(void *object, const struct pw_client_info *info) +{ + struct client *c = object; + uint32_t i; + + pw_log_debug(NAME" %p: info for client %d", c->obj.impl, c->obj.id); + c->info = pw_client_info_update(c->info, info); + for (i = 0; i < info->props->n_items; i++) + pw_log_debug(NAME" %p: %s = %s", c, + info->props->items[i].key, + info->props->items[i].value); +} + +static const struct pw_client_proxy_events client_events = { + PW_VERSION_CLIENT_PROXY_EVENTS, + .info = client_event_info, +}; + +static void client_proxy_destroy(void *data) +{ + struct client *c = data; + + pw_log_debug(NAME " %p: proxy destroy client %d", c->obj.impl, c->obj.id); + + spa_list_remove(&c->l); + if (c->info) + pw_client_info_free(c->info); +} + +static const struct pw_proxy_events client_proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = client_proxy_destroy, +}; + +static int +handle_client(struct impl *impl, uint32_t id, + uint32_t type, const struct spa_dict *props) +{ + struct pw_proxy *p; + struct client *client; + struct pw_permission perms[2]; + const char *str; + + p = pw_registry_proxy_bind(impl->registry_proxy, + id, type, PW_VERSION_CLIENT_PROXY, + sizeof(struct client)); + + client = pw_proxy_get_user_data(p); + client->obj.impl = impl; + client->obj.id = id; + client->obj.type = type; + client->obj.proxy = p; + + pw_proxy_add_listener(p, &client->obj.listener, &client_proxy_events, client); + pw_proxy_add_object_listener(p, &client->listener, &client_events, client); + add_object(impl, &client->obj); + spa_list_append(&impl->client_list, &client->l); + + if (props == NULL) + return 0; + + str = spa_dict_lookup(props, PW_KEY_ACCESS); + if (str == NULL) + return 0; + + if (strcmp(str, "restricted") == 0) { + perms[0] = PW_PERMISSION_INIT(-1, PW_PERM_RWX); + pw_client_proxy_update_permissions((struct pw_client_proxy*)p, + 1, perms); + } + return 0; +} + +static void session_event_info(void *object, const struct pw_session_info *info) +{ + struct session *c = object; + pw_log_debug(NAME" %p: info for session %d", c->obj.impl, c->obj.id); +} + +static const struct pw_session_proxy_events session_events = { + PW_VERSION_SESSION_PROXY_EVENTS, + .info = session_event_info, +}; + +static void session_proxy_destroy(void *data) +{ + struct session *c = data; + + pw_log_debug(NAME " %p: proxy destroy session %d", c->obj.impl, c->obj.id); + + spa_list_remove(&c->l); +} + +static const struct pw_proxy_events session_proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = session_proxy_destroy, +}; + +static int +handle_session(struct impl *impl, uint32_t id, + uint32_t type, const struct spa_dict *props) +{ + struct pw_proxy *p; + struct session *session; + + p = pw_registry_proxy_bind(impl->registry_proxy, + id, type, PW_VERSION_SESSION_PROXY, + sizeof(struct session)); + + session = pw_proxy_get_user_data(p); + session->obj.impl = impl; + session->obj.id = id; + session->obj.type = type; + session->obj.proxy = p; + + pw_proxy_add_listener(p, &session->obj.listener, &session_proxy_events, session); + pw_proxy_add_object_listener(p, &session->listener, &session_events, session); + add_object(impl, &session->obj); + spa_list_append(&impl->session_list, &session->l); + + return 0; +} + +static void +registry_global(void *data,uint32_t id, + uint32_t permissions, uint32_t type, uint32_t version, + const struct spa_dict *props) +{ + struct impl *impl = data; + int res; + + pw_log_debug(NAME " %p: new global '%d' %d", impl, id, type); + + switch (type) { + case PW_TYPE_INTERFACE_Client: + res = handle_client(impl, id, type, props); + break; + + case PW_TYPE_INTERFACE_Session: + res = handle_session(impl, id, type, props); + break; + + case PW_TYPE_INTERFACE_Endpoint: + res = handle_endpoint(impl, id, type, props); + break; + + case PW_TYPE_INTERFACE_EndpointStream: + res = handle_stream(impl, id, type, props); + break; + + default: + res = 0; + break; + } + if (res < 0) { + pw_log_warn(NAME" %p: can't handle global %d", impl, id); + } + else + schedule_rescan(impl); +} + +static void +registry_global_remove(void *data, uint32_t id) +{ + struct impl *impl = data; + struct object *obj; + + pw_log_debug(NAME " %p: remove global '%d'", impl, id); + + if ((obj = find_object(impl, id)) == NULL) + return; + + remove_object(impl, obj); + schedule_rescan(impl); +} + +static const struct pw_registry_proxy_events registry_events = { + PW_VERSION_REGISTRY_PROXY_EVENTS, + .global = registry_global, + .global_remove = registry_global_remove, +}; + + +struct find_data { + struct impl *impl; + uint32_t path_id; + const char *media_class; + struct endpoint *endpoint; + bool exclusive; + int priority; + uint64_t plugged; +}; + +static int find_endpoint(void *data, struct endpoint *endpoint) +{ + struct find_data *find = data; + struct impl *impl = find->impl; + const struct spa_dict *props; + const char *str; + int priority = 0; + uint64_t plugged = 0; + + pw_log_debug(NAME " %p: looking at endpoint '%d' enabled:%d busy:%d exclusive:%d", + impl, endpoint->obj.id, endpoint->enabled, endpoint->busy, endpoint->exclusive); + + if (!endpoint->enabled) + return 0; + + if (find->path_id != SPA_ID_INVALID && endpoint->obj.id != find->path_id) + return 0; + + if (find->path_id == SPA_ID_INVALID) { + if (endpoint->info == NULL || + (props = endpoint->info->props) == NULL) + return 0; + + if ((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL) + return 0; + + if (strcmp(str, find->media_class) != 0) + return 0; + + plugged = endpoint->plugged; + priority = endpoint->priority; + } + + if ((find->exclusive && endpoint->busy) || endpoint->exclusive) { + pw_log_debug(NAME " %p: endpoint '%d' in use", impl, endpoint->obj.id); + return 0; + } + + pw_log_debug(NAME " %p: found endpoint '%d' %"PRIu64" prio:%d", impl, + endpoint->obj.id, plugged, priority); + + if (find->endpoint == NULL || + priority > find->priority || + (priority == find->priority && plugged > find->plugged)) { + pw_log_debug(NAME " %p: new best %d %" PRIu64, impl, priority, plugged); + find->endpoint = endpoint; + find->priority = priority; + find->plugged = plugged; + } + return 0; +} + +static int link_endpoints(struct endpoint *endpoint, enum pw_direction direction, struct endpoint *peer, int max) +{ + struct impl *impl = peer->obj.impl; + struct stream *s; + + pw_log_debug(NAME " %p: link endpoints %d %d %d", impl, max, endpoint->obj.id, peer->obj.id); + + if (endpoint->session == NULL) { + pw_log_debug(NAME " %p: endpoint has no session", impl); + return -EINVAL; + } + + spa_list_for_each(s, &endpoint->stream_list, l) { + struct pw_properties *props; + + pw_log_debug(NAME " %p: stream %p: %d %d", impl, s, s->direction, s->flags); + + if (s->direction == direction) + continue; + if (s->flags & STREAM_FLAG_SKIP) + continue; + + if (max-- == 0) + return 0; + + props = pw_properties_new(NULL, NULL); + if (s->direction == PW_DIRECTION_OUTPUT) { + pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->obj.id); + pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", s->obj.id); + pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", peer->obj.id); + pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", -1); + pw_log_debug(NAME " %p: stream %d:%d -> endpoint %d", impl, + endpoint->obj.id, s->obj.id, peer->obj.id); + + } + else { + pw_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", peer->obj.id); + pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", -1); + pw_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->obj.id); + pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", s->obj.id); + pw_log_debug(NAME " %p: endpoint %d -> stream %d:%d", impl, + peer->obj.id, endpoint->obj.id, s->obj.id); + } + + pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)endpoint->obj.proxy, + &props->dict); + + pw_properties_free(props); + } + endpoint->peer = peer; + peer->peer = endpoint; + + return 0; +} + +static int rescan_endpoint(struct impl *impl, struct endpoint *ep) +{ + struct spa_dict *props; + const char *str, *media, *category, *role; + bool exclusive; + struct find_data find; + struct pw_endpoint_info *info; + struct endpoint *peer; + enum pw_direction direction; + + if (ep->type == ENDPOINT_TYPE_DEVICE) + return 0; + + if (ep->info == NULL || ep->info->props == NULL) { + pw_log_debug(NAME " %p: endpoint %d has no properties", impl, ep->obj.id); + return 0; + } + + if (ep->peer != NULL) + return 0; + + info = ep->info; + props = info->props; + + str = spa_dict_lookup(props, PW_KEY_ENDPOINT_AUTOCONNECT); + if (str == NULL || !pw_properties_parse_bool(str)) { + pw_log_debug(NAME" %p: endpoint %d does not need autoconnect", impl, ep->obj.id); + return 0; + } + + if ((media = spa_dict_lookup(props, PW_KEY_MEDIA_TYPE)) == NULL) + media = ep->media; + if (media == NULL) { + pw_log_debug(NAME" %p: endpoint %d has unknown media", impl, ep->obj.id); + return 0; + } + + spa_zero(find); + + if ((category = spa_dict_lookup(props, PW_KEY_MEDIA_CATEGORY)) == NULL) { + pw_log_debug(NAME" %p: endpoint %d find category", + impl, ep->obj.id); + if (ep->direction == PW_DIRECTION_INPUT) { + category = "Capture"; + } else if (ep->direction == PW_DIRECTION_OUTPUT) { + category = "Playback"; + } else { + pw_log_warn(NAME" %p: endpoint %d can't determine category", + impl, ep->obj.id); + return -EINVAL; + } + } + + if ((role = spa_dict_lookup(props, PW_KEY_MEDIA_ROLE)) == NULL) { + if (strcmp(media, "Audio") == 0) { + if (strcmp(category, "Duplex") == 0) + role = "Communication"; + else if (strcmp(category, "Capture") == 0) + role = "Production"; + else + role = "Music"; + } + else if (strcmp(media, "Video") == 0) { + if (strcmp(category, "Duplex") == 0) + role = "Communication"; + else if (strcmp(category, "Capture") == 0) + role = "Camera"; + else + role = "Video"; + } + } + + if ((str = spa_dict_lookup(props, PW_KEY_NODE_EXCLUSIVE)) != NULL) + exclusive = pw_properties_parse_bool(str); + else + exclusive = false; + + if (strcmp(media, "Audio") == 0) { + if (strcmp(category, "Playback") == 0) + find.media_class = "Audio/Sink"; + else if (strcmp(category, "Capture") == 0) + find.media_class = "Audio/Source"; + else { + pw_log_debug(NAME" %p: endpoint %d unhandled category %s", + impl, ep->obj.id, category); + return -EINVAL; + } + } + else if (strcmp(media, "Video") == 0) { + if (strcmp(category, "Capture") == 0) + find.media_class = "Video/Source"; + else { + pw_log_debug(NAME" %p: endpoint %d unhandled category %s", + impl, ep->obj.id, category); + return -EINVAL; + } + } + else { + pw_log_debug(NAME" %p: endpoint %d unhandled media %s", + impl, ep->obj.id, media); + return -EINVAL; + } + + if (strcmp(category, "Capture") == 0) + direction = PW_DIRECTION_OUTPUT; + else if (strcmp(category, "Playback") == 0) + direction = PW_DIRECTION_INPUT; + else { + pw_log_debug(NAME" %p: endpoint %d unhandled category %s", + impl, ep->obj.id, category); + return -EINVAL; + } + + str = spa_dict_lookup(props, PW_KEY_NODE_TARGET); + if (str != NULL) + find.path_id = atoi(str); + else + find.path_id = SPA_ID_INVALID; + + pw_log_info(NAME " %p: '%s' '%s' '%s' exclusive:%d target %d", impl, + media, category, role, exclusive, find.path_id); + + find.impl = impl; + find.exclusive = exclusive; + + spa_list_for_each(peer, &impl->endpoint_list, l) + find_endpoint(&find, peer); + + if (find.endpoint == NULL && find.path_id != SPA_ID_INVALID) { + pw_log_debug(NAME " %p: no endpoint found for %d, try endpoint", impl, ep->obj.id); + + if ((peer = find_object(impl, find.path_id)) != NULL) { + if (peer->obj.type == PW_TYPE_INTERFACE_Endpoint) + goto do_link; + } + else { + str = spa_dict_lookup(props, PW_KEY_NODE_DONT_RECONNECT); + if (str != NULL && pw_properties_parse_bool(str)) { + pw_registry_proxy_destroy(impl->registry_proxy, ep->obj.id); + return -ENOENT; + } + } + } + + if (find.endpoint == NULL) { + struct client *client; + + pw_log_warn(NAME " %p: no endpoint found for %d", impl, ep->obj.id); + + client = find_object(impl, ep->client_id); + if (client && client->obj.type == PW_TYPE_INTERFACE_Client) { + pw_client_proxy_error((struct pw_client_proxy*)client->obj.proxy, + ep->obj.id, -ENOENT, "no endpoint available"); + } + return -ENOENT; + } + peer = find.endpoint; + + if (exclusive && peer->busy) { + pw_log_warn(NAME" %p: endpoint %d busy, can't get exclusive access", impl, peer->obj.id); + return -EBUSY; + } + peer->exclusive = exclusive; + + pw_log_debug(NAME" %p: linking to endpoint '%d'", impl, peer->obj.id); + + peer->busy = true; + +do_link: + link_endpoints(ep, direction, peer, 1); + + return 1; +} + +static void do_rescan(struct impl *impl) +{ + struct endpoint *ep; + + clock_gettime(CLOCK_MONOTONIC, &impl->now); + pw_log_debug("media-session %p: do rescan", impl); + + spa_list_for_each(ep, &impl->endpoint_list, l) + rescan_endpoint(impl, ep); +} + +static void core_done(void *data, uint32_t id, int seq) +{ + struct impl *impl = data; + pw_log_debug("media-session %p: sync %u %d/%d", impl, id, seq, impl->seq); + if (impl->seq == seq) + do_rescan(impl); +} + +static const struct pw_core_proxy_events core_events = { + PW_VERSION_CORE_EVENTS, + .done = core_done +}; + +static void on_state_changed(void *_data, enum pw_remote_state old, enum pw_remote_state state, const char *error) +{ + struct impl *impl = _data; + + switch (state) { + case PW_REMOTE_STATE_ERROR: + pw_log_error(NAME" %p: remote error: %s", impl, error); + break; + + case PW_REMOTE_STATE_CONNECTED: + pw_log_info(NAME" %p: connected", impl); + impl->core_proxy = pw_remote_get_core_proxy(impl->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); + schedule_rescan(impl); + break; + + case PW_REMOTE_STATE_UNCONNECTED: + pw_log_info(NAME" %p: disconnected", impl); + impl->core_proxy = NULL; + impl->registry_proxy = NULL; + break; + + default: + printf("remote state: \"%s\"\n", pw_remote_state_as_string(state)); + break; + } +} + +static const struct pw_remote_events remote_events = { + PW_VERSION_REMOTE_EVENTS, + .state_changed = on_state_changed, +}; + +int sm_policy_ep_start(struct pw_remote *remote) +{ + struct impl *impl; + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + return -errno; + + impl->core = pw_remote_get_core(remote); + impl->remote = remote; + + pw_map_init(&impl->globals, 64, 64); + + spa_list_init(&impl->client_list); + spa_list_init(&impl->session_list); + spa_list_init(&impl->endpoint_list); + + pw_remote_add_listener(impl->remote, &impl->remote_listener, &remote_events, impl); + + return 0; +} + +int sm_policy_ep_stop(struct pw_core *core) +{ + return 0; +} diff --git a/src/examples/media-session/policy.c b/src/examples/media-session/policy.c index c5b3be323..52cd08cb6 100644 --- a/src/examples/media-session/policy.c +++ b/src/examples/media-session/policy.c @@ -40,7 +40,7 @@ #include "pipewire/private.h" #include "extensions/session-manager.h" -#define NAME "media-session" +#define NAME "policy" #define DEFAULT_CHANNELS 2 #define DEFAULT_SAMPLERATE 48000 @@ -1236,13 +1236,13 @@ static const struct pw_remote_events remote_events = { .state_changed = on_state_changed, }; -int sm_policy_start(struct pw_remote *remote) +void *sm_policy_start(struct pw_remote *remote) { struct impl *impl; impl = calloc(1, sizeof(struct impl)); if (impl == NULL) - return -errno; + return NULL; impl->core = pw_remote_get_core(remote); impl->remote = remote; @@ -1255,10 +1255,10 @@ int sm_policy_start(struct pw_remote *remote) pw_remote_add_listener(impl->remote, &impl->remote_listener, &remote_events, impl); - return 0; + return impl; } -int sm_policy_stop(struct pw_core *core) +int sm_policy_stop(void *data) { return 0; } diff --git a/src/examples/media-session/stream-monitor.c b/src/examples/media-session/stream-monitor.c new file mode 100644 index 000000000..190a8e9bf --- /dev/null +++ b/src/examples/media-session/stream-monitor.c @@ -0,0 +1,756 @@ +/* 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 "pipewire/pipewire.h" +#include "pipewire/private.h" +#include "extensions/session-manager.h" + +#define NAME "stream-monitor" + +#define DEFAULT_CHANNELS 2 +#define DEFAULT_SAMPLERATE 48000 + +struct client_endpoint; + +struct impl { + struct timespec now; + + struct pw_core *core; + struct pw_remote *remote; + struct spa_hook remote_listener; + + uint32_t session_id; + + struct pw_core_proxy *core_proxy; + struct spa_hook core_listener; + + struct pw_registry_proxy *registry_proxy; + struct spa_hook registry_listener; + + struct pw_map globals; + + struct spa_list client_list; + struct spa_list node_list; + int seq; +}; + +struct object { + struct impl *impl; + uint32_t id; + uint32_t type; + struct pw_proxy *proxy; + struct spa_hook listener; +}; + +struct node { + struct object obj; + + struct spa_list l; + struct client_endpoint *endpoint; + + struct spa_hook listener; + struct pw_node_info *info; + + struct spa_list port_list; + + enum pw_direction direction; +#define NODE_TYPE_UNKNOWN 0 +#define NODE_TYPE_STREAM 1 + uint32_t type; + char *media; + + uint32_t media_type; + uint32_t media_subtype; + struct spa_audio_info_raw format; +}; + +struct endpoint { + struct object obj; +}; + +struct port { + struct object obj; + + struct spa_list l; + enum pw_direction direction; + struct pw_port_info *info; + struct node *node; +#define PORT_FLAG_NONE 0 +#define PORT_FLAG_DSP (1<<0) +#define PORT_FLAG_SKIP (1<<1) + uint32_t flags; + + struct spa_hook listener; +}; + +struct stream { + struct pw_properties *props; + struct pw_endpoint_stream_info info; + + unsigned int active:1; +}; + +struct client_endpoint { + struct spa_list link; + + struct impl *impl; + + struct pw_properties *props; + struct node *node; + + struct pw_client_endpoint_proxy *client_endpoint; + struct spa_hook client_endpoint_listener; + struct pw_endpoint_info info; + + struct stream stream; +}; + +static void add_object(struct impl *impl, struct object *obj) +{ + size_t size = pw_map_get_size(&impl->globals); + while (obj->id > size) + pw_map_insert_at(&impl->globals, size++, NULL); + pw_map_insert_at(&impl->globals, obj->id, obj); +} + +static void remove_object(struct impl *impl, struct object *obj) +{ + pw_map_insert_at(&impl->globals, obj->id, NULL); +} + +static void *find_object(struct impl *impl, uint32_t id) +{ + void *obj; + if ((obj = pw_map_lookup(&impl->globals, id)) != NULL) + return obj; + return NULL; +} + +static int client_endpoint_set_id(void *object, uint32_t id) +{ + struct client_endpoint *endpoint = object; + + endpoint->info.id = id; + + pw_client_endpoint_proxy_update(endpoint->client_endpoint, + PW_CLIENT_ENDPOINT_UPDATE_INFO, + 0, NULL, + &endpoint->info); + return 0; +} + +static int client_endpoint_set_session_id(void *object, uint32_t id) +{ + struct client_endpoint *endpoint = object; + 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 int client_endpoint_create_link(void *object, const struct spa_dict *props) +{ + struct client_endpoint *endpoint = object; + struct impl *impl = endpoint->impl; + const char *str; + struct endpoint *ep; + struct node *node = endpoint->node; + struct pw_properties *p; + int res; + + pw_log_debug("create link"); + + if (props == NULL) + return -EINVAL; + + p = pw_properties_new(NULL, NULL); + if (p == NULL) + return -errno; + + if (endpoint->info.direction == PW_DIRECTION_OUTPUT) { + pw_properties_setf(p, PW_KEY_LINK_OUTPUT_NODE, "%d", endpoint->node->info->id); + pw_properties_setf(p, PW_KEY_LINK_OUTPUT_PORT, "-1"); + str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_NODE); + } else { + pw_properties_setf(p, PW_KEY_LINK_INPUT_NODE, "%d", endpoint->node->info->id); + pw_properties_setf(p, PW_KEY_LINK_INPUT_PORT, "-1"); + str = spa_dict_lookup(props, PW_KEY_LINK_OUTPUT_NODE); + } + + if (!endpoint->stream.active) { + char buf[1024]; + struct spa_pod_builder b = { 0, }; + struct spa_pod *param; + + node->format.rate = 48000; + + spa_pod_builder_init(&b, buf, sizeof(buf)); + param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &node->format); + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, + SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(endpoint->info.direction), + SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), + SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_Bool(true), + SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); + + if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) + spa_debug_pod(2, NULL, param); + + pw_node_proxy_set_param((struct pw_node_proxy*)node->obj.proxy, + SPA_PARAM_PortConfig, 0, param); + + endpoint->stream.active = true; + } + + str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_NODE); + if (str == NULL) { + res = -EINVAL; + goto exit; + } + + ep = find_object(impl, atoi(str)); + if (ep == NULL) { + res = -EINVAL; + goto exit; + } + + pw_endpoint_proxy_create_link((struct pw_endpoint_proxy*)ep->obj.proxy, &p->dict); + + res = 0; + +exit: + pw_properties_free(p); + + return res; +} + +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, + .create_link = client_endpoint_create_link, +}; + +static struct client_endpoint *make_endpoint(struct node *node) +{ + struct impl *impl = node->obj.impl; + struct pw_properties *props; + struct client_endpoint *endpoint; + struct stream *s; + struct pw_proxy *proxy; + const char *str, *media_class = NULL, *name = NULL; + struct spa_dict *dict = node->info->props; + + props = pw_properties_new(NULL, NULL); + if (props == NULL) + return NULL; + + if (node->info && node->info->props) { + if ((media_class = spa_dict_lookup(dict, PW_KEY_MEDIA_CLASS)) != NULL) + pw_properties_set(props, PW_KEY_MEDIA_CLASS, media_class); + if ((name = spa_dict_lookup(dict, PW_KEY_MEDIA_NAME)) != NULL) + pw_properties_set(props, PW_KEY_ENDPOINT_NAME, name); + if ((str = spa_dict_lookup(dict, PW_KEY_NODE_AUTOCONNECT)) != NULL) + pw_properties_set(props, PW_KEY_ENDPOINT_AUTOCONNECT, str); + } + + proxy = pw_core_proxy_create_object(impl->core_proxy, + "client-endpoint", + PW_TYPE_INTERFACE_ClientEndpoint, + PW_VERSION_CLIENT_ENDPOINT_PROXY, + &props->dict, sizeof(*endpoint)); + if (proxy == NULL) { + pw_properties_free(props); + return NULL; + } + + endpoint = pw_proxy_get_user_data(proxy); + endpoint->impl = impl; + endpoint->node = node; + endpoint->props = props; + endpoint->client_endpoint = (struct pw_client_endpoint_proxy *) proxy; + endpoint->info.version = PW_VERSION_ENDPOINT_INFO; + endpoint->info.name = (char*)pw_properties_get(endpoint->props, PW_KEY_ENDPOINT_NAME); + endpoint->info.media_class = (char*)spa_dict_lookup(node->info->props, PW_KEY_MEDIA_CLASS); + endpoint->info.session_id = impl->session_id; + endpoint->info.direction = node->direction; + endpoint->info.flags = 0; + endpoint->info.change_mask = + PW_ENDPOINT_CHANGE_MASK_STREAMS | + PW_ENDPOINT_CHANGE_MASK_SESSION | + PW_ENDPOINT_CHANGE_MASK_PROPS; + endpoint->info.n_streams = 1; + endpoint->info.props = &endpoint->props->dict; + + pw_client_endpoint_proxy_add_listener(endpoint->client_endpoint, + &endpoint->client_endpoint_listener, + &client_endpoint_events, + endpoint); + + s = &endpoint->stream; + s->props = pw_properties_new(NULL, NULL); + if ((str = spa_dict_lookup(dict, PW_KEY_MEDIA_CLASS)) != NULL) + pw_properties_set(s->props, PW_KEY_MEDIA_CLASS, str); + if (node->direction == PW_DIRECTION_OUTPUT) + pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Playback"); + else + pw_properties_set(s->props, PW_KEY_STREAM_NAME, "Capture"); + + s->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; + s->info.id = 0; + s->info.endpoint_id = endpoint->info.id; + s->info.name = (char*)pw_properties_get(s->props, PW_KEY_STREAM_NAME); + s->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS; + s->info.props = &s->props->dict; + + pw_log_debug("stream %d", node->obj.id); + pw_client_endpoint_proxy_stream_update(endpoint->client_endpoint, + s->info.id, + PW_CLIENT_ENDPOINT_STREAM_UPDATE_INFO, + 0, NULL, + &s->info); + + return endpoint; +} +static void destroy_endpoint(struct client_endpoint *endpoint) +{ + pw_proxy_destroy((struct pw_proxy*)endpoint->client_endpoint); +} + +static void node_event_info(void *object, const struct pw_node_info *info) +{ + struct node *n = object; + struct impl *impl = n->obj.impl; + + pw_log_debug(NAME" %p: info for node %d type %d", impl, n->obj.id, n->type); + n->info = pw_node_info_update(n->info, info); +} + +static void node_event_param(void *object, int seq, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct node *n = object; + struct impl *impl = n->obj.impl; + struct spa_audio_info_raw info = { 0, }; + + pw_log_debug(NAME" %p: param for node %d, %d", impl, n->obj.id, id); + + if (id != SPA_PARAM_EnumFormat) + goto error; + + if (spa_format_parse(param, &n->media_type, &n->media_subtype) < 0) + goto error; + + if (n->media_type != SPA_MEDIA_TYPE_audio || + n->media_subtype != SPA_MEDIA_SUBTYPE_raw) + return; + + spa_pod_object_fixate((struct spa_pod_object*)param); + if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) + spa_debug_pod(2, NULL, param); + + if (spa_format_audio_raw_parse(param, &info) < 0) + goto error; + + if (n->format.channels < info.channels) + n->format = info; + + if (n->endpoint == NULL) { + n->endpoint = make_endpoint(n); + } + return; + + error: + pw_log_warn("unhandled param:"); + if (pw_log_level_enabled(SPA_LOG_LEVEL_WARN)) + spa_debug_pod(2, NULL, param); + return; +} + +static const struct pw_node_proxy_events node_events = { + PW_VERSION_NODE_PROXY_EVENTS, + .info = node_event_info, + .param = node_event_param, +}; + +static void node_proxy_destroy(void *data) +{ + struct node *n = data; + struct impl *impl = n->obj.impl; + struct port *p, *t; + + pw_log_debug(NAME " %p: proxy destroy node %d", impl, n->obj.id); + + spa_list_remove(&n->l); + + spa_list_for_each_safe(p, t, &n->port_list, l) { + spa_list_remove(&p->l); + p->node = NULL; + } + if (n->info) + pw_node_info_free(n->info); + if (n->endpoint) + destroy_endpoint(n->endpoint); + + free(n->media); +} + +static const struct pw_proxy_events node_proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = node_proxy_destroy, +}; + +static int +handle_node(struct impl *impl, uint32_t id, + uint32_t type, const struct spa_dict *props) +{ + const char *media_class; + enum pw_direction direction; + struct pw_proxy *p; + struct node *node; + + media_class = props ? spa_dict_lookup(props, PW_KEY_MEDIA_CLASS) : NULL; + + pw_log_debug(NAME" %p: node "PW_KEY_MEDIA_CLASS" %s", impl, media_class); + + if (media_class == NULL) + return 0; + + if (strstr(media_class, "Stream/") != media_class) + return 0; + + media_class += strlen("Stream/"); + + if (strstr(media_class, "Output/") == media_class) { + direction = PW_DIRECTION_OUTPUT; + media_class += strlen("Output/"); + } + else if (strstr(media_class, "Input/") == media_class) { + direction = PW_DIRECTION_INPUT; + media_class += strlen("Input/"); + } + else + return 0; + + p = pw_registry_proxy_bind(impl->registry_proxy, + id, type, PW_VERSION_NODE_PROXY, + sizeof(struct node)); + + node = pw_proxy_get_user_data(p); + node->obj.impl = impl; + node->obj.id = id; + node->obj.type = type; + node->obj.proxy = p; + spa_list_init(&node->port_list); + pw_proxy_add_listener(p, &node->obj.listener, &node_proxy_events, node); + pw_proxy_add_object_listener(p, &node->listener, &node_events, node); + add_object(impl, &node->obj); + spa_list_append(&impl->node_list, &node->l); + node->type = NODE_TYPE_UNKNOWN; + + node->direction = direction; + node->type = NODE_TYPE_STREAM; + node->media = strdup(media_class); + pw_log_debug(NAME "%p: node %d is stream %s", impl, id, node->media); + + pw_node_proxy_enum_params((struct pw_node_proxy*)p, + 0, SPA_PARAM_EnumFormat, + 0, -1, NULL); + return 1; +} + +static void port_event_info(void *object, const struct pw_port_info *info) +{ + struct port *p = object; + pw_log_debug(NAME" %p: info for port %d", p->obj.impl, p->obj.id); + p->info = pw_port_info_update(p->info, info); +} + +static const struct pw_port_proxy_events port_events = { + PW_VERSION_PORT_PROXY_EVENTS, + .info = port_event_info, +}; + +static void port_proxy_destroy(void *data) +{ + struct port *p = data; + + pw_log_debug(NAME " %p: proxy destroy port %d", p->obj.impl, p->obj.id); + + if (p->node) { + spa_list_remove(&p->l); + p->node = NULL; + } + if (p->info) + pw_port_info_free(p->info); +} + +static const struct pw_proxy_events port_proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = port_proxy_destroy, +}; + +static int +handle_port(struct impl *impl, uint32_t id, uint32_t type, + const struct spa_dict *props) +{ + struct port *port; + struct pw_proxy *p; + struct node *node; + const char *str; + uint32_t node_id; + + if (props == NULL || (str = spa_dict_lookup(props, PW_KEY_NODE_ID)) == NULL) + return -EINVAL; + + node_id = atoi(str); + + if ((node = find_object(impl, node_id)) == NULL) + return 0; + + if (props == NULL || (str = spa_dict_lookup(props, PW_KEY_PORT_DIRECTION)) == NULL) + return -EINVAL; + + p = pw_registry_proxy_bind(impl->registry_proxy, + id, type, PW_VERSION_PORT_PROXY, + sizeof(struct port)); + + port = pw_proxy_get_user_data(p); + port->obj.impl = impl; + port->obj.id = id; + port->obj.type = type; + port->obj.proxy = p; + port->node = node; + port->direction = strcmp(str, "out") ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT; + + if (props != NULL && (str = spa_dict_lookup(props, PW_KEY_FORMAT_DSP)) != NULL) + port->flags |= PORT_FLAG_DSP; + + pw_proxy_add_listener(p, &port->obj.listener, &port_proxy_events, port); + pw_proxy_add_object_listener(p, &port->listener, &port_events, port); + add_object(impl, &port->obj); + + spa_list_append(&node->port_list, &port->l); + + pw_log_debug(NAME" %p: new port %d for node %d type %d %08x", impl, id, node_id, + node->type, port->flags); + + return 0; +} + +static int +handle_endpoint(struct impl *impl, uint32_t id, uint32_t type, + const struct spa_dict *props) +{ + struct endpoint *ep; + struct pw_proxy *p; + + p = pw_registry_proxy_bind(impl->registry_proxy, + id, type, PW_VERSION_ENDPOINT_PROXY, + sizeof(struct endpoint)); + + ep = pw_proxy_get_user_data(p); + ep->obj.impl = impl; + ep->obj.id = id; + ep->obj.type = type; + ep->obj.proxy = p; + add_object(impl, &ep->obj); + + pw_log_debug(NAME" %p: new endpoint %d", impl, id); + + return 0; +} + +static void +registry_global(void *data,uint32_t id, + uint32_t permissions, uint32_t type, uint32_t version, + const struct spa_dict *props) +{ + struct impl *impl = data; + int res; + + pw_log_debug(NAME " %p: new global '%d' %d", impl, id, type); + + switch (type) { + case PW_TYPE_INTERFACE_Node: + res = handle_node(impl, id, type, props); + break; + + case PW_TYPE_INTERFACE_Port: + res = handle_port(impl, id, type, props); + break; + + case PW_TYPE_INTERFACE_Endpoint: + res = handle_endpoint(impl, id, type, props); + break; + + default: + res = 0; + break; + } + if (res < 0) { + pw_log_warn(NAME" %p: can't handle global %d: %s", impl, id, spa_strerror(res)); + } +} + +static void +registry_global_remove(void *data, uint32_t id) +{ + struct impl *impl = data; + struct object *obj; + + pw_log_debug(NAME " %p: remove global '%d'", impl, id); + + if ((obj = find_object(impl, id)) == NULL) + return; + + remove_object(impl, obj); +} + +static const struct pw_registry_proxy_events registry_events = { + PW_VERSION_REGISTRY_PROXY_EVENTS, + .global = registry_global, + .global_remove = registry_global_remove, +}; + + +#if 0 +static void stream_set_volume(struct impl *impl, struct node *node, float volume, bool mute) +{ + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + pw_log_debug(NAME " %p: node %d set volume:%f mute:%d", impl, node->obj.id, volume, mute); + + pw_node_proxy_set_param((struct pw_node_proxy*)node->obj.proxy, + SPA_PARAM_Props, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_volume, SPA_POD_Float(volume), + SPA_PROP_mute, SPA_POD_Bool(mute))); +} + +static void rescan_session(struct impl *impl, struct session *sess) +{ + struct node *node = sess->node; + struct spa_audio_info_raw info = { 0, }; + uint8_t buf[1024]; + struct spa_pod_builder b = { 0, }; + struct spa_pod *param; + + if (!sess->starting) + return; + + if (node->info->props == NULL) { + pw_log_debug(NAME " %p: node %p has no properties", impl, node); + return; + } + + if (node->media_type != SPA_MEDIA_TYPE_audio || + node->media_subtype != SPA_MEDIA_SUBTYPE_raw) { + pw_log_debug(NAME " %p: node %p has no media type", impl, node); + return; + } + + info = node->format; + info.rate = DEFAULT_SAMPLERATE; + + pw_log_debug(NAME" %p: setting profile for session %d %d", impl, sess->id, sess->direction); + + spa_pod_builder_init(&b, buf, sizeof(buf)); + param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info); + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, + SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(pw_direction_reverse(sess->direction)), + SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), + SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_Bool(true), + SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); + + pw_node_proxy_set_param((struct pw_node_proxy*)sess->node->obj.proxy, + SPA_PARAM_PortConfig, 0, param); + schedule_rescan(impl); + + sess->starting = false; +} +#endif + +void * sm_stream_monitor_start(struct pw_remote *remote, int session_id) +{ + struct impl *impl; + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + return NULL; + + impl->core = pw_remote_get_core(remote); + impl->remote = remote; + impl->session_id = session_id; + + pw_map_init(&impl->globals, 64, 64); + + spa_list_init(&impl->client_list); + spa_list_init(&impl->node_list); + + impl->core_proxy = pw_remote_get_core_proxy(impl->remote); + 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); + + return impl; +} + +int sm_stream_monitor_stop(struct impl *impl) +{ + return 0; +} diff --git a/src/examples/meson.build b/src/examples/meson.build index 53acaeddd..f5b664520 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -47,7 +47,9 @@ executable('export-spa-device', executable('media-session', 'media-session/media-session.c', 'media-session/monitor.c', + 'media-session/stream-monitor.c', 'media-session/policy.c', + 'media-session/policy-ep.c', c_args : [ '-D_GNU_SOURCE' ], install: false, dependencies : [dbus_dep, pipewire_dep, alsa_dep, mathlib], diff --git a/src/extensions/session-manager/impl-interfaces.h b/src/extensions/session-manager/impl-interfaces.h index fd1e1e039..3b77f4a13 100644 --- a/src/extensions/session-manager/impl-interfaces.h +++ b/src/extensions/session-manager/impl-interfaces.h @@ -43,7 +43,8 @@ struct pw_client_endpoint_proxy { struct spa_interface iface; }; #define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID 1 #define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM 2 #define PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM 3 -#define PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM 4 +#define PW_CLIENT_ENDPOINT_PROXY_EVENT_CREATE_LINK 4 +#define PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM 5 struct pw_client_endpoint_proxy_events { #define PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS 0 @@ -132,6 +133,8 @@ struct pw_client_endpoint_proxy_events { int (*stream_set_param) (void *object, uint32_t stream_id, uint32_t id, uint32_t flags, const struct spa_pod *param); + + int (*create_link) (void *object, const struct spa_dict *props); }; #define PW_CLIENT_ENDPOINT_PROXY_METHOD_ADD_LISTENER 0 @@ -140,7 +143,7 @@ struct pw_client_endpoint_proxy_events { #define PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM 3 struct pw_client_endpoint_proxy_methods { -#define PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS 0 +#define PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS 0 uint32_t version; /**< version of this structure */ int (*add_listener) (void *object, @@ -190,10 +193,8 @@ struct pw_client_session_proxy { struct spa_interface iface; }; #define PW_CLIENT_SESSION_PROXY_EVENT_SET_ID 0 #define PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM 1 #define PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM 2 -#define PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK 3 -#define PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK 4 -#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE 5 -#define PW_CLIENT_SESSION_PROXY_EVENT_NUM 6 +#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE 3 +#define PW_CLIENT_SESSION_PROXY_EVENT_NUM 4 struct pw_client_session_proxy_events { #define PW_VERSION_CLIENT_SESSION_PROXY_EVENTS 0 @@ -266,10 +267,6 @@ struct pw_client_session_proxy_events { uint32_t id, uint32_t flags, const struct spa_pod *param); - int (*create_link) (void *object, const struct spa_dict *props); - - int (*destroy_link) (void *object, uint32_t link_id); - int (*link_request_state) (void *object, uint32_t link_id, uint32_t state); }; @@ -279,7 +276,7 @@ struct pw_client_session_proxy_events { #define PW_CLIENT_SESSION_PROXY_METHOD_NUM 3 struct pw_client_session_proxy_methods { -#define PW_VERSION_CLIENT_SESSION_PROXY_METHODS 0 +#define PW_VERSION_CLIENT_SESSION_PROXY_METHODS 0 uint32_t version; /**< version of this structure */ int (*add_listener) (void *object, diff --git a/src/extensions/session-manager/interfaces.h b/src/extensions/session-manager/interfaces.h index 0651e8bfb..6b5d51b77 100644 --- a/src/extensions/session-manager/interfaces.h +++ b/src/extensions/session-manager/interfaces.h @@ -129,8 +129,6 @@ struct pw_session_proxy_methods { */ int (*set_param) (void *object, uint32_t id, uint32_t flags, const struct spa_pod *param); - - int (*create_link) (void *object, const struct spa_dict *props); }; #define pw_session_proxy_method(o,method,version,...) \ @@ -146,7 +144,8 @@ struct pw_session_proxy_methods { #define pw_session_proxy_add_listener(c,...) pw_session_proxy_method(c,add_listener,0,__VA_ARGS__) #define pw_session_proxy_subscribe_params(c,...) pw_session_proxy_method(c,subscribe_params,0,__VA_ARGS__) #define pw_session_proxy_enum_params(c,...) pw_session_proxy_method(c,enum_params,0,__VA_ARGS__) -#define pw_session_proxy_create_link(c,...) pw_session_proxy_method(c,create_link,0,__VA_ARGS__) +#define pw_session_proxy_set_param(c,...) pw_session_proxy_method(c,set_param,0,__VA_ARGS__) + /* Endpoint */ @@ -185,7 +184,8 @@ struct pw_endpoint_proxy_events { #define PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS 1 #define PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS 2 #define PW_ENDPOINT_PROXY_METHOD_SET_PARAM 3 -#define PW_ENDPOINT_PROXY_METHOD_NUM 4 +#define PW_ENDPOINT_PROXY_METHOD_CREATE_LINK 4 +#define PW_ENDPOINT_PROXY_METHOD_NUM 5 struct pw_endpoint_proxy_methods { #define PW_VERSION_ENDPOINT_PROXY_METHODS 0 @@ -232,6 +232,8 @@ struct pw_endpoint_proxy_methods { */ int (*set_param) (void *object, uint32_t id, uint32_t flags, const struct spa_pod *param); + + int (*create_link) (void *object, const struct spa_dict *props); }; #define pw_endpoint_proxy_method(o,method,version,...) \ @@ -247,6 +249,8 @@ struct pw_endpoint_proxy_methods { #define pw_endpoint_proxy_add_listener(c,...) pw_endpoint_proxy_method(c,add_listener,0,__VA_ARGS__) #define pw_endpoint_proxy_subscribe_params(c,...) pw_endpoint_proxy_method(c,subscribe_params,0,__VA_ARGS__) #define pw_endpoint_proxy_enum_params(c,...) pw_endpoint_proxy_method(c,enum_params,0,__VA_ARGS__) +#define pw_endpoint_proxy_set_param(c,...) pw_endpoint_proxy_method(c,set_param,0,__VA_ARGS__) +#define pw_endpoint_proxy_create_link(c,...) pw_endpoint_proxy_method(c,create_link,0,__VA_ARGS__) /* Endpoint Stream */ @@ -347,6 +351,7 @@ struct pw_endpoint_stream_proxy_methods { #define pw_endpoint_stream_proxy_add_listener(c,...) pw_endpoint_stream_proxy_method(c,add_listener,0,__VA_ARGS__) #define pw_endpoint_stream_proxy_subscribe_params(c,...) pw_endpoint_stream_proxy_method(c,subscribe_params,0,__VA_ARGS__) #define pw_endpoint_stream_proxy_enum_params(c,...) pw_endpoint_stream_proxy_method(c,enum_params,0,__VA_ARGS__) +#define pw_endpoint_stream_proxy_set_param(c,...) pw_endpoint_stream_proxy_method(c,set_param,0,__VA_ARGS__) /* Endpoint Link */ @@ -436,9 +441,6 @@ struct pw_endpoint_link_proxy_methods { const struct spa_pod *param); int (*request_state) (void *object, enum pw_endpoint_link_state state); - - int (*destroy) (void *object); - }; #define pw_endpoint_link_proxy_method(o,method,version,...) \ @@ -454,8 +456,8 @@ struct pw_endpoint_link_proxy_methods { #define pw_endpoint_link_proxy_add_listener(c,...) pw_endpoint_link_proxy_method(c,add_listener,0,__VA_ARGS__) #define pw_endpoint_link_proxy_subscribe_params(c,...) pw_endpoint_link_proxy_method(c,subscribe_params,0,__VA_ARGS__) #define pw_endpoint_link_proxy_enum_params(c,...) pw_endpoint_link_proxy_method(c,enum_params,0,__VA_ARGS__) +#define pw_endpoint_link_proxy_set_param(c,...) pw_endpoint_link_proxy_method(c,set_param,0,__VA_ARGS__) #define pw_endpoint_link_proxy_request_state(c,...) pw_endpoint_link_proxy_method(c,request_state,0,__VA_ARGS__) -#define pw_endpoint_link_proxy_destroy(c,...) pw_endpoint_link_proxy_method(c,destroy,0,__VA_ARGS__) #ifdef __cplusplus diff --git a/src/extensions/session-manager/introspect.h b/src/extensions/session-manager/introspect.h index 97e8d5442..3df3c8066 100644 --- a/src/extensions/session-manager/introspect.h +++ b/src/extensions/session-manager/introspect.h @@ -34,13 +34,6 @@ extern "C" { #endif -enum pw_endpoint_direction { - PW_ENDPOINT_DIRECTION_SINK_INPUT = SPA_DIRECTION_INPUT, - PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT = SPA_DIRECTION_OUTPUT, - PW_ENDPOINT_DIRECTION_SOURCE, - PW_ENDPOINT_DIRECTION_SINK, -}; - enum pw_endpoint_link_state { PW_ENDPOINT_LINK_STATE_ERROR = -1, PW_ENDPOINT_LINK_STATE_PREPARING, @@ -67,7 +60,7 @@ struct pw_endpoint_info { uint32_t id; /**< the endpoint id (global) */ char *name; /**< name of the endpoint */ char *media_class; /**< media class of the endpoint */ - enum pw_endpoint_direction direction; /**< direction of the endpoint */ + enum pw_direction direction; /**< direction of the endpoint */ #define PW_ENDPOINT_FLAG_PROVIDES_SESSION (1 << 0) uint32_t flags; /**< additional flags */ #define PW_ENDPOINT_CHANGE_MASK_STREAMS (1 << 0) diff --git a/src/extensions/session-manager/keys.h b/src/extensions/session-manager/keys.h index 644b119e9..19beb261b 100644 --- a/src/extensions/session-manager/keys.h +++ b/src/extensions/session-manager/keys.h @@ -36,6 +36,8 @@ extern "C" { #define PW_KEY_ENDPOINT_NAME "endpoint.name" /**< the name of an endpoint */ #define PW_KEY_ENDPOINT_ICON_NAME "endpoint.icon-name" /**< an XDG icon name for the device. * Ex. "sound-card-speakers-usb" */ +#define PW_KEY_ENDPOINT_AUTOCONNECT "endpoint.autoconnect" /**< try to automatically connect this + * endpoint. */ #define PW_KEY_STREAM_ID "stream.id" /**< id of a stream */ #define PW_KEY_STREAM_NAME "stream.name" /**< unique name of a stream */ diff --git a/src/modules/module-session-manager/client-endpoint.c b/src/modules/module-session-manager/client-endpoint.c index 5ff32cb58..713e776c8 100644 --- a/src/modules/module-session-manager/client-endpoint.c +++ b/src/modules/module-session-manager/client-endpoint.c @@ -83,7 +83,6 @@ static int client_endpoint_stream_update(void *object, PW_KEY_FACTORY_ID, PW_KEY_CLIENT_ID, PW_KEY_ENDPOINT_ID, - PW_KEY_MEDIA_CLASS, PW_KEY_PRIORITY_SESSION, PW_KEY_STREAM_NAME, PW_KEY_STREAM_DESCRIPTION, diff --git a/src/modules/module-session-manager/client-endpoint.h b/src/modules/module-session-manager/client-endpoint.h index 394e9fa8f..b59cde7df 100644 --- a/src/modules/module-session-manager/client-endpoint.h +++ b/src/modules/module-session-manager/client-endpoint.h @@ -50,6 +50,8 @@ struct client_endpoint { pw_client_endpoint_resource(r,set_param,0,__VA_ARGS__) #define pw_client_endpoint_resource_stream_set_param(r,...) \ pw_client_endpoint_resource(r,stream_set_param,0,__VA_ARGS__) +#define pw_client_endpoint_resource_create_link(r,...) \ + pw_client_endpoint_resource(r,create_link,0,__VA_ARGS__) int client_endpoint_factory_init(struct pw_module *module); diff --git a/src/modules/module-session-manager/endpoint-link.c b/src/modules/module-session-manager/endpoint-link.c index 215ceb71d..958122c0c 100644 --- a/src/modules/module-session-manager/endpoint-link.c +++ b/src/modules/module-session-manager/endpoint-link.c @@ -135,25 +135,12 @@ static int endpoint_link_request_state(void *object, enum pw_endpoint_link_state return 0; } -static int endpoint_link_destroy(void *object) -{ - struct pw_resource *resource = object; - struct resource_data *data = pw_resource_get_user_data(resource); - struct endpoint_link *this = data->link; - - pw_client_session_resource_destroy_link(this->client_sess->resource, - this->id); - - return 0; -} - static const struct pw_endpoint_link_proxy_methods methods = { PW_VERSION_ENDPOINT_LINK_PROXY_METHODS, .subscribe_params = endpoint_link_subscribe_params, .enum_params = endpoint_link_enum_params, .set_param = endpoint_link_set_param, .request_state = endpoint_link_request_state, - .destroy = endpoint_link_destroy, }; static void endpoint_link_notify_subscribed(struct endpoint_link *this, diff --git a/src/modules/module-session-manager/endpoint.c b/src/modules/module-session-manager/endpoint.c index 435c6a4d3..df339741b 100644 --- a/src/modules/module-session-manager/endpoint.c +++ b/src/modules/module-session-manager/endpoint.c @@ -117,17 +117,32 @@ static int endpoint_set_param (void *object, uint32_t id, uint32_t flags, struct resource_data *data = pw_resource_get_user_data(resource); struct endpoint *this = data->endpoint; + pw_log_debug("%p", this); pw_client_endpoint_resource_set_param(this->client_ep->resource, id, flags, param); return 0; } +static int endpoint_create_link(void *object, const struct spa_dict *props) +{ + struct pw_resource *resource = object; + struct resource_data *data = pw_resource_get_user_data(resource); + struct endpoint *this = data->endpoint; + + pw_log_debug("%p", this); + pw_client_endpoint_resource_create_link(this->client_ep->resource, + props); + + return 0; +} + static const struct pw_endpoint_proxy_methods methods = { PW_VERSION_ENDPOINT_PROXY_METHODS, .subscribe_params = endpoint_subscribe_params, .enum_params = endpoint_enum_params, .set_param = endpoint_set_param, + .create_link = endpoint_create_link, }; static void endpoint_notify_subscribed(struct endpoint *this, @@ -288,6 +303,7 @@ int endpoint_init(struct endpoint *this, PW_KEY_CLIENT_ID, PW_KEY_DEVICE_ID, PW_KEY_MEDIA_CLASS, + PW_KEY_PRIORITY_SESSION, PW_KEY_ENDPOINT_NAME, PW_KEY_ENDPOINT_ICON_NAME, NULL diff --git a/src/modules/module-session-manager/protocol-native.c b/src/modules/module-session-manager/protocol-native.c index f34f87244..1133c7d2c 100644 --- a/src/modules/module-session-manager/protocol-native.c +++ b/src/modules/module-session-manager/protocol-native.c @@ -170,6 +170,8 @@ marshal_pw_endpoint_info(struct spa_pod_builder *b, SPA_POD_Int(info->direction), SPA_POD_Int(info->flags), SPA_POD_Int(info->change_mask), + SPA_POD_Int(info->n_streams), + SPA_POD_Int(info->session_id), NULL); push_dict(b, info->props); push_param_infos(b, info->n_params, info->params); @@ -191,6 +193,8 @@ do { \ SPA_POD_Int(&(info)->direction), \ SPA_POD_Int(&(info)->flags), \ SPA_POD_Int(&(info)->change_mask), \ + SPA_POD_Int(&(info)->n_streams), \ + SPA_POD_Int(&(info)->session_id), \ NULL) < 0) \ return -EINVAL; \ \ @@ -369,6 +373,20 @@ static int client_endpoint_marshal_stream_set_param (void *object, return pw_protocol_native_end_resource(resource, b); } +static int client_endpoint_marshal_create_link (void *object, + const struct spa_dict *props) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, + PW_CLIENT_ENDPOINT_PROXY_EVENT_CREATE_LINK, NULL); + + push_dict(b, props); + + return pw_protocol_native_end_resource(resource, b); +} + static int client_endpoint_marshal_add_listener(void *object, struct spa_hook *listener, const struct pw_client_endpoint_proxy_events *events, @@ -518,6 +536,22 @@ static int client_endpoint_demarshal_stream_set_param(void *object, stream_set_param, 0, stream_id, id, flags, param); } +static int client_endpoint_demarshal_create_link(void *object, + const struct pw_protocol_native_message *msg) +{ + struct pw_proxy *proxy = object; + struct spa_pod_parser prs; + struct spa_pod_frame f; + struct spa_dict props = SPA_DICT_INIT(NULL, 0); + + spa_pod_parser_init(&prs, msg->data, msg->size); + + parse_dict(&prs, &f, &props); + + return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, + create_link, 0, &props); +} + static int client_endpoint_demarshal_update(void *object, const struct pw_protocol_native_message *msg) { @@ -603,6 +637,7 @@ static const struct pw_client_endpoint_proxy_events pw_protocol_native_client_en .set_session_id = client_endpoint_marshal_set_session_id, .set_param = client_endpoint_marshal_set_param, .stream_set_param = client_endpoint_marshal_stream_set_param, + .create_link = client_endpoint_marshal_create_link, }; static const struct pw_protocol_native_demarshal @@ -612,6 +647,7 @@ pw_protocol_native_client_endpoint_event_demarshal[PW_CLIENT_ENDPOINT_PROXY_EVEN [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID] = { client_endpoint_demarshal_set_session_id, 0 }, [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM] = { client_endpoint_demarshal_set_param, 0 }, [PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM] = { client_endpoint_demarshal_stream_set_param, 0 }, + [PW_CLIENT_ENDPOINT_PROXY_EVENT_CREATE_LINK] = { client_endpoint_demarshal_create_link, 0 }, }; static const struct pw_client_endpoint_proxy_methods pw_protocol_native_client_endpoint_method_marshal = { @@ -696,36 +732,6 @@ static int client_session_marshal_link_set_param (void *object, return pw_protocol_native_end_resource(resource, b); } -static int client_session_marshal_create_link(void *object, - const struct spa_dict *props) -{ - struct pw_resource *resource = object; - struct spa_pod_builder *b; - - spa_return_val_if_fail(props, -EINVAL); - - b = pw_protocol_native_begin_resource(resource, - PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK, NULL); - - push_dict(b, props); - - return pw_protocol_native_end_resource(resource, b); -} - -static int client_session_marshal_destroy_link (void *object, uint32_t link_id) -{ - struct pw_resource *resource = object; - struct spa_pod_builder *b; - - b = pw_protocol_native_begin_resource(resource, - PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK, NULL); - - spa_pod_builder_add_struct(b, - SPA_POD_Int(link_id)); - - return pw_protocol_native_end_resource(resource, b); -} - static int client_session_marshal_link_request_state (void *object, uint32_t link_id, uint32_t state) { @@ -875,37 +881,6 @@ static int client_session_demarshal_link_set_param(void *object, link_set_param, 0, link_id, id, flags, param); } -static int client_session_demarshal_create_link(void *object, - const struct pw_protocol_native_message *msg) -{ - struct pw_proxy *proxy = object; - struct spa_pod_parser prs; - struct spa_pod_frame f; - struct spa_dict props = SPA_DICT_INIT(NULL, 0); - - spa_pod_parser_init(&prs, msg->data, msg->size); - parse_dict(&prs, &f, &props); - - return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, - create_link, 0, &props); -} - -static int client_session_demarshal_destroy_link(void *object, - const struct pw_protocol_native_message *msg) -{ - struct pw_proxy *proxy = object; - struct spa_pod_parser prs; - uint32_t link_id; - - spa_pod_parser_init(&prs, msg->data, msg->size); - if (spa_pod_parser_get_struct(&prs, - SPA_POD_Int(&link_id)) < 0) - return -EINVAL; - - return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, - destroy_link, 0, link_id); -} - static int client_session_demarshal_link_request_state(void *object, const struct pw_protocol_native_message *msg) { @@ -1007,8 +982,6 @@ static const struct pw_client_session_proxy_events pw_protocol_native_client_ses .set_id = client_session_marshal_set_id, .set_param = client_session_marshal_set_param, .link_set_param = client_session_marshal_link_set_param, - .create_link = client_session_marshal_create_link, - .destroy_link = client_session_marshal_destroy_link, .link_request_state = client_session_marshal_link_request_state, }; @@ -1018,8 +991,6 @@ pw_protocol_native_client_session_event_demarshal[PW_CLIENT_SESSION_PROXY_EVENT_ [PW_CLIENT_SESSION_PROXY_EVENT_SET_ID] = { client_session_demarshal_set_id, 0 }, [PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM] = { client_session_demarshal_set_param, 0 }, [PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM] = { client_session_demarshal_link_set_param, 0 }, - [PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK] = { client_session_demarshal_create_link, 0 }, - [PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK] = { client_session_demarshal_destroy_link, 0 }, [PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE] = { client_session_demarshal_link_request_state, 0 }, }; @@ -1167,17 +1138,6 @@ static int endpoint_link_marshal_request_state(void *object, return pw_protocol_native_end_proxy(proxy, b); } -static int endpoint_link_marshal_destroy(void *object) -{ - struct pw_proxy *proxy = object; - struct spa_pod_builder *b; - - b = pw_protocol_native_begin_proxy(proxy, - PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY, NULL); - - return pw_protocol_native_end_proxy(proxy, b); -} - static int endpoint_link_demarshal_info(void *object, const struct pw_protocol_native_message *msg) { @@ -1294,15 +1254,6 @@ static int endpoint_link_demarshal_request_state(void *object, request_state, 0, state); } -static int endpoint_link_demarshal_destroy(void *object, - const struct pw_protocol_native_message *msg) -{ - struct pw_resource *resource = object; - - return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, - destroy, 0); -} - static const struct pw_endpoint_link_proxy_events pw_protocol_native_endpoint_link_event_marshal = { PW_VERSION_ENDPOINT_LINK_PROXY_EVENTS, .info = endpoint_link_marshal_info, @@ -1323,7 +1274,6 @@ static const struct pw_endpoint_link_proxy_methods pw_protocol_native_endpoint_l .enum_params = endpoint_link_marshal_enum_params, .set_param = endpoint_link_marshal_set_param, .request_state = endpoint_link_marshal_request_state, - .destroy = endpoint_link_marshal_destroy, }; static const struct pw_protocol_native_demarshal @@ -1334,7 +1284,6 @@ pw_protocol_native_endpoint_link_method_demarshal[PW_ENDPOINT_LINK_PROXY_METHOD_ [PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS] = { endpoint_link_demarshal_enum_params, 0 }, [PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM] = { endpoint_link_demarshal_set_param, PW_PERM_W }, [PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE] = { endpoint_link_demarshal_request_state, PW_PERM_W }, - [PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY] = { endpoint_link_demarshal_destroy, PW_PERM_W }, }; static const struct pw_protocol_marshal pw_protocol_native_endpoint_link_marshal = { @@ -1697,6 +1646,20 @@ static int endpoint_marshal_set_param(void *object, return pw_protocol_native_end_proxy(proxy, b); } +static int endpoint_marshal_create_link(void *object, + const struct spa_dict *props) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_proxy(proxy, + PW_ENDPOINT_PROXY_METHOD_CREATE_LINK, NULL); + + push_dict(b, props); + + return pw_protocol_native_end_proxy(proxy, b); +} + static int endpoint_demarshal_info(void *object, const struct pw_protocol_native_message *msg) { @@ -1797,6 +1760,22 @@ static int endpoint_demarshal_set_param(void *object, set_param, 0, id, flags, param); } +static int endpoint_demarshal_create_link(void *object, + const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + struct spa_pod_frame f; + struct spa_dict props = SPA_DICT_INIT(NULL, 0); + + spa_pod_parser_init(&prs, msg->data, msg->size); + + parse_dict(&prs, &f, &props); + + return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, + create_link, 0, &props); +} + static const struct pw_endpoint_proxy_events pw_protocol_native_endpoint_event_marshal = { PW_VERSION_ENDPOINT_PROXY_EVENTS, .info = endpoint_marshal_info, @@ -1816,6 +1795,7 @@ static const struct pw_endpoint_proxy_methods pw_protocol_native_endpoint_method .subscribe_params = endpoint_marshal_subscribe_params, .enum_params = endpoint_marshal_enum_params, .set_param = endpoint_marshal_set_param, + .create_link = endpoint_marshal_create_link, }; static const struct pw_protocol_native_demarshal @@ -1825,6 +1805,7 @@ pw_protocol_native_endpoint_method_demarshal[PW_ENDPOINT_PROXY_METHOD_NUM] = [PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_demarshal_subscribe_params, 0 }, [PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS] = { endpoint_demarshal_enum_params, 0 }, [PW_ENDPOINT_PROXY_METHOD_SET_PARAM] = { endpoint_demarshal_set_param, PW_PERM_W }, + [PW_ENDPOINT_PROXY_METHOD_CREATE_LINK] = { endpoint_demarshal_create_link, PW_PERM_X }, }; static const struct pw_protocol_marshal pw_protocol_native_endpoint_marshal = { @@ -1942,20 +1923,6 @@ static int session_marshal_set_param(void *object, return pw_protocol_native_end_proxy(proxy, b); } -static int session_marshal_create_link(void *object, - const struct spa_dict *props) -{ - struct pw_proxy *proxy = object; - struct spa_pod_builder *b; - - b = pw_protocol_native_begin_proxy(proxy, - PW_SESSION_PROXY_METHOD_CREATE_LINK, NULL); - - push_dict(b, props); - - return pw_protocol_native_end_proxy(proxy, b); -} - static int session_demarshal_info(void *object, const struct pw_protocol_native_message *msg) { @@ -2056,22 +2023,6 @@ static int session_demarshal_set_param(void *object, set_param, 0, id, flags, param); } -static int session_demarshal_create_link(void *object, - const struct pw_protocol_native_message *msg) -{ - struct pw_resource *resource = object; - struct spa_pod_parser prs; - struct spa_pod_frame f; - struct spa_dict props = SPA_DICT_INIT(NULL, 0); - - spa_pod_parser_init(&prs, msg->data, msg->size); - - parse_dict(&prs, &f, &props); - - return pw_resource_notify(resource, struct pw_session_proxy_methods, - create_link, 0, &props); -} - static const struct pw_session_proxy_events pw_protocol_native_session_event_marshal = { PW_VERSION_SESSION_PROXY_EVENTS, .info = session_marshal_info, @@ -2091,7 +2042,6 @@ static const struct pw_session_proxy_methods pw_protocol_native_session_method_m .subscribe_params = session_marshal_subscribe_params, .enum_params = session_marshal_enum_params, .set_param = session_marshal_set_param, - .create_link = session_marshal_create_link, }; static const struct pw_protocol_native_demarshal @@ -2101,7 +2051,6 @@ pw_protocol_native_session_method_demarshal[PW_SESSION_PROXY_METHOD_NUM] = [PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS] = { session_demarshal_subscribe_params, 0 }, [PW_SESSION_PROXY_METHOD_ENUM_PARAMS] = { session_demarshal_enum_params, 0 }, [PW_SESSION_PROXY_METHOD_SET_PARAM] = { session_demarshal_set_param, PW_PERM_W }, - [PW_SESSION_PROXY_METHOD_CREATE_LINK] = { session_demarshal_create_link, PW_PERM_W }, }; static const struct pw_protocol_marshal pw_protocol_native_session_marshal = { diff --git a/src/modules/module-session-manager/session.c b/src/modules/module-session-manager/session.c index 46a1362d4..488629b4d 100644 --- a/src/modules/module-session-manager/session.c +++ b/src/modules/module-session-manager/session.c @@ -123,24 +123,11 @@ static int session_set_param (void *object, uint32_t id, uint32_t flags, return 0; } -static int session_create_link(void *object, const struct spa_dict *props) -{ - struct pw_resource *resource = object; - struct resource_data *data = pw_resource_get_user_data(resource); - struct session *this = data->session; - - pw_client_session_resource_create_link(this->client_sess->resource, - props); - - return 0; -} - static const struct pw_session_proxy_methods methods = { PW_VERSION_SESSION_PROXY_METHODS, .subscribe_params = session_subscribe_params, .enum_params = session_enum_params, .set_param = session_set_param, - .create_link = session_create_link, }; static void session_notify_subscribed(struct session *this, diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c index 5a1bb5d10..4b880f2c3 100644 --- a/src/tools/pipewire-cli.c +++ b/src/tools/pipewire-cli.c @@ -678,16 +678,10 @@ static void info_endpoint(struct proxy_data *pd) fprintf(stdout, "\tname: %s\n", info->name); fprintf(stdout, "\tmedia-class: %s\n", info->media_class); switch(info->direction) { - case PW_ENDPOINT_DIRECTION_SINK_INPUT: - direction = "sink-input"; - break; - case PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT: - direction = "source-output"; - break; - case PW_ENDPOINT_DIRECTION_SOURCE: + case PW_DIRECTION_OUTPUT: direction = "source"; break; - case PW_ENDPOINT_DIRECTION_SINK: + case PW_DIRECTION_INPUT: direction = "sink"; break; default: @@ -697,7 +691,7 @@ static void info_endpoint(struct proxy_data *pd) fprintf(stdout, "\tdirection: %s\n", direction); fprintf(stdout, "\tflags: 0x%x\n", info->flags); fprintf(stdout, "%c\tstreams: %u\n", MARK_CHANGE(0), info->n_streams); - fprintf(stdout, "%c\tsession: %u\n", MARK_CHANGE(0), info->session_id); + fprintf(stdout, "%c\tsession: %u\n", MARK_CHANGE(1), info->session_id); print_properties(info->props, MARK_CHANGE(2), true); print_params(info->params, info->n_params, MARK_CHANGE(3), true); info->change_mask = 0;