diff --git a/spa/include/spa/node/keys.h b/spa/include/spa/node/keys.h index 23f7fb32a..fea7b7895 100644 --- a/spa/include/spa/node/keys.h +++ b/spa/include/spa/node/keys.h @@ -34,6 +34,7 @@ extern "C" { #define SPA_KEY_PORT_ALIAS "port.alias" /**< a port alias */ #define SPA_KEY_PORT_MONITOR "port.monitor" /**< this port is a monitor port */ #define SPA_KEY_PORT_IGNORE_LATENCY "port.ignore-latency" /**< latency ignored by peers */ +#define SPA_KEY_PORT_GROUP "port.group" /**< the port group this port belongs to */ /** diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c index ef660d1ba..03d85530c 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -150,7 +151,7 @@ static int emit_node(struct impl *this, struct acp_device *dev) info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; - items = alloca((dev->props.n_items + 8) * sizeof(*items)); + items = alloca((dev->props.n_items + 9) * sizeof(*items)); n_items = 0; snprintf(card_index, sizeof(card_index), "%d", card->index); @@ -174,6 +175,7 @@ static int emit_node(struct impl *this, struct acp_device *dev) items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_OPEN_UCM, "true"); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PCM_CARD, card_index); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PCM_STREAM, stream); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, stream); snprintf(channels, sizeof(channels), "%d", dev->format.channels); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNELS, channels); diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 2e43383e8..100822333 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -3758,7 +3758,10 @@ void spa_alsa_emit_port_info(struct state *state, bool full) state->port_info.change_mask = state->port_info_all; if (state->port_info.change_mask) { uint32_t i; - + static const struct spa_dict_item info_items[] = { + { SPA_KEY_PORT_GROUP, "stream.0" }, + }; + state->port_info.props = &SPA_DICT_INIT_ARRAY(info_items); if (state->port_info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) { for (i = 0; i < state->port_info.n_params; i++) { if (state->port_params[i].user > 0) { diff --git a/spa/plugins/alsa/alsa-seq-bridge.c b/spa/plugins/alsa/alsa-seq-bridge.c index 748b4fd5c..3592fbd74 100644 --- a/spa/plugins/alsa/alsa-seq-bridge.c +++ b/spa/plugins/alsa/alsa-seq-bridge.c @@ -225,7 +225,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f if (full) port->info.change_mask = port->info_all; if (port->info.change_mask) { - struct spa_dict_item items[5]; + struct spa_dict_item items[6]; uint32_t n_items = 0; int id; snd_seq_port_info_t *info; @@ -234,6 +234,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f char name[256]; char path[128]; char alias[128]; + char stream[32]; snd_seq_port_info_alloca(&info); snd_seq_get_any_port_info(this->sys.hndl, @@ -273,9 +274,12 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f } clean_name(name); - snprintf(path, sizeof(path), "alsa:seq:%s:client_%d:%s_%d", + snprintf(stream, sizeof(stream), "client_%d", port->addr.client); + clean_name(stream); + + snprintf(path, sizeof(path), "alsa:seq:%s:%s:%s_%d", this->props.device, - port->addr.client, + stream, port->direction == SPA_DIRECTION_OUTPUT ? "capture" : "playback", port->addr.port); clean_name(path); @@ -285,10 +289,12 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f snd_seq_port_info_get_name(info)); clean_name(alias); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_OBJECT_PATH, path); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, name); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_ALIAS, alias); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, stream); if ((id = snd_seq_client_info_get_card(client_info)) != -1) { snprintf(card, sizeof(card), "%d", id); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, card); diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index 61480f431..72aebac06 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -240,6 +240,8 @@ struct impl { unsigned int port_ignore_latency:1; unsigned int monitor_passthrough:1; + char group_name[128]; + uint32_t scratch_size; uint32_t scratch_ports; float *empty; @@ -287,7 +289,7 @@ static void emit_port_info(struct impl *this, struct port *port, bool full) if (full) port->info.change_mask = port->info_all; if (port->info.change_mask) { - struct spa_dict_item items[4]; + struct spa_dict_item items[5]; uint32_t n_items = 0; if (PORT_IS_DSP(this, port->direction, port->id)) { @@ -301,6 +303,8 @@ static void emit_port_info(struct impl *this, struct port *port, bool full) items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "control"); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"); } + if (this->group_name[0] != '\0') + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, this->group_name); port->info.props = &SPA_DICT_INIT(items, n_items); if (port->info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) { @@ -3465,6 +3469,8 @@ impl_init(const struct spa_handle_factory *factory, } else if (spa_streq(k, SPA_KEY_PORT_IGNORE_LATENCY)) this->port_ignore_latency = spa_atob(s); + else if (spa_streq(k, SPA_KEY_PORT_GROUP)) + spa_scnprintf(this->group_name, sizeof(this->group_name), "%s", s); else if (spa_streq(k, "monitor.passthrough")) this->monitor_passthrough = spa_atob(s); else diff --git a/spa/plugins/bluez5/midi-node.c b/spa/plugins/bluez5/midi-node.c index 242bd47c5..317b0294b 100644 --- a/spa/plugins/bluez5/midi-node.c +++ b/spa/plugins/bluez5/midi-node.c @@ -2011,11 +2011,13 @@ impl_init(const struct spa_handle_factory *factory, SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"), SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "in"), SPA_DICT_ITEM_INIT(SPA_KEY_PORT_ALIAS, "in"), + SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, "group.0"), }; static const struct spa_dict_item out_port_items[] = { SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"), SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "out"), SPA_DICT_ITEM_INIT(SPA_KEY_PORT_ALIAS, "out"), + SPA_DICT_ITEM_INIT(SPA_KEY_PORT_GROUP, "group.0"), }; static const struct spa_dict in_port_props = SPA_DICT_INIT_ARRAY(in_port_items); static const struct spa_dict out_port_props = SPA_DICT_INIT_ARRAY(out_port_items); diff --git a/spa/plugins/libcamera/libcamera-source.cpp b/spa/plugins/libcamera/libcamera-source.cpp index e7b350424..f9a1adbcb 100644 --- a/spa/plugins/libcamera/libcamera-source.cpp +++ b/spa/plugins/libcamera/libcamera-source.cpp @@ -76,7 +76,8 @@ struct port { struct spa_ringbuffer ring = SPA_RINGBUFFER_INIT(); uint32_t ring_ids[MAX_BUFFERS]; - static constexpr uint64_t info_all = SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_PARAMS; + static constexpr uint64_t info_all = SPA_PORT_CHANGE_MASK_FLAGS | + SPA_PORT_CHANGE_MASK_PROPS | SPA_PORT_CHANGE_MASK_PARAMS; struct spa_port_info info = SPA_PORT_INFO_INIT(); struct spa_io_buffers *io = nullptr; struct spa_io_sequence *control = nullptr; @@ -402,15 +403,14 @@ static int impl_node_send_command(void *object, const struct spa_command *comman return 0; } -static const struct spa_dict_item info_items[] = { - { SPA_KEY_DEVICE_API, "libcamera" }, - { SPA_KEY_MEDIA_CLASS, "Video/Source" }, - { SPA_KEY_MEDIA_ROLE, "Camera" }, - { SPA_KEY_NODE_DRIVER, "true" }, -}; - static void emit_node_info(struct impl *impl, bool full) { + static const struct spa_dict_item info_items[] = { + { SPA_KEY_DEVICE_API, "libcamera" }, + { SPA_KEY_MEDIA_CLASS, "Video/Source" }, + { SPA_KEY_MEDIA_ROLE, "Camera" }, + { SPA_KEY_NODE_DRIVER, "true" }, + }; uint64_t old = full ? impl->info.change_mask : 0; if (full) impl->info.change_mask = impl->info_all; @@ -424,10 +424,15 @@ static void emit_node_info(struct impl *impl, bool full) static void emit_port_info(struct impl *impl, struct port *port, bool full) { + static const struct spa_dict_item info_items[] = { + { SPA_KEY_PORT_GROUP, "stream.0" }, + }; uint64_t old = full ? port->info.change_mask : 0; if (full) port->info.change_mask = port->info_all; if (port->info.change_mask) { + struct spa_dict dict = SPA_DICT_INIT_ARRAY(info_items); + port->info.props = &dict; spa_node_emit_port_info(&impl->hooks, SPA_DIRECTION_OUTPUT, 0, &port->info); port->info.change_mask = old; diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index e003fe07d..ac956c695 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -153,15 +153,14 @@ struct impl { #include "v4l2-utils.c" -static const struct spa_dict_item info_items[] = { - { SPA_KEY_DEVICE_API, "v4l2" }, - { SPA_KEY_MEDIA_CLASS, "Video/Source" }, - { SPA_KEY_MEDIA_ROLE, "Camera" }, - { SPA_KEY_NODE_DRIVER, "true" }, -}; - static void emit_node_info(struct impl *this, bool full) { + static const struct spa_dict_item info_items[] = { + { SPA_KEY_DEVICE_API, "v4l2" }, + { SPA_KEY_MEDIA_CLASS, "Video/Source" }, + { SPA_KEY_MEDIA_ROLE, "Camera" }, + { SPA_KEY_NODE_DRIVER, "true" }, + }; uint64_t old = full ? this->info.change_mask : 0; if (full) this->info.change_mask = this->info_all; @@ -174,10 +173,14 @@ static void emit_node_info(struct impl *this, bool full) static void emit_port_info(struct impl *this, struct port *port, bool full) { + static const struct spa_dict_item info_items[] = { + { SPA_KEY_PORT_GROUP, "stream.0" }, + }; uint64_t old = full ? port->info.change_mask : 0; if (full) port->info.change_mask = port->info_all; if (port->info.change_mask) { + port->info.props = &SPA_DICT_INIT_ARRAY(info_items); spa_node_emit_port_info(&this->hooks, SPA_DIRECTION_OUTPUT, 0, &port->info); port->info.change_mask = old; @@ -1023,6 +1026,7 @@ impl_init(const struct spa_handle_factory *factory, port->impl = this; spa_list_init(&port->queue); port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | + SPA_PORT_CHANGE_MASK_PROPS | SPA_PORT_CHANGE_MASK_PARAMS; port->info = SPA_PORT_INFO_INIT(); port->info.flags = SPA_PORT_FLAG_LIVE | diff --git a/src/modules/module-adapter/adapter.c b/src/modules/module-adapter/adapter.c index c19fcdedc..93ba0d305 100644 --- a/src/modules/module-adapter/adapter.c +++ b/src/modules/module-adapter/adapter.c @@ -176,6 +176,9 @@ struct pw_impl_node *pw_adapter_new(struct pw_context *context, if ((str = pw_properties_get(props, PW_KEY_NODE_ID)) != NULL) pw_properties_set(props, PW_KEY_NODE_SESSION, str); + if (pw_properties_get(props, PW_KEY_PORT_GROUP) == NULL) + pw_properties_setf(props, PW_KEY_PORT_GROUP, "stream.0"); + if ((res = find_format(follower, direction, &media_type, &media_subtype)) < 0) goto error; diff --git a/src/pipewire/filter.c b/src/pipewire/filter.c index 3d894a994..23fc9cc59 100644 --- a/src/pipewire/filter.c +++ b/src/pipewire/filter.c @@ -1793,6 +1793,9 @@ void *pw_filter_add_port(struct pw_filter *filter, if ((p = alloc_port(impl, direction, port_data_size)) == NULL) goto error_cleanup; + if (pw_properties_get(props, PW_KEY_PORT_GROUP) == NULL) + pw_properties_setf(props, PW_KEY_PORT_GROUP, "stream.%u", p->id); + p->props = props; p->flags = flags; diff --git a/src/pipewire/impl-port.c b/src/pipewire/impl-port.c index 938fa600f..1a365aa0e 100644 --- a/src/pipewire/impl-port.c +++ b/src/pipewire/impl-port.c @@ -1148,6 +1148,7 @@ int pw_impl_port_register(struct pw_impl_port *port, PW_KEY_PORT_ALIAS, PW_KEY_PORT_EXTRA, PW_KEY_PORT_IGNORE_LATENCY, + PW_KEY_PORT_GROUP, NULL }; diff --git a/src/pipewire/keys.h b/src/pipewire/keys.h index 9b82fe64e..9ee5de433 100644 --- a/src/pipewire/keys.h +++ b/src/pipewire/keys.h @@ -231,6 +231,7 @@ extern "C" { * should be prefixed. "jack:flags:56" */ #define PW_KEY_PORT_PASSIVE "port.passive" /**< the ports wants passive links, since 0.3.67 */ #define PW_KEY_PORT_IGNORE_LATENCY "port.ignore-latency" /**< latency ignored by peers, since 0.3.71 */ +#define PW_KEY_PORT_GROUP "port.group" /**< the port group of the port 1.2.0 */ /** link properties */ #define PW_KEY_LINK_ID "link.id" /**< a link id */ diff --git a/src/pipewire/stream.c b/src/pipewire/stream.c index 41d7ee1a9..93f63a5d0 100644 --- a/src/pipewire/stream.c +++ b/src/pipewire/stream.c @@ -1993,6 +1993,8 @@ pw_stream_connect(struct pw_stream *stream, else if (impl->media_type == SPA_MEDIA_TYPE_application && impl->media_subtype == SPA_MEDIA_SUBTYPE_control) pw_properties_set(impl->port_props, PW_KEY_FORMAT_DSP, "8 bit raw midi"); + if (pw_properties_get(impl->port_props, PW_KEY_PORT_GROUP) == NULL) + pw_properties_set(impl->port_props, PW_KEY_PORT_GROUP, "stream.0"); if ((str = pw_properties_get(stream->properties, PW_KEY_NODE_ASYNC)) != NULL && spa_atob(str)) SPA_FLAG_SET(impl->info.flags, SPA_NODE_FLAG_ASYNC); @@ -2028,8 +2030,6 @@ pw_stream_connect(struct pw_stream *stream, if ((str = pw_properties_get(stream->properties, "mem.allow-mlock")) != NULL) impl->allow_mlock = pw_properties_parse_bool(str); - impl->port_info.props = &impl->port_props->dict; - if (stream->core == NULL) { stream->core = pw_context_connect(impl->context, pw_properties_copy(stream->properties), 0); @@ -2056,6 +2056,8 @@ pw_stream_connect(struct pw_stream *stream, pw_properties_set(props, "channelmix.normalize", "true"); pw_properties_set(props, PW_KEY_PORT_IGNORE_LATENCY, "true"); } + if (pw_properties_get(props, PW_KEY_PORT_GROUP) == NULL) + pw_properties_set(props, PW_KEY_PORT_GROUP, "stream.0"); if (impl->media_type == SPA_MEDIA_TYPE_audio || (impl->media_type == SPA_MEDIA_TYPE_video