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/doc/dox/api/spa-pod.dox b/doc/dox/api/spa-pod.dox index 67ee20750..76d7266ee 100644 --- a/doc/dox/api/spa-pod.dox +++ b/doc/dox/api/spa-pod.dox @@ -435,10 +435,14 @@ spa_pod_parser_get_object(&p, \endcode `spa_pod_get_values()` is a useful function. It returns a -`struct spa_pod*` with and array of values. For normal POD's -and choice none values, it simply returns the POD and one value. -For other choice values it returns the choice type and an array -of values: +`struct spa_pod*` with and array of values. For invalid PODs +it returns the POD and no values. For normal PODs it returns +the POD and one value. For choice values it returns the choice +type and an array of values. If the choice doesn't fit even a +single value, the array will have no values. If the choice is +of the `SPA_CHOICE_None` or the length of each element is +misaligned, the array will have one value. Otherwise, +the array will have the same length as the choice. \code{.c} struct spa_pod *value; 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/ctl_pipewire.c b/pipewire-alsa/alsa-plugins/ctl_pipewire.c index 7fcfd5726..36bc4be5e 100644 --- a/pipewire-alsa/alsa-plugins/ctl_pipewire.c +++ b/pipewire-alsa/alsa-plugins/ctl_pipewire.c @@ -22,9 +22,11 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.ctl"); #define VOLUME_MIN ((uint32_t) 0U) #define VOLUME_MAX ((uint32_t) 0x10000U) +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + struct volume { uint32_t channels; - long values[SPA_AUDIO_MAX_CHANNELS]; + long values[MAX_CHANNELS]; }; typedef struct { @@ -498,7 +500,7 @@ static struct spa_pod *build_volume_mute(struct spa_pod_builder *b, struct volum spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); if (volume) { - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; uint32_t i, n_volumes = 0; n_volumes = volume->channels; @@ -850,11 +852,11 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi break; case SPA_PROP_channelVolumes: { - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; uint32_t n_volumes, i; n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - volumes, SPA_AUDIO_MAX_CHANNELS); + volumes, SPA_N_ELEMENTS(volumes)); g->node.channel_volume.channels = n_volumes; for (i = 0; i < n_volumes; i++) diff --git a/pipewire-alsa/alsa-plugins/pcm_pipewire.c b/pipewire-alsa/alsa-plugins/pcm_pipewire.c index 4b92bbb13..24a66eabc 100644 --- a/pipewire-alsa/alsa-plugins/pcm_pipewire.c +++ b/pipewire-alsa/alsa-plugins/pcm_pipewire.c @@ -31,6 +31,7 @@ PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.pcm"); #define MAX_BUFFERS 64u #define MAX_RATE (48000*8) +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MIN_PERIOD 64 @@ -642,7 +643,7 @@ static int snd_pcm_pipewire_pause(snd_pcm_ioplug_t * io, int enable) #define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE #endif -static int set_default_channels(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS]) +static int set_default_channels(uint32_t channels, uint32_t position[MAX_CHANNELS]) { switch (channels) { case 8: @@ -915,12 +916,16 @@ static int snd_pcm_pipewire_set_chmap(snd_pcm_ioplug_t * io, default: return -EINVAL; } + if (map->channels > MAX_CHANNELS) + return -ENOTSUP; + for (i = 0; i < map->channels; i++) { + 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, - position[i])); + spa_type_audio_channel_make_short_name(position[i], + buf, sizeof(buf), "UNK")); } return 1; } @@ -1097,7 +1102,7 @@ struct param_info infos[] = { { "alsa.rate", SND_PCM_IOPLUG_HW_RATE, TYPE_MIN_MAX, { 1, MAX_RATE }, 2, collect_int }, { "alsa.channels", SND_PCM_IOPLUG_HW_CHANNELS, TYPE_MIN_MAX, - { 1, SPA_AUDIO_MAX_CHANNELS }, 2, collect_int }, + { 1, MAX_CHANNELS }, 2, collect_int }, { "alsa.buffer-bytes", SND_PCM_IOPLUG_HW_BUFFER_BYTES, TYPE_MIN_MAX, { MIN_BUFFER_BYTES, MAX_BUFFER_BYTES }, 2, collect_int }, { "alsa.period-bytes", SND_PCM_IOPLUG_HW_PERIOD_BYTES, TYPE_MIN_MAX, diff --git a/pipewire-v4l2/src/pipewire-v4l2.c b/pipewire-v4l2/src/pipewire-v4l2.c index 8fc07151a..e8efa60e5 100644 --- a/pipewire-v4l2/src/pipewire-v4l2.c +++ b/pipewire-v4l2/src/pipewire-v4l2.c @@ -2255,8 +2255,8 @@ static int vidioc_queryctrl(struct file *file, struct v4l2_queryctrl *arg) // check type and populate range pod = spa_pod_get_values(type, &n_vals, &choice); - if (spa_pod_is_int(pod)) { - if (n_vals < 4) + if (pod->type == SPA_TYPE_Int) { + if (pod->size != sizeof(int) || n_vals < 4) break; arg->type = V4L2_CTRL_TYPE_INTEGER; int *v = SPA_POD_BODY(pod); @@ -2330,7 +2330,7 @@ static int vidioc_g_ctrl(struct file *file, struct v4l2_control *arg) if (ctrl_id == arg->id) { // TODO: support getting true ctrl values instead of defaults pod = spa_pod_get_values(type, &n_vals, &choice); - if (spa_pod_is_int(pod)) { + if (pod->type == SPA_TYPE_Int) { if (n_vals < 4) break; int *v = SPA_POD_BODY(pod); 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/debug/format.h b/spa/include/spa/debug/format.h index 874dd094a..1448ae223 100644 --- a/spa/include/spa/debug/format.h +++ b/spa/include/spa/debug/format.h @@ -164,8 +164,7 @@ SPA_API_DEBUG_FORMAT int spa_debugc_format(struct spa_debug_context *ctx, int in type = val->type; size = val->size; - if (type < SPA_TYPE_None || type >= _SPA_TYPE_LAST || n_vals < 1 || - size < spa_pod_type_size(type)) + if (type < SPA_TYPE_None || type >= _SPA_TYPE_LAST || n_vals < 1) continue; vals = SPA_POD_BODY(val); diff --git a/spa/include/spa/param/audio/dsd-utils.h b/spa/include/spa/param/audio/dsd-utils.h index 980bdf971..2d99e79f2 100644 --- a/spa/include/spa/param/audio/dsd-utils.h +++ b/spa/include/spa/param/audio/dsd-utils.h @@ -42,7 +42,7 @@ spa_format_audio_dsd_parse(const struct spa_pod *format, struct spa_audio_info_d SPA_FORMAT_AUDIO_channels, SPA_POD_OPT_Int(&info->channels), SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)); if (position == NULL || - !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS)) + !spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_N_ELEMENTS(info->position))) SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED); return res; diff --git a/spa/include/spa/param/audio/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/raw-json.h b/spa/include/spa/param/audio/raw-json.h index 92d7a1aad..38fcb449c 100644 --- a/spa/include/spa/param/audio/raw-json.h +++ b/spa/include/spa/param/audio/raw-json.h @@ -28,8 +28,8 @@ extern "C" { #endif SPA_API_AUDIO_RAW_JSON int -spa_audio_parse_position(const char *str, size_t len, - uint32_t *position, uint32_t *n_channels) +spa_audio_parse_position_n(const char *str, size_t len, + uint32_t *position, uint32_t max_position, uint32_t *n_channels) { struct spa_json iter; char v[256]; @@ -38,18 +38,32 @@ spa_audio_parse_position(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 < SPA_AUDIO_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_info_raw_update(struct spa_audio_info_raw *info, const char *key, const char *val, bool force) +spa_audio_parse_position(const char *str, size_t len, + uint32_t *position, uint32_t *n_channels) +{ + return spa_audio_parse_position_n(str, len, position, SPA_AUDIO_MAX_CHANNELS, n_channels); +} + +SPA_API_AUDIO_RAW_JSON int +spa_audio_info_raw_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); @@ -57,41 +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_AUDIO_MAX_CHANNELS); + 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(val, strlen(val), info->position, &info->channels) > 0) + if (spa_audio_parse_position_n(val, strlen(val), info->position, + 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 c36491445..3bb94eaa3 100644 --- a/spa/include/spa/param/audio/raw-utils.h +++ b/spa/include/spa/param/audio/raw-utils.h @@ -29,10 +29,15 @@ extern "C" { #endif 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, @@ -40,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_AUDIO_MAX_CHANNELS)) + 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), @@ -66,7 +86,10 @@ 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, info->channels, info->position), 0); @@ -75,6 +98,13 @@ spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, 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 2fd7fcfe3..bcc0a122d 100644 --- a/spa/include/spa/param/audio/raw.h +++ b/spa/include/spa/param/audio/raw.h @@ -18,7 +18,11 @@ extern "C" { * \{ */ -#define SPA_AUDIO_MAX_CHANNELS 128u +/* 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 64u +#endif enum spa_audio_format { SPA_AUDIO_FORMAT_UNKNOWN, @@ -259,6 +263,8 @@ enum spa_audio_channel { SPA_AUDIO_CHANNEL_START_Custom = 0x10000, }; +#define SPA_AUDIO_CHANNEL_IS_AUX(ch) ((ch)>=SPA_AUDIO_CHANNEL_START_Aux && (ch)<=SPA_AUDIO_CHANNEL_LAST_Aux) + enum spa_audio_volume_ramp_scale { SPA_AUDIO_VOLUME_RAMP_INVALID, SPA_AUDIO_VOLUME_RAMP_LINEAR, @@ -269,17 +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 */ + 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/pod/body.h b/spa/include/spa/pod/body.h index 07758c7e6..901537f81 100644 --- a/spa/include/spa/pod/body.h +++ b/spa/include/spa/pod/body.h @@ -78,6 +78,65 @@ SPA_API_POD_BODY uint32_t spa_pod_type_size(uint32_t type) return 0; } +SPA_API_POD_BODY uint32_t spa_pod_type_align(uint32_t type) +{ + switch (type) { + case SPA_TYPE_None: + case SPA_TYPE_Bytes: + case SPA_TYPE_String: + case SPA_TYPE_Bitmap: + return 1; + case SPA_TYPE_Bool: + case SPA_TYPE_Int: + return SPA_ALIGNOF(int32_t); + case SPA_TYPE_Id: + return SPA_ALIGNOF(uint32_t); + case SPA_TYPE_Long: + return SPA_ALIGNOF(int64_t); + case SPA_TYPE_Float: + return SPA_ALIGNOF(float); + case SPA_TYPE_Double: + return SPA_ALIGNOF(double); + case SPA_TYPE_Rectangle: + return SPA_ALIGNOF(struct spa_rectangle); + case SPA_TYPE_Fraction: + return SPA_ALIGNOF(struct spa_fraction); + case SPA_TYPE_Pointer: + return SPA_ALIGNOF(struct spa_pod_pointer_body); + case SPA_TYPE_Fd: + return SPA_ALIGNOF(int64_t); + case SPA_TYPE_Sequence: + case SPA_TYPE_Choice: + case SPA_TYPE_Struct: + case SPA_TYPE_Pod: + case SPA_TYPE_Object: + case SPA_TYPE_Array: + default: + return SPA_POD_ALIGN; + } +} + +SPA_API_POD_BODY uint32_t spa_pod_choice_min_values(uint32_t choice_type) +{ + switch (choice_type) { + case SPA_CHOICE_Enum: + return 2; + case SPA_CHOICE_Range: + return 3; + case SPA_CHOICE_Step: + return 4; + case SPA_CHOICE_None: + case SPA_CHOICE_Flags: + default: + /* + * This must always return at least 1, because callers + * assume that n_vals >= spa_pod_choice_min_values() + * mean that n_vals is at least 1. + */ + return 1; + } +} + SPA_API_POD_BODY int spa_pod_body_from_data(void *data, size_t maxsize, off_t offset, size_t size, struct spa_pod *pod, const void **body) { @@ -326,13 +385,22 @@ SPA_API_POD_BODY int spa_pod_body_get_array(const struct spa_pod *pod, const voi *arr_body = SPA_PTROFF(body, sizeof(struct spa_pod_array_body), void); return 0; } -SPA_API_POD_BODY const void *spa_pod_array_body_get_values(const struct spa_pod_array *arr, +SPA_API_POD_BODY const void *spa_pod_array_body_get_values(const struct spa_pod_array *pod, const void *body, uint32_t *n_values, uint32_t *val_size, uint32_t *val_type) { - uint32_t child_size = arr->body.child.size; - *n_values = child_size ? (arr->pod.size - sizeof(arr->body)) / child_size : 0; - *val_size = child_size; - *val_type = arr->body.child.type; + uint32_t child_size = *val_size = pod->body.child.size; + uint32_t child_type = *val_type = pod->body.child.type; + uint32_t child_type_size = spa_pod_type_size(child_size); + uint32_t max_body_size = pod->pod.size - sizeof(pod->body); + + if (SPA_UNLIKELY(child_size < child_type_size) || + child_size == 0) { + *n_values = 0; + } else if (SPA_UNLIKELY(child_size != child_type_size) && + SPA_UNLIKELY((child_size & (spa_pod_type_align(child_type) - 1)) != 0)) + *n_values = max_body_size >= child_size ? 1u : 0u; + else + *n_values = max_body_size / child_size; return body; } @@ -366,13 +434,23 @@ SPA_API_POD_BODY const void *spa_pod_choice_body_get_values(const struct spa_pod const void *body, uint32_t *n_values, uint32_t *choice, uint32_t *val_size, uint32_t *val_type) { - uint32_t child_size = pod->body.child.size; - *val_size = child_size; - *val_type = pod->body.child.type; - *n_values = child_size ? (pod->pod.size - sizeof(pod->body)) / child_size : 0; - *choice = pod->body.type; - if (*choice == SPA_CHOICE_None) - *n_values = SPA_MIN(1u, *n_values); + /* precondition check */ + spa_assert_se(pod->pod.size >= sizeof(pod->body)); + uint32_t child_size = *val_size = pod->body.child.size; + uint32_t child_type = *val_type = pod->body.child.type; + uint32_t child_type_size = spa_pod_type_size(child_size); + uint32_t choice_type = *choice = pod->body.type; + uint32_t max_body_size = pod->pod.size - sizeof(pod->body); + + if (SPA_UNLIKELY(child_size < child_type_size) || + child_size == 0) { + *n_values = 0; + } else if (choice_type == SPA_CHOICE_None || + (SPA_UNLIKELY(child_size != child_type_size) && + SPA_UNLIKELY((child_size & (spa_pod_type_align(child_type) - 1)) != 0))) + *n_values = max_body_size >= child_size ? 1 : 0; + else + *n_values = max_body_size / child_size; return body; } diff --git a/spa/include/spa/pod/compare.h b/spa/include/spa/pod/compare.h index 144bd4a5a..708568e91 100644 --- a/spa/include/spa/pod/compare.h +++ b/spa/include/spa/pod/compare.h @@ -226,6 +226,8 @@ SPA_API_POD_COMPARE int spa_pod_compare_is_in_range(uint32_t type, const void *v SPA_API_POD_COMPARE int spa_pod_compare_is_valid_choice(uint32_t type, uint32_t size, const void *val, const void *vals, uint32_t n_vals, uint32_t choice) { + if (n_vals < spa_pod_choice_min_values(choice)) + return 0; switch (choice) { case SPA_CHOICE_None: if (spa_pod_compare_value(type, val, vals, size) == 0) diff --git a/spa/include/spa/pod/filter.h b/spa/include/spa/pod/filter.h index 6e238c2e6..8870a8a88 100644 --- a/spa/include/spa/pod/filter.h +++ b/spa/include/spa/pod/filter.h @@ -82,8 +82,9 @@ spa_pod_filter_prop(struct spa_pod_builder *b, v1 = spa_pod_get_values(&p1->value, &nalt1, &p1c); v2 = spa_pod_get_values(&p2->value, &nalt2, &p2c); - /* empty choices */ - if (nalt1 < 1 || nalt2 < 1) + /* empty or bogus choices */ + if (nalt1 < spa_pod_choice_min_values(p1c) || + nalt2 < spa_pod_choice_min_values(p2c)) return -EINVAL; alt1 = SPA_POD_BODY(v1); @@ -95,8 +96,6 @@ spa_pod_filter_prop(struct spa_pod_builder *b, /* incompatible property types */ if (type != v2->type || size != v2->size || p1->key != p2->key) return -EINVAL; - if (size < spa_pod_type_size(type)) - return -EINVAL; /* start with copying the property */ spa_pod_builder_prop(b, p1->key, p1->flags & p2->flags); @@ -406,7 +405,7 @@ SPA_API_POD_FILTER int spa_pod_filter_object_make(struct spa_pod_object *pod) struct spa_pod *v = spa_pod_get_values(&res->value, &nvals, &choice); const void *vals = SPA_POD_BODY(v); - if (v->size < spa_pod_type_size(v->type)) + if (nvals < spa_pod_choice_min_values(choice)) continue; if (spa_pod_compare_is_valid_choice(v->type, v->size, diff --git a/spa/include/spa/pod/iter.h b/spa/include/spa/pod/iter.h index a0da580ea..d3d580475 100644 --- a/spa/include/spa/pod/iter.h +++ b/spa/include/spa/pod/iter.h @@ -228,7 +228,7 @@ SPA_API_POD_ITER struct spa_pod *spa_pod_get_values(const struct spa_pod *pod, spa_pod_choice_body_get_values(p, SPA_POD_BODY_CONST(p), n_vals, choice, &size, &type); return (struct spa_pod*)&p->body.child; } else { - *n_vals = 1; + *n_vals = (pod->size >= spa_pod_type_size(pod->type) ? 1u : 0u); *choice = SPA_CHOICE_None; return (struct spa_pod*)pod; } 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 b2e06f67f..44342a7a3 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -38,6 +38,7 @@ extern struct spa_i18n *acp_i18n; #define MAX_POLL 16 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DEFAULT_DEVICE "hw:0" #define DEFAULT_AUTO_PROFILE true @@ -155,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 positions[SPA_AUDIO_MAX_CHANNELS * 12]; + 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; @@ -199,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) { @@ -673,8 +677,8 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct struct spa_pod_prop *prop; struct spa_pod_object *obj = (struct spa_pod_object *) props; int changed = 0; - float volumes[SPA_AUDIO_MAX_CHANNELS]; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; uint32_t n_volumes = 0; if (!spa_pod_is_object_type(props, SPA_TYPE_OBJECT_Props)) @@ -696,13 +700,13 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct break; case SPA_PROP_channelVolumes: if ((n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + volumes, SPA_N_ELEMENTS(volumes))) > 0) { changed++; } break; case SPA_PROP_channelMap: if (spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - channels, SPA_AUDIO_MAX_CHANNELS) > 0) { + channels, SPA_N_ELEMENTS(channels)) > 0) { changed++; } break; diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 138a36cda..4f2fde8b6 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -163,10 +163,10 @@ static int alsa_set_param(struct state *state, const char *k, const char *s) int fmt_change = 0; if (spa_streq(k, SPA_KEY_AUDIO_CHANNELS)) { state->default_channels = atoi(s); - if (state->default_channels > SPA_AUDIO_MAX_CHANNELS) { + if (state->default_channels > MAX_CHANNELS) { spa_log_warn(state->log, "%p: %s: %s > %d, clamping", - state, k, s, SPA_AUDIO_MAX_CHANNELS); - state->default_channels = SPA_AUDIO_MAX_CHANNELS; + state, k, s, MAX_CHANNELS); + state->default_channels = MAX_CHANNELS; } fmt_change++; } else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) { @@ -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) @@ -1584,8 +1582,8 @@ static int add_channels(struct state *state, bool all, uint32_t index, uint32_t spa_log_debug(state->log, "channels (%d %d) default:%d all:%d", min, max, state->default_channels, all); - min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS); - max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS); + min = SPA_MIN(min, MAX_CHANNELS); + max = SPA_MIN(max, MAX_CHANNELS); if (state->default_channels != 0 && !all) { if (min > state->default_channels || @@ -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 bc96a3924..8b8a07721 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -36,6 +36,7 @@ extern "C" { #endif #define MAX_RATES 16 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DEFAULT_PERIOD 1024u #define DEFAULT_RATE 48000u @@ -71,8 +72,8 @@ struct buffer { #define BW_PERIOD (3 * SPA_NSEC_PER_SEC) struct channel_map { - uint32_t channels; - uint32_t pos[SPA_AUDIO_MAX_CHANNELS]; + uint32_t n_pos; + uint32_t pos[MAX_CHANNELS]; }; struct card { @@ -314,7 +315,7 @@ void spa_alsa_emit_port_info(struct state *state, bool full); static inline void spa_alsa_parse_position(struct channel_map *map, const char *val, size_t len) { - spa_audio_parse_position(val, len, map->pos, &map->channels); + spa_audio_parse_position_n(val, len, map->pos, SPA_N_ELEMENTS(map->pos), &map->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 0891462a2..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; @@ -2046,11 +2052,12 @@ static int do_auto_port_config(struct impl *this, const char *str) return -ENOENT; if (format.media_subtype == SPA_MEDIA_SUBTYPE_raw) { + uint32_t n_pos = SPA_MIN(SPA_N_ELEMENTS(format.info.raw.position), format.info.raw.channels); if (position == POSITION_AUX) { - for (i = 0; i < format.info.raw.channels; i++) + for (i = 0; i < n_pos; i++) format.info.raw.position[i] = SPA_AUDIO_CHANNEL_START_Aux + i; } else if (position == POSITION_UNKNOWN) { - for (i = 0; i < format.info.raw.channels; i++) + for (i = 0; i < n_pos; i++) format.info.raw.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN; } } diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index d2e22d603..f201522f8 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -47,10 +47,11 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert"); #define DEFAULT_RATE 48000 #define DEFAULT_CHANNELS 2 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_ALIGN FMT_OPS_MAX_ALIGN #define MAX_BUFFERS 32 -#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS -#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1) +#define MAX_DATAS MAX_CHANNELS +#define MAX_PORTS (MAX_CHANNELS+1) #define MAX_STAGES 64 #define MAX_GRAPH 9 /* 8 active + 1 replacement slot */ @@ -62,7 +63,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert"); struct volumes { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; static void init_volumes(struct volumes *vol) @@ -70,7 +71,7 @@ static void init_volumes(struct volumes *vol) uint32_t i; vol->mute = DEFAULT_MUTE; vol->n_volumes = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) vol->volumes[i] = DEFAULT_VOLUME; } @@ -91,7 +92,7 @@ struct props { float max_volume; float prev_volume; uint32_t n_channels; - uint32_t channel_map[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channel_map[MAX_CHANNELS]; struct volumes channel; struct volumes soft; struct volumes monitor; @@ -112,7 +113,7 @@ static void props_reset(struct props *props) props->min_volume = DEFAULT_MIN_VOLUME; props->max_volume = DEFAULT_MAX_VOLUME; props->n_channels = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) props->channel_map[i] = SPA_AUDIO_CHANNEL_UNKNOWN; init_volumes(&props->channel); init_volumes(&props->soft); @@ -241,9 +242,9 @@ struct filter_graph { struct spa_filter_graph *graph; struct spa_hook listener; uint32_t n_inputs; - uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t inputs_position[MAX_CHANNELS]; uint32_t n_outputs; - uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t outputs_position[MAX_CHANNELS]; uint32_t latency; bool removing; bool setup; @@ -423,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); @@ -438,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 | @@ -1103,11 +1102,11 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info) else if (spa_streq(k, "n_outputs")) spa_atou32(s, &g->n_outputs, 0); else if (spa_streq(k, "inputs.audio.position")) - spa_audio_parse_position(s, strlen(s), - g->inputs_position, &g->n_inputs); + spa_audio_parse_position_n(s, strlen(s), g->inputs_position, + SPA_N_ELEMENTS(g->inputs_position), &g->n_inputs); else if (spa_streq(k, "outputs.audio.position")) - spa_audio_parse_position(s, strlen(s), - g->outputs_position, &g->n_outputs); + spa_audio_parse_position_n(s, strlen(s), g->outputs_position, + SPA_N_ELEMENTS(g->outputs_position), &g->n_outputs); else if (spa_streq(k, "latency")) { double latency; if (spa_atod(s, &latency)) @@ -1539,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; } @@ -1551,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; } @@ -1569,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"); @@ -1748,7 +1719,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) case SPA_PROP_channelVolumes: if (!p->lock_volumes && (n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->channel.volumes, SPA_N_ELEMENTS(p->channel.volumes))) > 0) { have_channel_volume = true; p->channel.n_volumes = n; changed++; @@ -1756,7 +1727,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) break; case SPA_PROP_channelMap: if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - p->channel_map, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->channel_map, SPA_N_ELEMENTS(p->channel_map))) > 0) { p->n_channels = n; changed++; } @@ -1771,7 +1742,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) case SPA_PROP_softVolumes: if (!p->lock_volumes && (n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->soft.volumes, SPA_N_ELEMENTS(p->soft.volumes))) > 0) { have_soft_volume = true; p->soft.n_volumes = n; changed++; @@ -1783,7 +1754,7 @@ static int apply_props(struct impl *this, const struct spa_pod *param) break; case SPA_PROP_monitorVolumes: if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->monitor.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { + p->monitor.volumes, SPA_N_ELEMENTS(p->monitor.volumes))) > 0) { p->monitor.n_volumes = n; changed++; } @@ -1895,10 +1866,11 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m this->dir[SPA_DIRECTION_OUTPUT].n_ports = dir->n_ports + 1; for (i = 0; i < dir->n_ports; i++) { - init_port(this, direction, i, info->info.raw.position[i], true, false, false); + uint32_t pos = 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, - info->info.raw.position[i], true, true, false); + pos, true, true, false); } break; } @@ -1966,7 +1938,7 @@ static int node_set_param_port_config(struct impl *this, uint32_t flags, return -EINVAL; if (info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; infop = &info; @@ -2056,22 +2028,24 @@ static int setup_in_convert(struct impl *this) dst_info.info.raw.rate); qsort(dst_info.info.raw.position, dst_info.info.raw.channels, - sizeof(uint32_t), int32_cmp); + sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { - if (src_info.info.raw.position[i] != - dst_info.info.raw.position[j]) + uint32_t pi, pj; + char b1[8], b2[8]; + + pi = src_info.info.raw.position[i]; + pj = dst_info.info.raw.position[j]; + if (pi != pj) continue; in->remap[i] = j; if (i != j) remap = true; spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this, i, in->remap[i], j, - spa_debug_type_find_short_name(spa_type_audio_channel, - src_info.info.raw.position[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - dst_info.info.raw.position[j])); + 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; } @@ -2120,9 +2094,10 @@ static int remap_volumes(struct impl *this, const struct spa_audio_info *info) for (i = 0; i < p->n_channels; i++) { for (j = i; j < target; j++) { + uint32_t pj = info->info.raw.position[j]; spa_log_debug(this->log, "%d %d: %d <-> %d", i, j, - p->channel_map[i], info->info.raw.position[j]); - if (p->channel_map[i] != info->info.raw.position[j]) + p->channel_map[i], pj); + if (p->channel_map[i] != pj) continue; if (i != j) { SPA_SWAP(p->channel_map[i], p->channel_map[j]); @@ -2153,7 +2128,7 @@ static void set_volume(struct impl *this) { struct volumes *vol; uint32_t i; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; struct dir *dir = &this->dir[this->direction]; spa_log_debug(this->log, "%p set volume %f have_format:%d", this, this->props.volume, dir->have_format); @@ -2184,10 +2159,11 @@ static void set_volume(struct impl *this) static char *format_position(char *str, size_t len, uint32_t channels, uint32_t *position) { uint32_t 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])); + spa_type_audio_channel_make_short_name(position[i], + buf, sizeof(buf), "UNK")); return str; } @@ -2344,12 +2320,16 @@ static int setup_out_convert(struct impl *this) dst_info.info.raw.rate); qsort(src_info.info.raw.position, src_info.info.raw.channels, - sizeof(uint32_t), int32_cmp); + sizeof(uint32_t), int32_cmp); for (i = 0; i < src_info.info.raw.channels; i++) { for (j = 0; j < dst_info.info.raw.channels; j++) { - if (src_info.info.raw.position[i] != - dst_info.info.raw.position[j]) + uint32_t pi, pj; + char b1[8], b2[8]; + + pi = src_info.info.raw.position[i]; + pj = dst_info.info.raw.position[j]; + if (pi != pj) continue; out->remap[i] = j; if (i != j) @@ -2357,10 +2337,9 @@ static int setup_out_convert(struct impl *this) spa_log_debug(this->log, "%p: channel %d (%d) -> %d (%s -> %s)", this, i, out->remap[i], j, - spa_debug_type_find_short_name(spa_type_audio_channel, - src_info.info.raw.position[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - dst_info.info.raw.position[j])); + 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; } @@ -2671,7 +2650,7 @@ static int port_param_enum_formats(struct impl *impl, struct port *port, uint32_ } spa_pod_builder_add(b, SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( - DEFAULT_CHANNELS, 1, SPA_AUDIO_MAX_CHANNELS), + DEFAULT_CHANNELS, 1, MAX_CHANNELS), 0); *param = spa_pod_builder_pop(b, &f[0]); } @@ -3064,7 +3043,7 @@ static int port_set_format(void *object, if (info.info.raw.format == 0 || (!this->props.resample_disabled && info.info.raw.rate == 0) || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) { + info.info.raw.channels > MAX_CHANNELS) { spa_log_error(this->log, "invalid format:%d rate:%d channels:%d", info.info.raw.format, info.info.raw.rate, info.info.raw.channels); @@ -4296,8 +4275,9 @@ impl_init(const struct spa_handle_factory *factory, } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { if (s != NULL) - spa_audio_parse_position(s, strlen(s), this->props.channel_map, - &this->props.n_channels); + spa_audio_parse_position_n(s, strlen(s), + this->props.channel_map, SPA_N_ELEMENTS(this->props.channel_map), + &this->props.n_channels); } else if (spa_streq(k, SPA_KEY_PORT_IGNORE_LATENCY)) this->port_ignore_latency = spa_atob(s); diff --git a/spa/plugins/audioconvert/channelmix-ops.c b/spa/plugins/audioconvert/channelmix-ops.c index 792cf7e09..ce67042e1 100644 --- a/spa/plugins/audioconvert/channelmix-ops.c +++ b/spa/plugins/audioconvert/channelmix-ops.c @@ -142,29 +142,29 @@ static uint32_t mask_to_ch(struct channelmix *mix, uint64_t mask) } static void distribute_mix(struct channelmix *mix, - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + float matrix[MAX_CHANNELS][MAX_CHANNELS], uint64_t mask) { uint32_t i, ch = mask_to_ch(mix, mask); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][ch]= 1.0f; } static void average_mix(struct channelmix *mix, - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + float matrix[MAX_CHANNELS][MAX_CHANNELS], uint64_t mask) { uint32_t i, ch = mask_to_ch(mix, mask); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[ch][i]= 1.0f; } -static void pair_mix(float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS]) +static void pair_mix(float matrix[MAX_CHANNELS][MAX_CHANNELS]) { uint32_t i; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][i]= 1.0f; } static bool match_mix(struct channelmix *mix, - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS], + float matrix[MAX_CHANNELS][MAX_CHANNELS], uint64_t src_mask, uint64_t dst_mask) { bool matched = false; @@ -181,7 +181,7 @@ static bool match_mix(struct channelmix *mix, static int make_matrix(struct channelmix *mix) { - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS] = {{ 0.0f }}; + float matrix[MAX_CHANNELS][MAX_CHANNELS] = {{ 0.0f }}; uint64_t src_mask = mix->src_mask, src_paired; uint64_t dst_mask = mix->dst_mask, dst_paired; uint32_t src_chan = mix->src_chan; @@ -293,7 +293,7 @@ static int make_matrix(struct channelmix *mix) keep &= ~STEREO; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign FC to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][_CH(FC)]= 1.0f; normalize = true; } else { @@ -313,7 +313,7 @@ static int make_matrix(struct channelmix *mix) keep &= ~FRONT; } else if ((dst_mask & _MASK(MONO))){ spa_log_info(mix->log, "assign STEREO to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(FL)]= 1.0f; matrix[i][_CH(FR)]= 1.0f; } @@ -352,7 +352,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,RC) += slev * SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign RC to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][_CH(RC)]= 1.0f; normalize = true; } else { @@ -398,7 +398,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,RR)+= slev * SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign RL+RR to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(RL)]= 1.0f; matrix[i][_CH(RR)]= 1.0f; } @@ -450,7 +450,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,SR) += slev * SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign SL+SR to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(SL)]= 1.0f; matrix[i][_CH(SR)]= 1.0f; } @@ -471,7 +471,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FC,FRC)+= SQRT1_2; } else if (dst_mask & _MASK(MONO)){ spa_log_info(mix->log, "assign FLC+FRC to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { matrix[i][_CH(FLC)]= 1.0f; matrix[i][_CH(FRC)]= 1.0f; } @@ -492,7 +492,7 @@ static int make_matrix(struct channelmix *mix) _MATRIX(FR,LFE) += llev * SQRT1_2; } else if ((dst_mask & _MASK(MONO))){ spa_log_info(mix->log, "assign LFE to MONO (%f)", 1.0f); - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) + for (i = 0; i < MAX_CHANNELS; i++) matrix[i][_CH(LFE)]= 1.0f; normalize = true; } else { @@ -690,7 +690,7 @@ done: static void impl_channelmix_set_volume(struct channelmix *mix, float volume, bool mute, uint32_t n_channel_volumes, float *channel_volumes) { - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; float vol = mute ? 0.0f : volume, t; uint32_t i, j; uint32_t src_chan = mix->src_chan; @@ -760,8 +760,8 @@ int channelmix_init(struct channelmix *mix) { const struct channelmix_info *info; - if (mix->src_chan > SPA_AUDIO_MAX_CHANNELS || - mix->dst_chan > SPA_AUDIO_MAX_CHANNELS) + if (mix->src_chan > MAX_CHANNELS || + mix->dst_chan > MAX_CHANNELS) return -EINVAL; info = find_channelmix_info(mix->src_chan, mix->src_mask, mix->dst_chan, mix->dst_mask, diff --git a/spa/plugins/audioconvert/channelmix-ops.h b/spa/plugins/audioconvert/channelmix-ops.h index 26e2efc3a..6ea2b9451 100644 --- a/spa/plugins/audioconvert/channelmix-ops.h +++ b/spa/plugins/audioconvert/channelmix-ops.h @@ -24,6 +24,7 @@ #define BUFFER_SIZE 4096 #define MAX_TAPS 255u +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define CHANNELMIX_OPS_MAX_ALIGN 16 @@ -50,8 +51,8 @@ struct channelmix { #define CHANNELMIX_FLAG_EQUAL (1<<2) /**< all values are equal */ #define CHANNELMIX_FLAG_COPY (1<<3) /**< 1 on diagonal, can be nxm */ uint32_t flags; - float matrix_orig[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS]; - float matrix[SPA_AUDIO_MAX_CHANNELS][SPA_AUDIO_MAX_CHANNELS]; + float matrix_orig[MAX_CHANNELS][MAX_CHANNELS]; + float matrix[MAX_CHANNELS][MAX_CHANNELS]; float freq; /* sample frequency */ float lfe_cutoff; /* in Hz, 0 is disabled */ @@ -59,7 +60,7 @@ struct channelmix { float rear_delay; /* in ms, 0 is disabled */ float widen; /* stereo widen. 0 is disabled */ uint32_t hilbert_taps; /* to phase shift, 0 disabled */ - struct lr4 lr4[SPA_AUDIO_MAX_CHANNELS]; + struct lr4 lr4[MAX_CHANNELS]; float buffer_mem[2 * BUFFER_SIZE*2 + CHANNELMIX_OPS_MAX_ALIGN/4]; float *buffer[2]; diff --git a/spa/plugins/audioconvert/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/audioconvert/test-audioconvert.c b/spa/plugins/audioconvert/test-audioconvert.c index 0546e629a..de3ebb8b5 100644 --- a/spa/plugins/audioconvert/test-audioconvert.c +++ b/spa/plugins/audioconvert/test-audioconvert.c @@ -25,7 +25,8 @@ SPA_LOG_IMPL(logger); extern const struct spa_handle_factory test_source_factory; -#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1) +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS +#define MAX_PORTS (MAX_CHANNELS+1) struct context { struct spa_handle *convert_handle; diff --git a/spa/plugins/avb/avb-pcm.c b/spa/plugins/avb/avb-pcm.c index 59b05a3a5..0402b8c75 100644 --- a/spa/plugins/avb/avb-pcm.c +++ b/spa/plugins/avb/avb-pcm.c @@ -40,7 +40,8 @@ static int avb_set_param(struct state *state, const char *k, const char *s) state->default_format = spa_type_audio_format_from_short_name(s); fmt_change++; } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { - spa_audio_parse_position(s, strlen(s), state->default_pos.pos, + spa_audio_parse_position_n(s, strlen(s), state->default_pos.pos, + SPA_N_ELEMENTS(state->default_pos.pos), &state->default_pos.channels); fmt_change++; } else if (spa_streq(k, SPA_KEY_AUDIO_ALLOWED_RATES)) { @@ -85,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/avb/avb-pcm.h b/spa/plugins/avb/avb-pcm.h index 7e026741d..41f803d4c 100644 --- a/spa/plugins/avb/avb-pcm.h +++ b/spa/plugins/avb/avb-pcm.h @@ -109,6 +109,7 @@ static inline char *format_streamid(char *str, size_t size, const uint64_t strea return str; } +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_BUFFERS 32 struct buffer { @@ -127,7 +128,7 @@ struct buffer { struct channel_map { uint32_t channels; - uint32_t pos[SPA_AUDIO_MAX_CHANNELS]; + uint32_t pos[MAX_CHANNELS]; }; struct port { diff --git a/spa/plugins/bluez5/a2dp-codec-aac.c b/spa/plugins/bluez5/a2dp-codec-aac.c index 505066c3e..6d4db7fc4 100644 --- a/spa/plugins/bluez5/a2dp-codec-aac.c +++ b/spa/plugins/bluez5/a2dp-codec-aac.c @@ -200,7 +200,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_aac_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-aptx.c b/spa/plugins/bluez5/a2dp-codec-aptx.c index 7ebdc99d8..75ceb229f 100644 --- a/spa/plugins/bluez5/a2dp-codec-aptx.c +++ b/spa/plugins/bluez5/a2dp-codec-aptx.c @@ -205,7 +205,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_aptx_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-faststream.c b/spa/plugins/bluez5/a2dp-codec-faststream.c index 5b78fb38e..edc833292 100644 --- a/spa/plugins/bluez5/a2dp-codec-faststream.c +++ b/spa/plugins/bluez5/a2dp-codec-faststream.c @@ -114,7 +114,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_faststream_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-lc3plus.c b/spa/plugins/bluez5/a2dp-codec-lc3plus.c index c5a191e29..ee25fef0c 100644 --- a/spa/plugins/bluez5/a2dp-codec-lc3plus.c +++ b/spa/plugins/bluez5/a2dp-codec-lc3plus.c @@ -175,7 +175,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_lc3plus_hr_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; uint32_t i = 0; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-ldac.c b/spa/plugins/bluez5/a2dp-codec-ldac.c index cf1526faf..11185800e 100644 --- a/spa/plugins/bluez5/a2dp-codec-ldac.c +++ b/spa/plugins/bluez5/a2dp-codec-ldac.c @@ -158,7 +158,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_frame f[2]; struct spa_pod_choice *choice; uint32_t i = 0; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; if (caps_size < sizeof(conf)) return -EINVAL; diff --git a/spa/plugins/bluez5/a2dp-codec-opus-g.c b/spa/plugins/bluez5/a2dp-codec-opus-g.c index 2fe34bf5e..130dea433 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus-g.c +++ b/spa/plugins/bluez5/a2dp-codec-opus-g.c @@ -164,7 +164,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, { a2dp_opus_g_t conf; struct spa_pod_frame f[1]; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; int channels; if (caps_size < sizeof(conf)) diff --git a/spa/plugins/bluez5/a2dp-codec-opus.c b/spa/plugins/bluez5/a2dp-codec-opus.c index 21ede7d34..c5b042b20 100644 --- a/spa/plugins/bluez5/a2dp-codec-opus.c +++ b/spa/plugins/bluez5/a2dp-codec-opus.c @@ -58,6 +58,8 @@ static struct spa_log *log; #define BITRATE_DUPLEX_BIDI 160000 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + #define OPUS_05_MAX_BYTES (15 * 1024) struct props { @@ -251,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; @@ -313,14 +309,14 @@ static void parse_settings(struct props *props, const struct spa_dict *settings) return; if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.channels"), &v, 0)) - props->channels = SPA_CLAMP(v, 1u, SPA_AUDIO_MAX_CHANNELS); + props->channels = SPA_CLAMP(v, 1u, MAX_CHANNELS); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.max-bitrate"), &v, 0)) props->max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.coupled-streams"), &v, 0)) props->coupled_streams = SPA_CLAMP(v, 0u, props->channels / 2); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.channels"), &v, 0)) - props->bidi_channels = SPA_CLAMP(v, 0u, SPA_AUDIO_MAX_CHANNELS); + props->bidi_channels = SPA_CLAMP(v, 0u, MAX_CHANNELS); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.max-bitrate"), &v, 0)) props->bidi_max_bitrate = SPA_MAX(v, (uint32_t)BITRATE_MIN); if (spa_atou32(spa_dict_lookup(settings, "bluez5.a2dp.opus.pro.bidi.coupled-streams"), &v, 0)) @@ -503,7 +499,7 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc const uint8_t *permutation = NULL; size_t i, j; - if (channels > SPA_AUDIO_MAX_CHANNELS) + if (channels > MAX_CHANNELS) return -EINVAL; if (2 * coupled_streams > channels) return -EINVAL; @@ -542,10 +538,9 @@ static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direc const struct audio_location loc = audio_locations[i]; if (location & loc.mask) { - if (permutation) - positions[permutation[j++]] = loc.position; - else - positions[j++] = loc.position; + uint32_t idx = permutation ? permutation[j] : j; + positions[idx] = loc.position; + j++; } } for (i = SPA_AUDIO_CHANNEL_START_Aux; j < channels; ++i, ++j) @@ -561,7 +556,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, a2dp_opus_05_t a2dp_opus_05 = { .info = codec->vendor, .main = { - .channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS), + .channels = SPA_MIN(255u, MAX_CHANNELS), .frame_duration = (OPUS_05_FRAME_DURATION_25 | OPUS_05_FRAME_DURATION_50 | OPUS_05_FRAME_DURATION_100 | @@ -571,7 +566,7 @@ static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, OPUS_05_INIT_BITRATE(0) }, .bidi = { - .channels = SPA_MIN(255u, SPA_AUDIO_MAX_CHANNELS), + .channels = SPA_MIN(255u, MAX_CHANNELS), .frame_duration = (OPUS_05_FRAME_DURATION_25 | OPUS_05_FRAME_DURATION_50 | OPUS_05_FRAME_DURATION_100 | @@ -771,7 +766,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, a2dp_opus_05_t conf; a2dp_opus_05_direction_t *dir; struct spa_pod_frame f[1]; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[MAX_CHANNELS]; if (caps_size < sizeof(conf)) return -EINVAL; @@ -835,7 +830,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags } info->info.raw.channels = dir1->channels; - if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL, info->info.raw.position) < 0) + if (get_mapping(codec, dir1, surround_encoder, NULL, NULL, NULL, + info->info.raw.position) < 0) return -EINVAL; if (get_mapping(codec, dir2, surround_encoder, NULL, NULL, NULL, NULL) < 0) return -EINVAL; diff --git a/spa/plugins/bluez5/a2dp-codec-sbc.c b/spa/plugins/bluez5/a2dp-codec-sbc.c index f2bc3e7ef..a91109e52 100644 --- a/spa/plugins/bluez5/a2dp-codec-sbc.c +++ b/spa/plugins/bluez5/a2dp-codec-sbc.c @@ -346,7 +346,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_frame f[2]; struct spa_pod_choice *choice; uint32_t i = 0; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[2]; if (caps_size < sizeof(conf)) return -EINVAL; diff --git a/spa/plugins/bluez5/bap-codec-lc3.c b/spa/plugins/bluez5/bap-codec-lc3.c index 818f4ee36..10d9eecc8 100644 --- a/spa/plugins/bluez5/bap-codec-lc3.c +++ b/spa/plugins/bluez5/bap-codec-lc3.c @@ -901,12 +901,12 @@ static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t f return conf_cmp(&conf1, res1, &conf2, res2); } -static uint8_t channels_to_positions(uint32_t channels, uint32_t *position) +static uint8_t channels_to_positions(uint32_t channels, uint32_t *position, uint32_t max_position) { uint32_t n_channels = get_channel_count(channels); uint8_t n_positions = 0; - spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS); + spa_assert(n_channels <= max_position); if (channels == 0) { position[0] = SPA_AUDIO_CHANNEL_MONO; @@ -932,7 +932,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, bap_lc3_t conf; struct spa_pod_frame f[2]; struct spa_pod_choice *choice; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[LC3_MAX_CHANNELS]; uint32_t i = 0; uint8_t res; @@ -990,7 +990,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, if (i == 0) return -EINVAL; - res = channels_to_positions(conf.channels, position); + res = channels_to_positions(conf.channels, position, SPA_N_ELEMENTS(position)); if (res == 0) return -EINVAL; spa_pod_builder_add(b, @@ -1044,7 +1044,8 @@ static int codec_validate_config(const struct media_codec *codec, uint32_t flags return -EINVAL; } - res = channels_to_positions(conf.channels, info->info.raw.position); + res = channels_to_positions(conf.channels, info->info.raw.position, + SPA_N_ELEMENTS(info->info.raw.position)); if (res == 0) return -EINVAL; info->info.raw.channels = res; diff --git a/spa/plugins/bluez5/bluez5-dbus.c b/spa/plugins/bluez5/bluez5-dbus.c index 4b5ca1d65..ac91c6cff 100644 --- a/spa/plugins/bluez5/bluez5-dbus.c +++ b/spa/plugins/bluez5/bluez5-dbus.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "codec-loader.h" @@ -5037,6 +5038,10 @@ 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; memcpy(transport->channels, info.info.raw.position, transport->n_channels * sizeof(uint32_t)); @@ -6850,7 +6855,7 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di const char *key, uint32_t *value) { const char *str; - uint32_t position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t position[MAX_CHANNELS]; uint32_t n_channels; uint32_t locations; unsigned int i, j; @@ -6861,7 +6866,8 @@ static void parse_bap_locations(struct spa_bt_monitor *this, const struct spa_di if (spa_atou32(str, value, 0)) return; - if (!spa_audio_parse_position(str, strlen(str), position, &n_channels)) { + if (!spa_audio_parse_position_n(str, strlen(str), position, + SPA_N_ELEMENTS(position), &n_channels)) { spa_log_error(this->log, "property %s '%s' is not valid position array", key, str); return; } diff --git a/spa/plugins/bluez5/bluez5-device.c b/spa/plugins/bluez5/bluez5-device.c index dde098ace..6546abf4f 100644 --- a/spa/plugins/bluez5/bluez5-device.c +++ b/spa/plugins/bluez5/bluez5-device.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.device"); #undef SPA_LOG_TOPIC_DEFAULT #define SPA_LOG_TOPIC_DEFAULT &log_topic -#define MAX_NODES (2*SPA_AUDIO_MAX_CHANNELS) +#define MAX_NODES (2*MAX_CHANNELS) #define DEVICE_ID_SOURCE 0 #define DEVICE_ID_SINK 1 @@ -99,9 +100,9 @@ struct node { unsigned int offload_acquired:1; uint32_t n_channels; int64_t latency_offset; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; - float volumes[SPA_AUDIO_MAX_CHANNELS]; - float soft_volumes[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; + float soft_volumes[MAX_CHANNELS]; }; struct dynamic_node @@ -129,8 +130,8 @@ struct device_set { bool leader; uint32_t sinks; uint32_t sources; - struct device_set_member sink[SPA_AUDIO_MAX_CHANNELS]; - struct device_set_member source[SPA_AUDIO_MAX_CHANNELS]; + struct device_set_member sink[MAX_CHANNELS]; + struct device_set_member source[MAX_CHANNELS]; }; struct impl { @@ -182,7 +183,7 @@ static void init_node(struct impl *this, struct node *node, uint32_t id) spa_zero(*node); node->id = id; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { + for (i = 0; i < MAX_CHANNELS; i++) { node->volumes[i] = 1.0f; node->soft_volumes[i] = 1.0f; } @@ -476,6 +477,7 @@ static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t *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) @@ -546,7 +548,7 @@ static void emit_device_set_node(struct impl *this, uint32_t id) if (node->channels[k] == t->channels[j]) break; } - if (k == node->n_channels && node->n_channels < SPA_AUDIO_MAX_CHANNELS) + if (k == node->n_channels && node->n_channels < MAX_CHANNELS) node->channels[node->n_channels++] = t->channels[j]; } } @@ -2946,8 +2948,8 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p struct spa_pod_prop *prop; struct spa_pod_object *obj = (struct spa_pod_object *) props; int changed = 0; - float volumes[SPA_AUDIO_MAX_CHANNELS]; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; uint32_t n_volumes = 0, SPA_UNUSED n_channels = 0; int64_t latency_offset = 0; @@ -2972,11 +2974,11 @@ static int apply_device_props(struct impl *this, struct node *node, struct spa_p break; case SPA_PROP_channelVolumes: n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - volumes, SPA_AUDIO_MAX_CHANNELS); + volumes, SPA_N_ELEMENTS(volumes)); break; case SPA_PROP_channelMap: n_channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - channels, SPA_AUDIO_MAX_CHANNELS); + channels, SPA_N_ELEMENTS(channels)); break; case SPA_PROP_latencyOffsetNsec: if (spa_pod_get_long(&prop->value, &latency_offset) == 0) { diff --git a/spa/plugins/bluez5/defs.h b/spa/plugins/bluez5/defs.h index 45754c19a..0e25592e2 100644 --- a/spa/plugins/bluez5/defs.h +++ b/spa/plugins/bluez5/defs.h @@ -157,6 +157,8 @@ extern "C" { #define SPA_BT_NO_BATTERY ((uint8_t)255) +#define MAX_CHANNELS (SPA_AUDIO_MAX_CHANNELS) + enum spa_bt_media_direction { SPA_BT_MEDIA_SOURCE, SPA_BT_MEDIA_SINK, @@ -678,7 +680,7 @@ struct spa_bt_transport { struct spa_list bap_transport_linked; uint32_t n_channels; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; struct spa_bt_transport_volume volumes[SPA_BT_VOLUME_ID_TERM]; diff --git a/spa/plugins/bluez5/hfp-codec-cvsd.c b/spa/plugins/bluez5/hfp-codec-cvsd.c index 66b801ecc..19f908188 100644 --- a/spa/plugins/bluez5/hfp-codec-cvsd.c +++ b/spa/plugins/bluez5/hfp-codec-cvsd.c @@ -34,7 +34,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/spa/plugins/bluez5/hfp-codec-lc3-a127.c b/spa/plugins/bluez5/hfp-codec-lc3-a127.c index 703e153a8..b10bba874 100644 --- a/spa/plugins/bluez5/hfp-codec-lc3-a127.c +++ b/spa/plugins/bluez5/hfp-codec-lc3-a127.c @@ -39,7 +39,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/spa/plugins/bluez5/hfp-codec-lc3-swb.c b/spa/plugins/bluez5/hfp-codec-lc3-swb.c index 685806286..1cd679958 100644 --- a/spa/plugins/bluez5/hfp-codec-lc3-swb.c +++ b/spa/plugins/bluez5/hfp-codec-lc3-swb.c @@ -42,7 +42,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/spa/plugins/bluez5/hfp-codec-msbc.c b/spa/plugins/bluez5/hfp-codec-msbc.c index d2aabe2b0..5175a68d7 100644 --- a/spa/plugins/bluez5/hfp-codec-msbc.c +++ b/spa/plugins/bluez5/hfp-codec-msbc.c @@ -49,7 +49,7 @@ static int codec_enum_config(const struct media_codec *codec, uint32_t flags, struct spa_pod_builder *b, struct spa_pod **param) { struct spa_pod_frame f[1]; - const uint32_t position[SPA_AUDIO_MAX_CHANNELS] = { SPA_AUDIO_CHANNEL_MONO }; + const uint32_t position[1] = { SPA_AUDIO_CHANNEL_MONO }; const int channels = 1; spa_assert(caps == NULL && caps_size == 0); diff --git a/spa/plugins/bluez5/media-sink.c b/spa/plugins/bluez5/media-sink.c index 3d3f2ec75..bac1e84ab 100644 --- a/spa/plugins/bluez5/media-sink.c +++ b/spa/plugins/bluez5/media-sink.c @@ -2139,7 +2139,7 @@ static int port_set_format(struct impl *this, struct port *port, if (info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; if (this->transport && this->transport->iso_io) { diff --git a/spa/plugins/bluez5/media-source.c b/spa/plugins/bluez5/media-source.c index 4fab044a0..dc52a09b4 100644 --- a/spa/plugins/bluez5/media-source.c +++ b/spa/plugins/bluez5/media-source.c @@ -1439,7 +1439,7 @@ static int port_set_format(struct impl *this, struct port *port, if (info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; port->frame_size = info.info.raw.channels; diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c index d32e4a37f..9271ace34 100644 --- a/spa/plugins/filter-graph/filter-graph.c +++ b/spa/plugins/filter-graph/filter-graph.c @@ -39,6 +39,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.filter-graph"); #define MAX_HNDL 64 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DEFAULT_RATE 48000 @@ -154,15 +155,15 @@ struct graph_hndl { struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; uint32_t n_ports; - struct port *ports[SPA_AUDIO_MAX_CHANNELS]; - float min[SPA_AUDIO_MAX_CHANNELS]; - float max[SPA_AUDIO_MAX_CHANNELS]; + struct port *ports[MAX_CHANNELS]; + float min[MAX_CHANNELS]; + float max[MAX_CHANNELS]; #define SCALE_LINEAR 0 #define SCALE_CUBIC 1 - int scale[SPA_AUDIO_MAX_CHANNELS]; + int scale[MAX_CHANNELS]; }; struct graph { @@ -194,9 +195,9 @@ struct graph { uint32_t n_inputs; uint32_t n_outputs; - uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t inputs_position[MAX_CHANNELS]; uint32_t n_inputs_position; - uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS]; + uint32_t outputs_position[MAX_CHANNELS]; uint32_t n_outputs_position; float min_latency; @@ -231,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, "]"); } @@ -256,8 +259,8 @@ static void emit_filter_graph_info(struct impl *impl, bool full) char n_inputs[64], n_outputs[64], latency[64]; struct spa_dict_item items[6]; struct spa_dict dict = SPA_DICT(items, 0); - char in_pos[SPA_AUDIO_MAX_CHANNELS * 8]; - char out_pos[SPA_AUDIO_MAX_CHANNELS * 8]; + char in_pos[MAX_CHANNELS * 8]; + char out_pos[MAX_CHANNELS * 8]; snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs); snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs); @@ -745,10 +748,10 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru case SPA_PROP_channelVolumes: { uint32_t i, n_vols; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, vols, - SPA_AUDIO_MAX_CHANNELS)) > 0) { + SPA_N_ELEMENTS(vols))) > 0) { if (vol->n_volumes != n_vols) do_volume = true; vol->n_volumes = n_vols; @@ -772,7 +775,7 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru } } if (do_volume && vol->n_ports != 0) { - float soft_vols[SPA_AUDIO_MAX_CHANNELS]; + float soft_vols[MAX_CHANNELS]; uint32_t i; for (i = 0; i < vol->n_volumes; i++) @@ -1264,7 +1267,7 @@ static int parse_volume(struct graph *graph, struct spa_json *json, enum spa_dir spa_log_error(impl->log, "unknown control port %s", control); return -ENOENT; } - if (vol->n_ports >= SPA_AUDIO_MAX_CHANNELS) { + if (vol->n_ports >= MAX_CHANNELS) { spa_log_error(impl->log, "too many volume controls"); return -ENOSPC; } @@ -2118,8 +2121,9 @@ static int load_graph(struct graph *graph, const struct spa_dict *props) spa_log_error(impl->log, "%s expects an array", key); return -EINVAL; } - spa_audio_parse_position(val, len, graph->inputs_position, - &graph->n_inputs_position); + spa_audio_parse_position_n(val, len, graph->inputs_position, + SPA_N_ELEMENTS(graph->inputs_position), + &graph->n_inputs_position); impl->info.n_inputs = graph->n_inputs_position; } else if (spa_streq("outputs.audio.position", key)) { @@ -2128,8 +2132,9 @@ static int load_graph(struct graph *graph, const struct spa_dict *props) spa_log_error(impl->log, "%s expects an array", key); return -EINVAL; } - spa_audio_parse_position(val, len, graph->outputs_position, - &graph->n_outputs_position); + spa_audio_parse_position_n(val, len, graph->outputs_position, + SPA_N_ELEMENTS(graph->outputs_position), + &graph->n_outputs_position); impl->info.n_outputs = graph->n_outputs_position; } else if (spa_streq("nodes", key)) { diff --git a/spa/plugins/support/null-audio-sink.c b/spa/plugins/support/null-audio-sink.c index 70edd4ff2..acaec6f7b 100644 --- a/spa/plugins/support/null-audio-sink.c +++ b/spa/plugins/support/null-audio-sink.c @@ -35,12 +35,13 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.null-audio-sink"); #define DEFAULT_CLOCK_NAME "clock.system.monotonic" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS struct props { uint32_t format; uint32_t channels; uint32_t rate; - uint32_t pos[SPA_AUDIO_MAX_CHANNELS]; + uint32_t pos[MAX_CHANNELS]; char clock_name[64]; unsigned int debug:1; unsigned int driver:1; @@ -636,7 +637,7 @@ port_set_format(struct impl *this, if (info.info.raw.rate == 0 || info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels > MAX_CHANNELS) return -EINVAL; if (this->props.format != 0) { @@ -949,7 +950,8 @@ impl_init(const struct spa_handle_factory *factory, } else if (spa_streq(k, SPA_KEY_NODE_DRIVER)) { this->props.driver = spa_atob(s); } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { - spa_audio_parse_position(s, strlen(s), this->props.pos, &this->props.channels); + spa_audio_parse_position_n(s, strlen(s), this->props.pos, + SPA_N_ELEMENTS(this->props.pos), &this->props.channels); } else if (spa_streq(k, "clock.name")) { spa_scnprintf(this->props.clock_name, sizeof(this->props.clock_name), diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index 9cfd0afae..26effd8a8 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -399,7 +399,7 @@ enum_filter_format(uint32_t media_type, int32_t media_subtype, if (index == 0) video_format = values[0]; } else { - if (index < n_values - 1) + if (index < n_values - 1 && val->size == sizeof(values[0])) video_format = values[index + 1]; } } else { diff --git a/spa/plugins/videoconvert/videoconvert-ffmpeg.c b/spa/plugins/videoconvert/videoconvert-ffmpeg.c index 525b2c933..d0fd4bbbd 100644 --- a/spa/plugins/videoconvert/videoconvert-ffmpeg.c +++ b/spa/plugins/videoconvert/videoconvert-ffmpeg.c @@ -1209,10 +1209,7 @@ static struct spa_pod *transform_format(struct impl *this, struct port *port, co uint32_t n_vals, choice, *id_vals; struct spa_pod *val = spa_pod_get_values(&prop->value, &n_vals, &choice); - if (n_vals < 1) - return 0; - - if (!spa_pod_is_id(val)) + if (n_vals < 1 || val->type != SPA_TYPE_Id) return 0; id_vals = SPA_POD_BODY(val); diff --git a/spa/plugins/volume/volume.c b/spa/plugins/volume/volume.c index 164e46a6a..0c750a96a 100644 --- a/spa/plugins/volume/volume.c +++ b/spa/plugins/volume/volume.c @@ -454,8 +454,7 @@ static int port_set_format(void *object, return -EINVAL; if (info.info.raw.format != SPA_AUDIO_FORMAT_S16 || - info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels == 0) return -EINVAL; this->bpf = 2 * info.info.raw.channels; diff --git a/src/examples/export-source.c b/src/examples/export-source.c index 85af5b727..a0e983de1 100644 --- a/src/examples/export-source.c +++ b/src/examples/export-source.c @@ -268,8 +268,7 @@ static int port_set_format(void *object, d->format.format != SPA_AUDIO_FORMAT_F32) return -EINVAL; if (d->format.rate == 0 || - d->format.channels == 0 || - d->format.channels > SPA_AUDIO_MAX_CHANNELS) + d->format.channels == 0) return -EINVAL; } diff --git a/src/gst/gstpipewireformat.c b/src/gst/gstpipewireformat.c index 20b93065a..c594ea44b 100644 --- a/src/gst/gstpipewireformat.c +++ b/src/gst/gstpipewireformat.c @@ -649,7 +649,7 @@ handle_video_fields (ConvertData *d) static void set_default_channels (struct spa_pod_builder *b, uint32_t channels) { - uint32_t position[SPA_AUDIO_MAX_CHANNELS] = {0}; + uint32_t position[8] = {0}; gboolean ok = TRUE; switch (channels) { @@ -1117,7 +1117,7 @@ handle_int_prop (const struct spa_pod_prop *prop, const char *key, GstCaps *res) case SPA_CHOICE_Range: case SPA_CHOICE_Step: { - if (n_items < 3) + if (n_items < 3 || val->size != sizeof(ints[0])) return; gst_caps_set_simple (res, key, GST_TYPE_INT_RANGE, ints[1], ints[2], NULL); break; @@ -1225,7 +1225,7 @@ handle_fraction_prop (const struct spa_pod_prop *prop, const char *key, GstCaps case SPA_CHOICE_Range: case SPA_CHOICE_Step: { - if (n_items < 3) + if (n_items < 3 || val->size != sizeof(fract[0])) return; if (fract[1].num == fract[2].num && diff --git a/src/modules/module-combine-stream.c b/src/modules/module-combine-stream.c index 842138688..29f57cb50 100644 --- a/src/modules/module-combine-stream.c +++ b/src/modules/module-combine-stream.c @@ -231,9 +231,9 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); "( stream.props= ) " \ "( stream.rules= ) " +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define DELAYBUF_MAX_SIZE (20 * sizeof(float) * 96000) - static const struct spa_dict_item module_props[] = { { PW_KEY_MODULE_AUTHOR, "Wim Taymans " }, { PW_KEY_MODULE_DESCRIPTION, "Combine multiple streams into a single stream" }, @@ -312,10 +312,10 @@ struct stream { struct spa_latency_info latency; struct spa_audio_info_raw info; - uint32_t remap[SPA_AUDIO_MAX_CHANNELS]; + uint32_t remap[MAX_CHANNELS]; void *delaybuf; - struct ringbuffer delay[SPA_AUDIO_MAX_CHANNELS]; + struct ringbuffer delay[MAX_CHANNELS]; int64_t delay_samples; /* for main loop */ int64_t data_delay_samples; /* for data loop */ @@ -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)), @@ -509,7 +509,7 @@ static void update_latency(struct impl *impl) struct replace_delay_info { struct stream *stream; void *buf; - struct ringbuffer delay[SPA_AUDIO_MAX_CHANNELS]; + struct ringbuffer delay[MAX_CHANNELS]; }; static int do_replace_delay(struct spa_loop *loop, bool async, uint32_t seq, @@ -866,13 +866,15 @@ static int create_stream(struct stream_info *info) s->info = impl->info; if ((str = pw_properties_get(info->stream_props, SPA_KEY_AUDIO_POSITION)) != NULL) - spa_audio_parse_position(str, strlen(str), s->info.position, &s->info.channels); + spa_audio_parse_position_n(str, strlen(str), s->info.position, + SPA_N_ELEMENTS(s->info.position), &s->info.channels); if (s->info.channels == 0) s->info = impl->info; spa_zero(remap_info); if ((str = pw_properties_get(info->stream_props, "combine.audio.position")) != NULL) - spa_audio_parse_position(str, strlen(str), remap_info.position, &remap_info.channels); + spa_audio_parse_position_n(str, strlen(str), remap_info.position, + SPA_N_ELEMENTS(remap_info.position), &remap_info.channels); if (remap_info.channels == 0) remap_info = s->info; @@ -880,7 +882,10 @@ static int create_stream(struct stream_info *info) for (i = 0; i < remap_info.channels; i++) { s->remap[i] = i; for (j = 0; j < tmp_info.channels; j++) { - if (tmp_info.position[j] == remap_info.position[i]) { + uint32_t pj, pi; + pj = tmp_info.position[j]; + pi = remap_info.position[i]; + if (pj == pi) { s->remap[i] = j; break; } @@ -1228,7 +1233,7 @@ static void combine_output_process(void *d) struct pw_buffer *in, *out; struct stream *s; bool delay_changed = false; - bool mix[SPA_AUDIO_MAX_CHANNELS]; + bool mix[MAX_CHANNELS]; if ((out = pw_stream_dequeue_buffer(impl->combine)) == NULL) { pw_log_debug("%p: out of output buffers: %m", impl); @@ -1633,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 d241369c7..98efa35c5 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -154,6 +154,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define DEFAULT_RATE 48000 #define DEFAULT_POSITION "[ FL FR ]" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS /* Hopefully this is enough for any combination of AEC engine and resampler * input requirement for rate matching */ @@ -203,7 +204,7 @@ struct impl { struct spa_hook source_listener; struct spa_audio_info_raw source_info; - void *rec_buffer[SPA_AUDIO_MAX_CHANNELS]; + void *rec_buffer[MAX_CHANNELS]; uint32_t rec_ringsize; struct spa_ringbuffer rec_ring; @@ -215,21 +216,23 @@ struct impl { struct pw_properties *sink_props; struct pw_stream *sink; struct spa_hook sink_listener; - void *play_buffer[SPA_AUDIO_MAX_CHANNELS]; + void *play_buffer[MAX_CHANNELS]; uint32_t play_ringsize; struct spa_ringbuffer play_ring; struct spa_ringbuffer play_delayed_ring; struct spa_audio_info_raw sink_info; - void *out_buffer[SPA_AUDIO_MAX_CHANNELS]; + void *out_buffer[MAX_CHANNELS]; uint32_t out_ringsize; struct spa_ringbuffer out_ring; 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; @@ -306,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 */ @@ -330,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]; @@ -430,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]; @@ -453,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) @@ -478,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) @@ -545,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); } @@ -739,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) @@ -929,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); } @@ -954,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 @@ -1202,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)), @@ -1290,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; @@ -1370,21 +1439,21 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) } if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->capture_info.position, &impl->capture_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->capture_info.position, + SPA_N_ELEMENTS(impl->capture_info.position), &impl->capture_info.channels); } if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->source_info.position, &impl->source_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->source_info.position, + SPA_N_ELEMENTS(impl->source_info.position), &impl->source_info.channels); } if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->sink_info.position, &impl->sink_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->sink_info.position, + SPA_N_ELEMENTS(impl->sink_info.position), &impl->sink_info.channels); impl->playback_info = impl->sink_info; } if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) { - spa_audio_parse_position(str, strlen(str), - impl->playback_info.position, &impl->playback_info.channels); + spa_audio_parse_position_n(str, strlen(str), impl->playback_info.position, + SPA_N_ELEMENTS(impl->playback_info.position), &impl->playback_info.channels); if (impl->playback_info.channels != impl->sink_info.channels) impl->playback_info = impl->sink_info; } diff --git a/src/modules/module-example-filter.c b/src/modules/module-example-filter.c index ec9cdbd19..60b6dda95 100644 --- a/src/modules/module-example-filter.c +++ b/src/modules/module-example-filter.c @@ -298,8 +298,7 @@ static void capture_param_changed(void *data, uint32_t id, const struct spa_pod if (spa_format_audio_raw_parse(param, &info) < 0) return; if (info.rate == 0 || - info.channels == 0 || - info.channels > SPA_AUDIO_MAX_CHANNELS) + info.channels == 0) return; break; } @@ -468,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)), @@ -574,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 8c94edcdd..7094feaf4 100644 --- a/src/modules/module-ffado-driver.c +++ b/src/modules/module-ffado-driver.c @@ -112,6 +112,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_PORTS 128 #define FFADO_RT_PRIORITY_PACKETIZER_RELATIVE 5 @@ -179,7 +180,7 @@ struct port { struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; struct stream { @@ -760,7 +761,7 @@ static int make_stream_ports(struct stream *s) struct port *port = s->ports[i]; char channel[32]; - snprintf(channel, sizeof(channel), "AUX%u", n_channels % SPA_AUDIO_MAX_CHANNELS); + snprintf(channel, sizeof(channel), "AUX%u", n_channels); switch (port->stream_type) { case ffado_stream_type_audio: @@ -873,9 +874,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; @@ -1227,8 +1228,9 @@ static int probe_ffado_device(struct impl *impl) impl->source.ports[i] = port; } if (impl->source.info.channels != n_channels) { - impl->source.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->source.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->source.info.position)); + impl->source.info.channels = n_pos; + for (i = 0; i < n_pos; i++) impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1253,8 +1255,9 @@ static int probe_ffado_device(struct impl *impl) impl->sink.ports[i] = port; } if (impl->sink.info.channels != n_channels) { - impl->sink.info.channels = n_channels; - for (i = 0; i < SPA_MIN(impl->sink.info.channels, SPA_AUDIO_MAX_CHANNELS); i++) + uint32_t n_pos = SPA_MIN(n_channels, SPA_N_ELEMENTS(impl->sink.info.position)); + impl->sink.info.channels = n_pos; + for (i = 0; i < n_pos; i++) impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1426,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)), @@ -1579,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 84b70d651..a2d1f5361 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -1832,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, @@ -1927,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 226caf6fe..637001423 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -115,6 +115,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define MAX_PORTS 128 #define DEFAULT_CLIENT_NAME "PipeWire" @@ -157,7 +158,7 @@ struct port { struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; struct stream { @@ -513,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; @@ -522,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, - s->info.position[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 @@ -624,9 +626,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; @@ -1050,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)), @@ -1175,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 b23d167c1..e921cd701 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -538,8 +538,7 @@ static void param_format_changed(struct impl *impl, const struct spa_pod *param, spa_zero(info); if (param != NULL) { if (spa_format_audio_raw_parse(param, &info) < 0 || - info.channels == 0 || - info.channels > SPA_AUDIO_MAX_CHANNELS) + info.channels == 0) return; if ((impl->info.format != 0 && impl->info.format != info.format) || @@ -839,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, @@ -953,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 cacdf9368..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, - s->info.position[i % SPA_AUDIO_MAX_CHANNELS]); + 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); @@ -512,9 +512,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; @@ -863,7 +863,7 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p } impl->sink.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.send_audio_channels != impl->sink.info.channels) { - impl->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + impl->sink.info.channels = peer->params.send_audio_channels; for (i = 0; i < impl->sink.info.channels; i++) impl->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -874,7 +874,7 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p } impl->source.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.recv_audio_channels != impl->source.info.channels) { - impl->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + impl->source.info.channels = peer->params.recv_audio_channels; for (i = 0; i < impl->source.info.channels; i++) impl->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1218,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, @@ -1336,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 c5d8d308b..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, - s->info.position[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); @@ -677,9 +676,9 @@ static void parse_props(struct stream *s, const struct spa_pod *param) case SPA_PROP_channelVolumes: { uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { s->volume.n_volumes = n; for (n = 0; n < s->volume.n_volumes; n++) s->volume.volumes[n] = vols[n]; @@ -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 ? @@ -1026,14 +1028,14 @@ static int handle_follower_available(struct impl *impl, struct nj2_session_param follower->source.n_ports = peer->params.recv_audio_channels + peer->params.recv_midi_channels; follower->source.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.recv_audio_channels != follower->source.info.channels) { - follower->source.info.channels = SPA_MIN(peer->params.recv_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + follower->source.info.channels = peer->params.recv_audio_channels; for (i = 0; i < follower->source.info.channels; i++) follower->source.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } follower->sink.n_ports = peer->params.send_audio_channels + peer->params.send_midi_channels; follower->sink.info.rate = peer->params.sample_rate; if ((uint32_t)peer->params.send_audio_channels != follower->sink.info.channels) { - follower->sink.info.channels = SPA_MIN(peer->params.send_audio_channels, (int)SPA_AUDIO_MAX_CHANNELS); + follower->sink.info.channels = peer->params.send_audio_channels; for (i = 0; i < follower->sink.info.channels; i++) follower->sink.info.position[i] = SPA_AUDIO_CHANNEL_AUX0 + i; } @@ -1290,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-netjack2/peer.c b/src/modules/module-netjack2/peer.c index 37f7854db..eacc1c95b 100644 --- a/src/modules/module-netjack2/peer.c +++ b/src/modules/module-netjack2/peer.c @@ -7,10 +7,12 @@ #include #endif +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + struct volume { bool mute; uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; + float volumes[MAX_CHANNELS]; }; static inline float bswap_f32(float f) diff --git a/src/modules/module-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 e3d438c93..c02503fc4 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -12,6 +12,8 @@ #include "format.h" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + static const struct format audio_formats[] = { [SAMPLE_U8] = { SAMPLE_U8, SPA_AUDIO_FORMAT_U8, "u8", 1 }, [SAMPLE_ALAW] = { SAMPLE_ALAW, SPA_AUDIO_FORMAT_ALAW, "alaw", 1 }, @@ -296,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) @@ -346,9 +348,9 @@ uint32_t channel_paname2id(const char *name, size_t size) } -void channel_map_to_positions(const struct channel_map *map, uint32_t *pos) +void channel_map_to_positions(const struct channel_map *map, uint32_t *pos, uint32_t max_pos) { - uint32_t i, channels = SPA_MIN(map->channels, SPA_AUDIO_MAX_CHANNELS); + uint32_t i, channels = SPA_MIN(map->channels, max_pos); for (i = 0; i < channels; i++) pos[i] = map->map[i]; } @@ -448,8 +450,9 @@ void channel_map_parse(const char *str, struct channel_map *map) void channel_map_parse_position(const char *str, struct channel_map *map) { - uint32_t channels = 0, position[SPA_AUDIO_MAX_CHANNELS]; - spa_audio_parse_position(str, strlen(str), position, &channels); + uint32_t channels = 0, position[CHANNELS_MAX]; + spa_audio_parse_position_n(str, strlen(str), position, + SPA_N_ELEMENTS(position), &channels); positions_to_channel_map(position, channels, map); } @@ -532,8 +535,7 @@ int format_parse_param(const struct spa_pod *param, bool collect, info.info.raw.rate = 48000; if (info.info.raw.format == 0 || info.info.raw.rate == 0 || - info.info.raw.channels == 0 || - info.info.raw.channels > SPA_AUDIO_MAX_CHANNELS) + info.info.raw.channels == 0) return -ENOTSUP; } break; @@ -631,8 +633,8 @@ const struct spa_pod *format_build_param(struct spa_pod_builder *b, uint32_t id, SPA_FORMAT_AUDIO_channels, SPA_POD_Int(spec->channels), 0); if (map && map->channels == spec->channels) { - uint32_t positions[SPA_AUDIO_MAX_CHANNELS]; - channel_map_to_positions(map, positions); + uint32_t positions[spec->channels]; + channel_map_to_positions(map, positions, spec->channels); spa_pod_builder_add(b, SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, spec->channels, positions), 0); @@ -683,7 +685,7 @@ static int add_int(struct format_info *info, const char *k, struct spa_pod *para return -ENOENT; val = spa_pod_get_values(&prop->value, &n_values, &choice); - if (!spa_pod_is_int(val)) + if (val->type != SPA_TYPE_Int || val->size != sizeof(values[0])) return -ENOTSUP; if (n_values == 0) @@ -745,7 +747,7 @@ static int format_info_iec958_from_param(struct format_info *info, struct spa_po return -ENOENT; val = spa_pod_get_values(&prop->value, &n_values, &choice); - if (val->type != SPA_TYPE_Id) + if (val->type != SPA_TYPE_Id || val->size != sizeof(values[0])) return -ENOTSUP; if (index >= n_values) diff --git a/src/modules/module-protocol-pulse/format.h b/src/modules/module-protocol-pulse/format.h index d564b1822..50bb8e2f8 100644 --- a/src/modules/module-protocol-pulse/format.h +++ b/src/modules/module-protocol-pulse/format.h @@ -190,13 +190,13 @@ 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); uint32_t channel_paname2id(const char *name, size_t size); -void channel_map_to_positions(const struct channel_map *map, uint32_t *pos); +void channel_map_to_positions(const struct channel_map *map, uint32_t *pos, uint32_t max_pos); void channel_map_parse(const char *str, struct channel_map *map); bool channel_map_valid(const struct channel_map *map); void channel_map_parse_position(const char *str, struct channel_map *map); diff --git a/src/modules/module-protocol-pulse/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 a0de01d0d..2b47d93e9 100644 --- a/src/modules/module-protocol-pulse/module.c +++ b/src/modules/module-protocol-pulse/module.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -226,14 +227,15 @@ int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props info->channels, map.channels); return -EINVAL; } - channel_map_to_positions(&map, info->position); + channel_map_to_positions(&map, info->position, SPA_N_ELEMENTS(info->position)); pw_properties_set(props, key_channel_map, NULL); } else { if (info->channels == 0) info->channels = impl->defs.sample_spec.channels; if (info->channels == impl->defs.channel_map.channels) { - channel_map_to_positions(&impl->defs.channel_map, info->position); + channel_map_to_positions(&impl->defs.channel_map, + info->position, SPA_N_ELEMENTS(info->position)); } else if (info->channels == 1) { info->position[0] = SPA_AUDIO_CHANNEL_MONO; } else if (info->channels == 2) { @@ -281,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(info->position[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-pulse/volume.c b/src/modules/module-protocol-pulse/volume.c index de71baa53..e53f967ec 100644 --- a/src/modules/module-protocol-pulse/volume.c +++ b/src/modules/module-protocol-pulse/volume.c @@ -53,7 +53,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo if (monitor) continue; info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - info->volume.values, CHANNELS_MAX); + info->volume.values, SPA_N_ELEMENTS(info->volume.values)); SPA_FLAG_UPDATE(info->flags, VOLUME_HW_VOLUME, prop->flags & SPA_POD_PROP_FLAG_HARDWARE); break; @@ -68,7 +68,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo if (!monitor) continue; info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - info->volume.values, CHANNELS_MAX); + info->volume.values, SPA_N_ELEMENTS(info->volume.values)); SPA_FLAG_CLEAR(info->flags, VOLUME_HW_VOLUME); break; case SPA_PROP_volumeBase: @@ -84,7 +84,7 @@ int volume_parse_param(const struct spa_pod *param, struct volume_info *info, bo } case SPA_PROP_channelMap: info->map.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - info->map.map, CHANNELS_MAX); + info->map.map, SPA_N_ELEMENTS(info->map.map)); break; default: break; diff --git a/src/modules/module-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 c9b9e5f9d..4c4ab1a26 100644 --- a/src/modules/module-pulse-tunnel.c +++ b/src/modules/module-pulse-tunnel.c @@ -117,6 +117,8 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION "[ FL FR ]" +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + #define MODULE_USAGE "( remote.name= ] " \ "( node.latency= ] " \ "( node.name= ] " \ @@ -295,10 +297,10 @@ static void stream_param_changed(void *d, uint32_t id, const struct spa_pod *par { struct pa_cvolume volume; uint32_t n; - float vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { volume.channels = SPA_MIN(PA_CHANNELS_MAX, n); for (n = 0; n < volume.channels; n++) volume.values[n] = pa_sw_volume_from_linear(vols[n]); @@ -752,7 +754,8 @@ static int create_pulse_stream(struct impl *impl) map.channels = impl->info.channels; for (i = 0; i < map.channels; i++) - map.map[i] = (pa_channel_position_t)channel_id2pa(impl->info.position[i], &aux); + map.map[i] = (pa_channel_position_t)channel_id2pa( + impl->info.position[i], &aux); snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"), pw_get_user_name(), pw_get_host_name()); @@ -832,10 +835,10 @@ do_stream_sync_volumes(struct spa_loop *loop, struct spa_pod_frame f[1]; struct spa_pod *param; uint32_t i, channels; - float vols[SPA_AUDIO_MAX_CHANNELS]; - float soft_vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS]; + float soft_vols[MAX_CHANNELS]; - channels = SPA_MIN(impl->volume.channels, SPA_AUDIO_MAX_CHANNELS); + channels = SPA_MIN(impl->volume.channels, MAX_CHANNELS); for (i = 0; i < channels; i++) { vols[i] = (float)pa_sw_volume_to_linear(impl->volume.values[i]); soft_vols[i] = 1.0f; @@ -1045,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)), @@ -1189,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-raop-sink.c b/src/modules/module-raop-sink.c index 11948807b..accc2a9da 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -158,6 +158,7 @@ PW_LOG_TOPIC(mod_topic, "mod." NAME); #define RAOP_LATENCY_MS 250 #define DEFAULT_LATENCY_MS 1500 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define VOLUME_MAX 0.0 #define VOLUME_MIN -30.0 #define VOLUME_MUTE -144.0 @@ -1612,11 +1613,11 @@ static void stream_props_changed(struct impl *impl, uint32_t id, const struct sp case SPA_PROP_channelVolumes: { uint32_t i, n_vols; - float vols[SPA_AUDIO_MAX_CHANNELS], volume; - float soft_vols[SPA_AUDIO_MAX_CHANNELS]; + float vols[MAX_CHANNELS], volume; + float soft_vols[MAX_CHANNELS]; if ((n_vols = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - vols, SPA_AUDIO_MAX_CHANNELS)) > 0) { + vols, SPA_N_ELEMENTS(vols))) > 0) { volume = 0.0f; for (i = 0; i < n_vols; i++) { volume += vols[i]; diff --git a/src/modules/module-rtp-sap.c b/src/modules/module-rtp-sap.c index 0c00beb61..22fe89b68 100644 --- a/src/modules/module-rtp-sap.c +++ b/src/modules/module-rtp-sap.c @@ -156,6 +156,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define DEFAULT_LOOP false #define MAX_SDP 2048 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS #define USAGE "( local.ifname= ) " \ "( sap.ip= ) " \ @@ -1404,7 +1405,7 @@ static int parse_sdp_i(struct impl *impl, char *c, struct sdp_info *info) c[strcspn(c, " ")] = '\0'; uint32_t channels; - if (sscanf(c, "%u", &channels) != 1 || channels <= 0 || channels > SPA_AUDIO_MAX_CHANNELS) + if (sscanf(c, "%u", &channels) != 1 || channels <= 0 || channels > MAX_CHANNELS) return 0; c += strcspn(c, "\0"); diff --git a/src/modules/module-rtp/opus.c b/src/modules/module-rtp/opus.c index 6f27bd2bd..7eeda7f43 100644 --- a/src/modules/module-rtp/opus.c +++ b/src/modules/module-rtp/opus.c @@ -334,9 +334,12 @@ static void rtp_opus_deinit(struct impl *impl, enum spa_direction direction) static int rtp_opus_init(struct impl *impl, enum spa_direction direction) { int err; - unsigned char mapping[SPA_AUDIO_MAX_CHANNELS]; + unsigned char mapping[255]; uint32_t i; + if (impl->info.info.opus.channels > 255) + return -EINVAL; + if (impl->psamples >= 2880) impl->psamples = 2880; else if (impl->psamples >= 1920) 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 3a7a31827..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)), @@ -274,9 +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)) + 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 e165a1174..b324db403 100644 --- a/src/modules/module-zeroconf-discover.c +++ b/src/modules/module-zeroconf-discover.c @@ -188,17 +188,17 @@ 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); - channel_map_to_positions(&channel_map, pos); + channel_map_to_positions(&channel_map, pos, CHANNELS_MAX); p = s = alloca(4 + channel_map.channels * 8); p += spa_scnprintf(p, 2, "["); 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/pipewire/stream.c b/src/pipewire/stream.c index 4085e0885..1a6311200 100644 --- a/src/pipewire/stream.c +++ b/src/pipewire/stream.c @@ -1270,6 +1270,8 @@ static int node_event_param(void *object, int seq, return 0; c = calloc(1, sizeof(*c) + SPA_POD_SIZE(param)); + if (c == NULL) + return -ENOMEM; c->info = SPA_PTROFF(c, sizeof(*c), struct spa_pod); memcpy(c->info, param, SPA_POD_SIZE(param)); c->control.n_values = 0; @@ -1290,6 +1292,10 @@ static int node_event_param(void *object, int seq, free(c); return -EINVAL; } + if (n_vals > 1 && pod->size != spa_pod_type_size(pod->type)) { + free(c); + return -ENOTSUP; + } c->type = pod->type; if (spa_pod_is_float(pod)) diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index cc1a3bda2..632a513bf 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -70,6 +70,8 @@ #define DEFAULT_VOLUME 1.0 #define DEFAULT_QUALITY 4 +#define MAX_CHANNELS SPA_AUDIO_MAX_CHANNELS + enum mode { mode_none, mode_playback, @@ -91,7 +93,7 @@ typedef int (*fill_fn)(struct data *d, void *dest, unsigned int n_frames, bool * struct channelmap { uint32_t n_channels; - uint32_t channels[SPA_AUDIO_MAX_CHANNELS]; + uint32_t channels[MAX_CHANNELS]; }; struct data { @@ -702,7 +704,8 @@ static int parse_channelmap(const char *channel_map, struct channelmap *map) } } - spa_audio_parse_position(channel_map, strlen(channel_map), map->channels, &map->n_channels); + spa_audio_parse_position_n(channel_map, strlen(channel_map), + map->channels, SPA_N_ELEMENTS(map->channels), &map->n_channels); return 0; } @@ -744,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")); } } @@ -2344,9 +2348,23 @@ int main(int argc, char *argv[]) .rate = data.rate, .channels = data.channels); - if (data.channelmap.n_channels) - memcpy(info.position, data.channelmap.channels, data.channels * sizeof(int)); - + if (data.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) { + 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; }