From c727e8f8f9512796befb5d054aff0cfb65f7533f Mon Sep 17 00:00:00 2001 From: ZZyVSmOzMz OZaAEBlUIZ <1080187-ZZyVSmOzMz@users.noreply.gitlab.freedesktop.org> Date: Sun, 22 Jun 2025 11:22:26 +0000 Subject: [PATCH 01/43] bluez5: use ldacBT for both decode and encode --- spa/meson.build | 11 ++++++++--- spa/plugins/bluez5/a2dp-codec-ldac.c | 2 +- spa/plugins/bluez5/meson.build | 1 - 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/spa/meson.build b/spa/meson.build index 48a0000a1..e889db237 100644 --- a/spa/meson.build +++ b/spa/meson.build @@ -72,13 +72,18 @@ if get_option('spa-plugins').allowed() summary({'LDAC ABR': ldac_abr_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') if get_option('bluez5-codec-ldac-dec').allowed() - ldac_dec_dep = dependency('ldacBT-dec', required : false) - if not ldac_dec_dep.found() - dep = cc.find_library('ldacBT_dec', required : false) + dep = dependency('ldacBT', required : false) + if dep.found() and cc.has_function('ldacBT_decode', dependencies : dep) + ldac_dec_dep = dep + else + dep = cc.find_library('ldacBT', required : false) if dep.found() and cc.has_function('ldacBT_decode', dependencies : dep) ldac_dec_dep = dep endif endif + if ldac_dec_dep.found() + ldac_dep = ldac_dec_dep # use ldacBT for both decode and encode + endif if not ldac_dec_dep.found() and get_option('bluez5-codec-ldac-dec').enabled() error('LDAC decoder library not found') endif diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index 4ae86b571..a1bf1f678 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -470,7 +470,7 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags, #endif #ifdef ENABLE_LDAC_ABR - this->ldac_abr = ldac_ABR_get_handle(); + this->ldac_abr = ldac_ABR_get_handle(NULL); if (this->ldac_abr == NULL) goto error_errno; #endif diff --git a/spa/plugins/bluez5/meson.build b/spa/plugins/bluez5/meson.build index bf9c6374b..519b39bb1 100644 --- a/spa/plugins/bluez5/meson.build +++ b/spa/plugins/bluez5/meson.build @@ -141,7 +141,6 @@ if ldac_dep.found() endif if get_option('bluez5-codec-ldac-dec').allowed() and ldac_dec_dep.found() ldac_args += [ '-DENABLE_LDAC_DEC' ] - ldac_dep = [ldac_dep, ldac_dec_dep] endif bluez_codec_ldac = shared_library('spa-codec-bluez5-ldac', [ 'a2dp-codec-ldac.c', 'media-codecs.c' ], From ae1f79c514ba601b3f604c09d890491b463fa308 Mon Sep 17 00:00:00 2001 From: ZZyVSmOzMz OZaAEBlUIZ <1080187-ZZyVSmOzMz@users.noreply.gitlab.freedesktop.org> Date: Sun, 22 Jun 2025 12:00:40 +0000 Subject: [PATCH 02/43] fixup --- spa/meson.build | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spa/meson.build b/spa/meson.build index e889db237..1cff83c00 100644 --- a/spa/meson.build +++ b/spa/meson.build @@ -71,6 +71,7 @@ if get_option('spa-plugins').allowed() ldac_abr_dep = dependency('ldacBT-abr', required : get_option('bluez5-codec-ldac')) summary({'LDAC ABR': ldac_abr_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') + ldac_dec_dep = dependency('', required: false) if get_option('bluez5-codec-ldac-dec').allowed() dep = dependency('ldacBT', required : false) if dep.found() and cc.has_function('ldacBT_decode', dependencies : dep) @@ -87,8 +88,6 @@ if get_option('spa-plugins').allowed() if not ldac_dec_dep.found() and get_option('bluez5-codec-ldac-dec').enabled() error('LDAC decoder library not found') endif - else - ldac_dec_dep = dependency('', required: false) endif summary({'LDAC DEC': ldac_dec_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') From 541b6fca270932f2979b6b4e3562e5034fa5c071 Mon Sep 17 00:00:00 2001 From: ZZyVSmOzMz OZaAEBlUIZ <1080187-ZZyVSmOzMz@users.noreply.gitlab.freedesktop.org> Date: Sun, 22 Jun 2025 12:30:53 +0000 Subject: [PATCH 03/43] check version --- spa/plugins/bluez5/a2dp-codec-ldac.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index a1bf1f678..63f87c202 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -470,7 +470,11 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags, #endif #ifdef ENABLE_LDAC_ABR - this->ldac_abr = ldac_ABR_get_handle(NULL); +#ifdef ENABLE_LDAC_DEC + this->ldac_abr = ldac_ABR_get_handle(NULL); +#else + this->ldac_abr = ldac_ABR_get_handle(); +#endif if (this->ldac_abr == NULL) goto error_errno; #endif From f453b1545dc229f992a8da9dbb31a2fab634c6c1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 20 Oct 2025 15:28:51 +0200 Subject: [PATCH 04/43] audio: don't use SPA_AUDIO_MAX_CHANNELS in some places When we know the max size of the array, just use this instead of the SPA_AUDIO_MAX_CHANNELS constant. --- spa/plugins/bluez5/a2dp-codec-aac.c | 2 +- spa/plugins/bluez5/a2dp-codec-aptx.c | 2 +- spa/plugins/bluez5/a2dp-codec-faststream.c | 2 +- spa/plugins/bluez5/a2dp-codec-lc3plus.c | 2 +- spa/plugins/bluez5/a2dp-codec-ldac.c | 2 +- spa/plugins/bluez5/a2dp-codec-opus-g.c | 2 +- spa/plugins/bluez5/a2dp-codec-sbc.c | 2 +- spa/plugins/bluez5/bap-codec-lc3.c | 11 ++++++----- spa/plugins/bluez5/hfp-codec-cvsd.c | 2 +- spa/plugins/bluez5/hfp-codec-lc3-a127.c | 2 +- spa/plugins/bluez5/hfp-codec-lc3-swb.c | 2 +- spa/plugins/bluez5/hfp-codec-msbc.c | 2 +- src/gst/gstpipewireformat.c | 2 +- src/modules/module-rtp/opus.c | 5 ++++- 14 files changed, 22 insertions(+), 18 deletions(-) diff --git a/spa/plugins/bluez5/a2dp-codec-aac.c b/spa/plugins/bluez5/a2dp-codec-aac.c index 505066c3e..6d4db7fc4 100644 --- a/spa/plugins/bluez5/a2dp-codec-aac.c +++ b/spa/plugins/bluez5/a2dp-codec-aac.c @@ -200,7 +200,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_aac_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-aptx.c b/spa/plugins/bluez5/a2dp-codec-aptx.c index 7ebdc99d8..75ceb229f 100644 --- a/spa/plugins/bluez5/a2dp-codec-aptx.c +++ b/spa/plugins/bluez5/a2dp-codec-aptx.c @@ -205,7 +205,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_aptx_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-faststream.c b/spa/plugins/bluez5/a2dp-codec-faststream.c index 5b78fb38e..edc833292 100644 --- a/spa/plugins/bluez5/a2dp-codec-faststream.c +++ b/spa/plugins/bluez5/a2dp-codec-faststream.c @@ -114,7 +114,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_faststream_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-lc3plus.c b/spa/plugins/bluez5/a2dp-codec-lc3plus.c index c5a191e29..ee25fef0c 100644 --- a/spa/plugins/bluez5/a2dp-codec-lc3plus.c +++ b/spa/plugins/bluez5/a2dp-codec-lc3plus.c @@ -175,7 +175,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_lc3plus_hr_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index cf1526faf..11185800e 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -158,7 +158,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_frame f[2]; struct spa_pod_choice *choice; uint32_t i = 0; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; if (caps_size < sizeof(conf)) return -EINVAL; diff --git a/spa/plugins/bluez5/a2dp-codec-opus-g.c b/spa/plugins/bluez5/a2dp-codec-opus-g.c index 2fe34bf5e..130dea433 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus-g.c +++ b/spa/plugins/bluez5/a2dp-codec-opus-g.c @@ -164,7 +164,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, { a2dp_opus_g_t conf; struct spa_pod_frame f[1]; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; int channels; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-sbc.c b/spa/plugins/bluez5/a2dp-codec-sbc.c index f2bc3e7ef..a91109e52 100644 --- a/spa/plugins/bluez5/a2dp-codec-sbc.c +++ b/spa/plugins/bluez5/a2dp-codec-sbc.c @@ -346,7 +346,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_frame f[2]; struct spa_pod_choice *choice; uint32_t i = 0; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; if (caps_size < sizeof(conf)) return -EINVAL; diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c index 818f4ee36..10d9eecc8 100644 --- a/spa/plugins/bluez5/bap-codec-lc3.c +++ b/spa/plugins/bluez5/bap-codec-lc3.c @@ -901,12 +901,12 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f return conf_cmp(&conf1, res1, &conf2, res2); } -static uint8_t channels_to_positions(uint32_t channels, uint32_t *position) +static uint8_t channels_to_positions(uint32_t channels, uint32_t *position, uint32_t max_position) { uint32_t n_channels = get_channel_count(channels); uint8_t n_positions = 0; - spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS); + spa_assert(n_channels <= max_position); if (channels == 0) { position[0] = SPA_AUDIO_CHANNEL_MONO; @@ -932,7 +932,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, bap_lc3_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[LC3_MAX_CHANNELS]; uint32_t i = 0; uint8_t res; @@ -990,7 +990,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, if (i == 0) return -EINVAL; - res = channels_to_positions(conf.channels, position); + res = channels_to_positions(conf.channels, position, SPA_N_ELEMENTS(position)); if (res == 0) return -EINVAL; spa_pod_builder_add(b, @@ -1044,7 +1044,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags return -EINVAL; } - res = channels_to_positions(conf.channels, info->info.raw.position); + res = channels_to_positions(conf.channels, info->info.raw.position, + SPA_N_ELEMENTS(info->info.raw.position)); if (res == 0) return -EINVAL; info->info.raw.channels = res; diff --git a/spa/plugins/bluez5/hfp-codec-cvsd.c b/spa/plugins/bluez5/hfp-codec-cvsd.c index 66b801ecc..19f908188 100644 --- a/spa/plugins/bluez5/hfp-codec-cvsd.c +++ b/spa/plugins/bluez5/hfp-codec-cvsd.c @@ -34,7 +34,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/spa/plugins/bluez5/hfp-codec-lc3-a127.c b/spa/plugins/bluez5/hfp-codec-lc3-a127.c index 703e153a8..b10bba874 100644 --- a/spa/plugins/bluez5/hfp-codec-lc3-a127.c +++ b/spa/plugins/bluez5/hfp-codec-lc3-a127.c @@ -39,7 +39,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/spa/plugins/bluez5/hfp-codec-lc3-swb.c b/spa/plugins/bluez5/hfp-codec-lc3-swb.c index 685806286..1cd679958 100644 --- a/spa/plugins/bluez5/hfp-codec-lc3-swb.c +++ b/spa/plugins/bluez5/hfp-codec-lc3-swb.c @@ -42,7 +42,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/spa/plugins/bluez5/hfp-codec-msbc.c b/spa/plugins/bluez5/hfp-codec-msbc.c index d2aabe2b0..5175a68d7 100644 --- a/spa/plugins/bluez5/hfp-codec-msbc.c +++ b/spa/plugins/bluez5/hfp-codec-msbc.c @@ -49,7 +49,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/src/gst/gstpipewireformat.c b/src/gst/gstpipewireformat.c index 20b93065a..fa97d3e01 100644 --- a/src/gst/gstpipewireformat.c +++ b/src/gst/gstpipewireformat.c @@ -649,7 +649,7 @@ handle_video_fields (ConvertData *d) static void set_default_channels (struct spa_pod_builder *b, uint32_t channels) { - uint32_t position[SPA_AUDIO_MAX_CHANNELS] = {0}; + uint32_t position[8] = {0}; gboolean ok = TRUE; switch (channels) { diff --git a/src/modules/module-rtp/opus.c b/src/modules/module-rtp/opus.c index 6f27bd2bd..7eeda7f43 100644 --- a/src/modules/module-rtp/opus.c +++ b/src/modules/module-rtp/opus.c @@ -334,9 +334,12 @@ static void rtp_opus_deinit(struct impl *impl, enum spa_direction direction) static int rtp_opus_init(struct impl *impl, enum spa_direction direction) { int err; - unsigned char mapping[SPA_AUDIO_MAX_CHANNELS]; + unsigned char mapping[255]; uint32_t i; + if (impl->info.info.opus.channels > 255) + return -EINVAL; + if (impl->psamples >= 2880) impl->psamples = 2880; else if (impl->psamples >= 1920) From ede13a8cb5cee59a984da64428555476b9269f31 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 20 Oct 2025 18:11:42 +0200 Subject: [PATCH 05/43] spa: don't add more channels than we have positions --- spa/include/spa/param/audio/raw-utils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index c36491445..8dd816026 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -69,7 +69,8 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, - info->channels, info->position), 0); + SPA_MIN(info->channels, SPA_N_ELEMENTS(info->position)), + info->position), 0); } } return (struct spa_pod*)spa_pod_builder_pop(builder, &f); From eb096bfb622e7ffb692c773d6ba72b3560643788 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 Oct 2025 09:39:15 +0200 Subject: [PATCH 06/43] spa: provide information about channels > SPA_AUDIO_MAX_CHANNELS Define some rules for how the position information works for channels > SPA_AUDIO_MAX_CHANNELS. We basically wrap around and incrementing the AUX channel counters. Make a function to implement this. --- spa/include/spa/param/audio/raw-utils.h | 14 ++++++++++++++ spa/include/spa/param/audio/raw.h | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 8dd816026..505965972 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -28,6 +28,20 @@ extern "C" { #endif #endif +SPA_API_AUDIO_RAW_UTILS uint32_t +spa_format_audio_get_position(struct spa_audio_info_raw *info, uint32_t idx) +{ + uint32_t pos; + if (idx < SPA_AUDIO_MAX_CHANNELS) { + pos = info->position[idx]; + } else { + pos = info->position[idx % SPA_AUDIO_MAX_CHANNELS]; + if (SPA_AUDIO_CHANNEL_IS_AUX(pos)) + pos += (idx / SPA_AUDIO_MAX_CHANNELS) * SPA_AUDIO_MAX_CHANNELS; + } + return pos; +} + SPA_API_AUDIO_RAW_UTILS int spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info) { diff --git a/spa/include/spa/param/audio/raw.h b/spa/include/spa/param/audio/raw.h index 804e5df1d..6defe5020 100644 --- a/spa/include/spa/param/audio/raw.h +++ b/spa/include/spa/param/audio/raw.h @@ -18,6 +18,7 @@ extern "C" { * \{ */ +/* this is the max number of channels for position info */ #define SPA_AUDIO_MAX_CHANNELS 64u enum spa_audio_format { @@ -259,6 +260,8 @@ enum spa_audio_channel { SPA_AUDIO_CHANNEL_START_Custom = 0x10000, }; +#define SPA_AUDIO_CHANNEL_IS_AUX(ch) ((ch)>=SPA_AUDIO_CHANNEL_START_Aux && (ch)<=SPA_AUDIO_CHANNEL_LAST_Aux) + enum spa_audio_volume_ramp_scale { SPA_AUDIO_VOLUME_RAMP_INVALID, SPA_AUDIO_VOLUME_RAMP_LINEAR, @@ -274,7 +277,9 @@ struct spa_audio_info_raw { enum spa_audio_format format; /*< format, one of enum spa_audio_format */ uint32_t flags; /*< extra flags */ uint32_t rate; /*< sample rate */ - uint32_t channels; /*< number of channels */ + uint32_t channels; /*< number of channels. This can be larger than + * SPA_AUDIO_MAX_CHANNELS, the position is taken + * (index % SPA_AUDIO_MAX_CHANNELS) */ uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */ }; From 13b8c237679d9c9bef06a879ebc6af7f841c9a5f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 20 Oct 2025 15:33:17 +0200 Subject: [PATCH 07/43] Don't use SPA_AUDIO_MAX_CHANNELS directly Make a MAX_CHANNELS define and use that one in code. This makes it easier to change the constant later. --- pipewire-alsa/alsa-plugins/ctl_pipewire.c | 8 +++-- pipewire-alsa/alsa-plugins/pcm_pipewire.c | 5 +-- spa/plugins/alsa/alsa-acp-device.c | 7 ++-- spa/plugins/alsa/alsa-pcm.c | 10 +++--- spa/plugins/alsa/alsa-pcm.h | 3 +- spa/plugins/audioconvert/audioconvert.c | 25 +++++++------- spa/plugins/audioconvert/channelmix-ops.c | 36 ++++++++++---------- spa/plugins/audioconvert/channelmix-ops.h | 7 ++-- spa/plugins/audioconvert/test-audioconvert.c | 3 +- spa/plugins/avb/avb-pcm.h | 3 +- spa/plugins/bluez5/a2dp-codec-opus.c | 14 ++++---- spa/plugins/bluez5/bluez5-dbus.c | 2 +- spa/plugins/bluez5/bluez5-device.c | 20 +++++------ spa/plugins/bluez5/defs.h | 4 ++- spa/plugins/bluez5/media-sink.c | 2 +- spa/plugins/bluez5/media-source.c | 2 +- spa/plugins/filter-graph/filter-graph.c | 25 +++++++------- spa/plugins/support/null-audio-sink.c | 3 +- src/modules/module-combine-stream.c | 10 +++--- src/modules/module-echo-cancel.c | 7 ++-- src/modules/module-ffado-driver.c | 11 +++--- src/modules/module-jack-tunnel.c | 5 +-- src/modules/module-netjack2-driver.c | 2 +- src/modules/module-netjack2-manager.c | 2 +- src/modules/module-netjack2/peer.c | 4 ++- src/modules/module-protocol-pulse/format.c | 2 ++ src/modules/module-pulse-tunnel.c | 10 +++--- src/modules/module-raop-sink.c | 5 +-- src/modules/module-rtp-sap.c | 3 +- src/tools/pw-cat.c | 4 ++- 30 files changed, 136 insertions(+), 108 deletions(-) diff --git a/pipewire-alsa/alsa-plugins/ctl_pipewire.c b/pipewire-alsa/alsa-plugins/ctl_pipewire.c index 7fcfd5726..459355c8f 100644 --- a/pipewire-alsa/alsa-plugins/ctl_pipewire.c +++ b/pipewire-alsa/alsa-plugins/ctl_pipewire.c @@ -22,9 +22,11 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.ctl"); #define VOLUME_MIN ((uint32_t) 0U) #define VOLUME_MAX ((uint32_t) 0x10000U) +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + struct volume { uint32_t channels; - long values[SPA_AUDIO_MAX_CHANNELS]; + long values[MAX_CHANNELS]; }; typedef struct { @@ -498,7 +500,7 @@ static struct spa_pod *build_volume_mute(struct spa_pod_builder *b, struct volum spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); if (volume) { - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; uint32_t i, n_volumes = 0; n_volumes = volume->channels; @@ -850,7 +852,7 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi break; case SPA_PROP_channelVolumes: { - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; uint32_t n_volumes, i; n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, diff --git a/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index 4b92bbb13..4bd55c2a8 100644 --- a/pipewire-alsa/alsa-plugins/pcm_pipewire.c +++ b/pipewire-alsa/alsa-plugins/pcm_pipewire.c @@ -31,6 +31,7 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.pcm"); #define MAX_BUFFERS 64u #define MAX_RATE (48000*8) +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MIN_PERIOD 64 @@ -642,7 +643,7 @@ static int snd_pcm_pipewire_pause(snd_pcm_ioplug_t * io, int enable) #define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE #endif -static int set_default_channels(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS]) +static int set_default_channels(uint32_t channels, uint32_t position[MAX_CHANNELS]) { switch (channels) { case 8: @@ -1097,7 +1098,7 @@ struct param_info infos[] = { { "alsa.rate", SND_PCM_IOPLUG_HW_RATE, TYPE_MIN_MAX, { 1, MAX_RATE }, 2, collect_int }, { "alsa.channels", SND_PCM_IOPLUG_HW_CHANNELS, TYPE_MIN_MAX, - { 1, SPA_AUDIO_MAX_CHANNELS }, 2, collect_int }, + { 1, MAX_CHANNELS }, 2, collect_int }, { "alsa.buffer-bytes", SND_PCM_IOPLUG_HW_BUFFER_BYTES, TYPE_MIN_MAX, { MIN_BUFFER_BYTES, MAX_BUFFER_BYTES }, 2, collect_int }, { "alsa.period-bytes", SND_PCM_IOPLUG_HW_PERIOD_BYTES, TYPE_MIN_MAX, diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c index b2e06f67f..bdcb40fa5 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -38,6 +38,7 @@ extern struct spa_i18n *acp_i18n; #define MAX_POLL 16 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DEFAULT_DEVICE "hw:0" #define DEFAULT_AUTO_PROFILE true @@ -156,7 +157,7 @@ static int emit_node(struct impl *this, struct acp_device *dev) uint32_t n_items, i; char device_name[128], path[210], channels[16], ch[12], routes[16]; char card_index[16], card_name[64], *p; - char positions[SPA_AUDIO_MAX_CHANNELS * 12]; + char positions[MAX_CHANNELS * 12]; char codecs[512]; struct spa_device_object_info info; struct acp_card *card = this->card; @@ -673,8 +674,8 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct struct spa_pod_prop *prop; struct spa_pod_object *obj = (struct spa_pod_object *) props; int changed = 0; - float volumes[SPA_AUDIO_MAX_CHANNELS]; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; uint32_t n_volumes = 0; if (!spa_pod_is_object_type(props, SPA_TYPE_OBJECT_Props)) diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 138a36cda..bad6da9fe 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -163,10 +163,10 @@ static int alsa_set_param(struct state *state, const char *k, const char *s) int fmt_change = 0; if (spa_streq(k, SPA_KEY_AUDIO_CHANNELS)) { state->default_channels = atoi(s); - if (state->default_channels > SPA_AUDIO_MAX_CHANNELS) { + if (state->default_channels > MAX_CHANNELS) { spa_log_warn(state->log, "%p: %s: %s > %d, clamping", - state, k, s, SPA_AUDIO_MAX_CHANNELS); - state->default_channels = SPA_AUDIO_MAX_CHANNELS; + state, k, s, MAX_CHANNELS); + state->default_channels = MAX_CHANNELS; } fmt_change++; } else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) { @@ -1584,8 +1584,8 @@ static int add_channels(struct state *state, bool all, uint32_t index, uint32_t spa_log_debug(state->log, "channels (%d %d) default:%d all:%d", min, max, state->default_channels, all); - min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS); - max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS); + min = SPA_MIN(min, MAX_CHANNELS); + max = SPA_MIN(max, MAX_CHANNELS); if (state->default_channels != 0 && !all) { if (min > state->default_channels || diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index bc96a3924..5bad22159 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -36,6 +36,7 @@ extern "C" { #endif #define MAX_RATES 16 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DEFAULT_PERIOD 1024u #define DEFAULT_RATE 48000u @@ -72,7 +73,7 @@ struct buffer { struct channel_map { uint32_t channels; - uint32_t pos[SPA_AUDIO_MAX_CHANNELS]; + uint32_t pos[MAX_CHANNELS]; }; struct card { diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index d2e22d603..0658dea1c 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -47,10 +47,11 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert"); #define DEFAULT_RATE 48000 #define DEFAULT_CHANNELS 2 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_ALIGN FMT_OPS_MAX_ALIGN #define MAX_BUFFERS 32 -#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS -#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1) +#define MAX_DATAS MAX_CHANNELS +#define MAX_PORTS (MAX_CHANNELS+1) #define MAX_STAGES 64 #define MAX_GRAPH 9 /* 8 active + 1 replacement slot */ @@ -62,7 +63,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert"); struct volumes { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; static void init_volumes(struct volumes *vol) @@ -70,7 +71,7 @@ static void init_volumes(struct volumes *vol) uint32_t i; vol->mute = DEFAULT_MUTE; vol->n_volumes = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) vol->volumes[i] = DEFAULT_VOLUME; } @@ -91,7 +92,7 @@ struct props { float max_volume; float prev_volume; uint32_t n_channels; - uint32_t channel_map[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channel_map[MAX_CHANNELS]; struct volumes channel; struct volumes soft; struct volumes monitor; @@ -112,7 +113,7 @@ static void props_reset(struct props *props) props->min_volume = DEFAULT_MIN_VOLUME; props->max_volume = DEFAULT_MAX_VOLUME; props->n_channels = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) props->channel_map[i] = SPA_AUDIO_CHANNEL_UNKNOWN; init_volumes(&props->channel); init_volumes(&props->soft); @@ -241,9 +242,9 @@ struct filter_graph { struct spa_filter_graph *graph; struct spa_hook listener; uint32_t n_inputs; - uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t inputs_position[MAX_CHANNELS]; uint32_t n_outputs; - uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t outputs_position[MAX_CHANNELS]; uint32_t latency; bool removing; bool setup; @@ -1966,7 +1967,7 @@ static int node_set_param_port_config(struct impl *this, uint32_t flags, return -EINVAL; if (info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; infop = &info; @@ -2153,7 +2154,7 @@ static void set_volume(struct impl *this) { struct volumes *vol; uint32_t i; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; struct dir *dir = &this->dir[this->direction]; spa_log_debug(this->log, "%p set volume %f have_format:%d", this, this->props.volume, dir->have_format); @@ -2671,7 +2672,7 @@ static int port_param_enum_formats(struct impl *impl, struct port *port, uint32_ } spa_pod_builder_add(b, SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( - DEFAULT_CHANNELS, 1, SPA_AUDIO_MAX_CHANNELS), + DEFAULT_CHANNELS, 1, MAX_CHANNELS), 0); *param = spa_pod_builder_pop(b, &f[0]); } @@ -3064,7 +3065,7 @@ static int port_set_format(void *object, if (info.info.raw.format == 0 || (!this->props.resample_disabled && info.info.raw.rate == 0) || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) { + info.info.raw.channels > MAX_CHANNELS) { spa_log_error(this->log, "invalid format:%d rate:%d channels:%d", info.info.raw.format, info.info.raw.rate, info.info.raw.channels); diff --git a/spa/plugins/audioconvert/channelmix-ops.c b/spa/plugins/audioconvert/channelmix-ops.c index 792cf7e09..ce67042e1 100644 --- a/spa/plugins/audioconvert/channelmix-ops.c +++ b/spa/plugins/audioconvert/channelmix-ops.c @@ -142,29 +142,29 @@ static uint32_t mask_to_ch(struct channelmix *mix, uint64_t mask) } static void distribute_mix(struct channelmix *mix, - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + float matrix[MAX_CHANNELS][MAX_CHANNELS], uint64_t mask) { uint32_t i, ch = mask_to_ch(mix, mask); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][ch]= 1.0f; } static void average_mix(struct channelmix *mix, - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + float matrix[MAX_CHANNELS][MAX_CHANNELS], uint64_t mask) { uint32_t i, ch = mask_to_ch(mix, mask); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[ch][i]= 1.0f; } -static void pair_mix(float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS]) +static void pair_mix(float matrix[MAX_CHANNELS][MAX_CHANNELS]) { uint32_t i; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][i]= 1.0f; } static bool match_mix(struct channelmix *mix, - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + float matrix[MAX_CHANNELS][MAX_CHANNELS], uint64_t src_mask, uint64_t dst_mask) { bool matched = false; @@ -181,7 +181,7 @@ static bool match_mix(struct channelmix *mix, static int make_matrix(struct channelmix *mix) { - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS] = {{ 0.0f }}; + float matrix[MAX_CHANNELS][MAX_CHANNELS] = {{ 0.0f }}; uint64_t src_mask = mix->src_mask, src_paired; uint64_t dst_mask = mix->dst_mask, dst_paired; uint32_t src_chan = mix->src_chan; @@ -293,7 +293,7 @@ static int make_matrix(struct channelmix *mix) keep &= ~STEREO; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign FC to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][_CH(FC)]= 1.0f; normalize = true; } else { @@ -313,7 +313,7 @@ static int make_matrix(struct channelmix *mix) keep &= ~FRONT; } else if ((dst_mask & _MASK(MONO))){ spa_log_info(mix->log, "assign STEREO to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(FL)]= 1.0f; matrix[i][_CH(FR)]= 1.0f; } @@ -352,7 +352,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,RC) += slev * SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign RC to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][_CH(RC)]= 1.0f; normalize = true; } else { @@ -398,7 +398,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,RR)+= slev * SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign RL+RR to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(RL)]= 1.0f; matrix[i][_CH(RR)]= 1.0f; } @@ -450,7 +450,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,SR) += slev * SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign SL+SR to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(SL)]= 1.0f; matrix[i][_CH(SR)]= 1.0f; } @@ -471,7 +471,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,FRC)+= SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign FLC+FRC to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(FLC)]= 1.0f; matrix[i][_CH(FRC)]= 1.0f; } @@ -492,7 +492,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FR,LFE) += llev * SQRT1_2; } else if ((dst_mask & _MASK(MONO))){ spa_log_info(mix->log, "assign LFE to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][_CH(LFE)]= 1.0f; normalize = true; } else { @@ -690,7 +690,7 @@ done: static void impl_channelmix_set_volume(struct channelmix *mix, float volume, bool mute, uint32_t n_channel_volumes, float *channel_volumes) { - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; float vol = mute ? 0.0f : volume, t; uint32_t i, j; uint32_t src_chan = mix->src_chan; @@ -760,8 +760,8 @@ int channelmix_init(struct channelmix *mix) { const struct channelmix_info *info; - if (mix->src_chan > SPA_AUDIO_MAX_CHANNELS || - mix->dst_chan > SPA_AUDIO_MAX_CHANNELS) + if (mix->src_chan > MAX_CHANNELS || + mix->dst_chan > MAX_CHANNELS) return -EINVAL; info = find_channelmix_info(mix->src_chan, mix->src_mask, mix->dst_chan, mix->dst_mask, diff --git a/spa/plugins/audioconvert/channelmix-ops.h b/spa/plugins/audioconvert/channelmix-ops.h index 26e2efc3a..6ea2b9451 100644 --- a/spa/plugins/audioconvert/channelmix-ops.h +++ b/spa/plugins/audioconvert/channelmix-ops.h @@ -24,6 +24,7 @@ #define BUFFER_SIZE 4096 #define MAX_TAPS 255u +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define CHANNELMIX_OPS_MAX_ALIGN 16 @@ -50,8 +51,8 @@ struct channelmix { #define CHANNELMIX_FLAG_EQUAL (1<<2) /**< all values are equal */ #define CHANNELMIX_FLAG_COPY (1<<3) /**< 1 on diagonal, can be nxm */ uint32_t flags; - float matrix_orig[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS]; - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS]; + float matrix_orig[MAX_CHANNELS][MAX_CHANNELS]; + float matrix[MAX_CHANNELS][MAX_CHANNELS]; float freq; /* sample frequency */ float lfe_cutoff; /* in Hz, 0 is disabled */ @@ -59,7 +60,7 @@ struct channelmix { float rear_delay; /* in ms, 0 is disabled */ float widen; /* stereo widen. 0 is disabled */ uint32_t hilbert_taps; /* to phase shift, 0 disabled */ - struct lr4 lr4[SPA_AUDIO_MAX_CHANNELS]; + struct lr4 lr4[MAX_CHANNELS]; float buffer_mem[2 * BUFFER_SIZE*2 + CHANNELMIX_OPS_MAX_ALIGN/4]; float *buffer[2]; diff --git a/spa/plugins/audioconvert/test-audioconvert.c b/spa/plugins/audioconvert/test-audioconvert.c index 0546e629a..de3ebb8b5 100644 --- a/spa/plugins/audioconvert/test-audioconvert.c +++ b/spa/plugins/audioconvert/test-audioconvert.c @@ -25,7 +25,8 @@ SPA_LOG_IMPL(logger); extern const struct spa_handle_factory test_source_factory; -#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1) +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS +#define MAX_PORTS (MAX_CHANNELS+1) struct context { struct spa_handle *convert_handle; diff --git a/spa/plugins/avb/avb-pcm.h b/spa/plugins/avb/avb-pcm.h index 7e026741d..41f803d4c 100644 --- a/spa/plugins/avb/avb-pcm.h +++ b/spa/plugins/avb/avb-pcm.h @@ -109,6 +109,7 @@ static inline char *format_streamid(char *str, size_t size, const uint64_t strea return str; } +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_BUFFERS 32 struct buffer { @@ -127,7 +128,7 @@ struct buffer { struct channel_map { uint32_t channels; - uint32_t pos[SPA_AUDIO_MAX_CHANNELS]; + uint32_t pos[MAX_CHANNELS]; }; struct port { diff --git a/spa/plugins/bluez5/a2dp-codec-opus.c b/spa/plugins/bluez5/a2dp-codec-opus.c index 21ede7d34..d9cdc9a0e 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus.c +++ b/spa/plugins/bluez5/a2dp-codec-opus.c @@ -58,6 +58,8 @@ static struct spa_log *log; #define BITRATE_DUPLEX_BIDI 160000 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + #define OPUS_05_MAX_BYTES (15 * 1024) struct props { @@ -313,14 +315,14 @@ static void parse_settings(struct props *props, const struct spa_dict *settings) return; if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.channels"), &v, 0)) - props->channels = SPA_CLAMP(v, 1u, SPA_AUDIO_MAX_CHANNELS); + props->channels = SPA_CLAMP(v, 1u, MAX_CHANNELS); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.max-bitrate"), &v, 0)) props->max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.coupled-streams"), &v, 0)) props->coupled_streams = SPA_CLAMP(v, 0u, props->channels / 2); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.channels"), &v, 0)) - props->bidi_channels = SPA_CLAMP(v, 0u, SPA_AUDIO_MAX_CHANNELS); + props->bidi_channels = SPA_CLAMP(v, 0u, MAX_CHANNELS); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.max-bitrate"), &v, 0)) props->bidi_max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.coupled-streams"), &v, 0)) @@ -503,7 +505,7 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc const uint8_t *permutation = NULL; size_t i, j; - if (channels > SPA_AUDIO_MAX_CHANNELS) + if (channels > MAX_CHANNELS) return -EINVAL; if (2 * coupled_streams > channels) return -EINVAL; @@ -561,7 +563,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, a2dp_opus_05_t a2dp_opus_05 = { .info = codec->vendor, .main = { - .channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS), + .channels = SPA_MIN(255u, MAX_CHANNELS), .frame_duration = (OPUS_05_FRAME_DURATION_25 | OPUS_05_FRAME_DURATION_50 | OPUS_05_FRAME_DURATION_100 | @@ -571,7 +573,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, OPUS_05_INIT_BITRATE(0) }, .bidi = { - .channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS), + .channels = SPA_MIN(255u, MAX_CHANNELS), .frame_duration = (OPUS_05_FRAME_DURATION_25 | OPUS_05_FRAME_DURATION_50 | OPUS_05_FRAME_DURATION_100 | @@ -771,7 +773,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_opus_05_t conf; a2dp_opus_05_direction_t *dir; struct spa_pod_frame f[1]; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[MAX_CHANNELS]; if (caps_size < sizeof(conf)) return -EINVAL; diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index 4b5ca1d65..b3af4615b 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -6850,7 +6850,7 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di const char *key, uint32_t *value) { const char *str; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[MAX_CHANNELS]; uint32_t n_channels; uint32_t locations; unsigned int i, j; diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index dde098ace..64d7064f7 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -38,7 +38,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.device"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic -#define MAX_NODES (2*SPA_AUDIO_MAX_CHANNELS) +#define MAX_NODES (2*MAX_CHANNELS) #define DEVICE_ID_SOURCE 0 #define DEVICE_ID_SINK 1 @@ -99,9 +99,9 @@ struct node { unsigned int offload_acquired:1; uint32_t n_channels; int64_t latency_offset; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; - float volumes[SPA_AUDIO_MAX_CHANNELS]; - float soft_volumes[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; + float soft_volumes[MAX_CHANNELS]; }; struct dynamic_node @@ -129,8 +129,8 @@ struct device_set { bool leader; uint32_t sinks; uint32_t sources; - struct device_set_member sink[SPA_AUDIO_MAX_CHANNELS]; - struct device_set_member source[SPA_AUDIO_MAX_CHANNELS]; + struct device_set_member sink[MAX_CHANNELS]; + struct device_set_member source[MAX_CHANNELS]; }; struct impl { @@ -182,7 +182,7 @@ static void init_node(struct impl *this, struct node *node, uint32_t id) spa_zero(*node); node->id = id; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { node->volumes[i] = 1.0f; node->soft_volumes[i] = 1.0f; } @@ -546,7 +546,7 @@ static void emit_device_set_node(struct impl *this, uint32_t id) if (node->channels[k] == t->channels[j]) break; } - if (k == node->n_channels && node->n_channels < SPA_AUDIO_MAX_CHANNELS) + if (k == node->n_channels && node->n_channels < MAX_CHANNELS) node->channels[node->n_channels++] = t->channels[j]; } } @@ -2946,8 +2946,8 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p struct spa_pod_prop *prop; struct spa_pod_object *obj = (struct spa_pod_object *) props; int changed = 0; - float volumes[SPA_AUDIO_MAX_CHANNELS]; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; uint32_t n_volumes = 0, SPA_UNUSED n_channels = 0; int64_t latency_offset = 0; diff --git a/spa/plugins/bluez5/defs.h b/spa/plugins/bluez5/defs.h index 45754c19a..0e25592e2 100644 --- a/spa/plugins/bluez5/defs.h +++ b/spa/plugins/bluez5/defs.h @@ -157,6 +157,8 @@ extern "C" { #define SPA_BT_NO_BATTERY ((uint8_t)255) +#define MAX_CHANNELS (SPA_AUDIO_MAX_CHANNELS) + enum spa_bt_media_direction { SPA_BT_MEDIA_SOURCE, SPA_BT_MEDIA_SINK, @@ -678,7 +680,7 @@ struct spa_bt_transport { struct spa_list bap_transport_linked; uint32_t n_channels; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; struct spa_bt_transport_volume volumes[SPA_BT_VOLUME_ID_TERM]; diff --git a/spa/plugins/bluez5/media-sink.c b/spa/plugins/bluez5/media-sink.c index 3d3f2ec75..bac1e84ab 100644 --- a/spa/plugins/bluez5/media-sink.c +++ b/spa/plugins/bluez5/media-sink.c @@ -2139,7 +2139,7 @@ static int port_set_format(struct impl *this, struct port *port, if (info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; if (this->transport && this->transport->iso_io) { diff --git a/spa/plugins/bluez5/media-source.c b/spa/plugins/bluez5/media-source.c index 4fab044a0..dc52a09b4 100644 --- a/spa/plugins/bluez5/media-source.c +++ b/spa/plugins/bluez5/media-source.c @@ -1439,7 +1439,7 @@ static int port_set_format(struct impl *this, struct port *port, if (info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; port->frame_size = info.info.raw.channels; diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c index d32e4a37f..871eda4da 100644 --- a/spa/plugins/filter-graph/filter-graph.c +++ b/spa/plugins/filter-graph/filter-graph.c @@ -39,6 +39,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.filter-graph"); #define MAX_HNDL 64 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DEFAULT_RATE 48000 @@ -154,15 +155,15 @@ struct graph_hndl { struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; uint32_t n_ports; - struct port *ports[SPA_AUDIO_MAX_CHANNELS]; - float min[SPA_AUDIO_MAX_CHANNELS]; - float max[SPA_AUDIO_MAX_CHANNELS]; + struct port *ports[MAX_CHANNELS]; + float min[MAX_CHANNELS]; + float max[MAX_CHANNELS]; #define SCALE_LINEAR 0 #define SCALE_CUBIC 1 - int scale[SPA_AUDIO_MAX_CHANNELS]; + int scale[MAX_CHANNELS]; }; struct graph { @@ -194,9 +195,9 @@ struct graph { uint32_t n_inputs; uint32_t n_outputs; - uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t inputs_position[MAX_CHANNELS]; uint32_t n_inputs_position; - uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t outputs_position[MAX_CHANNELS]; uint32_t n_outputs_position; float min_latency; @@ -256,8 +257,8 @@ static void emit_filter_graph_info(struct impl *impl, bool full) char n_inputs[64], n_outputs[64], latency[64]; struct spa_dict_item items[6]; struct spa_dict dict = SPA_DICT(items, 0); - char in_pos[SPA_AUDIO_MAX_CHANNELS * 8]; - char out_pos[SPA_AUDIO_MAX_CHANNELS * 8]; + char in_pos[MAX_CHANNELS * 8]; + char out_pos[MAX_CHANNELS * 8]; snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs); snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs); @@ -745,7 +746,7 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru case SPA_PROP_channelVolumes: { uint32_t i, n_vols; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { @@ -772,7 +773,7 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru } } if (do_volume && vol->n_ports != 0) { - float soft_vols[SPA_AUDIO_MAX_CHANNELS]; + float soft_vols[MAX_CHANNELS]; uint32_t i; for (i = 0; i < vol->n_volumes; i++) @@ -1264,7 +1265,7 @@ static int parse_volume(struct graph *graph, struct spa_json *json, enum spa_dir spa_log_error(impl->log, "unknown control port %s", control); return -ENOENT; } - if (vol->n_ports >= SPA_AUDIO_MAX_CHANNELS) { + if (vol->n_ports >= MAX_CHANNELS) { spa_log_error(impl->log, "too many volume controls"); return -ENOSPC; } diff --git a/spa/plugins/support/null-audio-sink.c b/spa/plugins/support/null-audio-sink.c index 70edd4ff2..0943fbb91 100644 --- a/spa/plugins/support/null-audio-sink.c +++ b/spa/plugins/support/null-audio-sink.c @@ -35,12 +35,13 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.null-audio-sink"); #define DEFAULT_CLOCK_NAME "clock.system.monotonic" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS struct props { uint32_t format; uint32_t channels; uint32_t rate; - uint32_t pos[SPA_AUDIO_MAX_CHANNELS]; + uint32_t pos[MAX_CHANNELS]; char clock_name[64]; unsigned int debug:1; unsigned int driver:1; diff --git a/src/modules/module-combine-stream.c b/src/modules/module-combine-stream.c index 842138688..6166f161f 100644 --- a/src/modules/module-combine-stream.c +++ b/src/modules/module-combine-stream.c @@ -231,9 +231,9 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); "( stream.props= ) " \ "( stream.rules= ) " +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DELAYBUF_MAX_SIZE (20 * sizeof(float) * 96000) - static const struct spa_dict_item module_props[] = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans " }, { PW_KEY_MODULE_DESCRIPTION, "Combine multiple streams into a single stream" }, @@ -312,10 +312,10 @@ struct stream { struct spa_latency_info latency; struct spa_audio_info_raw info; - uint32_t remap[SPA_AUDIO_MAX_CHANNELS]; + uint32_t remap[MAX_CHANNELS]; void *delaybuf; - struct ringbuffer delay[SPA_AUDIO_MAX_CHANNELS]; + struct ringbuffer delay[MAX_CHANNELS]; int64_t delay_samples; /* for main loop */ int64_t data_delay_samples; /* for data loop */ @@ -509,7 +509,7 @@ static void update_latency(struct impl *impl) struct replace_delay_info { struct stream *stream; void *buf; - struct ringbuffer delay[SPA_AUDIO_MAX_CHANNELS]; + struct ringbuffer delay[MAX_CHANNELS]; }; static int do_replace_delay(struct spa_loop *loop, bool async, uint32_t seq, @@ -1228,7 +1228,7 @@ static void combine_output_process(void *d) struct pw_buffer *in, *out; struct stream *s; bool delay_changed = false; - bool mix[SPA_AUDIO_MAX_CHANNELS]; + bool mix[MAX_CHANNELS]; if ((out = pw_stream_dequeue_buffer(impl->combine)) == NULL) { pw_log_debug("%p: out of output buffers: %m", impl); diff --git a/src/modules/module-echo-cancel.c b/src/modules/module-echo-cancel.c index d241369c7..bb9ea3adb 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -154,6 +154,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define DEFAULT_RATE 48000 #define DEFAULT_POSITION "[ FL FR ]" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS /* Hopefully this is enough for any combination of AEC engine and resampler * input requirement for rate matching */ @@ -203,7 +204,7 @@ struct impl { struct spa_hook source_listener; struct spa_audio_info_raw source_info; - void *rec_buffer[SPA_AUDIO_MAX_CHANNELS]; + void *rec_buffer[MAX_CHANNELS]; uint32_t rec_ringsize; struct spa_ringbuffer rec_ring; @@ -215,13 +216,13 @@ struct impl { struct pw_properties *sink_props; struct pw_stream *sink; struct spa_hook sink_listener; - void *play_buffer[SPA_AUDIO_MAX_CHANNELS]; + void *play_buffer[MAX_CHANNELS]; uint32_t play_ringsize; struct spa_ringbuffer play_ring; struct spa_ringbuffer play_delayed_ring; struct spa_audio_info_raw sink_info; - void *out_buffer[SPA_AUDIO_MAX_CHANNELS]; + void *out_buffer[MAX_CHANNELS]; uint32_t out_ringsize; struct spa_ringbuffer out_ring; diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c index 8c94edcdd..e4ad5cab8 100644 --- a/src/modules/module-ffado-driver.c +++ b/src/modules/module-ffado-driver.c @@ -112,6 +112,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_PORTS 128 #define FFADO_RT_PRIORITY_PACKETIZER_RELATIVE 5 @@ -179,7 +180,7 @@ struct port { struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; struct stream { @@ -760,7 +761,7 @@ static int make_stream_ports(struct stream *s) struct port *port = s->ports[i]; char channel[32]; - snprintf(channel, sizeof(channel), "AUX%u", n_channels % SPA_AUDIO_MAX_CHANNELS); + snprintf(channel, sizeof(channel), "AUX%u", n_channels % MAX_CHANNELS); switch (port->stream_type) { case ffado_stream_type_audio: @@ -873,7 +874,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { s->volume.n_volumes = n; @@ -1228,7 +1229,7 @@ static int probe_ffado_device(struct impl *impl) } if (impl->source.info.channels != n_channels) { impl->source.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + for (i = 0; i < SPA_MIN(impl->source.info.channels, MAX_CHANNELS); i++) impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1254,7 +1255,7 @@ static int probe_ffado_device(struct impl *impl) } if (impl->sink.info.channels != n_channels) { impl->sink.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + for (i = 0; i < SPA_MIN(impl->sink.info.channels, MAX_CHANNELS); i++) impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index 226caf6fe..2c88a5cbc 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -115,6 +115,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_PORTS 128 #define DEFAULT_CLIENT_NAME "PipeWire" @@ -157,7 +158,7 @@ struct port { struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; struct stream { @@ -624,7 +625,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { s->volume.n_volumes = n; diff --git a/src/modules/module-netjack2-driver.c b/src/modules/module-netjack2-driver.c index cacdf9368..cfc48f69b 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -512,7 +512,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { s->volume.n_volumes = n; diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c index c5d8d308b..aa61013ea 100644 --- a/src/modules/module-netjack2-manager.c +++ b/src/modules/module-netjack2-manager.c @@ -677,7 +677,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { s->volume.n_volumes = n; diff --git a/src/modules/module-netjack2/peer.c b/src/modules/module-netjack2/peer.c index 37f7854db..eacc1c95b 100644 --- a/src/modules/module-netjack2/peer.c +++ b/src/modules/module-netjack2/peer.c @@ -7,10 +7,12 @@ #include #endif +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; static inline float bswap_f32(float f) diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index e3d438c93..0bea31bc0 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -12,6 +12,8 @@ #include "format.h" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + static const struct format audio_formats[] = { [SAMPLE_U8] = { SAMPLE_U8, SPA_AUDIO_FORMAT_U8, "u8", 1 }, [SAMPLE_ALAW] = { SAMPLE_ALAW, SPA_AUDIO_FORMAT_ALAW, "alaw", 1 }, diff --git a/src/modules/module-pulse-tunnel.c b/src/modules/module-pulse-tunnel.c index c9b9e5f9d..26af5aa20 100644 --- a/src/modules/module-pulse-tunnel.c +++ b/src/modules/module-pulse-tunnel.c @@ -117,6 +117,8 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION "[ FL FR ]" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + #define MODULE_USAGE "( remote.name= ] " \ "( node.latency= ] " \ "( node.name= ] " \ @@ -295,7 +297,7 @@ static void stream_param_changed(void *d, uint32_t id, const struct spa_pod *par { struct pa_cvolume volume; uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { @@ -832,10 +834,10 @@ do_stream_sync_volumes(struct spa_loop *loop, struct spa_pod_frame f[1]; struct spa_pod *param; uint32_t i, channels; - float vols[SPA_AUDIO_MAX_CHANNELS]; - float soft_vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; + float soft_vols[MAX_CHANNELS]; - channels = SPA_MIN(impl->volume.channels, SPA_AUDIO_MAX_CHANNELS); + channels = SPA_MIN(impl->volume.channels, MAX_CHANNELS); for (i = 0; i < channels; i++) { vols[i] = (float)pa_sw_volume_to_linear(impl->volume.values[i]); soft_vols[i] = 1.0f; diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 11948807b..2fdb80618 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -158,6 +158,7 @@ PW_LOG_TOPIC(mod_topic, "mod." NAME); #define RAOP_LATENCY_MS 250 #define DEFAULT_LATENCY_MS 1500 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define VOLUME_MAX 0.0 #define VOLUME_MIN -30.0 #define VOLUME_MUTE -144.0 @@ -1612,8 +1613,8 @@ static void stream_props_changed(struct impl *impl, uint32_t id, const struct sp case SPA_PROP_channelVolumes: { uint32_t i, n_vols; - float vols[SPA_AUDIO_MAX_CHANNELS], volume; - float soft_vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS], volume; + float soft_vols[MAX_CHANNELS]; if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { diff --git a/src/modules/module-rtp-sap.c b/src/modules/module-rtp-sap.c index 0c00beb61..22fe89b68 100644 --- a/src/modules/module-rtp-sap.c +++ b/src/modules/module-rtp-sap.c @@ -156,6 +156,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define DEFAULT_LOOP false #define MAX_SDP 2048 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define USAGE "( local.ifname= ) " \ "( sap.ip= ) " \ @@ -1404,7 +1405,7 @@ static int parse_sdp_i(struct impl *impl, char *c, struct sdp_info *info) c[strcspn(c, " ")] = '\0'; uint32_t channels; - if (sscanf(c, "%u", &channels) != 1 || channels <= 0 || channels > SPA_AUDIO_MAX_CHANNELS) + if (sscanf(c, "%u", &channels) != 1 || channels <= 0 || channels > MAX_CHANNELS) return 0; c += strcspn(c, "\0"); diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index cc1a3bda2..199046e80 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -70,6 +70,8 @@ #define DEFAULT_VOLUME 1.0 #define DEFAULT_QUALITY 4 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + enum mode { mode_none, mode_playback, @@ -91,7 +93,7 @@ typedef int (*fill_fn)(struct data *d, void *dest, unsigned int n_frames, bool * struct channelmap { uint32_t n_channels; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; }; struct data { From 9e7cae13df10382574ed405d67cbda6257056aee Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 20 Oct 2025 18:09:11 +0200 Subject: [PATCH 08/43] alsa: use the amount of positions we will write --- pipewire-alsa/alsa-plugins/pcm_pipewire.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index 4bd55c2a8..4b91e82c0 100644 --- a/pipewire-alsa/alsa-plugins/pcm_pipewire.c +++ b/pipewire-alsa/alsa-plugins/pcm_pipewire.c @@ -643,7 +643,7 @@ static int snd_pcm_pipewire_pause(snd_pcm_ioplug_t * io, int enable) #define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE #endif -static int set_default_channels(uint32_t channels, uint32_t position[MAX_CHANNELS]) +static int set_default_channels(uint32_t channels, uint32_t position[8]) { switch (channels) { case 8: From 8bbca3b8f36e7aea04ce07998c30acf28b23c48a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 20 Oct 2025 15:16:54 +0200 Subject: [PATCH 09/43] spa: add spa_audio_parse_position_n Add a function that accepts the size of the position array when reading the audio positions. This makes it possible to decouple the position array size from SPA_AUDIO_MAX_CHANNELS. Also use SPA_N_ELEMENTS to pass the number of array elements to functions instead of a fixed constant. This makes it easier to change the array size later to a different constant without having to patch up all the places where the size is used. --- pipewire-alsa/alsa-plugins/ctl_pipewire.c | 2 +- spa/include/spa/param/audio/dsd-utils.h | 2 +- spa/include/spa/param/audio/raw-json.h | 17 ++++++++++++----- spa/include/spa/param/audio/raw-utils.h | 2 +- spa/plugins/alsa/alsa-acp-device.c | 4 ++-- spa/plugins/alsa/alsa-pcm.h | 2 +- spa/plugins/audioconvert/audioconvert.c | 21 +++++++++++---------- spa/plugins/avb/avb-pcm.c | 3 ++- spa/plugins/bluez5/bluez5-dbus.c | 3 ++- spa/plugins/bluez5/bluez5-device.c | 4 ++-- spa/plugins/filter-graph/filter-graph.c | 12 +++++++----- spa/plugins/support/null-audio-sink.c | 5 +++-- spa/plugins/volume/volume.c | 2 +- src/examples/export-source.c | 2 +- src/modules/module-combine-stream.c | 6 ++++-- src/modules/module-echo-cancel.c | 16 ++++++++-------- src/modules/module-example-filter.c | 2 +- src/modules/module-ffado-driver.c | 2 +- src/modules/module-jack-tunnel.c | 2 +- src/modules/module-loopback.c | 2 +- src/modules/module-netjack2-driver.c | 10 ++++++---- src/modules/module-netjack2-manager.c | 8 +++++--- src/modules/module-protocol-pulse/format.c | 5 +++-- src/modules/module-protocol-pulse/volume.c | 6 +++--- src/modules/module-pulse-tunnel.c | 2 +- src/modules/module-raop-sink.c | 2 +- src/tools/pw-cat.c | 3 ++- 27 files changed, 84 insertions(+), 63 deletions(-) diff --git a/pipewire-alsa/alsa-plugins/ctl_pipewire.c b/pipewire-alsa/alsa-plugins/ctl_pipewire.c index 459355c8f..36bc4be5e 100644 --- a/pipewire-alsa/alsa-plugins/ctl_pipewire.c +++ b/pipewire-alsa/alsa-plugins/ctl_pipewire.c @@ -856,7 +856,7 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi uint32_t n_volumes, i; n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - volumes, SPA_AUDIO_MAX_CHANNELS); + volumes, SPA_N_ELEMENTS(volumes)); g->node.channel_volume.channels = n_volumes; for (i = 0; i < n_volumes; i++) diff --git a/spa/include/spa/param/audio/dsd-utils.h b/spa/include/spa/param/audio/dsd-utils.h index 980bdf971..2d99e79f2 100644 --- a/spa/include/spa/param/audio/dsd-utils.h +++ b/spa/include/spa/param/audio/dsd-utils.h @@ -42,7 +42,7 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); if (position == NULL || - !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS)) + !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_N_ELEMENTS(info->position))) SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); return res; diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index 92d7a1aad..50bd63181 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -28,8 +28,8 @@ extern "C" { #endif SPA_API_AUDIO_RAW_JSON int -spa_audio_parse_position(const char *str, size_t len, - uint32_t *position, uint32_t *n_channels) +spa_audio_parse_position_n(const char *str, size_t len, + uint32_t *position, uint32_t max_channels, uint32_t *n_channels) { struct spa_json iter; char v[256]; @@ -39,12 +39,18 @@ spa_audio_parse_position(const char *str, size_t len, return 0; while (spa_json_get_string(&iter, v, sizeof(v)) > 0 && - channels < SPA_AUDIO_MAX_CHANNELS) { + channels < max_channels) { position[channels++] = spa_type_audio_channel_from_short_name(v); } *n_channels = channels; return channels; } +SPA_API_AUDIO_RAW_JSON int +spa_audio_parse_position(const char *str, size_t len, + uint32_t *position, uint32_t *n_channels) +{ + return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_CHANNELS, n_channels); +} SPA_API_AUDIO_RAW_JSON int spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, const char *val, bool force) @@ -58,10 +64,11 @@ spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, cons info->rate = v; } else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) { if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) - info->channels = SPA_MIN(v, SPA_AUDIO_MAX_CHANNELS); + info->channels = SPA_MIN(v, SPA_N_ELEMENTS(info->position)); } else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) { if (force || info->channels == 0) { - if (spa_audio_parse_position(val, strlen(val), info->position, &info->channels) > 0) + if (spa_audio_parse_position_n(val, strlen(val), info->position, + SPA_N_ELEMENTS(info->position), &info->channels) > 0) SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); } } diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 505965972..1f1dfe45d 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -55,7 +55,7 @@ spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_r SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); if (position == NULL || - !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS)) + !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_N_ELEMENTS(info->position))) SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); return res; diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c index bdcb40fa5..6ab41c8d2 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -697,13 +697,13 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct break; case SPA_PROP_channelVolumes: if ((n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + volumes, SPA_N_ELEMENTS(volumes))) > 0) { changed++; } break; case SPA_PROP_channelMap: if (spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - channels, SPA_AUDIO_MAX_CHANNELS) > 0) { + channels, SPA_N_ELEMENTS(channels)) > 0) { changed++; } break; diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index 5bad22159..09b5140b9 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -315,7 +315,7 @@ void spa_alsa_emit_port_info(struct state *state, bool full); static inline void spa_alsa_parse_position(struct channel_map *map, const char *val, size_t len) { - spa_audio_parse_position(val, len, map->pos, &map->channels); + spa_audio_parse_position_n(val, len, map->pos, SPA_N_ELEMENTS(map->pos), &map->channels); } static inline uint32_t spa_alsa_parse_rates(uint32_t *rates, uint32_t max, const char *val, size_t len) diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index 0658dea1c..2ff270fb1 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -1104,11 +1104,11 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info) else if (spa_streq(k, "n_outputs")) spa_atou32(s, &g->n_outputs, 0); else if (spa_streq(k, "inputs.audio.position")) - spa_audio_parse_position(s, strlen(s), - g->inputs_position, &g->n_inputs); + spa_audio_parse_position_n(s, strlen(s), g->inputs_position, + SPA_N_ELEMENTS(g->inputs_position), &g->n_inputs); else if (spa_streq(k, "outputs.audio.position")) - spa_audio_parse_position(s, strlen(s), - g->outputs_position, &g->n_outputs); + spa_audio_parse_position_n(s, strlen(s), g->outputs_position, + SPA_N_ELEMENTS(g->outputs_position), &g->n_outputs); else if (spa_streq(k, "latency")) { double latency; if (spa_atod(s, &latency)) @@ -1749,7 +1749,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) case SPA_PROP_channelVolumes: if (!p->lock_volumes && (n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->channel.volumes, SPA_N_ELEMENTS(p->channel.volumes))) > 0) { have_channel_volume = true; p->channel.n_volumes = n; changed++; @@ -1757,7 +1757,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) break; case SPA_PROP_channelMap: if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - p->channel_map, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->channel_map, SPA_N_ELEMENTS(p->channel_map))) > 0) { p->n_channels = n; changed++; } @@ -1772,7 +1772,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) case SPA_PROP_softVolumes: if (!p->lock_volumes && (n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->soft.volumes, SPA_N_ELEMENTS(p->soft.volumes))) > 0) { have_soft_volume = true; p->soft.n_volumes = n; changed++; @@ -1784,7 +1784,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) break; case SPA_PROP_monitorVolumes: if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->monitor.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->monitor.volumes, SPA_N_ELEMENTS(p->monitor.volumes))) > 0) { p->monitor.n_volumes = n; changed++; } @@ -4297,8 +4297,9 @@ impl_init(const struct spa_handle_factory *factory, } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { if (s != NULL) - spa_audio_parse_position(s, strlen(s), this->props.channel_map, - &this->props.n_channels); + spa_audio_parse_position_n(s, strlen(s), + this->props.channel_map, SPA_N_ELEMENTS(this->props.channel_map), + &this->props.n_channels); } else if (spa_streq(k, SPA_KEY_PORT_IGNORE_LATENCY)) this->port_ignore_latency = spa_atob(s); diff --git a/spa/plugins/avb/avb-pcm.c b/spa/plugins/avb/avb-pcm.c index 59b05a3a5..f54e9dabf 100644 --- a/spa/plugins/avb/avb-pcm.c +++ b/spa/plugins/avb/avb-pcm.c @@ -40,7 +40,8 @@ static int avb_set_param(struct state *state, const char *k, const char *s) state->default_format = spa_type_audio_format_from_short_name(s); fmt_change++; } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { - spa_audio_parse_position(s, strlen(s), state->default_pos.pos, + spa_audio_parse_position_n(s, strlen(s), state->default_pos.pos, + SPA_N_ELEMENTS(state->default_pos.pos), &state->default_pos.channels); fmt_change++; } else if (spa_streq(k, SPA_KEY_AUDIO_ALLOWED_RATES)) { diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index b3af4615b..d4305c535 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -6861,7 +6861,8 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di if (spa_atou32(str, value, 0)) return; - if (!spa_audio_parse_position(str, strlen(str), position, &n_channels)) { + if (!spa_audio_parse_position_n(str, strlen(str), position, + SPA_N_ELEMENTS(position), &n_channels)) { spa_log_error(this->log, "property %s '%s' is not valid position array", key, str); return; } diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index 64d7064f7..aee6d8cab 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -2972,11 +2972,11 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p break; case SPA_PROP_channelVolumes: n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - volumes, SPA_AUDIO_MAX_CHANNELS); + volumes, SPA_N_ELEMENTS(volumes)); break; case SPA_PROP_channelMap: n_channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - channels, SPA_AUDIO_MAX_CHANNELS); + channels, SPA_N_ELEMENTS(channels)); break; case SPA_PROP_latencyOffsetNsec: if (spa_pod_get_long(&prop->value, &latency_offset) == 0) { diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c index 871eda4da..bb7f24831 100644 --- a/spa/plugins/filter-graph/filter-graph.c +++ b/spa/plugins/filter-graph/filter-graph.c @@ -749,7 +749,7 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru float vols[MAX_CHANNELS]; if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, - SPA_AUDIO_MAX_CHANNELS)) > 0) { + SPA_N_ELEMENTS(vols))) > 0) { if (vol->n_volumes != n_vols) do_volume = true; vol->n_volumes = n_vols; @@ -2119,8 +2119,9 @@ static int load_graph(struct graph *graph, const struct spa_dict *props) spa_log_error(impl->log, "%s expects an array", key); return -EINVAL; } - spa_audio_parse_position(val, len, graph->inputs_position, - &graph->n_inputs_position); + spa_audio_parse_position_n(val, len, graph->inputs_position, + SPA_N_ELEMENTS(graph->inputs_position), + &graph->n_inputs_position); impl->info.n_inputs = graph->n_inputs_position; } else if (spa_streq("outputs.audio.position", key)) { @@ -2129,8 +2130,9 @@ static int load_graph(struct graph *graph, const struct spa_dict *props) spa_log_error(impl->log, "%s expects an array", key); return -EINVAL; } - spa_audio_parse_position(val, len, graph->outputs_position, - &graph->n_outputs_position); + spa_audio_parse_position_n(val, len, graph->outputs_position, + SPA_N_ELEMENTS(graph->outputs_position), + &graph->n_outputs_position); impl->info.n_outputs = graph->n_outputs_position; } else if (spa_streq("nodes", key)) { diff --git a/spa/plugins/support/null-audio-sink.c b/spa/plugins/support/null-audio-sink.c index 0943fbb91..27ae8d0f0 100644 --- a/spa/plugins/support/null-audio-sink.c +++ b/spa/plugins/support/null-audio-sink.c @@ -637,7 +637,7 @@ port_set_format(struct impl *this, if (info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > SPA_N_ELEMENTS(info.info.raw.position)) return -EINVAL; if (this->props.format != 0) { @@ -950,7 +950,8 @@ impl_init(const struct spa_handle_factory *factory, } else if (spa_streq(k, SPA_KEY_NODE_DRIVER)) { this->props.driver = spa_atob(s); } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { - spa_audio_parse_position(s, strlen(s), this->props.pos, &this->props.channels); + spa_audio_parse_position_n(s, strlen(s), this->props.pos, + SPA_N_ELEMENTS(this->props.pos), &this->props.channels); } else if (spa_streq(k, "clock.name")) { spa_scnprintf(this->props.clock_name, sizeof(this->props.clock_name), diff --git a/spa/plugins/volume/volume.c b/spa/plugins/volume/volume.c index 164e46a6a..7881703db 100644 --- a/spa/plugins/volume/volume.c +++ b/spa/plugins/volume/volume.c @@ -455,7 +455,7 @@ static int port_set_format(void *object, if (info.info.raw.format != SPA_AUDIO_FORMAT_S16 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > SPA_N_ELEMENTS(info.info.raw.position)) return -EINVAL; this->bpf = 2 * info.info.raw.channels; diff --git a/src/examples/export-source.c b/src/examples/export-source.c index 85af5b727..3d3859273 100644 --- a/src/examples/export-source.c +++ b/src/examples/export-source.c @@ -269,7 +269,7 @@ static int port_set_format(void *object, return -EINVAL; if (d->format.rate == 0 || d->format.channels == 0 || - d->format.channels > SPA_AUDIO_MAX_CHANNELS) + d->format.channels > SPA_N_ELEMENTS(d->format.position)) return -EINVAL; } diff --git a/src/modules/module-combine-stream.c b/src/modules/module-combine-stream.c index 6166f161f..54a98970b 100644 --- a/src/modules/module-combine-stream.c +++ b/src/modules/module-combine-stream.c @@ -866,13 +866,15 @@ static int create_stream(struct stream_info *info) s->info = impl->info; if ((str = pw_properties_get(info->stream_props, SPA_KEY_AUDIO_POSITION)) != NULL) - spa_audio_parse_position(str, strlen(str), s->info.position, &s->info.channels); + spa_audio_parse_position_n(str, strlen(str), s->info.position, + SPA_N_ELEMENTS(s->info.position), &s->info.channels); if (s->info.channels == 0) s->info = impl->info; spa_zero(remap_info); if ((str = pw_properties_get(info->stream_props, "combine.audio.position")) != NULL) - spa_audio_parse_position(str, strlen(str), remap_info.position, &remap_info.channels); + spa_audio_parse_position_n(str, strlen(str), remap_info.position, + SPA_N_ELEMENTS(remap_info.position), &remap_info.channels); if (remap_info.channels == 0) remap_info = s->info; diff --git a/src/modules/module-echo-cancel.c b/src/modules/module-echo-cancel.c index bb9ea3adb..0d640f162 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -1371,21 +1371,21 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) } if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->capture_info.position, &impl->capture_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->capture_info.position, + SPA_N_ELEMENTS(impl->capture_info.position), &impl->capture_info.channels); } if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->source_info.position, &impl->source_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->source_info.position, + SPA_N_ELEMENTS(impl->source_info.position), &impl->source_info.channels); } if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->sink_info.position, &impl->sink_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->sink_info.position, + SPA_N_ELEMENTS(impl->sink_info.position), &impl->sink_info.channels); impl->playback_info = impl->sink_info; } if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->playback_info.position, &impl->playback_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->playback_info.position, + SPA_N_ELEMENTS(impl->playback_info.position), &impl->playback_info.channels); if (impl->playback_info.channels != impl->sink_info.channels) impl->playback_info = impl->sink_info; } diff --git a/src/modules/module-example-filter.c b/src/modules/module-example-filter.c index ec9cdbd19..92f1f38cb 100644 --- a/src/modules/module-example-filter.c +++ b/src/modules/module-example-filter.c @@ -299,7 +299,7 @@ static void capture_param_changed(void *data, uint32_t id, const struct spa_pod return; if (info.rate == 0 || info.channels == 0 || - info.channels > SPA_AUDIO_MAX_CHANNELS) + info.channels > SPA_N_ELEMENTS(info.position)) return; break; } diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c index e4ad5cab8..db6fe8b71 100644 --- a/src/modules/module-ffado-driver.c +++ b/src/modules/module-ffado-driver.c @@ -876,7 +876,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) uint32_t n; float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index 2c88a5cbc..f5066abc4 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -627,7 +627,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) uint32_t n; float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index b23d167c1..ce1da189d 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -539,7 +539,7 @@ static void param_format_changed(struct impl *impl, const struct spa_pod *param, if (param != NULL) { if (spa_format_audio_raw_parse(param, &info) < 0 || info.channels == 0 || - info.channels > SPA_AUDIO_MAX_CHANNELS) + info.channels > SPA_N_ELEMENTS(info.position)) return; if ((impl->info.format != 0 && impl->info.format != info.format) || diff --git a/src/modules/module-netjack2-driver.c b/src/modules/module-netjack2-driver.c index cfc48f69b..f1aefaa35 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -442,7 +442,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_debug_type_find_short_name(spa_type_audio_channel, - s->info.position[i % SPA_AUDIO_MAX_CHANNELS]); + s->info.position[i % SPA_N_ELEMENTS(s->info.position)]); props = pw_properties_new( PW_KEY_FORMAT_DSP, "32 bit float mono audio", PW_KEY_AUDIO_CHANNEL, str ? str : "UNK", @@ -514,7 +514,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) uint32_t n; float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; @@ -863,7 +863,8 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p } impl->sink.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.send_audio_channels != impl->sink.info.channels) { - impl->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + impl->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, + (int)SPA_N_ELEMENTS(impl->sink.info.position)); for (i = 0; i < impl->sink.info.channels; i++) impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -874,7 +875,8 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p } impl->source.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.recv_audio_channels != impl->source.info.channels) { - impl->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + impl->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, + (int)SPA_N_ELEMENTS(impl->source.info.position)); for (i = 0; i < impl->source.info.channels; i++) impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c index aa61013ea..6b3285305 100644 --- a/src/modules/module-netjack2-manager.c +++ b/src/modules/module-netjack2-manager.c @@ -679,7 +679,7 @@ static void parse_props(struct stream *s, const struct spa_pod *param) uint32_t n; float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; @@ -1026,14 +1026,16 @@ static int handle_follower_available(struct impl *impl, struct nj2_session_param follower->source.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels; follower->source.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.recv_audio_channels != follower->source.info.channels) { - follower->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + follower->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, + (int)SPA_N_ELEMENTS(follower->source.info.position)); for (i = 0; i < follower->source.info.channels; i++) follower->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } follower->sink.n_ports = peer->params.send_audio_channels + peer->params.send_midi_channels; follower->sink.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.send_audio_channels != follower->sink.info.channels) { - follower->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + follower->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, + (int)SPA_N_ELEMENTS(follower->sink.info.position)); for (i = 0; i < follower->sink.info.channels; i++) follower->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 0bea31bc0..189e60f17 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -451,7 +451,8 @@ void channel_map_parse(const char *str, struct channel_map *map) void channel_map_parse_position(const char *str, struct channel_map *map) { uint32_t channels = 0, position[SPA_AUDIO_MAX_CHANNELS]; - spa_audio_parse_position(str, strlen(str), position, &channels); + spa_audio_parse_position_n(str, strlen(str), position, + SPA_N_ELEMENTS(position), &channels); positions_to_channel_map(position, channels, map); } @@ -535,7 +536,7 @@ int format_parse_param(const struct spa_pod *param, bool collect, if (info.info.raw.format == 0 || info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > SPA_N_ELEMENTS(info.info.raw.position)) return -ENOTSUP; } break; diff --git a/src/modules/module-protocol-pulse/volume.c b/src/modules/module-protocol-pulse/volume.c index de71baa53..e53f967ec 100644 --- a/src/modules/module-protocol-pulse/volume.c +++ b/src/modules/module-protocol-pulse/volume.c @@ -53,7 +53,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo if (monitor) continue; info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - info->volume.values, CHANNELS_MAX); + info->volume.values, SPA_N_ELEMENTS(info->volume.values)); SPA_FLAG_UPDATE(info->flags, VOLUME_HW_VOLUME, prop->flags & SPA_POD_PROP_FLAG_HARDWARE); break; @@ -68,7 +68,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo if (!monitor) continue; info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - info->volume.values, CHANNELS_MAX); + info->volume.values, SPA_N_ELEMENTS(info->volume.values)); SPA_FLAG_CLEAR(info->flags, VOLUME_HW_VOLUME); break; case SPA_PROP_volumeBase: @@ -84,7 +84,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo } case SPA_PROP_channelMap: info->map.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - info->map.map, CHANNELS_MAX); + info->map.map, SPA_N_ELEMENTS(info->map.map)); break; default: break; diff --git a/src/modules/module-pulse-tunnel.c b/src/modules/module-pulse-tunnel.c index 26af5aa20..afda6f484 100644 --- a/src/modules/module-pulse-tunnel.c +++ b/src/modules/module-pulse-tunnel.c @@ -300,7 +300,7 @@ static void stream_param_changed(void *d, uint32_t id, const struct spa_pod *par float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { volume.channels = SPA_MIN(PA_CHANNELS_MAX, n); for (n = 0; n < volume.channels; n++) volume.values[n] = pa_sw_volume_from_linear(vols[n]); diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 2fdb80618..accc2a9da 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -1617,7 +1617,7 @@ static void stream_props_changed(struct impl *impl, uint32_t id, const struct sp float soft_vols[MAX_CHANNELS]; if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { volume = 0.0f; for (i = 0; i < n_vols; i++) { volume += vols[i]; diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index 199046e80..0fefa2377 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -704,7 +704,8 @@ static int parse_channelmap(const char *channel_map, struct channelmap *map) } } - spa_audio_parse_position(channel_map, strlen(channel_map), map->channels, &map->n_channels); + spa_audio_parse_position_n(channel_map, strlen(channel_map), + map->channels, SPA_N_ELEMENTS(map->channels), &map->n_channels); return 0; } From 818d1435cea73b12e0fcc36c601bffb4b8a145ec Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 Oct 2025 13:06:25 +0200 Subject: [PATCH 10/43] treewide: access the position information using helpers Make sure we don't access out of bounds and that we use the helpers wherever we can to access the position information. --- pipewire-alsa/alsa-plugins/pcm_pipewire.c | 28 +++++--- spa/include/spa/param/audio/raw-utils.h | 17 ++++- spa/plugins/audioconvert/audioadapter.c | 5 +- spa/plugins/audioconvert/audioconvert.c | 78 ++++++++++++---------- spa/plugins/bluez5/a2dp-codec-opus.c | 21 +++--- spa/plugins/bluez5/bluez5-dbus.c | 5 +- spa/plugins/bluez5/bluez5-device.c | 10 +-- spa/plugins/support/null-audio-sink.c | 2 +- spa/plugins/volume/volume.c | 3 +- src/examples/export-source.c | 3 +- src/modules/module-combine-stream.c | 5 +- src/modules/module-example-filter.c | 3 +- src/modules/module-ffado-driver.c | 4 +- src/modules/module-filter-chain.c | 2 +- src/modules/module-jack-tunnel.c | 2 +- src/modules/module-loopback.c | 5 +- src/modules/module-netjack2-driver.c | 14 ++-- src/modules/module-netjack2-manager.c | 14 ++-- src/modules/module-protocol-pulse/format.c | 13 ++-- src/modules/module-protocol-pulse/format.h | 2 +- src/modules/module-protocol-pulse/module.c | 10 +-- src/modules/module-pulse-tunnel.c | 3 +- src/modules/module-vban/stream.c | 10 +-- src/modules/module-zeroconf-discover.c | 2 +- src/tools/pw-cat.c | 8 ++- 25 files changed, 155 insertions(+), 114 deletions(-) diff --git a/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index 4b91e82c0..27495a296 100644 --- a/pipewire-alsa/alsa-plugins/pcm_pipewire.c +++ b/pipewire-alsa/alsa-plugins/pcm_pipewire.c @@ -643,8 +643,11 @@ static int snd_pcm_pipewire_pause(snd_pcm_ioplug_t * io, int enable) #define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE #endif -static int set_default_channels(uint32_t channels, uint32_t position[8]) +static int set_default_channels(uint32_t channels, uint32_t *position, uint32_t max_position) { + if (max_position < 8) + return -ENOSPC; + switch (channels) { case 8: position[6] = SPA_AUDIO_CHANNEL_SL; @@ -772,7 +775,8 @@ static int snd_pcm_pipewire_hw_params(snd_pcm_ioplug_t * io, case SPA_MEDIA_SUBTYPE_raw: pw->requested.info.raw.channels = io->channels; pw->requested.info.raw.rate = io->rate; - set_default_channels(io->channels, pw->requested.info.raw.position); + set_default_channels(io->channels, pw->requested.info.raw.position, + SPA_N_ELEMENTS(pw->requested.info.raw.position)); fmt_str = spa_type_audio_format_to_short_name(pw->requested.info.raw.format); pw->format = pw->requested; break; @@ -780,7 +784,8 @@ static int snd_pcm_pipewire_hw_params(snd_pcm_ioplug_t * io, pw->requested.info.dsd.bitorder = SPA_PARAM_BITORDER_msb; pw->requested.info.dsd.channels = io->channels; pw->requested.info.dsd.rate = io->rate * SPA_ABS(pw->requested.info.dsd.interleave); - set_default_channels(io->channels, pw->requested.info.dsd.position); + set_default_channels(io->channels, pw->requested.info.dsd.position, + SPA_N_ELEMENTS(pw->requested.info.dsd.position)); pw->format = pw->requested; /* we need to let the server decide these values */ pw->format.info.dsd.bitorder = 0; @@ -902,26 +907,29 @@ static int snd_pcm_pipewire_set_chmap(snd_pcm_ioplug_t * io, { snd_pcm_pipewire_t *pw = io->private_data; unsigned int i; - uint32_t *position; + uint32_t *position, max_position; switch (pw->requested.media_subtype) { case SPA_MEDIA_SUBTYPE_raw: pw->requested.info.raw.channels = map->channels; position = pw->requested.info.raw.position; + max_position = SPA_N_ELEMENTS(pw->requested.info.raw.position); break; case SPA_MEDIA_SUBTYPE_dsd: pw->requested.info.dsd.channels = map->channels; position = pw->requested.info.dsd.position; + max_position = SPA_N_ELEMENTS(pw->requested.info.dsd.position); break; default: return -EINVAL; } for (i = 0; i < map->channels; i++) { - position[i] = chmap_to_channel(map->pos[i]); + uint32_t pos = chmap_to_channel(map->pos[i]); + if (i < max_position) + position[i] = pos; pw_log_debug("map %d: %s / %s", i, snd_pcm_chmap_name(map->pos[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - position[i])); + spa_debug_type_find_short_name(spa_type_audio_channel, pos)); } return 1; } @@ -930,16 +938,18 @@ static snd_pcm_chmap_t * snd_pcm_pipewire_get_chmap(snd_pcm_ioplug_t * io) { snd_pcm_pipewire_t *pw = io->private_data; snd_pcm_chmap_t *map; - uint32_t i, channels, *position; + uint32_t i, channels, *position, max_position; switch (pw->requested.media_subtype) { case SPA_MEDIA_SUBTYPE_raw: channels = pw->requested.info.raw.channels; position = pw->requested.info.raw.position; + max_position = SPA_N_ELEMENTS(pw->requested.info.raw.position); break; case SPA_MEDIA_SUBTYPE_dsd: channels = pw->requested.info.dsd.channels; position = pw->requested.info.dsd.position; + max_position = SPA_N_ELEMENTS(pw->requested.info.dsd.position); break; default: return NULL; @@ -949,7 +959,7 @@ static snd_pcm_chmap_t * snd_pcm_pipewire_get_chmap(snd_pcm_ioplug_t * io) channels * sizeof(unsigned int)); map->channels = channels; for (i = 0; i < channels; i++) - map->pos[i] = channel_to_chmap(position[i]); + map->pos[i] = channel_to_chmap(position[i % max_position]); return map; } diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 1f1dfe45d..03d6e3ac9 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -29,7 +29,7 @@ extern "C" { #endif SPA_API_AUDIO_RAW_UTILS uint32_t -spa_format_audio_get_position(struct spa_audio_info_raw *info, uint32_t idx) +spa_format_audio_raw_get_position(const struct spa_audio_info_raw *info, uint32_t idx) { uint32_t pos; if (idx < SPA_AUDIO_MAX_CHANNELS) { @@ -41,6 +41,21 @@ spa_format_audio_get_position(struct spa_audio_info_raw *info, uint32_t idx) } return pos; } +SPA_API_AUDIO_RAW_UTILS void +spa_format_audio_raw_set_position(struct spa_audio_info_raw *info, uint32_t idx, uint32_t position) +{ + if (idx < SPA_AUDIO_MAX_CHANNELS) + info->position[idx] = position; +} + +SPA_API_AUDIO_RAW_UTILS uint32_t +spa_format_audio_raw_copy_positions(const struct spa_audio_info_raw *info, uint32_t *position, uint32_t max_position) +{ + uint32_t i, n_pos = SPA_MIN(info->channels, max_position); + for (i = 0; i < n_pos; i++) + position[i] = spa_format_audio_raw_get_position(info, i); + return n_pos; +} SPA_API_AUDIO_RAW_UTILS int spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info) diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 0891462a2..26cb4af74 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -2046,11 +2046,12 @@ static int do_auto_port_config(struct impl *this, const char *str) return -ENOENT; if (format.media_subtype == SPA_MEDIA_SUBTYPE_raw) { + uint32_t n_pos = SPA_MIN(SPA_AUDIO_MAX_CHANNELS, format.info.raw.channels); if (position == POSITION_AUX) { - for (i = 0; i < format.info.raw.channels; i++) + for (i = 0; i < n_pos; i++) format.info.raw.position[i] = SPA_AUDIO_CHANNEL_START_Aux + i; } else if (position == POSITION_UNKNOWN) { - for (i = 0; i < format.info.raw.channels; i++) + for (i = 0; i < n_pos; i++) format.info.raw.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN; } } diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index 2ff270fb1..b91a46ff8 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -1150,7 +1150,7 @@ struct spa_filter_graph_events graph_events = { }; static int setup_filter_graph(struct impl *this, struct filter_graph *g, - uint32_t channels, uint32_t *position) + uint32_t channels, uint32_t *position, uint32_t max_position) { int res; char rate_str[64], in_ports[64]; @@ -1165,8 +1165,9 @@ static int setup_filter_graph(struct impl *this, struct filter_graph *g, snprintf(in_ports, sizeof(in_ports), "%d", channels); g->n_inputs = channels; if (position) { - memcpy(g->inputs_position, position, sizeof(uint32_t) * channels); - memcpy(g->outputs_position, position, sizeof(uint32_t) * channels); + uint32_t n_pos = SPA_MIN(channels, max_position); + memcpy(g->inputs_position, position, sizeof(uint32_t) * n_pos); + memcpy(g->outputs_position, position, sizeof(uint32_t) * n_pos); } } @@ -1181,7 +1182,7 @@ static int setup_filter_graph(struct impl *this, struct filter_graph *g, return res; } -static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position); +static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position, uint32_t max_position); static void free_tmp(struct impl *this) { @@ -1263,7 +1264,7 @@ static int ensure_tmp(struct impl *this) static int setup_filter_graphs(struct impl *impl, bool force) { int res; - uint32_t channels, *position; + uint32_t channels, *position, max_position; struct dir *in, *out; struct filter_graph *g, *t; @@ -1272,6 +1273,7 @@ static int setup_filter_graphs(struct impl *impl, bool force) channels = in->format.info.raw.channels; position = in->format.info.raw.position; + max_position = SPA_N_ELEMENTS(in->format.info.raw.position); impl->maxports = SPA_MAX(in->format.info.raw.channels, out->format.info.raw.channels); spa_list_for_each_safe(g, t, &impl->active_graphs, link) { @@ -1279,19 +1281,20 @@ static int setup_filter_graphs(struct impl *impl, bool force) continue; if (force) g->setup = false; - if ((res = setup_filter_graph(impl, g, channels, position)) < 0) { + if ((res = setup_filter_graph(impl, g, channels, position, max_position)) < 0) { g->removing = true; spa_log_warn(impl->log, "failed to activate graph %d: %s", g->order, spa_strerror(res)); } else { channels = g->n_outputs; position = g->outputs_position; + max_position = SPA_N_ELEMENTS(g->outputs_position); impl->maxports = SPA_MAX(impl->maxports, channels); } } if ((res = ensure_tmp(impl)) < 0) return res; - if ((res = setup_channelmix(impl, channels, position)) < 0) + if ((res = setup_channelmix(impl, channels, position, max_position)) < 0) return res; return 0; @@ -1896,10 +1899,11 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m this->dir[SPA_DIRECTION_OUTPUT].n_ports = dir->n_ports + 1; for (i = 0; i < dir->n_ports; i++) { - init_port(this, direction, i, info->info.raw.position[i], true, false, false); + uint32_t pos = spa_format_audio_raw_get_position(&info->info.raw, i); + init_port(this, direction, i, pos, true, false, false); if (this->monitor && direction == SPA_DIRECTION_INPUT) init_port(this, SPA_DIRECTION_OUTPUT, i+1, - info->info.raw.position[i], true, true, false); + pos, true, true, false); } break; } @@ -2056,24 +2060,25 @@ static int setup_in_convert(struct impl *this) dst_info.info.raw.channels, dst_info.info.raw.rate); - qsort(dst_info.info.raw.position, dst_info.info.raw.channels, + qsort(dst_info.info.raw.position, SPA_MIN(dst_info.info.raw.channels, SPA_AUDIO_MAX_CHANNELS), sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { - if (src_info.info.raw.position[i] != - dst_info.info.raw.position[j]) + uint32_t pi, pj; + + pi = spa_format_audio_raw_get_position(&src_info.info.raw, i); + pj = spa_format_audio_raw_get_position(&dst_info.info.raw, j); + if (pi != pj) continue; in->remap[i] = j; if (i != j) remap = true; spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this, i, in->remap[i], j, - spa_debug_type_find_short_name(spa_type_audio_channel, - src_info.info.raw.position[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - dst_info.info.raw.position[j])); - dst_info.info.raw.position[j] = -1; + spa_debug_type_find_short_name(spa_type_audio_channel, pi), + spa_debug_type_find_short_name(spa_type_audio_channel, pj)); + spa_format_audio_raw_set_position(&dst_info.info.raw, j, -1); break; } } @@ -2121,9 +2126,10 @@ static int remap_volumes(struct impl *this, const struct spa_audio_info *info) for (i = 0; i < p->n_channels; i++) { for (j = i; j < target; j++) { + uint32_t pj = spa_format_audio_raw_get_position(&info->info.raw, j); spa_log_debug(this->log, "%d %d: %d <-> %d", i, j, - p->channel_map[i], info->info.raw.position[j]); - if (p->channel_map[i] != info->info.raw.position[j]) + p->channel_map[i], pj); + if (p->channel_map[i] != pj) continue; if (i != j) { SPA_SWAP(p->channel_map[i], p->channel_map[j]); @@ -2136,7 +2142,7 @@ static int remap_volumes(struct impl *this, const struct spa_audio_info *info) } p->n_channels = target; for (i = 0; i < p->n_channels; i++) - p->channel_map[i] = info->info.raw.position[i]; + p->channel_map[i] = spa_format_audio_raw_get_position(&info->info.raw, i); if (target == 0) return 0; @@ -2182,17 +2188,17 @@ static void set_volume(struct impl *this) this->params[IDX_Props].user++; } -static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position) +static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position, uint32_t max_position) { uint32_t i, idx = 0; for (i = 0; i < channels; i++) idx += snprintf(str + idx, len - idx, "%s%s", i == 0 ? "" : " ", spa_debug_type_find_short_name(spa_type_audio_channel, - position[i])); + position[i % max_position])); return str; } -static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position) +static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position, uint32_t max_position) { struct dir *in = &this->dir[SPA_DIRECTION_INPUT]; struct dir *out = &this->dir[SPA_DIRECTION_OUTPUT]; @@ -2205,18 +2211,18 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi dst_chan = out->format.info.raw.channels; for (i = 0, src_mask = 0; i < src_chan; i++) { - p = position[i]; + p = position[i % max_position]; src_mask |= 1ULL << (p < 64 ? p : 0); } for (i = 0, dst_mask = 0; i < dst_chan; i++) { - p = out->format.info.raw.position[i]; + p = spa_format_audio_raw_get_position(&out->format.info.raw, i); dst_mask |= 1ULL << (p < 64 ? p : 0); } spa_log_info(this->log, "in %s (%016"PRIx64")", format_position(str, sizeof(str), - src_chan, position), src_mask); + src_chan, position, max_position), src_mask); spa_log_info(this->log, "out %s (%016"PRIx64")", format_position(str, sizeof(str), - dst_chan, out->format.info.raw.position), dst_mask); + dst_chan, out->format.info.raw.position, SPA_AUDIO_MAX_CHANNELS), dst_mask); spa_log_info(this->log, "%p: %s/%d@%d->%s/%d@%d %08"PRIx64":%08"PRIx64, this, spa_debug_type_find_name(spa_type_audio_format, SPA_AUDIO_FORMAT_DSP_F32), @@ -2344,13 +2350,16 @@ static int setup_out_convert(struct impl *this) dst_info.info.raw.channels, dst_info.info.raw.rate); - qsort(src_info.info.raw.position, src_info.info.raw.channels, + qsort(src_info.info.raw.position, SPA_MIN(src_info.info.raw.channels, SPA_AUDIO_MAX_CHANNELS), sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { - if (src_info.info.raw.position[i] != - dst_info.info.raw.position[j]) + uint32_t pi, pj; + + pi = spa_format_audio_raw_get_position(&src_info.info.raw, i); + pj = spa_format_audio_raw_get_position(&dst_info.info.raw, j); + if (pi != pj) continue; out->remap[i] = j; if (i != j) @@ -2358,11 +2367,10 @@ static int setup_out_convert(struct impl *this) spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this, i, out->remap[i], j, - spa_debug_type_find_short_name(spa_type_audio_channel, - src_info.info.raw.position[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - dst_info.info.raw.position[j])); - dst_info.info.raw.position[j] = -1; + spa_debug_type_find_short_name(spa_type_audio_channel, pi), + spa_debug_type_find_short_name(spa_type_audio_channel, pj)); + + spa_format_audio_raw_set_position(&dst_info.info.raw, j, -1); break; } } diff --git a/spa/plugins/bluez5/a2dp-codec-opus.c b/spa/plugins/bluez5/a2dp-codec-opus.c index d9cdc9a0e..7a330e8e0 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus.c +++ b/spa/plugins/bluez5/a2dp-codec-opus.c @@ -497,7 +497,7 @@ static void get_default_bitrates(const struct media_codec *codec, bool bidi, int static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direction_t *conf, bool use_surround_encoder, uint8_t *streams_ret, uint8_t *coupled_streams_ret, - const uint8_t **surround_mapping, uint32_t *positions) + const uint8_t **surround_mapping, uint32_t *positions, uint32_t max_positions) { const uint32_t channels = conf->channels; const uint32_t location = OPUS_05_GET_LOCATION(*conf); @@ -544,13 +544,13 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc const struct audio_location loc = audio_locations[i]; if (location & loc.mask) { - if (permutation) - positions[permutation[j++]] = loc.position; - else - positions[j++] = loc.position; + uint32_t idx = permutation ? permutation[j] : j; + if (idx < max_positions) + positions[idx] = loc.position; + j++; } } - for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels; ++i, ++j) + for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels && j < max_positions; ++i, ++j) positions[j] = i; } @@ -785,7 +785,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, dir = !is_duplex_codec(codec) ? &conf.main : &conf.bidi; - if (get_mapping(codec, dir, surround_encoder, NULL, NULL, NULL, position) < 0) + if (get_mapping(codec, dir, surround_encoder, NULL, NULL, NULL, position, MAX_CHANNELS) < 0) return -EINVAL; spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id); @@ -837,9 +837,10 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags } info->info.raw.channels = dir1->channels; - if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL, info->info.raw.position) < 0) + if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL, + info->info.raw.position, SPA_N_ELEMENTS(info->info.raw.position)) < 0) return -EINVAL; - if (get_mapping(codec, dir2, surround_encoder, NULL, NULL, NULL, NULL) < 0) + if (get_mapping(codec, dir2, surround_encoder, NULL, NULL, NULL, NULL, 0) < 0) return -EINVAL; return 0; @@ -930,7 +931,7 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags, if ((res = codec_validate_config(codec, flags, config, config_len, &config_info)) < 0) goto error; if ((res = get_mapping(codec, dir, surround_encoder, &this->streams, &this->coupled_streams, - &enc_mapping, NULL)) < 0) + &enc_mapping, NULL, 0)) < 0) goto error; if (config_info.info.raw.channels != info->info.raw.channels) { res = -EINVAL; diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index d4305c535..2e92696bc 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "codec-loader.h" @@ -5038,8 +5039,8 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } transport->n_channels = info.info.raw.channels; - memcpy(transport->channels, info.info.raw.position, - transport->n_channels * sizeof(uint32_t)); + spa_format_audio_raw_copy_positions(&info.info.raw, + transport->channels, SPA_N_ELEMENTS(transport->channels)); } else { transport->n_channels = 2; transport->channels[0] = SPA_AUDIO_CHANNEL_FL; diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index aee6d8cab..838bb54f3 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -451,7 +452,8 @@ static int node_offload_set_active(struct node *node, bool active) return res; } -static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t *n_channels, uint32_t *channels) +static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t *n_channels, uint32_t *channels, + uint32_t max_channels) { const struct media_codec *codec; struct spa_audio_info info = { 0 }; @@ -473,9 +475,7 @@ static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t return; } - *n_channels = info.info.raw.channels; - memcpy(channels, info.info.raw.position, - info.info.raw.channels * sizeof(uint32_t)); + *n_channels = spa_format_audio_raw_copy_positions(&info.info.raw, channels, max_channels); } static const char *get_channel_name(uint32_t channel) @@ -686,7 +686,7 @@ static void emit_node(struct impl *this, struct spa_bt_transport *t, this->nodes[id].active = true; this->nodes[id].offload_acquired = false; this->nodes[id].a2dp_duplex = a2dp_duplex; - get_channels(t, a2dp_duplex, &this->nodes[id].n_channels, this->nodes[id].channels); + get_channels(t, a2dp_duplex, &this->nodes[id].n_channels, this->nodes[id].channels, MAX_CHANNELS); if (this->nodes[id].transport) spa_hook_remove(&this->nodes[id].transport_listener); this->nodes[id].transport = t; diff --git a/spa/plugins/support/null-audio-sink.c b/spa/plugins/support/null-audio-sink.c index 27ae8d0f0..acaec6f7b 100644 --- a/spa/plugins/support/null-audio-sink.c +++ b/spa/plugins/support/null-audio-sink.c @@ -637,7 +637,7 @@ port_set_format(struct impl *this, if (info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_N_ELEMENTS(info.info.raw.position)) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; if (this->props.format != 0) { diff --git a/spa/plugins/volume/volume.c b/spa/plugins/volume/volume.c index 7881703db..0c750a96a 100644 --- a/spa/plugins/volume/volume.c +++ b/spa/plugins/volume/volume.c @@ -454,8 +454,7 @@ static int port_set_format(void *object, return -EINVAL; if (info.info.raw.format != SPA_AUDIO_FORMAT_S16 || - info.info.raw.channels == 0 || - info.info.raw.channels > SPA_N_ELEMENTS(info.info.raw.position)) + info.info.raw.channels == 0) return -EINVAL; this->bpf = 2 * info.info.raw.channels; diff --git a/src/examples/export-source.c b/src/examples/export-source.c index 3d3859273..a0e983de1 100644 --- a/src/examples/export-source.c +++ b/src/examples/export-source.c @@ -268,8 +268,7 @@ static int port_set_format(void *object, d->format.format != SPA_AUDIO_FORMAT_F32) return -EINVAL; if (d->format.rate == 0 || - d->format.channels == 0 || - d->format.channels > SPA_N_ELEMENTS(d->format.position)) + d->format.channels == 0) return -EINVAL; } diff --git a/src/modules/module-combine-stream.c b/src/modules/module-combine-stream.c index 54a98970b..a9dec8c2b 100644 --- a/src/modules/module-combine-stream.c +++ b/src/modules/module-combine-stream.c @@ -882,7 +882,10 @@ static int create_stream(struct stream_info *info) for (i = 0; i < remap_info.channels; i++) { s->remap[i] = i; for (j = 0; j < tmp_info.channels; j++) { - if (tmp_info.position[j] == remap_info.position[i]) { + uint32_t pj, pi; + pj = spa_format_audio_raw_get_position(&tmp_info, j); + pi = spa_format_audio_raw_get_position(&remap_info, i); + if (pj == pi) { s->remap[i] = j; break; } diff --git a/src/modules/module-example-filter.c b/src/modules/module-example-filter.c index 92f1f38cb..6dcb155cc 100644 --- a/src/modules/module-example-filter.c +++ b/src/modules/module-example-filter.c @@ -298,8 +298,7 @@ static void capture_param_changed(void *data, uint32_t id, const struct spa_pod if (spa_format_audio_raw_parse(param, &info) < 0) return; if (info.rate == 0 || - info.channels == 0 || - info.channels > SPA_N_ELEMENTS(info.position)) + info.channels == 0) return; break; } diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c index db6fe8b71..521899433 100644 --- a/src/modules/module-ffado-driver.c +++ b/src/modules/module-ffado-driver.c @@ -1229,7 +1229,7 @@ static int probe_ffado_device(struct impl *impl) } if (impl->source.info.channels != n_channels) { impl->source.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->source.info.channels, MAX_CHANNELS); i++) + for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1255,7 +1255,7 @@ static int probe_ffado_device(struct impl *impl) } if (impl->sink.info.channels != n_channels) { impl->sink.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->sink.info.channels, MAX_CHANNELS); i++) + for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index 84b70d651..cdfcd0ecf 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -1691,7 +1691,7 @@ static void copy_position(struct spa_audio_info_raw *dst, const struct spa_audio { if (SPA_FLAG_IS_SET(dst->flags, SPA_AUDIO_FLAG_UNPOSITIONED) && !SPA_FLAG_IS_SET(src->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { - for (uint32_t i = 0; i < src->channels; i++) + for (uint32_t i = 0; i < SPA_MIN(src->channels, SPA_AUDIO_MAX_CHANNELS); i++) dst->position[i] = src->position[i]; SPA_FLAG_CLEAR(dst->flags, SPA_AUDIO_FLAG_UNPOSITIONED); } diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index f5066abc4..0e1b3c3fb 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -524,7 +524,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_debug_type_find_short_name(spa_type_audio_channel, - s->info.position[i]); + spa_format_audio_raw_get_position(&s->info, i)); if (str) snprintf(name, sizeof(name), "%s_%s", prefix, str); else diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index ce1da189d..365e6c5b9 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -538,8 +538,7 @@ static void param_format_changed(struct impl *impl, const struct spa_pod *param, spa_zero(info); if (param != NULL) { if (spa_format_audio_raw_parse(param, &info) < 0 || - info.channels == 0 || - info.channels > SPA_N_ELEMENTS(info.position)) + info.channels == 0) return; if ((impl->info.format != 0 && impl->info.format != info.format) || @@ -547,7 +546,7 @@ static void param_format_changed(struct impl *impl, const struct spa_pod *param, (impl->info.channels != 0 && (impl->info.channels != info.channels || memcmp(impl->info.position, info.position, - info.channels * sizeof(uint32_t)) != 0))) { + SPA_MIN(info.channels, SPA_AUDIO_MAX_CHANNELS) * sizeof(uint32_t)) != 0))) { uint8_t buffer[1024]; struct spa_pod_builder b; const struct spa_pod *params[1]; diff --git a/src/modules/module-netjack2-driver.c b/src/modules/module-netjack2-driver.c index f1aefaa35..5e3149b41 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -442,7 +442,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_debug_type_find_short_name(spa_type_audio_channel, - s->info.position[i % SPA_N_ELEMENTS(s->info.position)]); + spa_format_audio_raw_get_position(&s->info, i)); props = pw_properties_new( PW_KEY_FORMAT_DSP, "32 bit float mono audio", PW_KEY_AUDIO_CHANNEL, str ? str : "UNK", @@ -863,10 +863,10 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p } impl->sink.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.send_audio_channels != impl->sink.info.channels) { - impl->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, - (int)SPA_N_ELEMENTS(impl->sink.info.position)); + impl->sink.info.channels = peer->params.send_audio_channels; for (i = 0; i < impl->sink.info.channels; i++) - impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; + spa_format_audio_raw_set_position(&impl->sink.info, i, + SPA_AUDIO_CHANNEL_AUX0 + i); } impl->source.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels; if (impl->source.n_ports > MAX_PORTS) { @@ -875,10 +875,10 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p } impl->source.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.recv_audio_channels != impl->source.info.channels) { - impl->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, - (int)SPA_N_ELEMENTS(impl->source.info.position)); + impl->source.info.channels = peer->params.recv_audio_channels; for (i = 0; i < impl->source.info.channels; i++) - impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; + spa_format_audio_raw_set_position(&impl->source.info, i, + SPA_AUDIO_CHANNEL_AUX0 + i); } impl->samplerate = peer->params.sample_rate; impl->period_size = peer->params.period_size; diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c index 6b3285305..454632c8f 100644 --- a/src/modules/module-netjack2-manager.c +++ b/src/modules/module-netjack2-manager.c @@ -602,7 +602,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_debug_type_find_short_name(spa_type_audio_channel, - s->info.position[i]); + spa_format_audio_raw_get_position(&s->info, i)); props = pw_properties_new( PW_KEY_FORMAT_DSP, "32 bit float mono audio", @@ -1026,18 +1026,18 @@ static int handle_follower_available(struct impl *impl, struct nj2_session_param follower->source.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels; follower->source.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.recv_audio_channels != follower->source.info.channels) { - follower->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, - (int)SPA_N_ELEMENTS(follower->source.info.position)); + follower->source.info.channels = peer->params.recv_audio_channels; for (i = 0; i < follower->source.info.channels; i++) - follower->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; + spa_format_audio_raw_set_position(&follower->source.info, i, + SPA_AUDIO_CHANNEL_AUX0 + i); } follower->sink.n_ports = peer->params.send_audio_channels + peer->params.send_midi_channels; follower->sink.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.send_audio_channels != follower->sink.info.channels) { - follower->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, - (int)SPA_N_ELEMENTS(follower->sink.info.position)); + follower->sink.info.channels = peer->params.send_audio_channels; for (i = 0; i < follower->sink.info.channels; i++) - follower->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; + spa_format_audio_raw_set_position(&follower->sink.info, i, + SPA_AUDIO_CHANNEL_AUX0 + i); } if (follower->source.n_ports > MAX_PORTS || follower->sink.n_ports > MAX_PORTS) { diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 189e60f17..8c417446f 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -348,9 +348,9 @@ uint32_t channel_paname2id(const char *name, size_t size) } -void channel_map_to_positions(const struct channel_map *map, uint32_t *pos) +void channel_map_to_positions(const struct channel_map *map, uint32_t *pos, uint32_t max_pos) { - uint32_t i, channels = SPA_MIN(map->channels, SPA_AUDIO_MAX_CHANNELS); + uint32_t i, channels = SPA_MIN(map->channels, max_pos); for (i = 0; i < channels; i++) pos[i] = map->map[i]; } @@ -535,8 +535,7 @@ int format_parse_param(const struct spa_pod *param, bool collect, info.info.raw.rate = 48000; if (info.info.raw.format == 0 || info.info.raw.rate == 0 || - info.info.raw.channels == 0 || - info.info.raw.channels > SPA_N_ELEMENTS(info.info.raw.position)) + info.info.raw.channels == 0) return -ENOTSUP; } break; @@ -586,7 +585,7 @@ int format_parse_param(const struct spa_pod *param, bool collect, if (info.info.raw.channels) { map->channels = SPA_MIN(info.info.raw.channels, CHANNELS_MAX); for (i = 0; i < map->channels; i++) - map->map[i] = info.info.raw.position[i]; + map->map[i] = spa_format_audio_raw_get_position(&info.info.raw, i); } } return 0; @@ -634,8 +633,8 @@ const struct spa_pod *format_build_param(struct spa_pod_builder *b, uint32_t id, SPA_FORMAT_AUDIO_channels, SPA_POD_Int(spec->channels), 0); if (map && map->channels == spec->channels) { - uint32_t positions[SPA_AUDIO_MAX_CHANNELS]; - channel_map_to_positions(map, positions); + uint32_t positions[spec->channels]; + channel_map_to_positions(map, positions, spec->channels); spa_pod_builder_add(b, SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, spec->channels, positions), 0); diff --git a/src/modules/module-protocol-pulse/format.h b/src/modules/module-protocol-pulse/format.h index d564b1822..41cfb4f9d 100644 --- a/src/modules/module-protocol-pulse/format.h +++ b/src/modules/module-protocol-pulse/format.h @@ -196,7 +196,7 @@ enum channel_position channel_id2pa(uint32_t id, uint32_t *aux); const char *channel_id2paname(uint32_t id, uint32_t *aux); uint32_t channel_paname2id(const char *name, size_t size); -void channel_map_to_positions(const struct channel_map *map, uint32_t *pos); +void channel_map_to_positions(const struct channel_map *map, uint32_t *pos, uint32_t max_pos); void channel_map_parse(const char *str, struct channel_map *map); bool channel_map_valid(const struct channel_map *map); void channel_map_parse_position(const char *str, struct channel_map *map); diff --git a/src/modules/module-protocol-pulse/module.c b/src/modules/module-protocol-pulse/module.c index a0de01d0d..58bcce52b 100644 --- a/src/modules/module-protocol-pulse/module.c +++ b/src/modules/module-protocol-pulse/module.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -226,14 +227,15 @@ int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props info->channels, map.channels); return -EINVAL; } - channel_map_to_positions(&map, info->position); + channel_map_to_positions(&map, info->position, SPA_N_ELEMENTS(info->position)); pw_properties_set(props, key_channel_map, NULL); } else { if (info->channels == 0) info->channels = impl->defs.sample_spec.channels; if (info->channels == impl->defs.channel_map.channels) { - channel_map_to_positions(&impl->defs.channel_map, info->position); + channel_map_to_positions(&impl->defs.channel_map, + info->position, SPA_N_ELEMENTS(info->position)); } else if (info->channels == 1) { info->position[0] = SPA_AUDIO_CHANNEL_MONO; } else if (info->channels == 2) { @@ -242,7 +244,7 @@ int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props } else { /* FIXME add more mappings */ for (i = 0; i < info->channels; i++) - info->position[i] = SPA_AUDIO_CHANNEL_UNKNOWN; + spa_format_audio_raw_set_position(info, i, SPA_AUDIO_CHANNEL_UNKNOWN); } if (info->position[0] == SPA_AUDIO_CHANNEL_UNKNOWN) info->flags |= SPA_AUDIO_FLAG_UNPOSITIONED; @@ -288,7 +290,7 @@ void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properti p = s = alloca(info->channels * 8); for (i = 0; i < info->channels; i++) p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ", ", - channel_id2name(info->position[i])); + channel_id2name(spa_format_audio_raw_get_position(info, i))); pw_properties_setf(props, SPA_KEY_AUDIO_POSITION, "[ %s ]", s); } } diff --git a/src/modules/module-pulse-tunnel.c b/src/modules/module-pulse-tunnel.c index afda6f484..8849f7190 100644 --- a/src/modules/module-pulse-tunnel.c +++ b/src/modules/module-pulse-tunnel.c @@ -754,7 +754,8 @@ static int create_pulse_stream(struct impl *impl) map.channels = impl->info.channels; for (i = 0; i < map.channels; i++) - map.map[i] = (pa_channel_position_t)channel_id2pa(impl->info.position[i], &aux); + map.map[i] = (pa_channel_position_t)channel_id2pa( + spa_format_audio_raw_get_position(&impl->info, i), &aux); snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"), pw_get_user_name(), pw_get_host_name()); diff --git a/src/modules/module-vban/stream.c b/src/modules/module-vban/stream.c index 3a7a31827..4ae0eacd1 100644 --- a/src/modules/module-vban/stream.c +++ b/src/modules/module-vban/stream.c @@ -215,16 +215,16 @@ static const struct spa_audio_layout_info layouts[] = { { SPA_AUDIO_LAYOUT_7_1 }, }; -static void default_layout(uint32_t channels, uint32_t *position) +static void default_layout(uint32_t channels, uint32_t *position, uint32_t max_position) { SPA_FOR_EACH_ELEMENT_VAR(layouts, l) { if (l->n_channels == channels) { - for (uint32_t i = 0; i < l->n_channels; i++) + for (uint32_t i = 0; i < l->n_channels && i < max_position; i++) position[i] = l->position[i]; return; } } - for (uint32_t i = 0; i < channels; i++) + for (uint32_t i = 0; i < channels && i < max_position; i++) position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -276,7 +276,9 @@ struct vban_stream *vban_stream_new(struct pw_core *core, case SPA_MEDIA_SUBTYPE_raw: parse_audio_info(props, &impl->info.info.raw); if (SPA_FLAG_IS_SET(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED)) - default_layout(impl->info.info.raw.channels, impl->info.info.raw.position); + default_layout(impl->info.info.raw.channels, + impl->info.info.raw.position, + SPA_N_ELEMENTS(impl->info.info.raw.position)); impl->stream_info = impl->info; impl->format_info = find_audio_format_info(&impl->info); if (impl->format_info == NULL) { diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c index e165a1174..35e48279b 100644 --- a/src/modules/module-zeroconf-discover.c +++ b/src/modules/module-zeroconf-discover.c @@ -192,7 +192,7 @@ static void pw_properties_from_avahi_string(const char *key, const char *value, spa_zero(channel_map); channel_map_parse(value, &channel_map); - channel_map_to_positions(&channel_map, pos); + channel_map_to_positions(&channel_map, pos, CHANNELS_MAX); p = s = alloca(4 + channel_map.channels * 8); p += spa_scnprintf(p, 2, "["); diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index 0fefa2377..52789d421 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -2347,9 +2347,11 @@ int main(int argc, char *argv[]) .rate = data.rate, .channels = data.channels); - if (data.channelmap.n_channels) - memcpy(info.position, data.channelmap.channels, data.channels * sizeof(int)); - + if (data.channelmap.n_channels) { + uint32_t i, n_pos = SPA_MIN(data.channels, SPA_N_ELEMENTS(info.position)); + for (i = 0; i < n_pos; i++) + info.position[i] = data.channelmap.channels[i]; + } params[n_params++] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info); break; } From dbc5c81e4a0b34da0450c55db87592b48b7d2e3c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 Oct 2025 16:05:33 +0200 Subject: [PATCH 11/43] spa: avoid using SPA_AUDIO_MAX_CHANNELS Use SPA_N_ELEMENTS instead of the array we try to handle. --- spa/include/spa/param/audio/raw-utils.h | 10 +++++----- spa/plugins/audioconvert/audioadapter.c | 2 +- spa/plugins/audioconvert/audioconvert.c | 13 ++++++++----- src/modules/module-ffado-driver.c | 6 ++++-- src/modules/module-filter-chain.c | 3 ++- src/modules/module-loopback.c | 2 +- src/modules/module-protocol-pulse/format.c | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 03d6e3ac9..8a9542c2e 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -31,20 +31,20 @@ extern "C" { SPA_API_AUDIO_RAW_UTILS uint32_t spa_format_audio_raw_get_position(const struct spa_audio_info_raw *info, uint32_t idx) { - uint32_t pos; - if (idx < SPA_AUDIO_MAX_CHANNELS) { + uint32_t pos, max_position = SPA_N_ELEMENTS(info->position); + if (idx < max_position) { pos = info->position[idx]; } else { - pos = info->position[idx % SPA_AUDIO_MAX_CHANNELS]; + pos = info->position[idx % max_position]; if (SPA_AUDIO_CHANNEL_IS_AUX(pos)) - pos += (idx / SPA_AUDIO_MAX_CHANNELS) * SPA_AUDIO_MAX_CHANNELS; + pos += (idx / max_position) * max_position; } return pos; } SPA_API_AUDIO_RAW_UTILS void spa_format_audio_raw_set_position(struct spa_audio_info_raw *info, uint32_t idx, uint32_t position) { - if (idx < SPA_AUDIO_MAX_CHANNELS) + if (idx < SPA_N_ELEMENTS(info->position)) info->position[idx] = position; } diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 26cb4af74..01e94cf58 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -2046,7 +2046,7 @@ static int do_auto_port_config(struct impl *this, const char *str) return -ENOENT; if (format.media_subtype == SPA_MEDIA_SUBTYPE_raw) { - uint32_t n_pos = SPA_MIN(SPA_AUDIO_MAX_CHANNELS, format.info.raw.channels); + uint32_t n_pos = SPA_MIN(SPA_N_ELEMENTS(format.info.raw.position), format.info.raw.channels); if (position == POSITION_AUX) { for (i = 0; i < n_pos; i++) format.info.raw.position[i] = SPA_AUDIO_CHANNEL_START_Aux + i; diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index b91a46ff8..dd58ee9de 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -2060,8 +2060,9 @@ static int setup_in_convert(struct impl *this) dst_info.info.raw.channels, dst_info.info.raw.rate); - qsort(dst_info.info.raw.position, SPA_MIN(dst_info.info.raw.channels, SPA_AUDIO_MAX_CHANNELS), - sizeof(uint32_t), int32_cmp); + qsort(dst_info.info.raw.position, SPA_MIN(dst_info.info.raw.channels, + SPA_N_ELEMENTS(dst_info.info.raw.position)), + sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { @@ -2222,7 +2223,8 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi spa_log_info(this->log, "in %s (%016"PRIx64")", format_position(str, sizeof(str), src_chan, position, max_position), src_mask); spa_log_info(this->log, "out %s (%016"PRIx64")", format_position(str, sizeof(str), - dst_chan, out->format.info.raw.position, SPA_AUDIO_MAX_CHANNELS), dst_mask); + dst_chan, out->format.info.raw.position, + SPA_N_ELEMENTS(out->format.info.raw.position)), dst_mask); spa_log_info(this->log, "%p: %s/%d@%d->%s/%d@%d %08"PRIx64":%08"PRIx64, this, spa_debug_type_find_name(spa_type_audio_format, SPA_AUDIO_FORMAT_DSP_F32), @@ -2350,8 +2352,9 @@ static int setup_out_convert(struct impl *this) dst_info.info.raw.channels, dst_info.info.raw.rate); - qsort(src_info.info.raw.position, SPA_MIN(src_info.info.raw.channels, SPA_AUDIO_MAX_CHANNELS), - sizeof(uint32_t), int32_cmp); + qsort(src_info.info.raw.position, SPA_MIN(src_info.info.raw.channels, + SPA_N_ELEMENTS(src_info.info.raw.position)), + sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c index 521899433..c71639b3b 100644 --- a/src/modules/module-ffado-driver.c +++ b/src/modules/module-ffado-driver.c @@ -1228,8 +1228,9 @@ static int probe_ffado_device(struct impl *impl) impl->source.ports[i] = port; } if (impl->source.info.channels != n_channels) { + uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->source.info.position)); impl->source.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + for (i = 0; i < n_pos; i++) impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1254,8 +1255,9 @@ static int probe_ffado_device(struct impl *impl) impl->sink.ports[i] = port; } if (impl->sink.info.channels != n_channels) { + uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->sink.info.position)); impl->sink.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + for (i = 0; i < n_pos; i++) impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index cdfcd0ecf..d5b15d4ab 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -1691,7 +1691,8 @@ static void copy_position(struct spa_audio_info_raw *dst, const struct spa_audio { if (SPA_FLAG_IS_SET(dst->flags, SPA_AUDIO_FLAG_UNPOSITIONED) && !SPA_FLAG_IS_SET(src->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { - for (uint32_t i = 0; i < SPA_MIN(src->channels, SPA_AUDIO_MAX_CHANNELS); i++) + uint32_t i, n_pos = SPA_MIN(src->channels, SPA_N_ELEMENTS(dst->position)); + for (i = 0; i < n_pos; i++) dst->position[i] = src->position[i]; SPA_FLAG_CLEAR(dst->flags, SPA_AUDIO_FLAG_UNPOSITIONED); } diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 365e6c5b9..64022b69d 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -546,7 +546,7 @@ static void param_format_changed(struct impl *impl, const struct spa_pod *param, (impl->info.channels != 0 && (impl->info.channels != info.channels || memcmp(impl->info.position, info.position, - SPA_MIN(info.channels, SPA_AUDIO_MAX_CHANNELS) * sizeof(uint32_t)) != 0))) { + SPA_MIN(info.channels, SPA_N_ELEMENTS(info.position)) * sizeof(uint32_t)) != 0))) { uint8_t buffer[1024]; struct spa_pod_builder b; const struct spa_pod *params[1]; diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 8c417446f..f92e29e78 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -450,7 +450,7 @@ void channel_map_parse(const char *str, struct channel_map *map) void channel_map_parse_position(const char *str, struct channel_map *map) { - uint32_t channels = 0, position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channels = 0, position[CHANNELS_MAX]; spa_audio_parse_position_n(str, strlen(str), position, SPA_N_ELEMENTS(position), &channels); positions_to_channel_map(position, channels, map); From f19b075306900fb3650aa821ff2120b7804151a2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 Oct 2025 16:08:24 +0200 Subject: [PATCH 12/43] spa: add SPA_AUDIO_MAX_POSITION Add a new SPA_AUDIO_MAX_POSITION constant with the maximum number of channel positions that can be kept in the various audio_info structures. Repurpose the SPA_AUDIO_MAX_CHANNELS as a suggestion for applications for the max allowed number of channels in the system. Make it possible to make this a compile time constant. --- spa/include/spa/param/audio/dsd.h | 2 +- spa/include/spa/param/audio/layout.h | 2 +- spa/include/spa/param/audio/raw-json.h | 2 +- spa/include/spa/param/audio/raw.h | 15 +++++++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/spa/include/spa/param/audio/dsd.h b/spa/include/spa/param/audio/dsd.h index 73f3d4e8b..733823780 100644 --- a/spa/include/spa/param/audio/dsd.h +++ b/spa/include/spa/param/audio/dsd.h @@ -45,7 +45,7 @@ struct spa_audio_info_dsd { int32_t interleave; /*< interleave bytes */ uint32_t rate; /*< sample rate (in bytes per second) */ uint32_t channels; /*< channels */ - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */ + uint32_t position[SPA_AUDIO_MAX_POSITION]; /*< channel position from enum spa_audio_channel */ }; #define SPA_AUDIO_INFO_DSD_INIT(...) ((struct spa_audio_info_dsd) { __VA_ARGS__ }) diff --git a/spa/include/spa/param/audio/layout.h b/spa/include/spa/param/audio/layout.h index 19e3a3c42..e5e59caf9 100644 --- a/spa/include/spa/param/audio/layout.h +++ b/spa/include/spa/param/audio/layout.h @@ -20,7 +20,7 @@ extern "C" { struct spa_audio_layout_info { uint32_t n_channels; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[SPA_AUDIO_MAX_POSITION]; }; #define SPA_AUDIO_LAYOUT_Mono 1, { SPA_AUDIO_CHANNEL_MONO, } diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index 50bd63181..edbb715d1 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -49,7 +49,7 @@ SPA_API_AUDIO_RAW_JSON int spa_audio_parse_position(const char *str, size_t len, uint32_t *position, uint32_t *n_channels) { - return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_CHANNELS, n_channels); + return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_POSITION, n_channels); } SPA_API_AUDIO_RAW_JSON int diff --git a/spa/include/spa/param/audio/raw.h b/spa/include/spa/param/audio/raw.h index 6defe5020..21cdb3c2d 100644 --- a/spa/include/spa/param/audio/raw.h +++ b/spa/include/spa/param/audio/raw.h @@ -18,8 +18,15 @@ extern "C" { * \{ */ -/* this is the max number of channels for position info */ +/* This is the max number of position info, changing this will change the + * ABI */ +#define SPA_AUDIO_MAX_POSITION 64u + +/* The suggested number of max channels, can be a compile time constant and + * does not affect ABI or API */ +#ifndef SPA_AUDIO_MAX_CHANNELS #define SPA_AUDIO_MAX_CHANNELS 64u +#endif enum spa_audio_format { SPA_AUDIO_FORMAT_UNKNOWN, @@ -278,9 +285,9 @@ struct spa_audio_info_raw { uint32_t flags; /*< extra flags */ uint32_t rate; /*< sample rate */ uint32_t channels; /*< number of channels. This can be larger than - * SPA_AUDIO_MAX_CHANNELS, the position is taken - * (index % SPA_AUDIO_MAX_CHANNELS) */ - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */ + * SPA_AUDIO_MAX_POSITION, the position is taken + * (index % SPA_AUDIO_MAX_POSITION) */ + uint32_t position[SPA_AUDIO_MAX_POSITION]; /*< channel position from enum spa_audio_channel */ }; #define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ }) From 99bbac9cbf40af8ba6b55fccf11b0f0a9a6c6537 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 Oct 2025 17:01:31 +0200 Subject: [PATCH 13/43] spa: increase SPA_AUDIO_MAX_CHANNELS to 128 This should now not change the ABI because the position array size is now controlled with the SPA_AUDIO_MAX_POSITION constant. --- spa/include/spa/param/audio/raw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spa/include/spa/param/audio/raw.h b/spa/include/spa/param/audio/raw.h index 21cdb3c2d..5b57a5bf1 100644 --- a/spa/include/spa/param/audio/raw.h +++ b/spa/include/spa/param/audio/raw.h @@ -25,7 +25,7 @@ extern "C" { /* The suggested number of max channels, can be a compile time constant and * does not affect ABI or API */ #ifndef SPA_AUDIO_MAX_CHANNELS -#define SPA_AUDIO_MAX_CHANNELS 64u +#define SPA_AUDIO_MAX_CHANNELS 128u #endif enum spa_audio_format { From ae50bb5dc02f04d6adae37e2d06cc5e8293e58af Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 22 Oct 2025 09:39:15 +0200 Subject: [PATCH 14/43] audio: don't limit channels to max positions We can have more channels than we have positions. --- spa/include/spa/param/audio/raw-json.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index edbb715d1..282e76ed5 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -64,7 +64,7 @@ spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, cons info->rate = v; } else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) { if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) - info->channels = SPA_MIN(v, SPA_N_ELEMENTS(info->position)); + info->channels = v; } else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) { if (force || info->channels == 0) { if (spa_audio_parse_position_n(val, strlen(val), info->position, From 6465a63bf648caf827b9ce4e833d10beee9aafcf Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 22 Oct 2025 12:47:00 +0200 Subject: [PATCH 15/43] spa: parse the audio.position completetly Parse the audio.position spec completely so that we have the right number of channels but only store the first max_position channels. Also rename some field to make it clear that this is about the max number of channel positions. --- spa/include/spa/param/audio/raw-json.h | 9 +++++---- spa/plugins/alsa/alsa-pcm.c | 6 +++--- spa/plugins/alsa/alsa-pcm.h | 4 ++-- spa/plugins/filter-graph/filter-graph.c | 5 +++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index 282e76ed5..7ef1b27f3 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -29,7 +29,7 @@ extern "C" { SPA_API_AUDIO_RAW_JSON int spa_audio_parse_position_n(const char *str, size_t len, - uint32_t *position, uint32_t max_channels, uint32_t *n_channels) + uint32_t *position, uint32_t max_position, uint32_t *n_channels) { struct spa_json iter; char v[256]; @@ -38,9 +38,10 @@ spa_audio_parse_position_n(const char *str, size_t len, if (spa_json_begin_array_relax(&iter, str, len) <= 0) return 0; - while (spa_json_get_string(&iter, v, sizeof(v)) > 0 && - channels < max_channels) { - position[channels++] = spa_type_audio_channel_from_short_name(v); + while (spa_json_get_string(&iter, v, sizeof(v)) > 0) { + if (channels < max_position) + position[channels] = spa_type_audio_channel_from_short_name(v); + channels++; } *n_channels = channels; return channels; diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index bad6da9fe..2d9492e95 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -243,7 +243,7 @@ static int position_to_string(struct channel_map *map, char *val, size_t len) uint32_t i, o = 0; int r; o += snprintf(val, len, "[ "); - for (i = 0; i < map->channels; i++) { + for (i = 0; i < map->n_pos; i++) { r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ", spa_debug_type_find_short_name(spa_type_audio_channel, map->pos[i])); @@ -1645,7 +1645,7 @@ skip_channels: } else { const struct channel_map *map = NULL; spa_pod_builder_int(b, min); - if (state->default_pos.channels == min) { + if (state->default_pos.n_pos == min) { map = &state->default_pos; spa_log_debug(state->log, "%p: using provided default", state); } else if (min <= 8) { @@ -1655,7 +1655,7 @@ skip_channels: if (map) { spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0); spa_pod_builder_push_array(b, &f[0]); - for (i = 0; i < map->channels; i++) { + for (i = 0; i < map->n_pos; i++) { spa_log_debug(state->log, "%p: position %zd %d", state, i, map->pos[i]); spa_pod_builder_id(b, map->pos[i]); } diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index 09b5140b9..8b8a07721 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -72,7 +72,7 @@ struct buffer { #define BW_PERIOD (3 * SPA_NSEC_PER_SEC) struct channel_map { - uint32_t channels; + uint32_t n_pos; uint32_t pos[MAX_CHANNELS]; }; @@ -315,7 +315,7 @@ void spa_alsa_emit_port_info(struct state *state, bool full); static inline void spa_alsa_parse_position(struct channel_map *map, const char *val, size_t len) { - spa_audio_parse_position_n(val, len, map->pos, SPA_N_ELEMENTS(map->pos), &map->channels); + spa_audio_parse_position_n(val, len, map->pos, SPA_N_ELEMENTS(map->pos), &map->n_pos); } static inline uint32_t spa_alsa_parse_rates(uint32_t *rates, uint32_t max, const char *val, size_t len) diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c index bb7f24831..ce0765f83 100644 --- a/spa/plugins/filter-graph/filter-graph.c +++ b/spa/plugins/filter-graph/filter-graph.c @@ -232,16 +232,17 @@ struct impl { float *discard_data; }; -static inline void print_channels(char *buffer, size_t max_size, uint32_t n_channels, uint32_t *positions) +static inline void print_channels(char *buffer, size_t max_size, uint32_t n_positions, uint32_t *positions) { uint32_t i; struct spa_strbuf buf; spa_strbuf_init(&buf, buffer, max_size); spa_strbuf_append(&buf, "["); - for (i = 0; i < n_channels; i++) { + for (i = 0; i < n_positions; i++) { spa_strbuf_append(&buf, "%s%s", i ? "," : "", spa_type_audio_channel_to_short_name(positions[i])); + } spa_strbuf_append(&buf, "]"); } From 7177f8269d7df4a063b59dfafe90bd9bc9a44562 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 22 Oct 2025 12:54:30 +0200 Subject: [PATCH 16/43] bluez: use function to get the channel position from a name --- spa/plugins/bluez5/a2dp-codec-opus.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/spa/plugins/bluez5/a2dp-codec-opus.c b/spa/plugins/bluez5/a2dp-codec-opus.c index 7a330e8e0..0881c0255 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus.c +++ b/spa/plugins/bluez5/a2dp-codec-opus.c @@ -253,14 +253,8 @@ static const struct surround_encoder_mapping surround_encoders[] = { static uint32_t bt_channel_from_name(const char *name) { size_t i; - enum spa_audio_channel position = SPA_AUDIO_CHANNEL_UNKNOWN; + enum spa_audio_channel position = spa_type_audio_channel_from_short_name(name); - for (i = 0; spa_type_audio_channel[i].name; i++) { - if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channel[i].name))) { - position = spa_type_audio_channel[i].type; - break; - } - } for (i = 0; i < SPA_N_ELEMENTS(audio_locations); i++) { if (position == audio_locations[i].position) return audio_locations[i].mask; From 11f1298f533bff1a2bc2caa4c0861e5273ed7916 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 22 Oct 2025 13:04:53 +0200 Subject: [PATCH 17/43] spa: make a function to make a channel short name Make a function that can generate and parse a short name for the positions that are not in the type list, like the AUX channels. --- pipewire-alsa/alsa-plugins/pcm_pipewire.c | 3 ++- spa/include/spa/param/audio/raw-types.h | 24 ++++++++++++++++++- spa/plugins/alsa/alsa-pcm.c | 5 ++-- spa/plugins/audioconvert/audioconvert.c | 19 ++++++++------- spa/plugins/avb/avb-pcm.c | 5 ++-- spa/plugins/filter-graph/filter-graph.c | 5 ++-- src/modules/module-jack-tunnel.c | 6 +++-- src/modules/module-netjack2-driver.c | 7 +++--- src/modules/module-netjack2-manager.c | 8 +++---- src/modules/module-protocol-pulse/format.c | 4 ++-- src/modules/module-protocol-pulse/format.h | 2 +- src/modules/module-protocol-pulse/message.c | 4 +++- src/modules/module-protocol-pulse/module.c | 5 ++-- .../modules/module-stream-restore.c | 4 +++- src/modules/module-zeroconf-discover.c | 4 ++-- src/tools/pw-cat.c | 7 +++--- 16 files changed, 74 insertions(+), 38 deletions(-) diff --git a/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index 27495a296..bf7196898 100644 --- a/pipewire-alsa/alsa-plugins/pcm_pipewire.c +++ b/pipewire-alsa/alsa-plugins/pcm_pipewire.c @@ -925,11 +925,12 @@ static int snd_pcm_pipewire_set_chmap(snd_pcm_ioplug_t * io, } for (i = 0; i < map->channels; i++) { uint32_t pos = chmap_to_channel(map->pos[i]); + char buf[8]; if (i < max_position) position[i] = pos; pw_log_debug("map %d: %s / %s", i, snd_pcm_chmap_name(map->pos[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, pos)); + spa_type_audio_channel_make_short_name(pos, buf, sizeof(buf), "UNK")); } return 1; } diff --git a/spa/include/spa/param/audio/raw-types.h b/spa/include/spa/param/audio/raw-types.h index c862999d5..fbaa80abf 100644 --- a/spa/include/spa/param/audio/raw-types.h +++ b/spa/include/spa/param/audio/raw-types.h @@ -267,12 +267,34 @@ static const struct spa_type_info spa_type_audio_channel[] = { SPA_API_AUDIO_RAW_TYPES uint32_t spa_type_audio_channel_from_short_name(const char *name) { - return spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN); + uint32_t res; + if (spa_strstartswith(name, "AUX")) { + if (spa_atou32(name+3, &res, 10) && res < 0x1000) + res = SPA_AUDIO_CHANNEL_AUX0 + res; + else + res = SPA_AUDIO_CHANNEL_UNKNOWN; + } else { + res = spa_type_from_short_name(name, spa_type_audio_channel, SPA_AUDIO_CHANNEL_UNKNOWN); + } + return res; } SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_channel_to_short_name(uint32_t type) { return spa_type_to_short_name(type, spa_type_audio_channel, "UNK"); } +SPA_API_AUDIO_RAW_TYPES const char * spa_type_audio_channel_make_short_name(uint32_t type, + char *buf, size_t size, const char *unknown) +{ + if (SPA_AUDIO_CHANNEL_IS_AUX(type)) { + snprintf(buf, size, "AUX%u", type - SPA_AUDIO_CHANNEL_AUX0); + } else { + const char *str = spa_type_to_short_name(type, spa_type_audio_channel, NULL); + if (str == NULL) + return unknown; + snprintf(buf, size, "%.7s", str); + } + return buf; +} #define SPA_TYPE_INFO_AudioVolumeRampScale SPA_TYPE_INFO_ENUM_BASE "AudioVolumeRampScale" #define SPA_TYPE_INFO_AUDIO_VOLUME_RAMP_SCALE_BASE SPA_TYPE_INFO_AudioVolumeRampScale ":" diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 2d9492e95..676214685 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -241,12 +241,13 @@ static int alsa_set_param(struct state *state, const char *k, const char *s) static int position_to_string(struct channel_map *map, char *val, size_t len) { uint32_t i, o = 0; + char pos[8]; int r; o += snprintf(val, len, "[ "); for (i = 0; i < map->n_pos; i++) { r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ", - spa_debug_type_find_short_name(spa_type_audio_channel, - map->pos[i])); + spa_type_audio_channel_make_short_name(map->pos[i], + pos, sizeof(pos), "UNK")); if (r < 0 || o + r >= len) return -ENOSPC; o += r; diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index dd58ee9de..1b4ae18fb 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -424,7 +424,6 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p uint32_t position, bool is_dsp, bool is_monitor, bool is_control) { struct port *port = GET_PORT(this, direction, port_id); - const char *name; spa_assert(port_id < MAX_PORTS); @@ -439,8 +438,7 @@ static int init_port(struct impl *this, enum spa_direction direction, uint32_t p port->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); port->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); - name = spa_debug_type_find_short_name(spa_type_audio_channel, position); - snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK"); + spa_type_audio_channel_make_short_name(position, port->position, sizeof(port->position), "UNK"); port->info = SPA_PORT_INFO_INIT(); port->info.change_mask = port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | @@ -2067,6 +2065,7 @@ static int setup_in_convert(struct impl *this) for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { uint32_t pi, pj; + char b1[8], b2[8]; pi = spa_format_audio_raw_get_position(&src_info.info.raw, i); pj = spa_format_audio_raw_get_position(&dst_info.info.raw, j); @@ -2077,8 +2076,8 @@ static int setup_in_convert(struct impl *this) remap = true; spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this, i, in->remap[i], j, - spa_debug_type_find_short_name(spa_type_audio_channel, pi), - spa_debug_type_find_short_name(spa_type_audio_channel, pj)); + spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"), + spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK")); spa_format_audio_raw_set_position(&dst_info.info.raw, j, -1); break; } @@ -2192,10 +2191,11 @@ static void set_volume(struct impl *this) static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position, uint32_t max_position) { uint32_t i, idx = 0; + char buf[8]; for (i = 0; i < channels; i++) idx += snprintf(str + idx, len - idx, "%s%s", i == 0 ? "" : " ", - spa_debug_type_find_short_name(spa_type_audio_channel, - position[i % max_position])); + spa_type_audio_channel_make_short_name(position[i % max_position], + buf, sizeof(buf), "UNK")); return str; } @@ -2359,6 +2359,7 @@ static int setup_out_convert(struct impl *this) for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { uint32_t pi, pj; + char b1[8], b2[8]; pi = spa_format_audio_raw_get_position(&src_info.info.raw, i); pj = spa_format_audio_raw_get_position(&dst_info.info.raw, j); @@ -2370,8 +2371,8 @@ static int setup_out_convert(struct impl *this) spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this, i, out->remap[i], j, - spa_debug_type_find_short_name(spa_type_audio_channel, pi), - spa_debug_type_find_short_name(spa_type_audio_channel, pj)); + spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"), + spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK")); spa_format_audio_raw_set_position(&dst_info.info.raw, j, -1); break; diff --git a/spa/plugins/avb/avb-pcm.c b/spa/plugins/avb/avb-pcm.c index f54e9dabf..0402b8c75 100644 --- a/spa/plugins/avb/avb-pcm.c +++ b/spa/plugins/avb/avb-pcm.c @@ -86,11 +86,12 @@ static int position_to_string(struct channel_map *map, char *val, size_t len) { uint32_t i, o = 0; int r; + char pos[8]; o += snprintf(val, len, "[ "); for (i = 0; i < map->channels; i++) { r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ", - spa_debug_type_find_short_name(spa_type_audio_channel, - map->pos[i])); + spa_type_audio_channel_make_short_name(map->pos[i], + pos, sizeof(pos), "UNK")); if (r < 0 || o + r >= len) return -ENOSPC; o += r; diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c index ce0765f83..9271ace34 100644 --- a/spa/plugins/filter-graph/filter-graph.c +++ b/spa/plugins/filter-graph/filter-graph.c @@ -236,13 +236,14 @@ static inline void print_channels(char *buffer, size_t max_size, uint32_t n_posi { uint32_t i; struct spa_strbuf buf; + char pos[8]; spa_strbuf_init(&buf, buffer, max_size); spa_strbuf_append(&buf, "["); for (i = 0; i < n_positions; i++) { spa_strbuf_append(&buf, "%s%s", i ? "," : "", - spa_type_audio_channel_to_short_name(positions[i])); - + spa_type_audio_channel_make_short_name(positions[i], + pos, sizeof(pos), "UNK")); } spa_strbuf_append(&buf, "]"); } diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index 0e1b3c3fb..4ca51bae8 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -514,6 +514,7 @@ static void make_stream_ports(struct stream *s) for (i = 0; i < s->n_ports; i++) { struct port *port = s->ports[i]; char *link_port = NULL; + char pos[8]; if (port != NULL) { s->ports[i] = NULL; @@ -523,8 +524,9 @@ static void make_stream_ports(struct stream *s) } if (i < s->info.channels) { - str = spa_debug_type_find_short_name(spa_type_audio_channel, - spa_format_audio_raw_get_position(&s->info, i)); + str = spa_type_audio_channel_make_short_name( + spa_format_audio_raw_get_position(&s->info, i), + pos, sizeof(pos), NULL); if (str) snprintf(name, sizeof(name), "%s_%s", prefix, str); else diff --git a/src/modules/module-netjack2-driver.c b/src/modules/module-netjack2-driver.c index 5e3149b41..b71284841 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -441,11 +441,12 @@ static void make_stream_ports(struct stream *s) } if (i < s->info.channels) { - str = spa_debug_type_find_short_name(spa_type_audio_channel, - spa_format_audio_raw_get_position(&s->info, i)); + str = spa_type_audio_channel_make_short_name( + spa_format_audio_raw_get_position(&s->info, i), + name, sizeof(name), "UNK"); props = pw_properties_new( PW_KEY_FORMAT_DSP, "32 bit float mono audio", - PW_KEY_AUDIO_CHANNEL, str ? str : "UNK", + PW_KEY_AUDIO_CHANNEL, str, PW_KEY_PORT_PHYSICAL, "true", NULL); diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c index 454632c8f..e171b2785 100644 --- a/src/modules/module-netjack2-manager.c +++ b/src/modules/module-netjack2-manager.c @@ -601,12 +601,12 @@ static void make_stream_ports(struct stream *s) } if (i < s->info.channels) { - str = spa_debug_type_find_short_name(spa_type_audio_channel, - spa_format_audio_raw_get_position(&s->info, i)); - + str = spa_type_audio_channel_make_short_name( + spa_format_audio_raw_get_position(&s->info, i), + name, sizeof(name), "UNK"); props = pw_properties_new( PW_KEY_FORMAT_DSP, "32 bit float mono audio", - PW_KEY_AUDIO_CHANNEL, str ? str : "UNK", + PW_KEY_AUDIO_CHANNEL, str, PW_KEY_PORT_PHYSICAL, "true", NULL); diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index f92e29e78..b34765641 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -298,9 +298,9 @@ uint32_t channel_pa2id(enum channel_position channel) return audio_channels[channel].channel; } -const char *channel_id2name(uint32_t channel) +const char *channel_id2name(uint32_t channel, char *buf, size_t size) { - return spa_type_audio_channel_to_short_name(channel); + return spa_type_audio_channel_make_short_name(channel, buf, size, "UNK"); } uint32_t channel_name2id(const char *name) diff --git a/src/modules/module-protocol-pulse/format.h b/src/modules/module-protocol-pulse/format.h index 41cfb4f9d..50bb8e2f8 100644 --- a/src/modules/module-protocol-pulse/format.h +++ b/src/modules/module-protocol-pulse/format.h @@ -190,7 +190,7 @@ void sample_spec_fix(struct sample_spec *ss, struct channel_map *map, struct spa_dict *props); uint32_t channel_pa2id(enum channel_position channel); -const char *channel_id2name(uint32_t channel); +const char *channel_id2name(uint32_t channel, char *buf, size_t size); uint32_t channel_name2id(const char *name); enum channel_position channel_id2pa(uint32_t id, uint32_t *aux); const char *channel_id2paname(uint32_t id, uint32_t *aux); diff --git a/src/modules/module-protocol-pulse/message.c b/src/modules/module-protocol-pulse/message.c index dbebc437e..19b4f82d6 100644 --- a/src/modules/module-protocol-pulse/message.c +++ b/src/modules/module-protocol-pulse/message.c @@ -761,11 +761,13 @@ int message_dump(enum spa_log_level level, const char *prefix, struct message *m case TAG_CHANNEL_MAP: { struct channel_map map; + char pos[8]; if ((res = read_channel_map(m, &map)) < 0) return res; pw_log(level, "%s %u: channelmap: channels:%u", prefix, o, map.channels); for (i = 0; i < map.channels; i++) - pw_log(level, "%s %d: %s", prefix, i, channel_id2name(map.map[i])); + pw_log(level, "%s %d: %s", prefix, i, + channel_id2name(map.map[i], pos, sizeof(pos))); break; } case TAG_CVOLUME: diff --git a/src/modules/module-protocol-pulse/module.c b/src/modules/module-protocol-pulse/module.c index 58bcce52b..fd997883e 100644 --- a/src/modules/module-protocol-pulse/module.c +++ b/src/modules/module-protocol-pulse/module.c @@ -283,14 +283,15 @@ void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properti if (info->rate) pw_properties_setf(props, SPA_KEY_AUDIO_RATE, "%u", info->rate); if (info->channels) { - char *s, *p; + char *s, *p, pos[8]; pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", info->channels); p = s = alloca(info->channels * 8); for (i = 0; i < info->channels; i++) p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ", ", - channel_id2name(spa_format_audio_raw_get_position(info, i))); + channel_id2name(spa_format_audio_raw_get_position(info, i), + pos, sizeof(pos))); pw_properties_setf(props, SPA_KEY_AUDIO_POSITION, "[ %s ]", s); } } diff --git a/src/modules/module-protocol-pulse/modules/module-stream-restore.c b/src/modules/module-protocol-pulse/modules/module-stream-restore.c index bdb9cfd7d..a33ab6fa2 100644 --- a/src/modules/module-protocol-pulse/modules/module-stream-restore.c +++ b/src/modules/module-protocol-pulse/modules/module-stream-restore.c @@ -295,9 +295,11 @@ static int do_extension_stream_restore_write(struct module *module, struct clien fprintf(f, " ]"); } if (map.channels > 0) { + char pos[8]; fprintf(f, ", \"channels\": ["); for (i = 0; i < map.channels; i++) - fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "), channel_id2name(map.map[i])); + fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "), + channel_id2name(map.map[i], pos, sizeof(pos))); fprintf(f, " ]"); } if (device_name != NULL && device_name[0] && diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c index 35e48279b..b324db403 100644 --- a/src/modules/module-zeroconf-discover.c +++ b/src/modules/module-zeroconf-discover.c @@ -188,7 +188,7 @@ static void pw_properties_from_avahi_string(const char *key, const char *value, else if (spa_streq(key, "channel_map")) { struct channel_map channel_map; uint32_t i, pos[CHANNELS_MAX]; - char *p, *s; + char *p, *s, buf[8]; spa_zero(channel_map); channel_map_parse(value, &channel_map); @@ -198,7 +198,7 @@ static void pw_properties_from_avahi_string(const char *key, const char *value, p += spa_scnprintf(p, 2, "["); for (i = 0; i < channel_map.channels; i++) p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ",", - channel_id2name(pos[i])); + channel_id2name(pos[i], buf, sizeof(buf))); p += spa_scnprintf(p, 2, "]"); pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); } diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index 52789d421..8210addb6 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -747,10 +747,11 @@ static int channelmap_default(struct channelmap *map, int n_channels) static void channelmap_print(struct channelmap *map) { uint32_t i; - + char pos[8]; for (i = 0; i < map->n_channels; i++) { - const char *name = spa_type_audio_channel_to_short_name(map->channels[i]); - fprintf(stderr, "%s%s", name, i + 1 < map->n_channels ? "," : ""); + fprintf(stderr, "%s%s", i ? "," : "", + spa_type_audio_channel_make_short_name(map->channels[i], + pos, sizeof(pos), "UNK")); } } From c5533b3c32775dc52c54c6d4a33531d2f03c36e1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 22 Oct 2025 13:26:52 +0200 Subject: [PATCH 18/43] spa: add all channel positions to the params Pass all the positions of all channels to the format param, even when there are more channels then positions. --- spa/include/spa/param/audio/raw-utils.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 8a9542c2e..7c0a92776 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -80,8 +80,8 @@ SPA_API_AUDIO_RAW_UTILS struct spa_pod * spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, const struct spa_audio_info_raw *info) { - struct spa_pod_frame f; - spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); + struct spa_pod_frame f[2]; + spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, id); spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), @@ -96,13 +96,16 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0); if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { - spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position, - SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, - SPA_MIN(info->channels, SPA_N_ELEMENTS(info->position)), - info->position), 0); + uint32_t i; + spa_pod_builder_prop(builder, SPA_FORMAT_AUDIO_position, 0); + spa_pod_builder_push_array(builder, &f[1]); + for (i = 0; i < info->channels; i++) + spa_pod_builder_id(builder, + spa_format_audio_raw_get_position(info, i)); + spa_pod_builder_pop(builder, &f[1]); } } - return (struct spa_pod*)spa_pod_builder_pop(builder, &f); + return (struct spa_pod*)spa_pod_builder_pop(builder, &f[0]); } /** From b8eeb2db45adfb4fa6df2f4c675c700d6b32570e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 23 Oct 2025 14:37:46 +0200 Subject: [PATCH 19/43] spa: make it possible to extend the spa_audio_info struct Add functions that take the size of the spa_audio_info struct in various functions. We can use this to determine how many channels and channel positions we can store. Error out if we try to use more channels than we can fit positions. This is probably the safest thing to do because most code will blindly try to get the positions without checking the channel count. Make sure we also propagate errors to the callers. --- spa/include/spa/param/audio/format-utils.h | 23 ++++-- spa/include/spa/param/audio/raw-json.h | 89 ++++++++++++++++------ spa/include/spa/param/audio/raw-utils.h | 48 ++++++++---- spa/include/spa/param/audio/raw.h | 10 ++- 4 files changed, 124 insertions(+), 46 deletions(-) diff --git a/spa/include/spa/param/audio/format-utils.h b/spa/include/spa/param/audio/format-utils.h index d608a4bdf..07f5d94e3 100644 --- a/spa/include/spa/param/audio/format-utils.h +++ b/spa/include/spa/param/audio/format-utils.h @@ -47,7 +47,7 @@ extern "C" { #endif SPA_API_AUDIO_FORMAT_UTILS int -spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info) +spa_format_audio_ext_parse(const struct spa_pod *format, struct spa_audio_info *info, size_t size) { int res; @@ -59,7 +59,7 @@ spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info switch (info->media_subtype) { case SPA_MEDIA_SUBTYPE_raw: - return spa_format_audio_raw_parse(format, &info->info.raw); + return spa_format_audio_raw_ext_parse(format, &info->info.raw, size); case SPA_MEDIA_SUBTYPE_dsp: return spa_format_audio_dsp_parse(format, &info->info.dsp); case SPA_MEDIA_SUBTYPE_iec958: @@ -98,13 +98,19 @@ spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info return -ENOTSUP; } +SPA_API_AUDIO_FORMAT_UTILS int +spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info) +{ + return spa_format_audio_ext_parse(format, info, sizeof(*info)); +} + SPA_API_AUDIO_FORMAT_UTILS struct spa_pod * -spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id, - const struct spa_audio_info *info) +spa_format_audio_ext_build(struct spa_pod_builder *builder, uint32_t id, + const struct spa_audio_info *info, size_t size) { switch (info->media_subtype) { case SPA_MEDIA_SUBTYPE_raw: - return spa_format_audio_raw_build(builder, id, &info->info.raw); + return spa_format_audio_raw_ext_build(builder, id, &info->info.raw, size); case SPA_MEDIA_SUBTYPE_dsp: return spa_format_audio_dsp_build(builder, id, &info->info.dsp); case SPA_MEDIA_SUBTYPE_iec958: @@ -143,6 +149,13 @@ spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id, errno = ENOTSUP; return NULL; } + +SPA_API_AUDIO_FORMAT_UTILS struct spa_pod * +spa_format_audio_build(struct spa_pod_builder *builder, uint32_t id, + const struct spa_audio_info *info) +{ + return spa_format_audio_ext_build(builder, id, info, sizeof(*info)); +} /** * \} */ diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index 7ef1b27f3..7c8c11ca3 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -46,6 +46,7 @@ spa_audio_parse_position_n(const char *str, size_t len, *n_channels = channels; return channels; } + SPA_API_AUDIO_RAW_JSON int spa_audio_parse_position(const char *str, size_t len, uint32_t *position, uint32_t *n_channels) @@ -54,9 +55,11 @@ spa_audio_parse_position(const char *str, size_t len, } SPA_API_AUDIO_RAW_JSON int -spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, const char *val, bool force) +spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size, + const char *key, const char *val, bool force) { uint32_t v; + uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size); if (spa_streq(key, SPA_KEY_AUDIO_FORMAT)) { if (force || info->format == 0) info->format = (enum spa_audio_format)spa_type_audio_format_from_short_name(val); @@ -64,42 +67,84 @@ spa_audio_info_raw_update(struct spa_audio_info_raw *info, const char *key, cons if (spa_atou32(val, &v, 0) && (force || info->rate == 0)) info->rate = v; } else if (spa_streq(key, SPA_KEY_AUDIO_CHANNELS)) { - if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) + if (spa_atou32(val, &v, 0) && (force || info->channels == 0)) { + if (v > max_position) + return -ECHRNG; info->channels = v; + } } else if (spa_streq(key, SPA_KEY_AUDIO_POSITION)) { if (force || info->channels == 0) { if (spa_audio_parse_position_n(val, strlen(val), info->position, - SPA_N_ELEMENTS(info->position), &info->channels) > 0) + max_position, &v) > 0) { + if (v > max_position) + return -ECHRNG; SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); + } } } return 0; } +SPA_API_AUDIO_RAW_JSON int +spa_audio_info_raw_update(struct spa_audio_info_raw *info, + const char *key, const char *val, bool force) +{ + return spa_audio_info_raw_ext_update(info, sizeof(*info), key, val, force); +} + +SPA_API_AUDIO_RAW_JSON int +spa_audio_info_raw_ext_init_dict_keys_va(struct spa_audio_info_raw *info, size_t size, + const struct spa_dict *defaults, + const struct spa_dict *dict, va_list args) +{ + int res; + + memset(info, 0, size); + SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); + if (dict) { + const char *val, *key; + while ((key = va_arg(args, const char *))) { + if ((val = spa_dict_lookup(dict, key)) == NULL) + continue; + if ((res = spa_audio_info_raw_ext_update(info, size, + key, val, true)) < 0) + return res; + } + } + if (defaults) { + const struct spa_dict_item *it; + spa_dict_for_each(it, defaults) + if ((res = spa_audio_info_raw_ext_update(info, size, + it->key, it->value, false)) < 0) + return res; + } + return 0; +} + +SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL +spa_audio_info_raw_ext_init_dict_keys(struct spa_audio_info_raw *info, size_t size, + const struct spa_dict *defaults, + const struct spa_dict *dict, ...) +{ + va_list args; + int res; + va_start(args, dict); + res = spa_audio_info_raw_ext_init_dict_keys_va(info, size, defaults, dict, args); + va_end(args); + return res; +} + SPA_API_AUDIO_RAW_JSON int SPA_SENTINEL spa_audio_info_raw_init_dict_keys(struct spa_audio_info_raw *info, const struct spa_dict *defaults, const struct spa_dict *dict, ...) { - spa_zero(*info); - SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); - if (dict) { - const char *val, *key; - va_list args; - va_start(args, dict); - while ((key = va_arg(args, const char *))) { - if ((val = spa_dict_lookup(dict, key)) == NULL) - continue; - spa_audio_info_raw_update(info, key, val, true); - } - va_end(args); - } - if (defaults) { - const struct spa_dict_item *it; - spa_dict_for_each(it, defaults) - spa_audio_info_raw_update(info, it->key, it->value, false); - } - return 0; + va_list args; + int res; + va_start(args, dict); + res = spa_audio_info_raw_ext_init_dict_keys_va(info, sizeof(*info), defaults, dict, args); + va_end(args); + return res; } /** diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 7c0a92776..551f47af1 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -58,10 +58,12 @@ spa_format_audio_raw_copy_positions(const struct spa_audio_info_raw *info, uint3 } SPA_API_AUDIO_RAW_UTILS int -spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info) +spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_info_raw *info, size_t size) { struct spa_pod *position = NULL; int res; + uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size); + info->flags = 0; res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, @@ -69,19 +71,29 @@ spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_r SPA_FORMAT_AUDIO_rate, SPA_POD_OPT_Int(&info->rate), SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); + if (info->channels > max_position) + return -ENOTSUP; if (position == NULL || - !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_N_ELEMENTS(info->position))) + spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); return res; } -SPA_API_AUDIO_RAW_UTILS struct spa_pod * -spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, - const struct spa_audio_info_raw *info) +SPA_API_AUDIO_RAW_UTILS int +spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info) { - struct spa_pod_frame f[2]; - spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, id); + return spa_format_audio_raw_ext_parse(format, info, sizeof(*info)); +} + +SPA_API_AUDIO_RAW_UTILS struct spa_pod * +spa_format_audio_raw_ext_build(struct spa_pod_builder *builder, uint32_t id, + const struct spa_audio_info_raw *info, size_t size) +{ + struct spa_pod_frame f; + uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size); + + spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), @@ -95,17 +107,21 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, if (info->channels != 0) { spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0); - if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { - uint32_t i; - spa_pod_builder_prop(builder, SPA_FORMAT_AUDIO_position, 0); - spa_pod_builder_push_array(builder, &f[1]); - for (i = 0; i < info->channels; i++) - spa_pod_builder_id(builder, - spa_format_audio_raw_get_position(info, i)); - spa_pod_builder_pop(builder, &f[1]); + if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED) && + max_position > info->channels) { + spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position, + SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, + info->channels, info->position), 0); } } - return (struct spa_pod*)spa_pod_builder_pop(builder, &f[0]); + return (struct spa_pod*)spa_pod_builder_pop(builder, &f); +} + +SPA_API_AUDIO_RAW_UTILS struct spa_pod * +spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, + const struct spa_audio_info_raw *info) +{ + return spa_format_audio_raw_ext_build(builder, id, info, sizeof(*info)); } /** diff --git a/spa/include/spa/param/audio/raw.h b/spa/include/spa/param/audio/raw.h index 5b57a5bf1..1688595e1 100644 --- a/spa/include/spa/param/audio/raw.h +++ b/spa/include/spa/param/audio/raw.h @@ -284,14 +284,18 @@ struct spa_audio_info_raw { enum spa_audio_format format; /*< format, one of enum spa_audio_format */ uint32_t flags; /*< extra flags */ uint32_t rate; /*< sample rate */ - uint32_t channels; /*< number of channels. This can be larger than - * SPA_AUDIO_MAX_POSITION, the position is taken - * (index % SPA_AUDIO_MAX_POSITION) */ + uint32_t channels; /*< number of channels. This can be more than SPA_AUDIO_MAX_POSITION + * and you may assume there is enough padding for the extra + * channel positions. */ uint32_t position[SPA_AUDIO_MAX_POSITION]; /*< channel position from enum spa_audio_channel */ + /* more channels can be added here */ }; #define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ }) +#define SPA_AUDIO_INFO_RAW_MAX_POSITION(size) (((size)-offsetof(struct spa_audio_info_raw,position))/sizeof(uint32_t)) + + #define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string, * Ex. "S16LE" */ #define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string, From 5e1e3fca1ebf284e3d37a2e87fd1c866baed6bc1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 23 Oct 2025 18:01:35 +0200 Subject: [PATCH 20/43] modules: handle format parsing errors --- src/modules/module-combine-stream.c | 9 ++++++--- src/modules/module-echo-cancel.c | 9 ++++++--- src/modules/module-example-filter.c | 11 +++++++---- src/modules/module-example-sink.c | 9 ++++++--- src/modules/module-example-source.c | 9 ++++++--- src/modules/module-ffado-driver.c | 11 +++++++---- src/modules/module-filter-chain.c | 11 +++++++---- src/modules/module-jack-tunnel.c | 11 +++++++---- src/modules/module-loopback.c | 13 ++++++++----- src/modules/module-netjack2-driver.c | 11 +++++++---- src/modules/module-netjack2-manager.c | 13 ++++++++----- src/modules/module-pipe-tunnel.c | 9 ++++++--- src/modules/module-protocol-simple.c | 18 +++++++++++------- src/modules/module-pulse-tunnel.c | 9 ++++++--- src/modules/module-rtp/stream.c | 14 ++++++++++---- src/modules/module-snapcast-discover.c | 15 +++++++++++---- src/modules/module-vban/stream.c | 9 ++++++--- 17 files changed, 125 insertions(+), 66 deletions(-) diff --git a/src/modules/module-combine-stream.c b/src/modules/module-combine-stream.c index a9dec8c2b..a9833d68f 100644 --- a/src/modules/module-combine-stream.c +++ b/src/modules/module-combine-stream.c @@ -326,9 +326,9 @@ struct stream { unsigned int have_latency:1; }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"), SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)), @@ -1638,7 +1638,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(props, impl->combine_props, "resample.prefill"); copy_props(props, impl->combine_props, "resample.disable"); - parse_audio_info(impl->combine_props, &impl->info); + if ((res = parse_audio_info(impl->combine_props, &impl->info)) < 0) { + pw_log_error( "can't create format: %s", spa_strerror(res)); + goto error; + } copy_props(props, impl->stream_props, PW_KEY_NODE_LOOP_NAME); copy_props(props, impl->stream_props, PW_KEY_NODE_GROUP); diff --git a/src/modules/module-echo-cancel.c b/src/modules/module-echo-cancel.c index 0d640f162..997506e6f 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -1203,9 +1203,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -1291,7 +1291,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) if (pw_properties_get(props, "resample.prefill") == NULL) pw_properties_set(props, "resample.prefill", "true"); - parse_audio_info(props, &info); + if ((res = parse_audio_info(props, &info)) < 0) { + pw_log_error( "can't parse format: %s", spa_strerror(res)); + goto error; + } impl->capture_info = info; impl->source_info = info; diff --git a/src/modules/module-example-filter.c b/src/modules/module-example-filter.c index 6dcb155cc..60b6dda95 100644 --- a/src/modules/module-example-filter.c +++ b/src/modules/module-example-filter.c @@ -467,9 +467,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"), SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)), @@ -573,8 +573,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) if (pw_properties_get(impl->playback_props, PW_KEY_NODE_DESCRIPTION) == NULL) pw_properties_set(impl->playback_props, PW_KEY_NODE_DESCRIPTION, str); - parse_audio_info(impl->capture_props, &impl->capture_info); - parse_audio_info(impl->playback_props, &impl->playback_info); + if ((res = parse_audio_info(impl->capture_props, &impl->capture_info)) < 0 || + (res = parse_audio_info(impl->playback_props, &impl->playback_info)) < 0) { + pw_log_error( "can't parse formats: %s", spa_strerror(res)); + goto error; + } if (!impl->capture_info.rate && !impl->playback_info.rate) { if (pw_properties_get(impl->playback_props, "resample.disable") == NULL) diff --git a/src/modules/module-example-sink.c b/src/modules/module-example-sink.c index b44470f68..00b880175 100644 --- a/src/modules/module-example-sink.c +++ b/src/modules/module-example-sink.c @@ -273,9 +273,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -395,7 +395,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, PW_KEY_NODE_VIRTUAL); copy_props(impl, props, PW_KEY_MEDIA_CLASS); - parse_audio_info(impl->stream_props, &impl->info); + if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) { + pw_log_error( "can't parse format: %s", spa_strerror(res)); + goto error; + } impl->frame_size = calc_frame_size(&impl->info); if (impl->frame_size == 0) { diff --git a/src/modules/module-example-source.c b/src/modules/module-example-source.c index f04ae1afc..20f296a17 100644 --- a/src/modules/module-example-source.c +++ b/src/modules/module-example-source.c @@ -279,9 +279,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -401,7 +401,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, PW_KEY_NODE_VIRTUAL); copy_props(impl, props, PW_KEY_MEDIA_CLASS); - parse_audio_info(impl->stream_props, &impl->info); + if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) { + pw_log_error( "can't parse format: %s", spa_strerror(res)); + goto error; + } impl->frame_size = calc_frame_size(&impl->info); if (impl->frame_size == 0) { diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c index c71639b3b..de5ed0ff0 100644 --- a/src/modules/module-ffado-driver.c +++ b/src/modules/module-ffado-driver.c @@ -1429,9 +1429,9 @@ static void parse_devices(struct impl *impl, const char *val, size_t len) } } -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"), SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)), @@ -1582,8 +1582,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, PW_KEY_NODE_VIRTUAL); copy_props(impl, props, PW_KEY_NODE_PAUSE_ON_IDLE); - parse_audio_info(impl->source.props, &impl->source.info); - parse_audio_info(impl->sink.props, &impl->sink.info); + if ((res = parse_audio_info(impl->source.props, &impl->source.info)) < 0 || + (res = parse_audio_info(impl->sink.props, &impl->sink.info)) < 0) { + pw_log_error( "can't parse format: %s", spa_strerror(res)); + goto error; + } impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core); if (impl->core == NULL) { diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index d5b15d4ab..6b759434b 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -1833,9 +1833,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")), &props->dict, @@ -1928,8 +1928,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, PW_KEY_MEDIA_NAME); copy_props(impl, props, "resample.prefill"); - parse_audio_info(impl->capture_props, &impl->capture_info); - parse_audio_info(impl->playback_props, &impl->playback_info); + if ((res = parse_audio_info(impl->capture_props, &impl->capture_info)) < 0 || + (res = parse_audio_info(impl->playback_props, &impl->playback_info)) < 0) { + pw_log_error( "can't parse format: %s", spa_strerror(res)); + goto error; + } if (!impl->capture_info.rate && !impl->playback_info.rate) { if (pw_properties_get(impl->playback_props, "resample.disable") == NULL) diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index 4ca51bae8..8e8065c26 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -1053,9 +1053,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P"), SPA_DICT_ITEM(SPA_KEY_AUDIO_POSITION, DEFAULT_POSITION)), @@ -1178,8 +1178,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, "jack.connect-audio"); copy_props(impl, props, "jack.connect-midi"); - parse_audio_info(impl->source.props, &impl->source.info); - parse_audio_info(impl->sink.props, &impl->sink.info); + if ((res = parse_audio_info(impl->source.props, &impl->source.info)) < 0 || + (res = parse_audio_info(impl->sink.props, &impl->sink.info)) < 0) { + pw_log_error( "can't parse format: %s", spa_strerror(res)); + goto error; + } impl->source.n_midi = pw_properties_get_uint32(impl->source.props, "midi.ports", DEFAULT_MIDI_PORTS); diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 64022b69d..976a8c7a4 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -838,9 +838,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")), &props->dict, @@ -952,9 +952,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) if (pw_properties_get(impl->playback_props, PW_KEY_NODE_DESCRIPTION) == NULL) pw_properties_set(impl->playback_props, PW_KEY_NODE_DESCRIPTION, str); - parse_audio_info(props, &impl->info); - parse_audio_info(impl->capture_props, &impl->capture_info); - parse_audio_info(impl->playback_props, &impl->playback_info); + if ((res = parse_audio_info(props, &impl->info)) < 0 || + (res = parse_audio_info(impl->capture_props, &impl->capture_info)) < 0 || + (res = parse_audio_info(impl->playback_props, &impl->playback_info)) < 0) { + pw_log_error( "can't parse formats: %s", spa_strerror(res)); + goto error; + } if (!impl->capture_info.rate && !impl->playback_info.rate) { if (pw_properties_get(impl->playback_props, "resample.disable") == NULL) diff --git a/src/modules/module-netjack2-driver.c b/src/modules/module-netjack2-driver.c index b71284841..e98ac9584 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -1221,9 +1221,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")), &props->dict, @@ -1339,8 +1339,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, "midi.ports"); copy_props(impl, props, "audio.ports"); - parse_audio_info(impl->source.props, &impl->source.info); - parse_audio_info(impl->sink.props, &impl->sink.info); + if ((res = parse_audio_info(impl->source.props, &impl->source.info)) < 0 || + (res = parse_audio_info(impl->sink.props, &impl->sink.info)) < 0) { + pw_log_error( "can't parse format: %s", spa_strerror(res)); + goto error; + } impl->source.wanted_n_midi = pw_properties_get_int32(impl->source.props, "midi.ports", DEFAULT_MIDI_PORTS); diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c index e171b2785..4f3a1cf73 100644 --- a/src/modules/module-netjack2-manager.c +++ b/src/modules/module-netjack2-manager.c @@ -181,7 +181,7 @@ static const struct spa_dict_item module_props[] = { { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info); +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info); struct port { enum spa_direction direction; @@ -969,8 +969,11 @@ static int handle_follower_available(struct impl *impl, struct nj2_session_param follower->sink.direction = PW_DIRECTION_INPUT; follower->sink.props = pw_properties_copy(impl->sink_props); - parse_audio_info(follower->source.props, &follower->source.info); - parse_audio_info(follower->sink.props, &follower->sink.info); + if ((res = parse_audio_info(follower->source.props, &follower->source.info)) < 0 || + (res = parse_audio_info(follower->sink.props, &follower->sink.info)) < 0) { + pw_log_error("can't parse format: %s", spa_strerror(res)); + return res; + } follower->source.n_audio = pw_properties_get_uint32(follower->source.props, "audio.ports", follower->source.info.channels ? @@ -1292,9 +1295,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, "F32P")), &props->dict, diff --git a/src/modules/module-pipe-tunnel.c b/src/modules/module-pipe-tunnel.c index 9798336df..2f66a9ed7 100644 --- a/src/modules/module-pipe-tunnel.c +++ b/src/modules/module-pipe-tunnel.c @@ -745,9 +745,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -896,7 +896,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, PW_KEY_TARGET_OBJECT); copy_props(impl, props, "pipe.filename"); - parse_audio_info(impl->stream_props, &impl->info); + if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) { + pw_log_error("can't parse format: %s", spa_strerror(res)); + goto error; + } impl->frame_size = calc_frame_size(&impl->info); if (impl->frame_size == 0) { diff --git a/src/modules/module-protocol-simple.c b/src/modules/module-protocol-simple.c index 6fdef15b8..e1847577a 100644 --- a/src/modules/module-protocol-simple.c +++ b/src/modules/module-protocol-simple.c @@ -815,13 +815,14 @@ static int calc_frame_size(struct spa_audio_info_raw *info) case SPA_AUDIO_FORMAT_F64_OE: return res * 8; default: - return 0; + return -ENOTSUP; } } static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + int res; + if ((res = spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -830,7 +831,8 @@ static int parse_audio_info(const struct pw_properties *props, struct spa_audio_ SPA_KEY_AUDIO_FORMAT, SPA_KEY_AUDIO_RATE, SPA_KEY_AUDIO_CHANNELS, - SPA_KEY_AUDIO_POSITION, NULL); + SPA_KEY_AUDIO_POSITION, NULL)) < 0) + return res; return calc_frame_size(info); } @@ -851,6 +853,7 @@ static int parse_params(struct impl *impl) const char *str; struct spa_json it[1]; char value[512]; + int res; pw_properties_fetch_bool(impl->props, "capture", &impl->capture); pw_properties_fetch_bool(impl->props, "playback", &impl->playback); @@ -894,19 +897,20 @@ static int parse_params(struct impl *impl) copy_props(impl, PW_KEY_NODE_VIRTUAL); copy_props(impl, PW_KEY_NODE_NETWORK); - impl->capture_frame_size = parse_audio_info(impl->capture_props, &impl->capture_info); - if (impl->capture_frame_size == 0) { + if ((res = parse_audio_info(impl->capture_props, &impl->capture_info)) <= 0) { pw_log_error("unsupported capture audio format:%d channels:%d", impl->capture_info.format, impl->capture_info.channels); return -EINVAL; } + impl->capture_frame_size = res; - impl->playback_frame_size = parse_audio_info(impl->playback_props, &impl->playback_info); - if (impl->playback_frame_size == 0) { + if ((res = parse_audio_info(impl->playback_props, &impl->playback_info)) <= 0) { pw_log_error("unsupported playback audio format:%d channels:%d", impl->playback_info.format, impl->playback_info.channels); return -EINVAL; } + impl->playback_frame_size = res; + if (impl->capture_info.rate != 0 && pw_properties_get(impl->capture_props, PW_KEY_NODE_RATE) == NULL) pw_properties_setf(impl->capture_props, PW_KEY_NODE_RATE, diff --git a/src/modules/module-pulse-tunnel.c b/src/modules/module-pulse-tunnel.c index 8849f7190..d613fb13b 100644 --- a/src/modules/module-pulse-tunnel.c +++ b/src/modules/module-pulse-tunnel.c @@ -1048,9 +1048,9 @@ static const struct pw_impl_module_events module_events = { .destroy = module_destroy, }; -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -1192,7 +1192,10 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) copy_props(impl, props, PW_KEY_NODE_NETWORK); copy_props(impl, props, PW_KEY_MEDIA_CLASS); - parse_audio_info(impl->stream_props, &impl->info); + if ((res = parse_audio_info(impl->stream_props, &impl->info)) < 0) { + pw_log_error("can't parse format: %s", spa_strerror(res)); + goto error; + } impl->frame_size = calc_frame_size(&impl->info); if (impl->frame_size == 0) { diff --git a/src/modules/module-rtp/stream.c b/src/modules/module-rtp/stream.c index f5ef5188b..3834206ec 100644 --- a/src/modules/module-rtp/stream.c +++ b/src/modules/module-rtp/stream.c @@ -569,9 +569,9 @@ static const struct format_info *find_audio_format_info(const struct spa_audio_i return NULL; } -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -675,7 +675,10 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core, switch (impl->info.media_subtype) { case SPA_MEDIA_SUBTYPE_raw: - parse_audio_info(props, &impl->info.info.raw); + if ((res = parse_audio_info(props, &impl->info.info.raw)) < 0) { + pw_log_error("can't parse format: %s", spa_strerror(res)); + goto out; + } impl->stream_info = impl->info; impl->format_info = find_audio_format_info(&impl->info); if (impl->format_info == NULL) { @@ -704,7 +707,10 @@ struct rtp_stream *rtp_stream_new(struct pw_core *core, case SPA_MEDIA_SUBTYPE_opus: impl->stream_info.media_type = SPA_MEDIA_TYPE_audio; impl->stream_info.media_subtype = SPA_MEDIA_SUBTYPE_raw; - parse_audio_info(props, &impl->stream_info.info.raw); + if ((res = parse_audio_info(props, &impl->stream_info.info.raw)) < 0) { + pw_log_error("can't parse format: %s", spa_strerror(res)); + goto out; + } impl->stream_info.info.raw.format = SPA_AUDIO_FORMAT_F32; impl->info.info.opus.rate = impl->stream_info.info.raw.rate; impl->info.info.opus.channels = impl->stream_info.info.raw.channels; diff --git a/src/modules/module-snapcast-discover.c b/src/modules/module-snapcast-discover.c index c4f7c6580..ec4eb3e25 100644 --- a/src/modules/module-snapcast-discover.c +++ b/src/modules/module-snapcast-discover.c @@ -503,9 +503,11 @@ static int add_snapcast_stream(struct impl *impl, struct tunnel *t, return -ENOENT; } -static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + int res; + + if ((res = spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -514,12 +516,14 @@ static void parse_audio_info(struct pw_properties *props, struct spa_audio_info_ SPA_KEY_AUDIO_FORMAT, SPA_KEY_AUDIO_RATE, SPA_KEY_AUDIO_CHANNELS, - SPA_KEY_AUDIO_POSITION, NULL); + SPA_KEY_AUDIO_POSITION, NULL)) < 0) + return res; pw_properties_set(props, PW_KEY_AUDIO_FORMAT, spa_type_audio_format_to_short_name(info->format)); pw_properties_setf(props, PW_KEY_AUDIO_RATE, "%d", info->rate); pw_properties_setf(props, PW_KEY_AUDIO_CHANNELS, "%d", info->channels); + return res; } static int create_stream(struct impl *impl, struct pw_properties *props, @@ -545,7 +549,10 @@ static int create_stream(struct impl *impl, struct pw_properties *props, if ((str = pw_properties_get(props, "capture.props")) == NULL) pw_properties_set(props, "capture.props", "{ media.class = Audio/Sink }"); - parse_audio_info(props, &t->audio_info); + if ((res = parse_audio_info(props, &t->audio_info)) < 0) { + pw_log_error("Can't parse format: %s", spa_strerror(res)); + goto done; + } if ((f = open_memstream(&args, &size)) == NULL) { res = -errno; diff --git a/src/modules/module-vban/stream.c b/src/modules/module-vban/stream.c index 4ae0eacd1..22ef8bea5 100644 --- a/src/modules/module-vban/stream.c +++ b/src/modules/module-vban/stream.c @@ -185,9 +185,9 @@ static const struct format_info *find_audio_format_info(const struct spa_audio_i return NULL; } -static void parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) +static int parse_audio_info(const struct pw_properties *props, struct spa_audio_info_raw *info) { - spa_audio_info_raw_init_dict_keys(info, + return spa_audio_info_raw_init_dict_keys(info, &SPA_DICT_ITEMS( SPA_DICT_ITEM(SPA_KEY_AUDIO_FORMAT, DEFAULT_FORMAT), SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, SPA_STRINGIFY(DEFAULT_RATE)), @@ -274,7 +274,10 @@ struct vban_stream *vban_stream_new(struct pw_core *core, switch (impl->info.media_subtype) { case SPA_MEDIA_SUBTYPE_raw: - parse_audio_info(props, &impl->info.info.raw); + if ((res = parse_audio_info(props, &impl->info.info.raw)) < 0) { + pw_log_error("can't parse format: %s", spa_strerror(res)); + goto out; + } if (SPA_FLAG_IS_SET(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED)) default_layout(impl->info.info.raw.channels, impl->info.info.raw.position, From be29ae4ef60736dd5c67558ebc92a75a7c0f0c05 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 23 Oct 2025 18:05:22 +0200 Subject: [PATCH 21/43] audioadapter: add some more debug info when parsing fails --- spa/plugins/audioconvert/audioadapter.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 01e94cf58..0d0d3e15f 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -808,6 +808,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, if (spa_format_audio_parse(param, &info) < 0) { spa_log_error(this->log, "%p: cannot set Format param: " "parsing the POD failed", this); + spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, param); return -EINVAL; } if (info.media_subtype != SPA_MEDIA_SUBTYPE_raw) { @@ -841,6 +842,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, SPA_PARAM_PORT_CONFIG_format, SPA_POD_OPT_Pod(&format)) < 0) { spa_log_error(this->log, "%p: cannot set PortConfig param: " "parsing the POD failed", this); + spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, param); return -EINVAL; } @@ -848,8 +850,12 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, struct spa_audio_info info; spa_zero(info); - if ((res = spa_format_audio_parse(format, &info)) < 0) + if ((res = spa_format_audio_parse(format, &info)) < 0) { + spa_log_error(this->log, "%p: cannot set PortConfig param: " + "parsing format failed: %s", this, spa_strerror(res)); + spa_debug_log_pod(this->log, SPA_LOG_LEVEL_ERROR, 0, NULL, format); return res; + } if (info.media_subtype == SPA_MEDIA_SUBTYPE_raw) { info.info.raw.rate = 0; From 6d74eee874ac92d587050218bf7db95fefa4ef6a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 08:53:21 +0200 Subject: [PATCH 22/43] spa: bump channels to 128 again Remove the SPA_AUDIO_MAX_POSITION define and use the SPA_AUDIO_MAX_CHANNELS again. Make a compile time define to override the default max channels of 64. Make sure we compile the SPA library with the default 64 channels. If you use the SPA library on a spa_audio_info you will get 64 channel support, like before. If you want more channels, you will need to make a padded structure or redefine the MAX_CHANNELS before you use the spa_audio_info structures. You can use the padded structure with the new functions that take the structure size. With the extra checks in the parsing code, we avoid making a valid spa_audio_info with too many channels that don't fit in the structure. This means that code that receives a spa_audio_info can assume there is enough padding for all the channels. --- meson.build | 1 + spa/include/spa/param/audio/dsd.h | 2 +- spa/include/spa/param/audio/layout.h | 2 +- spa/include/spa/param/audio/raw-json.h | 2 +- spa/include/spa/param/audio/raw.h | 20 +++++++++----------- spa/lib/lib.c | 8 ++------ 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/meson.build b/meson.build index e0a333a8f..e81ce8214 100644 --- a/meson.build +++ b/meson.build @@ -115,6 +115,7 @@ cc_flags = common_flags + [ '-Werror=old-style-definition', '-Werror=missing-parameter-type', '-Werror=strict-prototypes', + '-DSPA_AUDIO_MAX_CHANNELS=128u', ] add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c') diff --git a/spa/include/spa/param/audio/dsd.h b/spa/include/spa/param/audio/dsd.h index 733823780..73f3d4e8b 100644 --- a/spa/include/spa/param/audio/dsd.h +++ b/spa/include/spa/param/audio/dsd.h @@ -45,7 +45,7 @@ struct spa_audio_info_dsd { int32_t interleave; /*< interleave bytes */ uint32_t rate; /*< sample rate (in bytes per second) */ uint32_t channels; /*< channels */ - uint32_t position[SPA_AUDIO_MAX_POSITION]; /*< channel position from enum spa_audio_channel */ + uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */ }; #define SPA_AUDIO_INFO_DSD_INIT(...) ((struct spa_audio_info_dsd) { __VA_ARGS__ }) diff --git a/spa/include/spa/param/audio/layout.h b/spa/include/spa/param/audio/layout.h index e5e59caf9..19e3a3c42 100644 --- a/spa/include/spa/param/audio/layout.h +++ b/spa/include/spa/param/audio/layout.h @@ -20,7 +20,7 @@ extern "C" { struct spa_audio_layout_info { uint32_t n_channels; - uint32_t position[SPA_AUDIO_MAX_POSITION]; + uint32_t position[SPA_AUDIO_MAX_CHANNELS]; }; #define SPA_AUDIO_LAYOUT_Mono 1, { SPA_AUDIO_CHANNEL_MONO, } diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index 7c8c11ca3..c33a9310f 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -51,7 +51,7 @@ SPA_API_AUDIO_RAW_JSON int spa_audio_parse_position(const char *str, size_t len, uint32_t *position, uint32_t *n_channels) { - return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_POSITION, n_channels); + return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_CHANNELS, n_channels); } SPA_API_AUDIO_RAW_JSON int diff --git a/spa/include/spa/param/audio/raw.h b/spa/include/spa/param/audio/raw.h index 1688595e1..392b65daa 100644 --- a/spa/include/spa/param/audio/raw.h +++ b/spa/include/spa/param/audio/raw.h @@ -18,14 +18,10 @@ extern "C" { * \{ */ -/* This is the max number of position info, changing this will change the - * ABI */ -#define SPA_AUDIO_MAX_POSITION 64u - -/* The suggested number of max channels, can be a compile time constant and - * does not affect ABI or API */ +/* This is the max number of channels, changing this will change the + * size of some helper structures. This value should be at least 64 */ #ifndef SPA_AUDIO_MAX_CHANNELS -#define SPA_AUDIO_MAX_CHANNELS 128u +#define SPA_AUDIO_MAX_CHANNELS 64u #endif enum spa_audio_format { @@ -279,16 +275,18 @@ enum spa_audio_volume_ramp_scale { #define SPA_AUDIO_FLAG_NONE (0) /*< no valid flag */ #define SPA_AUDIO_FLAG_UNPOSITIONED (1 << 0) /*< the position array explicitly * contains unpositioned channels. */ -/** Audio information description */ +/** Audio information description. You can assume when you receive this structure + * that there is enought padding to accomodate all channel positions in case the + * channel count is more than SPA_AUDIO_MAX_CHANNELS. */ struct spa_audio_info_raw { enum spa_audio_format format; /*< format, one of enum spa_audio_format */ uint32_t flags; /*< extra flags */ uint32_t rate; /*< sample rate */ - uint32_t channels; /*< number of channels. This can be more than SPA_AUDIO_MAX_POSITION + uint32_t channels; /*< number of channels. This can be more than SPA_AUDIO_MAX_CHANNELS * and you may assume there is enough padding for the extra * channel positions. */ - uint32_t position[SPA_AUDIO_MAX_POSITION]; /*< channel position from enum spa_audio_channel */ - /* more channels can be added here */ + uint32_t position[SPA_AUDIO_MAX_CHANNELS]; /*< channel position from enum spa_audio_channel */ + /* padding follows here when channels > SPA_AUDIO_MAX_CHANNELS */ }; #define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ }) diff --git a/spa/lib/lib.c b/spa/lib/lib.c index 165fc81ba..30aa91194 100644 --- a/spa/lib/lib.c +++ b/spa/lib/lib.c @@ -1,4 +1,6 @@ +#undef SPA_AUDIO_MAX_CHANNELS + #define SPA_API_IMPL SPA_EXPORT #include #include @@ -165,9 +167,3 @@ #include #include #include - - - - - - From 78219471fffa5e147502e7cc670867a2404dbe63 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 09:35:59 +0200 Subject: [PATCH 23/43] spa: remove some obsolete functions The spa_audio_info array now always holds enough positions for all channels and we don't need to wrap around. --- spa/include/spa/param/audio/raw-utils.h | 29 ---------------------- spa/plugins/audioconvert/audioconvert.c | 20 +++++++-------- spa/plugins/bluez5/bluez5-dbus.c | 8 ++++-- spa/plugins/bluez5/bluez5-device.c | 5 +++- src/modules/module-combine-stream.c | 4 +-- src/modules/module-jack-tunnel.c | 3 +-- src/modules/module-loopback.c | 2 +- src/modules/module-netjack2-driver.c | 9 +++---- src/modules/module-netjack2-manager.c | 9 +++---- src/modules/module-protocol-pulse/format.c | 2 +- src/modules/module-protocol-pulse/module.c | 5 ++-- src/modules/module-pulse-tunnel.c | 2 +- 12 files changed, 34 insertions(+), 64 deletions(-) diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 551f47af1..ea7097a69 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -28,35 +28,6 @@ extern "C" { #endif #endif -SPA_API_AUDIO_RAW_UTILS uint32_t -spa_format_audio_raw_get_position(const struct spa_audio_info_raw *info, uint32_t idx) -{ - uint32_t pos, max_position = SPA_N_ELEMENTS(info->position); - if (idx < max_position) { - pos = info->position[idx]; - } else { - pos = info->position[idx % max_position]; - if (SPA_AUDIO_CHANNEL_IS_AUX(pos)) - pos += (idx / max_position) * max_position; - } - return pos; -} -SPA_API_AUDIO_RAW_UTILS void -spa_format_audio_raw_set_position(struct spa_audio_info_raw *info, uint32_t idx, uint32_t position) -{ - if (idx < SPA_N_ELEMENTS(info->position)) - info->position[idx] = position; -} - -SPA_API_AUDIO_RAW_UTILS uint32_t -spa_format_audio_raw_copy_positions(const struct spa_audio_info_raw *info, uint32_t *position, uint32_t max_position) -{ - uint32_t i, n_pos = SPA_MIN(info->channels, max_position); - for (i = 0; i < n_pos; i++) - position[i] = spa_format_audio_raw_get_position(info, i); - return n_pos; -} - SPA_API_AUDIO_RAW_UTILS int spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_info_raw *info, size_t size) { diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index 1b4ae18fb..e40c4f2ab 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -1897,7 +1897,7 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m this->dir[SPA_DIRECTION_OUTPUT].n_ports = dir->n_ports + 1; for (i = 0; i < dir->n_ports; i++) { - uint32_t pos = spa_format_audio_raw_get_position(&info->info.raw, i); + uint32_t pos = info->info.raw.position[i]; init_port(this, direction, i, pos, true, false, false); if (this->monitor && direction == SPA_DIRECTION_INPUT) init_port(this, SPA_DIRECTION_OUTPUT, i+1, @@ -2067,8 +2067,8 @@ static int setup_in_convert(struct impl *this) uint32_t pi, pj; char b1[8], b2[8]; - pi = spa_format_audio_raw_get_position(&src_info.info.raw, i); - pj = spa_format_audio_raw_get_position(&dst_info.info.raw, j); + pi = src_info.info.raw.position[i]; + pj = dst_info.info.raw.position[j]; if (pi != pj) continue; in->remap[i] = j; @@ -2078,7 +2078,7 @@ static int setup_in_convert(struct impl *this) i, in->remap[i], j, spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"), spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK")); - spa_format_audio_raw_set_position(&dst_info.info.raw, j, -1); + dst_info.info.raw.position[j] = -1; break; } } @@ -2126,7 +2126,7 @@ static int remap_volumes(struct impl *this, const struct spa_audio_info *info) for (i = 0; i < p->n_channels; i++) { for (j = i; j < target; j++) { - uint32_t pj = spa_format_audio_raw_get_position(&info->info.raw, j); + uint32_t pj = info->info.raw.position[j]; spa_log_debug(this->log, "%d %d: %d <-> %d", i, j, p->channel_map[i], pj); if (p->channel_map[i] != pj) @@ -2142,7 +2142,7 @@ static int remap_volumes(struct impl *this, const struct spa_audio_info *info) } p->n_channels = target; for (i = 0; i < p->n_channels; i++) - p->channel_map[i] = spa_format_audio_raw_get_position(&info->info.raw, i); + p->channel_map[i] = info->info.raw.position[i]; if (target == 0) return 0; @@ -2216,7 +2216,7 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi src_mask |= 1ULL << (p < 64 ? p : 0); } for (i = 0, dst_mask = 0; i < dst_chan; i++) { - p = spa_format_audio_raw_get_position(&out->format.info.raw, i); + p = out->format.info.raw.position[i]; dst_mask |= 1ULL << (p < 64 ? p : 0); } @@ -2361,8 +2361,8 @@ static int setup_out_convert(struct impl *this) uint32_t pi, pj; char b1[8], b2[8]; - pi = spa_format_audio_raw_get_position(&src_info.info.raw, i); - pj = spa_format_audio_raw_get_position(&dst_info.info.raw, j); + pi = src_info.info.raw.position[i]; + pj = dst_info.info.raw.position[j]; if (pi != pj) continue; out->remap[i] = j; @@ -2374,7 +2374,7 @@ static int setup_out_convert(struct impl *this) spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"), spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK")); - spa_format_audio_raw_set_position(&dst_info.info.raw, j, -1); + dst_info.info.raw.position[j] = -1; break; } } diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index 2e92696bc..ac91c6cff 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -5038,9 +5038,13 @@ static DBusHandlerResult endpoint_set_configuration(DBusConnection *conn, spa_log_error(monitor->log, "invalid transport configuration"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + if (info.info.raw.channels > MAX_CHANNELS) { + spa_log_error(monitor->log, "too many channels in transport"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } transport->n_channels = info.info.raw.channels; - spa_format_audio_raw_copy_positions(&info.info.raw, - transport->channels, SPA_N_ELEMENTS(transport->channels)); + memcpy(transport->channels, info.info.raw.position, + transport->n_channels * sizeof(uint32_t)); } else { transport->n_channels = 2; transport->channels[0] = SPA_AUDIO_CHANNEL_FL; diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index 838bb54f3..796c73c1c 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -475,7 +475,10 @@ static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t return; } - *n_channels = spa_format_audio_raw_copy_positions(&info.info.raw, channels, max_channels); + *n_channels = info.info.raw.channels; + memcpy(channels, info.info.raw.position, + info.info.raw.channels * sizeof(uint32_t)); + } static const char *get_channel_name(uint32_t channel) diff --git a/src/modules/module-combine-stream.c b/src/modules/module-combine-stream.c index a9833d68f..29f57cb50 100644 --- a/src/modules/module-combine-stream.c +++ b/src/modules/module-combine-stream.c @@ -883,8 +883,8 @@ static int create_stream(struct stream_info *info) s->remap[i] = i; for (j = 0; j < tmp_info.channels; j++) { uint32_t pj, pi; - pj = spa_format_audio_raw_get_position(&tmp_info, j); - pi = spa_format_audio_raw_get_position(&remap_info, i); + pj = tmp_info.position[j]; + pi = remap_info.position[i]; if (pj == pi) { s->remap[i] = j; break; diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index 8e8065c26..637001423 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -525,8 +525,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_type_audio_channel_make_short_name( - spa_format_audio_raw_get_position(&s->info, i), - pos, sizeof(pos), NULL); + s->info.position[i], pos, sizeof(pos), NULL); if (str) snprintf(name, sizeof(name), "%s_%s", prefix, str); else diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 976a8c7a4..e921cd701 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -546,7 +546,7 @@ static void param_format_changed(struct impl *impl, const struct spa_pod *param, (impl->info.channels != 0 && (impl->info.channels != info.channels || memcmp(impl->info.position, info.position, - SPA_MIN(info.channels, SPA_N_ELEMENTS(info.position)) * sizeof(uint32_t)) != 0))) { + info.channels * sizeof(uint32_t)) != 0))) { uint8_t buffer[1024]; struct spa_pod_builder b; const struct spa_pod *params[1]; diff --git a/src/modules/module-netjack2-driver.c b/src/modules/module-netjack2-driver.c index e98ac9584..485570064 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -442,8 +442,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_type_audio_channel_make_short_name( - spa_format_audio_raw_get_position(&s->info, i), - name, sizeof(name), "UNK"); + s->info.position[i], name, sizeof(name), "UNK"); props = pw_properties_new( PW_KEY_FORMAT_DSP, "32 bit float mono audio", PW_KEY_AUDIO_CHANNEL, str, @@ -866,8 +865,7 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p if ((uint32_t)peer->params.send_audio_channels != impl->sink.info.channels) { impl->sink.info.channels = peer->params.send_audio_channels; for (i = 0; i < impl->sink.info.channels; i++) - spa_format_audio_raw_set_position(&impl->sink.info, i, - SPA_AUDIO_CHANNEL_AUX0 + i); + impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } impl->source.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels; if (impl->source.n_ports > MAX_PORTS) { @@ -878,8 +876,7 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p if ((uint32_t)peer->params.recv_audio_channels != impl->source.info.channels) { impl->source.info.channels = peer->params.recv_audio_channels; for (i = 0; i < impl->source.info.channels; i++) - spa_format_audio_raw_set_position(&impl->source.info, i, - SPA_AUDIO_CHANNEL_AUX0 + i); + impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } impl->samplerate = peer->params.sample_rate; impl->period_size = peer->params.period_size; diff --git a/src/modules/module-netjack2-manager.c b/src/modules/module-netjack2-manager.c index 4f3a1cf73..615a4553b 100644 --- a/src/modules/module-netjack2-manager.c +++ b/src/modules/module-netjack2-manager.c @@ -602,8 +602,7 @@ static void make_stream_ports(struct stream *s) if (i < s->info.channels) { str = spa_type_audio_channel_make_short_name( - spa_format_audio_raw_get_position(&s->info, i), - name, sizeof(name), "UNK"); + s->info.position[i], name, sizeof(name), "UNK"); props = pw_properties_new( PW_KEY_FORMAT_DSP, "32 bit float mono audio", PW_KEY_AUDIO_CHANNEL, str, @@ -1031,16 +1030,14 @@ static int handle_follower_available(struct impl *impl, struct nj2_session_param if ((uint32_t)peer->params.recv_audio_channels != follower->source.info.channels) { follower->source.info.channels = peer->params.recv_audio_channels; for (i = 0; i < follower->source.info.channels; i++) - spa_format_audio_raw_set_position(&follower->source.info, i, - SPA_AUDIO_CHANNEL_AUX0 + i); + follower->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } follower->sink.n_ports = peer->params.send_audio_channels + peer->params.send_midi_channels; follower->sink.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.send_audio_channels != follower->sink.info.channels) { follower->sink.info.channels = peer->params.send_audio_channels; for (i = 0; i < follower->sink.info.channels; i++) - spa_format_audio_raw_set_position(&follower->sink.info, i, - SPA_AUDIO_CHANNEL_AUX0 + i); + follower->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } if (follower->source.n_ports > MAX_PORTS || follower->sink.n_ports > MAX_PORTS) { diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index b34765641..24ef024f9 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -585,7 +585,7 @@ int format_parse_param(const struct spa_pod *param, bool collect, if (info.info.raw.channels) { map->channels = SPA_MIN(info.info.raw.channels, CHANNELS_MAX); for (i = 0; i < map->channels; i++) - map->map[i] = spa_format_audio_raw_get_position(&info.info.raw, i); + map->map[i] = info.info.raw.position[i]; } } return 0; diff --git a/src/modules/module-protocol-pulse/module.c b/src/modules/module-protocol-pulse/module.c index fd997883e..2b47d93e9 100644 --- a/src/modules/module-protocol-pulse/module.c +++ b/src/modules/module-protocol-pulse/module.c @@ -244,7 +244,7 @@ int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props } else { /* FIXME add more mappings */ for (i = 0; i < info->channels; i++) - spa_format_audio_raw_set_position(info, i, SPA_AUDIO_CHANNEL_UNKNOWN); + info->position[i] = SPA_AUDIO_CHANNEL_UNKNOWN; } if (info->position[0] == SPA_AUDIO_CHANNEL_UNKNOWN) info->flags |= SPA_AUDIO_FLAG_UNPOSITIONED; @@ -290,8 +290,7 @@ void audioinfo_to_properties(struct spa_audio_info_raw *info, struct pw_properti p = s = alloca(info->channels * 8); for (i = 0; i < info->channels; i++) p += spa_scnprintf(p, 8, "%s%s", i == 0 ? "" : ", ", - channel_id2name(spa_format_audio_raw_get_position(info, i), - pos, sizeof(pos))); + channel_id2name(info->position[i], pos, sizeof(pos))); pw_properties_setf(props, SPA_KEY_AUDIO_POSITION, "[ %s ]", s); } } diff --git a/src/modules/module-pulse-tunnel.c b/src/modules/module-pulse-tunnel.c index d613fb13b..4c4ab1a26 100644 --- a/src/modules/module-pulse-tunnel.c +++ b/src/modules/module-pulse-tunnel.c @@ -755,7 +755,7 @@ static int create_pulse_stream(struct impl *impl) map.channels = impl->info.channels; for (i = 0; i < map.channels; i++) map.map[i] = (pa_channel_position_t)channel_id2pa( - spa_format_audio_raw_get_position(&impl->info, i), &aux); + impl->info.position[i], &aux); snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"), pw_get_user_name(), pw_get_host_name()); From aa0272f6f357706307ec4cab7296fd4f691db2de Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 10:28:38 +0200 Subject: [PATCH 24/43] treewide: remove some obsolete channel checks The spa_audio_info can not be parsed with too many channels so there is always enough space for the positions. --- pipewire-alsa/alsa-plugins/pcm_pipewire.c | 31 ++++++++----------- spa/include/spa/param/audio/raw-utils.h | 4 ++- spa/plugins/audioconvert/audioconvert.c | 36 ++++++++++------------- spa/plugins/bluez5/a2dp-codec-opus.c | 15 +++++----- spa/plugins/bluez5/bluez5-device.c | 5 ++-- src/modules/module-ffado-driver.c | 6 ++-- src/modules/module-filter-chain.c | 3 +- src/modules/module-vban/stream.c | 14 ++++----- 8 files changed, 50 insertions(+), 64 deletions(-) diff --git a/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index bf7196898..24a66eabc 100644 --- a/pipewire-alsa/alsa-plugins/pcm_pipewire.c +++ b/pipewire-alsa/alsa-plugins/pcm_pipewire.c @@ -643,11 +643,8 @@ static int snd_pcm_pipewire_pause(snd_pcm_ioplug_t * io, int enable) #define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE #endif -static int set_default_channels(uint32_t channels, uint32_t *position, uint32_t max_position) +static int set_default_channels(uint32_t channels, uint32_t position[MAX_CHANNELS]) { - if (max_position < 8) - return -ENOSPC; - switch (channels) { case 8: position[6] = SPA_AUDIO_CHANNEL_SL; @@ -775,8 +772,7 @@ static int snd_pcm_pipewire_hw_params(snd_pcm_ioplug_t * io, case SPA_MEDIA_SUBTYPE_raw: pw->requested.info.raw.channels = io->channels; pw->requested.info.raw.rate = io->rate; - set_default_channels(io->channels, pw->requested.info.raw.position, - SPA_N_ELEMENTS(pw->requested.info.raw.position)); + set_default_channels(io->channels, pw->requested.info.raw.position); fmt_str = spa_type_audio_format_to_short_name(pw->requested.info.raw.format); pw->format = pw->requested; break; @@ -784,8 +780,7 @@ static int snd_pcm_pipewire_hw_params(snd_pcm_ioplug_t * io, pw->requested.info.dsd.bitorder = SPA_PARAM_BITORDER_msb; pw->requested.info.dsd.channels = io->channels; pw->requested.info.dsd.rate = io->rate * SPA_ABS(pw->requested.info.dsd.interleave); - set_default_channels(io->channels, pw->requested.info.dsd.position, - SPA_N_ELEMENTS(pw->requested.info.dsd.position)); + set_default_channels(io->channels, pw->requested.info.dsd.position); pw->format = pw->requested; /* we need to let the server decide these values */ pw->format.info.dsd.bitorder = 0; @@ -907,30 +902,30 @@ static int snd_pcm_pipewire_set_chmap(snd_pcm_ioplug_t * io, { snd_pcm_pipewire_t *pw = io->private_data; unsigned int i; - uint32_t *position, max_position; + uint32_t *position; switch (pw->requested.media_subtype) { case SPA_MEDIA_SUBTYPE_raw: pw->requested.info.raw.channels = map->channels; position = pw->requested.info.raw.position; - max_position = SPA_N_ELEMENTS(pw->requested.info.raw.position); break; case SPA_MEDIA_SUBTYPE_dsd: pw->requested.info.dsd.channels = map->channels; position = pw->requested.info.dsd.position; - max_position = SPA_N_ELEMENTS(pw->requested.info.dsd.position); break; default: return -EINVAL; } + if (map->channels > MAX_CHANNELS) + return -ENOTSUP; + for (i = 0; i < map->channels; i++) { - uint32_t pos = chmap_to_channel(map->pos[i]); char buf[8]; - if (i < max_position) - position[i] = pos; + position[i] = chmap_to_channel(map->pos[i]); pw_log_debug("map %d: %s / %s", i, snd_pcm_chmap_name(map->pos[i]), - spa_type_audio_channel_make_short_name(pos, buf, sizeof(buf), "UNK")); + spa_type_audio_channel_make_short_name(position[i], + buf, sizeof(buf), "UNK")); } return 1; } @@ -939,18 +934,16 @@ static snd_pcm_chmap_t * snd_pcm_pipewire_get_chmap(snd_pcm_ioplug_t * io) { snd_pcm_pipewire_t *pw = io->private_data; snd_pcm_chmap_t *map; - uint32_t i, channels, *position, max_position; + uint32_t i, channels, *position; switch (pw->requested.media_subtype) { case SPA_MEDIA_SUBTYPE_raw: channels = pw->requested.info.raw.channels; position = pw->requested.info.raw.position; - max_position = SPA_N_ELEMENTS(pw->requested.info.raw.position); break; case SPA_MEDIA_SUBTYPE_dsd: channels = pw->requested.info.dsd.channels; position = pw->requested.info.dsd.position; - max_position = SPA_N_ELEMENTS(pw->requested.info.dsd.position); break; default: return NULL; @@ -960,7 +953,7 @@ static snd_pcm_chmap_t * snd_pcm_pipewire_get_chmap(snd_pcm_ioplug_t * io) channels * sizeof(unsigned int)); map->channels = channels; for (i = 0; i < channels; i++) - map->pos[i] = channel_to_chmap(position[i % max_position]); + map->pos[i] = channel_to_chmap(position[i]); return map; } diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index ea7097a69..ce5e61e67 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -43,7 +43,7 @@ spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_in SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); if (info->channels > max_position) - return -ENOTSUP; + return -ECHRNG; if (position == NULL || spa_pod_copy_array(position, SPA_TYPE_Id, info->position, max_position) != info->channels) SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); @@ -78,6 +78,8 @@ spa_format_audio_raw_ext_build(struct spa_pod_builder *builder, uint32_t id, if (info->channels != 0) { spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info->channels), 0); + /* we drop the positions here when we can't read all of them. This is + * really a malformed spa_audio_info structure. */ if (!SPA_FLAG_IS_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED) && max_position > info->channels) { spa_pod_builder_add(builder, SPA_FORMAT_AUDIO_position, diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index e40c4f2ab..e8c95b44a 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -1148,7 +1148,7 @@ struct spa_filter_graph_events graph_events = { }; static int setup_filter_graph(struct impl *this, struct filter_graph *g, - uint32_t channels, uint32_t *position, uint32_t max_position) + uint32_t channels, uint32_t *position) { int res; char rate_str[64], in_ports[64]; @@ -1163,9 +1163,8 @@ static int setup_filter_graph(struct impl *this, struct filter_graph *g, snprintf(in_ports, sizeof(in_ports), "%d", channels); g->n_inputs = channels; if (position) { - uint32_t n_pos = SPA_MIN(channels, max_position); - memcpy(g->inputs_position, position, sizeof(uint32_t) * n_pos); - memcpy(g->outputs_position, position, sizeof(uint32_t) * n_pos); + memcpy(g->inputs_position, position, sizeof(uint32_t) * channels); + memcpy(g->outputs_position, position, sizeof(uint32_t) * channels); } } @@ -1180,7 +1179,7 @@ static int setup_filter_graph(struct impl *this, struct filter_graph *g, return res; } -static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position, uint32_t max_position); +static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position); static void free_tmp(struct impl *this) { @@ -1262,7 +1261,7 @@ static int ensure_tmp(struct impl *this) static int setup_filter_graphs(struct impl *impl, bool force) { int res; - uint32_t channels, *position, max_position; + uint32_t channels, *position; struct dir *in, *out; struct filter_graph *g, *t; @@ -1271,7 +1270,6 @@ static int setup_filter_graphs(struct impl *impl, bool force) channels = in->format.info.raw.channels; position = in->format.info.raw.position; - max_position = SPA_N_ELEMENTS(in->format.info.raw.position); impl->maxports = SPA_MAX(in->format.info.raw.channels, out->format.info.raw.channels); spa_list_for_each_safe(g, t, &impl->active_graphs, link) { @@ -1279,20 +1277,19 @@ static int setup_filter_graphs(struct impl *impl, bool force) continue; if (force) g->setup = false; - if ((res = setup_filter_graph(impl, g, channels, position, max_position)) < 0) { + if ((res = setup_filter_graph(impl, g, channels, position)) < 0) { g->removing = true; spa_log_warn(impl->log, "failed to activate graph %d: %s", g->order, spa_strerror(res)); } else { channels = g->n_outputs; position = g->outputs_position; - max_position = SPA_N_ELEMENTS(g->outputs_position); impl->maxports = SPA_MAX(impl->maxports, channels); } } if ((res = ensure_tmp(impl)) < 0) return res; - if ((res = setup_channelmix(impl, channels, position, max_position)) < 0) + if ((res = setup_channelmix(impl, channels, position)) < 0) return res; return 0; @@ -2058,8 +2055,7 @@ static int setup_in_convert(struct impl *this) dst_info.info.raw.channels, dst_info.info.raw.rate); - qsort(dst_info.info.raw.position, SPA_MIN(dst_info.info.raw.channels, - SPA_N_ELEMENTS(dst_info.info.raw.position)), + qsort(dst_info.info.raw.position, dst_info.info.raw.channels, sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { @@ -2188,18 +2184,18 @@ static void set_volume(struct impl *this) this->params[IDX_Props].user++; } -static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position, uint32_t max_position) +static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position) { uint32_t i, idx = 0; char buf[8]; for (i = 0; i < channels; i++) idx += snprintf(str + idx, len - idx, "%s%s", i == 0 ? "" : " ", - spa_type_audio_channel_make_short_name(position[i % max_position], + spa_type_audio_channel_make_short_name(position[i], buf, sizeof(buf), "UNK")); return str; } -static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position, uint32_t max_position) +static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position) { struct dir *in = &this->dir[SPA_DIRECTION_INPUT]; struct dir *out = &this->dir[SPA_DIRECTION_OUTPUT]; @@ -2212,7 +2208,7 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi dst_chan = out->format.info.raw.channels; for (i = 0, src_mask = 0; i < src_chan; i++) { - p = position[i % max_position]; + p = position[i]; src_mask |= 1ULL << (p < 64 ? p : 0); } for (i = 0, dst_mask = 0; i < dst_chan; i++) { @@ -2221,10 +2217,9 @@ static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *posi } spa_log_info(this->log, "in %s (%016"PRIx64")", format_position(str, sizeof(str), - src_chan, position, max_position), src_mask); + src_chan, position), src_mask); spa_log_info(this->log, "out %s (%016"PRIx64")", format_position(str, sizeof(str), - dst_chan, out->format.info.raw.position, - SPA_N_ELEMENTS(out->format.info.raw.position)), dst_mask); + dst_chan, out->format.info.raw.position), dst_mask); spa_log_info(this->log, "%p: %s/%d@%d->%s/%d@%d %08"PRIx64":%08"PRIx64, this, spa_debug_type_find_name(spa_type_audio_format, SPA_AUDIO_FORMAT_DSP_F32), @@ -2352,8 +2347,7 @@ static int setup_out_convert(struct impl *this) dst_info.info.raw.channels, dst_info.info.raw.rate); - qsort(src_info.info.raw.position, SPA_MIN(src_info.info.raw.channels, - SPA_N_ELEMENTS(src_info.info.raw.position)), + qsort(src_info.info.raw.position, src_info.info.raw.channels, sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { diff --git a/spa/plugins/bluez5/a2dp-codec-opus.c b/spa/plugins/bluez5/a2dp-codec-opus.c index 0881c0255..c5b042b20 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus.c +++ b/spa/plugins/bluez5/a2dp-codec-opus.c @@ -491,7 +491,7 @@ static void get_default_bitrates(const struct media_codec *codec, bool bidi, int static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direction_t *conf, bool use_surround_encoder, uint8_t *streams_ret, uint8_t *coupled_streams_ret, - const uint8_t **surround_mapping, uint32_t *positions, uint32_t max_positions) + const uint8_t **surround_mapping, uint32_t *positions) { const uint32_t channels = conf->channels; const uint32_t location = OPUS_05_GET_LOCATION(*conf); @@ -539,12 +539,11 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc if (location & loc.mask) { uint32_t idx = permutation ? permutation[j] : j; - if (idx < max_positions) - positions[idx] = loc.position; + positions[idx] = loc.position; j++; } } - for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels && j < max_positions; ++i, ++j) + for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels; ++i, ++j) positions[j] = i; } @@ -779,7 +778,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, dir = !is_duplex_codec(codec) ? &conf.main : &conf.bidi; - if (get_mapping(codec, dir, surround_encoder, NULL, NULL, NULL, position, MAX_CHANNELS) < 0) + if (get_mapping(codec, dir, surround_encoder, NULL, NULL, NULL, position) < 0) return -EINVAL; spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, id); @@ -832,9 +831,9 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags info->info.raw.channels = dir1->channels; if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL, - info->info.raw.position, SPA_N_ELEMENTS(info->info.raw.position)) < 0) + info->info.raw.position) < 0) return -EINVAL; - if (get_mapping(codec, dir2, surround_encoder, NULL, NULL, NULL, NULL, 0) < 0) + if (get_mapping(codec, dir2, surround_encoder, NULL, NULL, NULL, NULL) < 0) return -EINVAL; return 0; @@ -925,7 +924,7 @@ static void *codec_init(const struct media_codec *codec, uint32_t flags, if ((res = codec_validate_config(codec, flags, config, config_len, &config_info)) < 0) goto error; if ((res = get_mapping(codec, dir, surround_encoder, &this->streams, &this->coupled_streams, - &enc_mapping, NULL, 0)) < 0) + &enc_mapping, NULL)) < 0) goto error; if (config_info.info.raw.channels != info->info.raw.channels) { res = -EINVAL; diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index 796c73c1c..6546abf4f 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -452,8 +452,7 @@ static int node_offload_set_active(struct node *node, bool active) return res; } -static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t *n_channels, uint32_t *channels, - uint32_t max_channels) +static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t *n_channels, uint32_t *channels) { const struct media_codec *codec; struct spa_audio_info info = { 0 }; @@ -689,7 +688,7 @@ static void emit_node(struct impl *this, struct spa_bt_transport *t, this->nodes[id].active = true; this->nodes[id].offload_acquired = false; this->nodes[id].a2dp_duplex = a2dp_duplex; - get_channels(t, a2dp_duplex, &this->nodes[id].n_channels, this->nodes[id].channels, MAX_CHANNELS); + get_channels(t, a2dp_duplex, &this->nodes[id].n_channels, this->nodes[id].channels); if (this->nodes[id].transport) spa_hook_remove(&this->nodes[id].transport_listener); this->nodes[id].transport = t; diff --git a/src/modules/module-ffado-driver.c b/src/modules/module-ffado-driver.c index de5ed0ff0..7094feaf4 100644 --- a/src/modules/module-ffado-driver.c +++ b/src/modules/module-ffado-driver.c @@ -761,7 +761,7 @@ static int make_stream_ports(struct stream *s) struct port *port = s->ports[i]; char channel[32]; - snprintf(channel, sizeof(channel), "AUX%u", n_channels % MAX_CHANNELS); + snprintf(channel, sizeof(channel), "AUX%u", n_channels); switch (port->stream_type) { case ffado_stream_type_audio: @@ -1229,7 +1229,7 @@ static int probe_ffado_device(struct impl *impl) } if (impl->source.info.channels != n_channels) { uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->source.info.position)); - impl->source.info.channels = n_channels; + impl->source.info.channels = n_pos; for (i = 0; i < n_pos; i++) impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1256,7 +1256,7 @@ static int probe_ffado_device(struct impl *impl) } if (impl->sink.info.channels != n_channels) { uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->sink.info.position)); - impl->sink.info.channels = n_channels; + impl->sink.info.channels = n_pos; for (i = 0; i < n_pos; i++) impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index 6b759434b..a2d1f5361 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -1691,8 +1691,7 @@ static void copy_position(struct spa_audio_info_raw *dst, const struct spa_audio { if (SPA_FLAG_IS_SET(dst->flags, SPA_AUDIO_FLAG_UNPOSITIONED) && !SPA_FLAG_IS_SET(src->flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { - uint32_t i, n_pos = SPA_MIN(src->channels, SPA_N_ELEMENTS(dst->position)); - for (i = 0; i < n_pos; i++) + for (uint32_t i = 0; i < src->channels; i++) dst->position[i] = src->position[i]; SPA_FLAG_CLEAR(dst->flags, SPA_AUDIO_FLAG_UNPOSITIONED); } diff --git a/src/modules/module-vban/stream.c b/src/modules/module-vban/stream.c index 22ef8bea5..941fa1acd 100644 --- a/src/modules/module-vban/stream.c +++ b/src/modules/module-vban/stream.c @@ -215,16 +215,16 @@ static const struct spa_audio_layout_info layouts[] = { { SPA_AUDIO_LAYOUT_7_1 }, }; -static void default_layout(uint32_t channels, uint32_t *position, uint32_t max_position) +static void default_layout(uint32_t channels, uint32_t *position) { SPA_FOR_EACH_ELEMENT_VAR(layouts, l) { if (l->n_channels == channels) { - for (uint32_t i = 0; i < l->n_channels && i < max_position; i++) + for (uint32_t i = 0; i < l->n_channels; i++) position[i] = l->position[i]; return; } } - for (uint32_t i = 0; i < channels && i < max_position; i++) + for (uint32_t i = 0; i < channels; i++) position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -278,10 +278,10 @@ struct vban_stream *vban_stream_new(struct pw_core *core, pw_log_error("can't parse format: %s", spa_strerror(res)); goto out; } - if (SPA_FLAG_IS_SET(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED)) - default_layout(impl->info.info.raw.channels, - impl->info.info.raw.position, - SPA_N_ELEMENTS(impl->info.info.raw.position)); + if (SPA_FLAG_IS_SET(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { + default_layout(impl->info.info.raw.channels, impl->info.info.raw.position); + SPA_FLAG_CLEAR(impl->info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED); + } impl->stream_info = impl->info; impl->format_info = find_audio_format_info(&impl->info); if (impl->format_info == NULL) { From d18670d7bbfe88aaee7bc3c8926eb1c4fedd5173 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 10:42:05 +0200 Subject: [PATCH 25/43] pw-cat: improve channel checks Make sure we don't use too many channels. --- src/tools/pw-cat.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index 8210addb6..632a513bf 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -2348,10 +2348,22 @@ int main(int argc, char *argv[]) .rate = data.rate, .channels = data.channels); + if (data.channels > MAX_CHANNELS) { + fprintf(stderr, "error: too many channels %d > %d\n", + data.channels, MAX_CHANNELS); + goto error_bad_file; + } if (data.channelmap.n_channels) { - uint32_t i, n_pos = SPA_MIN(data.channels, SPA_N_ELEMENTS(info.position)); - for (i = 0; i < n_pos; i++) + if (data.channels > MAX_CHANNELS) { + fprintf(stderr, "error: too many channels in channelmap %d > %d\n", + data.channelmap.n_channels, MAX_CHANNELS); + goto error_bad_file; + } + uint32_t i; + for (i = 0; i < data.channelmap.n_channels; i++) info.position[i] = data.channelmap.channels[i]; + for (; i < data.channels; i++) + info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } params[n_params++] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info); break; From f7c3d379698e0c8b0eefec6cbab10a0b04f0bf8a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 12:46:38 +0200 Subject: [PATCH 26/43] fmt-ops: allocate shaper memory dynamically It is based on the number of channels so allocate it dynamically. --- spa/plugins/audioconvert/fmt-ops.c | 6 ++++-- spa/plugins/audioconvert/fmt-ops.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spa/plugins/audioconvert/fmt-ops.c b/spa/plugins/audioconvert/fmt-ops.c index 3296c220b..3fc2c5f0a 100644 --- a/spa/plugins/audioconvert/fmt-ops.c +++ b/spa/plugins/audioconvert/fmt-ops.c @@ -551,7 +551,7 @@ int convert_init(struct convert *conv) const struct dither_info *dinfo; const struct noise_info *ninfo; const struct clear_info *cinfo; - uint32_t i, conv_flags, data_size[3]; + uint32_t i, conv_flags, data_size[4]; /* we generate int32 bits of random values. With this scale * factor, we bring this in the [-1.0, 1.0] range */ @@ -615,15 +615,17 @@ int convert_init(struct convert *conv) data_size[0] = SPA_ROUND_UP(conv->noise_size * sizeof(float), FMT_OPS_MAX_ALIGN); data_size[1] = SPA_ROUND_UP(RANDOM_SIZE * sizeof(uint32_t), FMT_OPS_MAX_ALIGN); data_size[2] = SPA_ROUND_UP(RANDOM_SIZE * sizeof(int32_t), FMT_OPS_MAX_ALIGN); + data_size[3] = SPA_ROUND_UP(conv->n_channels * sizeof(struct shaper), FMT_OPS_MAX_ALIGN); conv->data = calloc(FMT_OPS_MAX_ALIGN + - data_size[0] + data_size[1] + data_size[2], 1); + data_size[0] + data_size[1] + data_size[2] + data_size[3], 1); if (conv->data == NULL) return -errno; conv->noise = SPA_PTR_ALIGN(conv->data, FMT_OPS_MAX_ALIGN, float); conv->random = SPA_PTROFF(conv->noise, data_size[0], uint32_t); conv->prev = SPA_PTROFF(conv->random, data_size[1], int32_t); + conv->shaper = SPA_PTROFF(conv->prev, data_size[2], struct shaper); for (i = 0; i < RANDOM_SIZE; i++) conv->random[i] = random(); diff --git a/spa/plugins/audioconvert/fmt-ops.h b/spa/plugins/audioconvert/fmt-ops.h index 9f4655c22..f738e3858 100644 --- a/spa/plugins/audioconvert/fmt-ops.h +++ b/spa/plugins/audioconvert/fmt-ops.h @@ -236,7 +236,7 @@ struct convert { uint32_t noise_size; const float *ns; uint32_t n_ns; - struct shaper shaper[64]; + struct shaper *shaper; void (*update_noise) (struct convert *conv, float *noise, uint32_t n_samples); void (*process) (struct convert *conv, void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], From c4244a6cf3e042af5c2c344ba0a52836ec45aac8 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 17:00:11 +0200 Subject: [PATCH 27/43] string: use spa_strbuf instead of snprintf magic --- spa/plugins/alsa/acp/acp.c | 19 ++++++++------- spa/plugins/alsa/acp/channelmap.h | 5 +--- spa/plugins/alsa/alsa-acp-device.c | 9 +++++--- spa/plugins/alsa/alsa-pcm.c | 37 ++++++++++++++---------------- src/pipewire/settings.c | 19 +++++++-------- 5 files changed, 41 insertions(+), 48 deletions(-) diff --git a/spa/plugins/alsa/acp/acp.c b/spa/plugins/alsa/acp/acp.c index 364d71eb0..f6f03a5ff 100644 --- a/spa/plugins/alsa/acp/acp.c +++ b/spa/plugins/alsa/acp/acp.c @@ -245,7 +245,7 @@ static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t pa_proplist_update(dev->proplist, PA_UPDATE_REPLACE, m->input_proplist); } if (m->split) { - char pos[2048]; + char pos[PA_CHANNELS_MAX*8]; struct spa_strbuf b; int i; @@ -1142,8 +1142,9 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) pa_proplist_unset(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED); } else { uint32_t positions[eld.lpcm_channels]; - char position[64]; - int i = 0, pos = 0; + char position[eld.lpcm_channels * 8]; + struct spa_strbuf b; + int i = 0; if (eld.speakers & 0x01) { positions[i++] = ACP_CHANNEL_FL; @@ -1172,16 +1173,14 @@ static int hdmi_eld_changed(snd_mixer_elem_t *melem, unsigned int mask) if (eld.speakers & 0x10) { positions[i++] = ACP_CHANNEL_RC; } - while (i < eld.lpcm_channels) positions[i++] = ACP_CHANNEL_UNKNOWN; - for (i = 0, pos = 0; i < eld.lpcm_channels; i++) { - pos += snprintf(&position[pos], sizeof(position) - pos, "%s,", channel_names[positions[i]]); - } - - /* Overwrite trailing , */ - position[pos - 1] = 0; + spa_strbuf_init(&b, position, sizeof(position)); + spa_strbuf_append(&b, "["); + for (i = 0; i < eld.lpcm_channels; i++) + spa_strbuf_append(&b, "%s%s", i ? "," : "", channel_names[positions[i]]); + spa_strbuf_append(&b, "]"); changed |= (old_position == NULL) || (!spa_streq(old_position, position)); pa_proplist_sets(p->proplist, ACP_KEY_AUDIO_POSITION_DETECTED, position); diff --git a/spa/plugins/alsa/acp/channelmap.h b/spa/plugins/alsa/acp/channelmap.h index 2d4b54444..bb8f3f5f3 100644 --- a/spa/plugins/alsa/acp/channelmap.h +++ b/spa/plugins/alsa/acp/channelmap.h @@ -451,7 +451,6 @@ static inline int pa_channel_map_equal(const pa_channel_map *a, const pa_channel static inline char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { unsigned channel; - bool first = true; char *e; if (!pa_channel_map_valid(map)) { pa_snprintf(s, l, "%s", _("(invalid)")); @@ -460,12 +459,10 @@ static inline char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_m *(e = s) = 0; for (channel = 0; channel < map->channels && l > 1; channel++) { l -= pa_snprintf(e, l, "%s%s", - first ? "" : ",", + channel == 0 ? "" : ",", pa_channel_position_to_string(map->map[channel])); e = strchr(e, 0); - first = false; } - return s; } diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c index 6ab41c8d2..44342a7a3 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -156,12 +156,13 @@ static int emit_node(struct impl *this, struct acp_device *dev) const struct acp_dict_item *it; uint32_t n_items, i; char device_name[128], path[210], channels[16], ch[12], routes[16]; - char card_index[16], card_name[64], *p; + char card_index[16], card_name[64]; char positions[MAX_CHANNELS * 12]; char codecs[512]; struct spa_device_object_info info; struct acp_card *card = this->card; const char *stream, *card_id, *bus; + struct spa_strbuf b; info = SPA_DEVICE_OBJECT_INFO_INIT(); info.type = SPA_TYPE_INTERFACE_Node; @@ -200,11 +201,13 @@ static int emit_node(struct impl *this, struct acp_device *dev) snprintf(channels, sizeof(channels), "%d", dev->format.channels); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNELS, channels); - p = positions; + spa_strbuf_init(&b, positions, sizeof(positions)); + spa_strbuf_append(&b, "["); for (i = 0; i < dev->format.channels; i++) { - p += snprintf(p, 12, "%s%s", i == 0 ? "" : ",", + spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ", acp_channel_str(ch, sizeof(ch), dev->format.map[i])); } + spa_strbuf_append(&b, " ]"); items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_POSITION, positions); if (dev->n_codecs > 0) { diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 676214685..309b2d91d 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -240,36 +240,33 @@ static int alsa_set_param(struct state *state, const char *k, const char *s) static int position_to_string(struct channel_map *map, char *val, size_t len) { - uint32_t i, o = 0; + uint32_t i; char pos[8]; - int r; - o += snprintf(val, len, "[ "); + struct spa_strbuf b; + + spa_strbuf_init(&b, val, len); + spa_strbuf_append(&b, "["); for (i = 0; i < map->n_pos; i++) { - r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ", + spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ", spa_type_audio_channel_make_short_name(map->pos[i], pos, sizeof(pos), "UNK")); - if (r < 0 || o + r >= len) - return -ENOSPC; - o += r; } - if (len > o) - o += snprintf(val+o, len-o, " ]"); + if (spa_strbuf_append(&b, " ]") < 2) + return -ENOSPC; return 0; } static int uint32_array_to_string(uint32_t *vals, uint32_t n_vals, char *val, size_t len) { - uint32_t i, o = 0; - int r; - o += snprintf(val, len, "[ "); - for (i = 0; i < n_vals; i++) { - r = snprintf(val+o, len-o, "%s%d", i == 0 ? "" : ", ", vals[i]); - if (r < 0 || o + r >= len) - return -ENOSPC; - o += r; - } - if (len > o) - o += snprintf(val+o, len-o, " ]"); + uint32_t i; + struct spa_strbuf b; + + spa_strbuf_init(&b, val, len); + spa_strbuf_append(&b, "["); + for (i = 0; i < n_vals; i++) + spa_strbuf_append(&b, "%s%d", i == 0 ? " " : ", ", vals[i]); + if (spa_strbuf_append(&b, " ]") < 2) + return -ENOSPC; return 0; } diff --git a/src/pipewire/settings.c b/src/pipewire/settings.c index 597e686a9..74d4cfcb2 100644 --- a/src/pipewire/settings.c +++ b/src/pipewire/settings.c @@ -245,24 +245,21 @@ void pw_settings_init(struct pw_context *this) static void expose_settings(struct pw_context *context, struct pw_impl_metadata *metadata) { struct settings *s = &context->settings; - uint32_t i, o; - char rates[MAX_RATES*16] = ""; + uint32_t i; + char rates[MAX_RATES*16]; + struct spa_strbuf b; pw_impl_metadata_set_propertyf(metadata, PW_ID_CORE, "log.level", "", "%d", s->log_level); pw_impl_metadata_set_propertyf(metadata, PW_ID_CORE, "clock.rate", "", "%d", s->clock_rate); - for (i = 0, o = 0; i < s->n_clock_rates; i++) { - int r = snprintf(rates+o, sizeof(rates)-o, "%s%d", i == 0 ? "" : ", ", + + spa_strbuf_init(&b, rates, sizeof(rates)); + for (i = 0; i < s->n_clock_rates; i++) + spa_strbuf_append(&b, "%s%d", i == 0 ? "" : ", ", s->clock_rates[i]); - if (r < 0 || o + r >= (int)sizeof(rates)) { - snprintf(rates, sizeof(rates), "%d", s->clock_rate); - break; - } - o += r; - } if (s->n_clock_rates == 0) - snprintf(rates, sizeof(rates), "%d", s->clock_rate); + spa_strbuf_append(&b, "%d", s->clock_rate); pw_impl_metadata_set_propertyf(metadata, PW_ID_CORE, "clock.allowed-rates", "", "[ %s ]", rates); From c8d4de5e77a016d94824e4240c851b5d032c9946 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 17:00:42 +0200 Subject: [PATCH 28/43] acp: bump max channels to 128 --- spa/plugins/alsa/acp/channelmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spa/plugins/alsa/acp/channelmap.h b/spa/plugins/alsa/acp/channelmap.h index bb8f3f5f3..72e59fdc6 100644 --- a/spa/plugins/alsa/acp/channelmap.h +++ b/spa/plugins/alsa/acp/channelmap.h @@ -27,7 +27,7 @@ extern "C" { #endif -#define PA_CHANNELS_MAX 64 +#define PA_CHANNELS_MAX 128 #define PA_CHANNEL_MAP_SNPRINT_MAX (PA_CHANNELS_MAX * 32) From 88c65932d8d8a002ae9e2d42ca2a25b06a804a19 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 24 Oct 2025 17:16:03 +0200 Subject: [PATCH 29/43] acp: use global max channels if defined --- spa/plugins/alsa/acp/channelmap.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spa/plugins/alsa/acp/channelmap.h b/spa/plugins/alsa/acp/channelmap.h index 72e59fdc6..2b13d2528 100644 --- a/spa/plugins/alsa/acp/channelmap.h +++ b/spa/plugins/alsa/acp/channelmap.h @@ -27,7 +27,11 @@ extern "C" { #endif -#define PA_CHANNELS_MAX 128 +#ifdef SPA_AUDIO_MAX_CHANNELS +#define PA_CHANNELS_MAX ((int)SPA_AUDIO_MAX_CHANNELS) +#else +#define PA_CHANNELS_MAX 64 +#endif #define PA_CHANNEL_MAP_SNPRINT_MAX (PA_CHANNELS_MAX * 32) From 9f1a149876c56ec07ae74b10e9c4ef8a97084bf7 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 22 Oct 2025 19:32:22 +0300 Subject: [PATCH 30/43] ci: add file package, for coverity Try to fix coverity by adding missing 'file' package to container --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9cfde68d0..448bfa06e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,7 @@ include: .fedora: variables: # Update this tag when you want to trigger a rebuild - FDO_DISTRIBUTION_TAG: '2025-10-15.0' + FDO_DISTRIBUTION_TAG: '2025-10-22.0' FDO_DISTRIBUTION_VERSION: '42' FDO_DISTRIBUTION_PACKAGES: >- alsa-lib-devel @@ -48,6 +48,7 @@ include: dbus-devel doxygen fdk-aac-free-devel + file findutils gcc gcc-c++ From 93495d3a754bc7492cfb27fd551180f0be9b6932 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 26 Oct 2025 15:38:17 +0200 Subject: [PATCH 31/43] spa: param: infer raw audio channels from position if unset The behavior before b8eeb2db45adfb was that spa_audio_info_raw_update() always sets audio.channels when audio.position is updated. The new behavior does not set audio.channels when parsing audio.position. This breaks things e.g. when combine-sink and loopback nodes are created with only audio.position specified. Restore the previous behavior. --- spa/include/spa/param/audio/raw-json.h | 1 + 1 file changed, 1 insertion(+) diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index c33a9310f..e83c4495b 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -78,6 +78,7 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size, max_position, &v) > 0) { if (v > max_position) return -ECHRNG; + info->channels = v; SPA_FLAG_CLEAR(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); } } From 3febf09b85c23a6cf69fbba1d250b85820abd76a Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Oct 2025 13:22:32 +0300 Subject: [PATCH 32/43] alsa: fix typoed braces in condition + assign --- spa/plugins/alsa/alsa-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 309b2d91d..4f2fde8b6 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -773,7 +773,7 @@ static void bind_ctl_event(struct spa_source *source) snd_ctl_elem_id_alloca(&bound_id); snd_ctl_elem_value_alloca(&old_value); - while ((err = snd_ctl_read(state->ctl, ev) > 0)) { + while ((err = snd_ctl_read(state->ctl, ev)) > 0) { bool changed = false; if (snd_ctl_event_get_type(ev) != SND_CTL_EVENT_ELEM) From fe2c62b9b163ff824647cf7c0ec9d8369f836a0e Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Oct 2025 13:59:47 +0300 Subject: [PATCH 33/43] meson.build: set project cc flags also for native builds Use the build flags also for all native build targets. Avoids spurious warnings in spa-json-dump --- meson.build | 4 ++-- spa/plugins/audioconvert/meson.build | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index e81ce8214..e82ba03b3 100644 --- a/meson.build +++ b/meson.build @@ -118,8 +118,8 @@ cc_flags = common_flags + [ '-DSPA_AUDIO_MAX_CHANNELS=128u', ] add_project_arguments(cc.get_supported_arguments(cc_flags), language: 'c') - -cc_flags_native = cc_native.get_supported_arguments(cc_flags) +add_project_arguments(cc_native.get_supported_arguments(cc_flags), + language: 'c', native: true) have_cpp = add_languages('cpp', native: false, required : false) diff --git a/spa/plugins/audioconvert/meson.build b/spa/plugins/audioconvert/meson.build index 394bc11eb..64379c845 100644 --- a/spa/plugins/audioconvert/meson.build +++ b/spa/plugins/audioconvert/meson.build @@ -125,7 +125,7 @@ sparesampledumpcoeffs_sources = [ sparesampledumpcoeffs = executable( 'spa-resample-dump-coeffs', sparesampledumpcoeffs_sources, - c_args : [ cc_flags_native, '-DRESAMPLE_DISABLE_PRECOMP' ], + c_args : [ '-DRESAMPLE_DISABLE_PRECOMP' ], dependencies : [ spa_dep, mathlib_native ], install : false, native : true, From b0e308e0dce9c78a3ee71eab0cd9a2a8f959d47b Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Oct 2025 14:27:10 +0300 Subject: [PATCH 34/43] spa: examples: fix getopt usage + typos in adapter-control --- spa/examples/adapter-control.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spa/examples/adapter-control.c b/spa/examples/adapter-control.c index 6aa36dfaa..0a48f2fd4 100644 --- a/spa/examples/adapter-control.c +++ b/spa/examples/adapter-control.c @@ -578,7 +578,7 @@ static int make_nodes(struct data *data) SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_OUTPUT), SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); - if ((res = spa_node_set_param(data->source_node, SPA_PARAM_PortConfig, 0, param) < 0)) { + if ((res = spa_node_set_param(data->source_node, SPA_PARAM_PortConfig, 0, param)) < 0) { printf("can't setup source node %d\n", res); return res; } @@ -647,7 +647,7 @@ static int make_nodes(struct data *data) SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); - if ((res = spa_node_set_param(data->sink_node, SPA_PARAM_PortConfig, 0, param) < 0)) { + if ((res = spa_node_set_param(data->sink_node, SPA_PARAM_PortConfig, 0, param)) < 0) { printf("can't setup sink node %d\n", res); return res; } @@ -987,7 +987,7 @@ int main(int argc, char *argv[]) setlocale(LC_ALL, ""); - while ((c = getopt_long(argc, argv, "hdmstiac:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "hd:m:s:t:i:a:c:", long_options, NULL)) != -1) { switch (c) { case 'h': show_help(&data, argv[0], false); From 68dc45cc62a6c79caca5e711faac2727e3b49154 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Oct 2025 13:49:46 +0300 Subject: [PATCH 35/43] audioconvert: simplify volume ramp generation Don't use floating point accumulators, interpolate from sample position. --- spa/plugins/audioconvert/audioconvert.c | 82 ++++++++----------------- 1 file changed, 27 insertions(+), 55 deletions(-) diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index e8c95b44a..f201522f8 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -1538,8 +1538,6 @@ static int get_ramp_samples(struct impl *this, struct volume_ramp_params *vrp) samples = (vrp->volume_ramp_time * vrp->rate) / 1000; spa_log_info(this->log, "volume ramp samples calculated from time is %d", samples); } - if (!samples) - samples = -1; return samples; } @@ -1550,12 +1548,10 @@ static int get_ramp_step_samples(struct impl *this, struct volume_ramp_params *v if (vrp->volume_ramp_step_samples) samples = vrp->volume_ramp_step_samples; else if (vrp->volume_ramp_step_time) { - /* convert the step time which is in nano seconds to seconds */ - samples = (vrp->volume_ramp_step_time/1000) * (vrp->rate/1000); + /* convert the step time which is in nano seconds to seconds, round up */ + samples = SPA_MAX(1u, vrp->volume_ramp_step_time/1000) * (vrp->rate/1000); spa_log_debug(this->log, "volume ramp step samples calculated from time is %d", samples); } - if (!samples) - samples = -1; return samples; } @@ -1568,76 +1564,52 @@ static float get_volume_at_scale(struct volume_ramp_params *vrp, float value) return 0.0; } -static struct spa_pod *generate_ramp_up_seq(struct impl *this, struct volume_ramp_params *vrp, +static struct spa_pod *generate_ramp_seq(struct impl *this, struct volume_ramp_params *vrp, void *buffer, size_t size) { struct spa_pod_dynamic_builder b; struct spa_pod_frame f[1]; - float start = vrp->start, end = vrp->end, volume_accum = start; - int ramp_samples = get_ramp_samples(this, vrp); - int ramp_step_samples = get_ramp_step_samples(this, vrp); - float volume_step = ((end - start) / (ramp_samples / ramp_step_samples)); - uint32_t volume_offs = 0; + float start = vrp->start, end = vrp->end; + int samples = get_ramp_samples(this, vrp); + int step = get_ramp_step_samples(this, vrp); + int offs = 0; + + if (samples < 0 || step < 0 || (samples > 0 && step == 0)) + return NULL; spa_pod_dynamic_builder_init(&b, buffer, size, 4096); spa_pod_builder_push_sequence(&b.b, &f[0], 0); - spa_log_info(this->log, "generating ramp up sequence from %f to %f with a" - " step value %f at scale %d", start, end, volume_step, vrp->scale); - do { - float vas = get_volume_at_scale(vrp, volume_accum); - spa_log_trace(this->log, "volume accum %f", vas); - spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties); - spa_pod_builder_add_object(&b.b, - SPA_TYPE_OBJECT_Props, 0, - SPA_PROP_volume, SPA_POD_Float(vas)); - volume_accum += volume_step; - volume_offs += ramp_step_samples; - } while (volume_accum < end); - return spa_pod_builder_pop(&b.b, &f[0]); -} + spa_log_info(this->log, "generating ramp sequence from %f to %f with " + "step %d/%d at scale %d", start, end, step, samples, vrp->scale); -static struct spa_pod *generate_ramp_down_seq(struct impl *this, struct volume_ramp_params *vrp, - void *buffer, size_t size) -{ - struct spa_pod_dynamic_builder b; - struct spa_pod_frame f[1]; - int ramp_samples = get_ramp_samples(this, vrp); - int ramp_step_samples = get_ramp_step_samples(this, vrp); - float start = vrp->start, end = vrp->end, volume_accum = start; - float volume_step = ((start - end) / (ramp_samples / ramp_step_samples)); - uint32_t volume_offs = 0; + while (1) { + float pos = (samples == 0) ? end : + SPA_CLAMP(start + (end - start) * offs / samples, + SPA_MIN(start, end), SPA_MAX(start, end)); + float vas = get_volume_at_scale(vrp, pos); - spa_pod_dynamic_builder_init(&b, buffer, size, 4096); - - spa_pod_builder_push_sequence(&b.b, &f[0], 0); - spa_log_info(this->log, "generating ramp down sequence from %f to %f with a" - " step value %f at scale %d", start, end, volume_step, vrp->scale); - do { - float vas = get_volume_at_scale(vrp, volume_accum); - spa_log_trace(this->log, "volume accum %f", vas); - spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties); + spa_log_trace(this->log, "volume %d accum %f", offs, vas); + spa_pod_builder_control(&b.b, offs, SPA_CONTROL_Properties); spa_pod_builder_add_object(&b.b, SPA_TYPE_OBJECT_Props, 0, SPA_PROP_volume, SPA_POD_Float(vas)); - volume_accum -= volume_step; - volume_offs += ramp_step_samples; - } while (volume_accum > end); + if (offs >= samples) + break; + + offs = SPA_MIN(samples, offs + step); + } + return spa_pod_builder_pop(&b.b, &f[0]); } static void generate_volume_ramp(struct impl *this, struct volume_ramp_params *vrp, void *buffer, size_t size) { - void *sequence = NULL; - if (vrp->start == vrp->end) - spa_log_error(this->log, "no change in volume, cannot ramp volume"); - else if (vrp->end > vrp->start) - sequence = generate_ramp_up_seq(this, vrp, buffer, size); - else - sequence = generate_ramp_down_seq(this, vrp, buffer, size); + void *sequence; + sequence = generate_ramp_seq(this, vrp, buffer, size); if (!sequence) spa_log_error(this->log, "unable to generate sequence"); From 3d08c0557f99d21c7fba9dd532ee14d683e440e2 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 25 Oct 2025 15:14:42 +0300 Subject: [PATCH 36/43] properties: fix assign + conditional expression --- src/pipewire/properties.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pipewire/properties.c b/src/pipewire/properties.c index de81088b2..ac0aac0d0 100644 --- a/src/pipewire/properties.c +++ b/src/pipewire/properties.c @@ -252,7 +252,8 @@ static int update_string(struct pw_properties *props, const char *str, size_t si continue; } /* item changed or added, apply changes later */ - if ((errno = -add_item(&changes, key, false, val, true) < 0)) { + if ((res = add_item(&changes, key, false, val, true)) < 0) { + errno = -res; it[0].state = SPA_JSON_ERROR_FLAG; break; } From 8a23b13798e52ea39dc2eb92dd134bae95d6c6a4 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 26 Oct 2025 17:44:03 +0200 Subject: [PATCH 37/43] spa: param: pass correct struct size to spa_format_audio_raw_ext_parse/build --- spa/include/spa/param/audio/format-utils.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spa/include/spa/param/audio/format-utils.h b/spa/include/spa/param/audio/format-utils.h index 07f5d94e3..86ea84f75 100644 --- a/spa/include/spa/param/audio/format-utils.h +++ b/spa/include/spa/param/audio/format-utils.h @@ -59,7 +59,8 @@ spa_format_audio_ext_parse(const struct spa_pod *format, struct spa_audio_info * switch (info->media_subtype) { case SPA_MEDIA_SUBTYPE_raw: - return spa_format_audio_raw_ext_parse(format, &info->info.raw, size); + return spa_format_audio_raw_ext_parse(format, &info->info.raw, + size - offsetof(struct spa_audio_info, info.raw)); case SPA_MEDIA_SUBTYPE_dsp: return spa_format_audio_dsp_parse(format, &info->info.dsp); case SPA_MEDIA_SUBTYPE_iec958: @@ -110,7 +111,8 @@ spa_format_audio_ext_build(struct spa_pod_builder *builder, uint32_t id, { switch (info->media_subtype) { case SPA_MEDIA_SUBTYPE_raw: - return spa_format_audio_raw_ext_build(builder, id, &info->info.raw, size); + return spa_format_audio_raw_ext_build(builder, id, &info->info.raw, + size - offsetof(struct spa_audio_info, info.raw)); case SPA_MEDIA_SUBTYPE_dsp: return spa_format_audio_dsp_build(builder, id, &info->info.dsp); case SPA_MEDIA_SUBTYPE_iec958: From c6d0b364ab0657a66687a761b602100f823b8957 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 26 Oct 2025 17:51:01 +0200 Subject: [PATCH 38/43] spa: param: add size checks for spa_audio_info* structs In the API that take struct size for spa_audio_info*, also check the struct size. --- spa/include/spa/param/audio/format-utils.h | 51 +++++++- spa/include/spa/param/audio/format.h | 2 + spa/include/spa/param/audio/raw-json.h | 7 ++ spa/include/spa/param/audio/raw-utils.h | 8 ++ spa/include/spa/param/audio/raw.h | 2 + test/meson.build | 1 + test/test-spa-format.c | 129 +++++++++++++++++++++ 7 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 test/test-spa-format.c diff --git a/spa/include/spa/param/audio/format-utils.h b/spa/include/spa/param/audio/format-utils.h index 86ea84f75..24d06dd1e 100644 --- a/spa/include/spa/param/audio/format-utils.h +++ b/spa/include/spa/param/audio/format-utils.h @@ -46,18 +46,58 @@ extern "C" { #endif #endif +SPA_API_AUDIO_FORMAT_UTILS bool +spa_format_audio_ext_valid_size(uint32_t media_subtype, size_t size) +{ + switch (media_subtype) { + case SPA_MEDIA_SUBTYPE_raw: + return size >= offsetof(struct spa_audio_info, info.raw) && + SPA_AUDIO_INFO_RAW_VALID_SIZE(size - offsetof(struct spa_audio_info, info.raw)); + +#define _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(format) \ + case SPA_MEDIA_SUBTYPE_ ## format: \ + return size >= offsetof(struct spa_audio_info, info.format) + sizeof(struct spa_audio_info_ ## format); + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dsp) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(iec958) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dsd) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(mp3) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(aac) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(vorbis) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(wma) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ra) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(amr) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(alac) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(flac) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ape) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(ac3) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(eac3) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(truehd) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(dts) + _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE(mpegh) +#undef _SPA_FORMAT_AUDIO_EXT_VALID_SIZE_CASE + } + return false; +} + SPA_API_AUDIO_FORMAT_UTILS int spa_format_audio_ext_parse(const struct spa_pod *format, struct spa_audio_info *info, size_t size) { int res; + uint32_t media_type, media_subtype; - if ((res = spa_format_parse(format, &info->media_type, &info->media_subtype)) < 0) + if ((res = spa_format_parse(format, &media_type, &media_subtype)) < 0) return res; - if (info->media_type != SPA_MEDIA_TYPE_audio) + if (media_type != SPA_MEDIA_TYPE_audio) return -EINVAL; - switch (info->media_subtype) { + if (!spa_format_audio_ext_valid_size(media_subtype, size)) + return -EINVAL; + + info->media_type = media_type; + info->media_subtype = media_subtype; + + switch (media_subtype) { case SPA_MEDIA_SUBTYPE_raw: return spa_format_audio_raw_ext_parse(format, &info->info.raw, size - offsetof(struct spa_audio_info, info.raw)); @@ -109,6 +149,11 @@ SPA_API_AUDIO_FORMAT_UTILS struct spa_pod * spa_format_audio_ext_build(struct spa_pod_builder *builder, uint32_t id, const struct spa_audio_info *info, size_t size) { + if (!spa_format_audio_ext_valid_size(info->media_subtype, size)) { + errno = EINVAL; + return NULL; + } + switch (info->media_subtype) { case SPA_MEDIA_SUBTYPE_raw: return spa_format_audio_raw_ext_build(builder, id, &info->info.raw, diff --git a/spa/include/spa/param/audio/format.h b/spa/include/spa/param/audio/format.h index ac9b10dda..6e7a71f6c 100644 --- a/spa/include/spa/param/audio/format.h +++ b/spa/include/spa/param/audio/format.h @@ -59,6 +59,8 @@ struct spa_audio_info { struct spa_audio_info_dts dts; struct spa_audio_info_mpegh mpegh; } info; + + /* padding follows here when info has flexible size */ }; /** diff --git a/spa/include/spa/param/audio/raw-json.h b/spa/include/spa/param/audio/raw-json.h index e83c4495b..38fcb449c 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -60,6 +60,10 @@ spa_audio_info_raw_ext_update(struct spa_audio_info_raw *info, size_t size, { uint32_t v; uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size); + + if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size)) + return -EINVAL; + if (spa_streq(key, SPA_KEY_AUDIO_FORMAT)) { if (force || info->format == 0) info->format = (enum spa_audio_format)spa_type_audio_format_from_short_name(val); @@ -100,6 +104,9 @@ spa_audio_info_raw_ext_init_dict_keys_va(struct spa_audio_info_raw *info, size_t { int res; + if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size)) + return -EINVAL; + memset(info, 0, size); SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); if (dict) { diff --git a/spa/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index ce5e61e67..3bb94eaa3 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -35,6 +35,9 @@ spa_format_audio_raw_ext_parse(const struct spa_pod *format, struct spa_audio_in int res; uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size); + if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size)) + return -EINVAL; + info->flags = 0; res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, @@ -64,6 +67,11 @@ spa_format_audio_raw_ext_build(struct spa_pod_builder *builder, uint32_t id, struct spa_pod_frame f; uint32_t max_position = SPA_AUDIO_INFO_RAW_MAX_POSITION(size); + if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size)) { + errno = EINVAL; + return NULL; + } + spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, id); spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), diff --git a/spa/include/spa/param/audio/raw.h b/spa/include/spa/param/audio/raw.h index 392b65daa..bcc0a122d 100644 --- a/spa/include/spa/param/audio/raw.h +++ b/spa/include/spa/param/audio/raw.h @@ -293,6 +293,8 @@ struct spa_audio_info_raw { #define SPA_AUDIO_INFO_RAW_MAX_POSITION(size) (((size)-offsetof(struct spa_audio_info_raw,position))/sizeof(uint32_t)) +#define SPA_AUDIO_INFO_RAW_VALID_SIZE(size) ((size) >= offsetof(struct spa_audio_info_raw, position)) + #define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string, * Ex. "S16LE" */ diff --git a/test/meson.build b/test/meson.build index ad658bcc6..5e38db383 100644 --- a/test/meson.build +++ b/test/meson.build @@ -112,6 +112,7 @@ test('test-spa', executable('test-spa', 'test-spa-buffer.c', 'test-spa-control.c', + 'test-spa-format.c', 'test-spa-json.c', 'test-spa-utils.c', 'test-spa-log.c', diff --git a/test/test-spa-format.c b/test/test-spa-format.c new file mode 100644 index 000000000..7cbec7690 --- /dev/null +++ b/test/test-spa-format.c @@ -0,0 +1,129 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2025 Pauli Virtanen */ +/* SPDX-License-Identifier: MIT */ + +#include +#include + +#include "pwtest.h" + +PWTEST(audio_format_sizes) +{ + union { + uint8_t buf[1024]; + struct spa_audio_info align; + } data; + struct spa_audio_info info; + size_t i; + + memset(&info, 0xf3, sizeof(info)); + info.media_type = SPA_MEDIA_TYPE_audio; + info.media_subtype = SPA_MEDIA_SUBTYPE_raw; + info.info.raw.channels = 5; + info.info.raw.format = SPA_AUDIO_FORMAT_F32P; + info.info.raw.rate = 12345; + info.info.raw.flags = 0; + info.info.raw.position[0] = 1; + info.info.raw.position[1] = 2; + info.info.raw.position[2] = 3; + info.info.raw.position[3] = 4; + info.info.raw.position[4] = 5; + + for (i = 0; i < sizeof(data.buf); ++i) { + struct spa_pod *pod; + uint8_t buf[4096]; + struct spa_pod_builder b; + + spa_pod_builder_init(&b, buf, sizeof(buf)); + memcpy(data.buf, &info, sizeof(info)); + + pod = spa_format_audio_ext_build(&b, 123, (void *)data.buf, i); + if (i < offsetof(struct spa_audio_info, info.raw) + + offsetof(struct spa_audio_info_raw, position)) + pwtest_bool_true(!pod); + else + pwtest_bool_true(pod); + } + + for (i = 0; i < sizeof(data.buf); ++i) { + struct spa_pod *pod; + uint8_t buf[4096]; + struct spa_pod_builder b; + int ret; + + spa_pod_builder_init(&b, buf, sizeof(buf)); + pod = spa_format_audio_ext_build(&b, 123, &info, sizeof(info)); + pwtest_bool_true(pod); + + memset(data.buf, 0xf3, sizeof(data.buf)); + + ret = spa_format_audio_ext_parse(pod, (void *)data.buf, i); + if (i < offsetof(struct spa_audio_info, info.raw) + + offsetof(struct spa_audio_info_raw, position) + + info.info.raw.channels*sizeof(uint32_t)) { + for (size_t j = i; j < sizeof(data.buf); ++j) + pwtest_int_eq(data.buf[j], 0xf3); + pwtest_int_lt(ret, 0); + } else { + pwtest_int_ge(ret, 0); + pwtest_bool_true(memcmp(data.buf, &info, SPA_MIN(i, sizeof(info))) == 0); + } + } + + memset(&info, 0xf3, sizeof(info)); + info.media_type = SPA_MEDIA_TYPE_audio; + info.media_subtype = SPA_MEDIA_SUBTYPE_aac; + info.info.aac.rate = 12345; + info.info.aac.channels = 6; + info.info.aac.bitrate = 54321; + info.info.aac.stream_format = SPA_AUDIO_AAC_STREAM_FORMAT_MP4LATM; + + for (i = 0; i < sizeof(data.buf); ++i) { + struct spa_pod *pod; + uint8_t buf[4096]; + struct spa_pod_builder b; + + spa_pod_builder_init(&b, buf, sizeof(buf)); + memcpy(data.buf, &info, sizeof(info)); + + pod = spa_format_audio_ext_build(&b, 123, (void *)data.buf, i); + if (i < offsetof(struct spa_audio_info, info.raw) + + sizeof(struct spa_audio_info_aac)) + pwtest_bool_true(!pod); + else + pwtest_bool_true(pod); + } + + for (i = 0; i < sizeof(data.buf); ++i) { + struct spa_pod *pod; + uint8_t buf[4096]; + struct spa_pod_builder b; + int ret; + + spa_pod_builder_init(&b, buf, sizeof(buf)); + pod = spa_format_audio_ext_build(&b, 123, &info, sizeof(info)); + pwtest_bool_true(pod); + + memset(data.buf, 0xf3, sizeof(data.buf)); + + ret = spa_format_audio_ext_parse(pod, (void *)data.buf, i); + if (i < offsetof(struct spa_audio_info, info.raw) + + sizeof(struct spa_audio_info_aac)) { + for (size_t j = i; j < sizeof(data.buf); ++j) + pwtest_int_eq(data.buf[j], 0xf3); + pwtest_int_lt(ret, 0); + } else { + pwtest_int_ge(ret, 0); + pwtest_bool_true(memcmp(data.buf, &info, SPA_MIN(i, sizeof(info))) == 0); + } + } + + return PWTEST_PASS; +} + +PWTEST_SUITE(spa_format) +{ + pwtest_add(audio_format_sizes, PWTEST_NOARG); + + return PWTEST_PASS; +} From 614186a59076e8c857eaf7d94702b20e5f5030e9 Mon Sep 17 00:00:00 2001 From: Jonas Holmberg Date: Wed, 22 Oct 2025 13:50:24 +0200 Subject: [PATCH 39/43] module-echo-cancel: Sync capture and sink buffers Call process() when capture and sink ringbuffers contain data from the same graph cycle and only process the latest block from them to avoid adding latency that can accumulate if one of the streams gets more than one buffer before the other gets its first buffer when starting up. --- src/modules/module-echo-cancel.c | 92 +++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/src/modules/module-echo-cancel.c b/src/modules/module-echo-cancel.c index 997506e6f..037509c35 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -229,8 +229,10 @@ struct impl { struct spa_audio_aec *aec; uint32_t aec_blocksize; - unsigned int capture_ready:1; - unsigned int sink_ready:1; + struct spa_io_position *capture_position; + struct spa_io_position *sink_position; + uint32_t capture_cycle; + uint32_t sink_cycle; unsigned int do_disconnect:1; @@ -309,11 +311,17 @@ static void process(struct impl *impl) struct spa_data *dd; uint32_t i, size; uint32_t rindex, pindex, oindex, pdindex, avail; + int32_t pavail, pdavail; size = impl->aec_blocksize; - /* First read a block from the playback and capture ring buffers */ - spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); + /* First read a block from the capture ring buffer */ + avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); + while (avail > size) { + /* drop samples from previous graph cycles */ + spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); + avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); + } for (i = 0; i < impl->rec_info.channels; i++) { /* captured samples, with echo from sink */ @@ -331,19 +339,30 @@ static void process(struct impl *impl) out[i] = &out_buf[i][0]; } - spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); - spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); + pavail = spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); + pdavail = spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) { pw_log_debug("out of playback buffers: %m"); /* playback stream may not yet be in streaming state, drop play * data to avoid introducing additional playback latency */ - spa_ringbuffer_read_update(&impl->play_ring, pindex + size); - spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); + spa_ringbuffer_read_update(&impl->play_ring, pindex + pavail); + spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + pdavail); goto done; } + while (pavail > size) { + /* drop samples from previous graph cycles */ + spa_ringbuffer_read_update(&impl->play_ring, pindex + size); + pavail = spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); + } + while (pdavail > size) { + /* drop samples from previous graph cycles */ + spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); + pdavail = spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); + } + for (i = 0; i < impl->play_info.channels; i++) { /* echo from sink */ play[i] = &play_buf[i][0]; @@ -454,8 +473,8 @@ static void process(struct impl *impl) } done: - impl->sink_ready = false; - impl->capture_ready = false; + impl->capture_cycle = 0; + impl->sink_cycle = 0; } static void reset_buffers(struct impl *impl) @@ -479,8 +498,8 @@ static void reset_buffers(struct impl *impl) spa_ringbuffer_get_read_index(&impl->play_ring, &index); spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay))); - impl->sink_ready = false; - impl->capture_ready = false; + impl->capture_cycle = 0; + impl->sink_cycle = 0; } static void capture_destroy(void *d) @@ -546,8 +565,11 @@ static void capture_process(void *data) spa_ringbuffer_write_update(&impl->rec_ring, index + size); if (avail + size >= impl->aec_blocksize) { - impl->capture_ready = true; - if (impl->sink_ready) + if (impl->capture_position) + impl->capture_cycle = impl->capture_position->clock.cycle; + else + pw_log_warn("no capture position"); + if (impl->capture_cycle == impl->sink_cycle) process(impl); } @@ -740,12 +762,26 @@ static void input_param_changed(void *data, uint32_t id, const struct spa_pod* p } } +static void capture_io_changed(void *data, uint32_t id, void *area, uint32_t size) +{ + struct impl *impl = data; + + switch (id) { + case SPA_IO_Position: + impl->capture_position = area; + break; + default: + break; + } +} + static const struct pw_stream_events capture_events = { PW_VERSION_STREAM_EVENTS, .destroy = capture_destroy, .state_changed = capture_state_changed, .process = capture_process, - .param_changed = input_param_changed + .param_changed = input_param_changed, + .io_changed = capture_io_changed }; static void source_destroy(void *d) @@ -930,10 +966,15 @@ static void sink_process(void *data) SPA_PTROFF(d->data, offs, void), size); } spa_ringbuffer_write_update(&impl->play_ring, index + size); + spa_ringbuffer_get_write_index(&impl->play_delayed_ring, &index); + spa_ringbuffer_write_update(&impl->play_delayed_ring, index + size); if (avail + size >= impl->aec_blocksize) { - impl->sink_ready = true; - if (impl->capture_ready) + if (impl->sink_position) + impl->sink_cycle = impl->sink_position->clock.cycle; + else + pw_log_warn("no sink position"); + if (impl->capture_cycle == impl->sink_cycle) process(impl); } @@ -955,12 +996,27 @@ static const struct pw_stream_events playback_events = { .state_changed = playback_state_changed, .param_changed = output_param_changed }; + +static void sink_io_changed(void *data, uint32_t id, void *area, uint32_t size) +{ + struct impl *impl = data; + + switch (id) { + case SPA_IO_Position: + impl->sink_position = area; + break; + default: + break; + } +} + static const struct pw_stream_events sink_events = { PW_VERSION_STREAM_EVENTS, .destroy = sink_destroy, .process = sink_process, .state_changed = sink_state_changed, - .param_changed = output_param_changed + .param_changed = output_param_changed, + .io_changed = sink_io_changed }; #define MAX_PARAMS 512u From 0276bb5b063d8b34a55b1292c4a01c1f7b5c9cc4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Oct 2025 11:43:04 +0100 Subject: [PATCH 40/43] modules: ringbuffer avail is signed --- src/modules/module-echo-cancel.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/module-echo-cancel.c b/src/modules/module-echo-cancel.c index 037509c35..a4cdd2435 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -309,15 +309,15 @@ static void process(struct impl *impl) const float *play_delayed[impl->play_info.channels]; float *out[impl->out_info.channels]; struct spa_data *dd; - uint32_t i, size; - uint32_t rindex, pindex, oindex, pdindex, avail; - int32_t pavail, pdavail; + uint32_t i; + uint32_t rindex, pindex, oindex, pdindex, size; + int32_t avail, pavail, pdavail; size = impl->aec_blocksize; /* First read a block from the capture ring buffer */ avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); - while (avail > size) { + while (avail > (int32_t)size) { /* drop samples from previous graph cycles */ spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); @@ -352,12 +352,12 @@ static void process(struct impl *impl) goto done; } - while (pavail > size) { + while (pavail > (int32_t)size) { /* drop samples from previous graph cycles */ spa_ringbuffer_read_update(&impl->play_ring, pindex + size); pavail = spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); } - while (pdavail > size) { + while (pdavail > (int32_t)size) { /* drop samples from previous graph cycles */ spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); pdavail = spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); @@ -450,7 +450,7 @@ static void process(struct impl *impl) * available on the source */ avail = spa_ringbuffer_get_read_index(&impl->out_ring, &oindex); - while (avail >= size) { + while (avail >= (int32_t)size) { if ((cout = pw_stream_dequeue_buffer(impl->source)) != NULL) { for (i = 0; i < impl->out_info.channels; i++) { dd = &cout->buffer->datas[i]; From 94d0d8bc095b001f36f4e27c23d22c7ce4aca8ca Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Oct 2025 13:32:03 +0100 Subject: [PATCH 41/43] spa: add spa_json_init_relax spa_json_init assumes that we start in an object and always requires a key/value pair. If the last part is a key, it returns and error and does not want to return the key value. This causes problems when parsing AUX0,AUX1,AUX2 or any relaxed array withand odd number of elements. Make a new spa_json_init_relax that takes the type of the container we're assuming we're in and set the state of the parser to array when we are parsing a relaxed array. Fixes #4944 --- spa/include/spa/utils/json-core.h | 9 +++++++++ spa/include/spa/utils/json.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/spa/include/spa/utils/json-core.h b/spa/include/spa/utils/json-core.h index 800763571..5616bffe1 100644 --- a/spa/include/spa/utils/json-core.h +++ b/spa/include/spa/utils/json-core.h @@ -54,6 +54,15 @@ SPA_API_JSON void spa_json_init(struct spa_json * iter, const char *data, size_t { *iter = SPA_JSON_INIT(data, size); } + +#define SPA_JSON_INIT_RELAX(type,data,size) \ + ((struct spa_json) { (data), (data)+(size), NULL, (uint32_t)((type) == '[' ? 0x10 : 0x0), 0 }) + +SPA_API_JSON void spa_json_init_relax(struct spa_json * iter, char type, const char *data, size_t size) +{ + *iter = SPA_JSON_INIT_RELAX(type, data, size); +} + #define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), (iter)->state & 0xff0, 0 }) SPA_API_JSON void spa_json_enter(struct spa_json * iter, struct spa_json * sub) diff --git a/spa/include/spa/utils/json.h b/spa/include/spa/utils/json.h index c8030345e..212637dab 100644 --- a/spa/include/spa/utils/json.h +++ b/spa/include/spa/utils/json.h @@ -105,7 +105,7 @@ SPA_API_JSON_UTILS int spa_json_begin_container(struct spa_json * iter, spa_json_init(iter, data, size); res = spa_json_enter_container(iter, iter, type); if (res == -EPROTO && relax) - spa_json_init(iter, data, size); + spa_json_init_relax(iter, type, data, size); else if (res <= 0) return res; return 1; From 23c449af5d6afc8dde0685d61e6ed11d89b09000 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Oct 2025 14:20:25 +0100 Subject: [PATCH 42/43] test: add test for an array with odd number of items We have to use the relax version to get the expected container type correct. --- test/test-spa-json.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test-spa-json.c b/test/test-spa-json.c index 66ef2eeac..0c3c46f59 100644 --- a/test/test-spa-json.c +++ b/test/test-spa-json.c @@ -609,7 +609,7 @@ static void test_array(const char *str, const char * const vals[]) spa_json_init(&it[0], str, strlen(str)); if (spa_json_enter_array(&it[0], &it[1]) <= 0) - spa_json_init(&it[1], str, strlen(str)); + spa_json_init_relax(&it[1], '[', str, strlen(str)); for (i = 0; vals[i]; i++) { pwtest_int_gt(spa_json_get_string(&it[1], val, sizeof(val)), 0); pwtest_str_eq(val, vals[i]); @@ -624,6 +624,7 @@ PWTEST(json_array) test_array("[FL FR]", (const char *[]){ "FL", "FR", NULL }); test_array("FL FR", (const char *[]){ "FL", "FR", NULL }); test_array("[ FL FR ]", (const char *[]){ "FL", "FR", NULL }); + test_array("FL FR FC", (const char *[]){ "FL", "FR", "FC", NULL }); return PWTEST_PASS; } From 76a31a47c2cf063abc70c29f42e60fb0fa03dee5 Mon Sep 17 00:00:00 2001 From: Jonas Holmberg Date: Mon, 27 Oct 2025 14:37:03 +0100 Subject: [PATCH 43/43] module-echo-cancel: Avoid discontinuity Keep the samples in the ringbuffer that are needed the next cycle to avoid discontinuity when the aec blocksize is not equal to or divisible by quantum. --- src/modules/module-echo-cancel.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/modules/module-echo-cancel.c b/src/modules/module-echo-cancel.c index a4cdd2435..98efa35c5 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -317,10 +317,15 @@ static void process(struct impl *impl) /* First read a block from the capture ring buffer */ avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); - while (avail > (int32_t)size) { - /* drop samples from previous graph cycles */ + while (avail >= (int32_t)size * 2) { + /* drop samples that are not needed this or next cycle. Note + * that samples are kept in the ringbuffer until next cycle if + * size is not equal to or divisible by quantum, to avoid + * discontinuity */ + pw_log_debug("avail %d", avail); spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); avail = spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex); + pw_log_debug("new avail %d, size %u", avail, size); } for (i = 0; i < impl->rec_info.channels; i++) { @@ -352,15 +357,19 @@ static void process(struct impl *impl) goto done; } - while (pavail > (int32_t)size) { - /* drop samples from previous graph cycles */ - spa_ringbuffer_read_update(&impl->play_ring, pindex + size); + if (pavail > avail) { + /* drop too old samples from previous graph cycles */ + pw_log_debug("pavail %d, dropping %d", pavail, pavail - avail); + spa_ringbuffer_read_update(&impl->play_ring, pindex + pavail - avail); pavail = spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); + pw_log_debug("new pavail %d, avail %d", pavail, avail); } - while (pdavail > (int32_t)size) { - /* drop samples from previous graph cycles */ - spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); + if (pdavail > avail) { + /* drop too old samples from previous graph cycles */ + pw_log_debug("pdavail %d, dropping %d", pdavail, pdavail - avail); + spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + pdavail - avail); pdavail = spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); + pw_log_debug("new pdavail %d, avail %d", pdavail, avail); } for (i = 0; i < impl->play_info.channels; i++) {