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++ diff --git a/meson.build b/meson.build index e0a333a8f..e82ba03b3 100644 --- a/meson.build +++ b/meson.build @@ -115,10 +115,11 @@ 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') - -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/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index 27495a296..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,29 +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]); - if (i < max_position) - position[i] = pos; + char buf[8]; + position[i] = chmap_to_channel(map->pos[i]); 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(position[i], + buf, sizeof(buf), "UNK")); } return 1; } @@ -938,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; @@ -959,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/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); 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/format-utils.h b/spa/include/spa/param/audio/format-utils.h index d608a4bdf..24d06dd1e 100644 --- a/spa/include/spa/param/audio/format-utils.h +++ b/spa/include/spa/param/audio/format-utils.h @@ -46,20 +46,61 @@ 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_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; + 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_parse(format, &info->info.raw); + 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: @@ -98,13 +139,25 @@ spa_format_audio_parse(const struct spa_pod *format, struct spa_audio_info *info return -ENOTSUP; } -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_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_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_build(builder, id, &info->info.raw); + 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: @@ -143,6 +196,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/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/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 edbb715d1..38fcb449c 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,24 +38,32 @@ 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; } + 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 -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_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); @@ -63,42 +71,88 @@ 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)) - info->channels = SPA_MIN(v, SPA_N_ELEMENTS(info->position)); + 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; + info->channels = v; 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; + + 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) { + 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-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/include/spa/param/audio/raw-utils.h b/spa/include/spa/param/audio/raw-utils.h index 8a9542c2e..3bb94eaa3 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -28,40 +28,16 @@ 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_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); + + if (!SPA_AUDIO_INFO_RAW_VALID_SIZE(size)) + return -EINVAL; + info->flags = 0; res = spa_pod_parse_object(format, SPA_TYPE_OBJECT_Format, NULL, @@ -69,18 +45,33 @@ 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 -ECHRNG; 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 int +spa_format_audio_raw_parse(const struct spa_pod *format, struct spa_audio_info_raw *info) +{ + return spa_format_audio_raw_ext_parse(format, info, sizeof(*info)); +} + 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_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); + + 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), @@ -95,16 +86,25 @@ 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)) { + /* 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, SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, - SPA_MIN(info->channels, SPA_N_ELEMENTS(info->position)), - info->position), 0); + info->channels, info->position), 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..bcc0a122d 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,19 +275,27 @@ 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 larger than - * 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 */ + 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_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__ }) +#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" */ #define SPA_KEY_AUDIO_CHANNEL "audio.channel" /**< an audio channel as string, 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; 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 - - - - - - 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..2b13d2528 100644 --- a/spa/plugins/alsa/acp/channelmap.h +++ b/spa/plugins/alsa/acp/channelmap.h @@ -27,7 +27,11 @@ extern "C" { #endif +#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) @@ -451,7 +455,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 +463,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 bad6da9fe..4f2fde8b6 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -240,35 +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; - int r; - 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])); - if (r < 0 || o + r >= len) - return -ENOSPC; - o += r; + uint32_t i; + char pos[8]; + struct spa_strbuf b; + + spa_strbuf_init(&b, val, len); + spa_strbuf_append(&b, "["); + for (i = 0; i < map->n_pos; i++) { + spa_strbuf_append(&b, "%s%s", i == 0 ? " " : ", ", + spa_type_audio_channel_make_short_name(map->pos[i], + pos, sizeof(pos), "UNK")); } - 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; } @@ -775,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) @@ -1645,7 +1643,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 +1653,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/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; diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index dd58ee9de..f201522f8 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 | @@ -1150,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]; @@ -1165,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); } } @@ -1182,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) { @@ -1264,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; @@ -1273,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) { @@ -1281,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; @@ -1543,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; } @@ -1555,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; } @@ -1573,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"); @@ -1899,7 +1866,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, @@ -2060,16 +2027,16 @@ 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++) { 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); + pi = src_info.info.raw.position[i]; + pj = dst_info.info.raw.position[j]; if (pi != pj) continue; in->remap[i] = j; @@ -2077,9 +2044,9 @@ 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_format_audio_raw_set_position(&dst_info.info.raw, j, -1); + spa_type_audio_channel_make_short_name(pi, b1, 8, "UNK"), + spa_type_audio_channel_make_short_name(pj, b2, 8, "UNK")); + dst_info.info.raw.position[j] = -1; break; } } @@ -2127,7 +2094,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) @@ -2143,7 +2110,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; @@ -2189,17 +2156,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_debug_type_find_short_name(spa_type_audio_channel, - 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,19 +2180,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 % max_position]; + p = position[i]; 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); } 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,16 +2319,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, 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++) { 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); + pi = src_info.info.raw.position[i]; + pj = dst_info.info.raw.position[j]; if (pi != pj) continue; out->remap[i] = j; @@ -2370,10 +2337,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, 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); + dst_info.info.raw.position[j] = -1; break; } } 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[], 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, 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/bluez5/a2dp-codec-opus.c b/spa/plugins/bluez5/a2dp-codec-opus.c index 7a330e8e0..c5b042b20 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; @@ -497,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); @@ -545,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; } @@ -785,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); @@ -838,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; @@ -931,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-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..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 }; @@ -475,7 +474,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) @@ -686,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/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c index bb7f24831..9271ace34 100644 --- a/spa/plugins/filter-graph/filter-graph.c +++ b/spa/plugins/filter-graph/filter-graph.c @@ -232,16 +232,18 @@ 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; + char pos[8]; 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_type_audio_channel_make_short_name(positions[i], + pos, sizeof(pos), "UNK")); } spa_strbuf_append(&buf, "]"); } diff --git a/spa/plugins/v4l2/v4l2-device.c b/spa/plugins/v4l2/v4l2-device.c index 2379d2105..4257a01c1 100644 --- a/spa/plugins/v4l2/v4l2-device.c +++ b/spa/plugins/v4l2/v4l2-device.c @@ -231,6 +231,7 @@ impl_init(const struct spa_handle_factory *factory, handle->clear = impl_clear, this = (struct impl *) handle; this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + v4l2_log_topic_init(this->log); spa_hook_list_init(&this->hooks); diff --git a/spa/plugins/v4l2/v4l2-udev.c b/spa/plugins/v4l2/v4l2-udev.c index df99f7b4a..85bda2bc0 100644 --- a/spa/plugins/v4l2/v4l2-udev.c +++ b/spa/plugins/v4l2/v4l2-udev.c @@ -775,6 +775,8 @@ impl_init(const struct spa_handle_factory *factory, #endif this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + v4l2_log_topic_init(this->log); + this->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); if (this->main_loop == NULL) { diff --git a/src/modules/module-combine-stream.c b/src/modules/module-combine-stream.c index a9dec8c2b..29f57cb50 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)), @@ -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; @@ -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..98efa35c5 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; @@ -307,13 +309,24 @@ 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; + 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 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 >= (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++) { /* captured samples, with echo from sink */ @@ -331,19 +344,34 @@ 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; } + 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); + } + 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++) { /* echo from sink */ play[i] = &play_buf[i][0]; @@ -431,7 +459,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]; @@ -454,8 +482,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 +507,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 +574,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 +771,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 +975,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 +1005,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 @@ -1203,9 +1268,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 +1356,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..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; } @@ -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..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); } @@ -1833,9 +1832,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 +1927,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 0e1b3c3fb..637001423 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,8 @@ 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( + s->info.position[i], pos, sizeof(pos), NULL); if (str) snprintf(name, sizeof(name), "%s_%s", prefix, str); else @@ -1051,9 +1052,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)), @@ -1176,8 +1177,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..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]; @@ -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 5e3149b41..485570064 100644 --- a/src/modules/module-netjack2-driver.c +++ b/src/modules/module-netjack2-driver.c @@ -441,11 +441,11 @@ 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( + 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 ? str : "UNK", + PW_KEY_AUDIO_CHANNEL, str, PW_KEY_PORT_PHYSICAL, "true", NULL); @@ -865,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) { @@ -877,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; @@ -1220,9 +1218,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, @@ -1338,8 +1336,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 454632c8f..615a4553b 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; @@ -601,12 +601,11 @@ 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( + 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 ? str : "UNK", + PW_KEY_AUDIO_CHANNEL, str, PW_KEY_PORT_PHYSICAL, "true", NULL); @@ -969,8 +968,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 ? @@ -1028,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) { @@ -1292,9 +1292,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-pulse/format.c b/src/modules/module-protocol-pulse/format.c index f92e29e78..24ef024f9 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) @@ -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/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..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; @@ -283,14 +283,14 @@ 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(info->position[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-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..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()); @@ -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..941fa1acd 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)), @@ -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; } @@ -274,11 +274,14 @@ 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 (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 ((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); + 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) { 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/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; } 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); diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index 52789d421..632a513bf 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")); } } @@ -2347,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; 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; +} 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; }