From 0f62d3442cba151db1a04a068ce6f3211823a883 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 23 Jun 2022 10:20:49 +0200 Subject: [PATCH] alsa: handle driver bugs better Use the NEAREST flag when setting a format. This only works for raw formats and will update the format with the nearest accepted rate or channels. We can then query the real configured format and use that for the converter. This makes things work when a driver tells us it can do 44100Hz but then refuses and changes the rate to 48000. See #2197, #2457, #2455, rhbz#2096193 --- spa/plugins/alsa/alsa-pcm-sink.c | 4 +-- spa/plugins/alsa/alsa-pcm-source.c | 4 +-- spa/plugins/alsa/alsa-pcm.c | 6 +++++ spa/plugins/audioconvert/audioadapter.c | 33 +++++++++++++++++++------ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/spa/plugins/alsa/alsa-pcm-sink.c b/spa/plugins/alsa/alsa-pcm-sink.c index b26554b68..6387ae17a 100644 --- a/spa/plugins/alsa/alsa-pcm-sink.c +++ b/spa/plugins/alsa/alsa-pcm-sink.c @@ -616,7 +616,7 @@ static int port_set_format(void *object, const struct spa_pod *format) { struct state *this = object; - int err; + int err = 0; if (format == NULL) { if (!this->have_format) @@ -673,7 +673,7 @@ static int port_set_format(void *object, } emit_port_info(this, false); - return 0; + return err; } static int diff --git a/spa/plugins/alsa/alsa-pcm-source.c b/spa/plugins/alsa/alsa-pcm-source.c index c4222dc89..c50a4dea7 100644 --- a/spa/plugins/alsa/alsa-pcm-source.c +++ b/spa/plugins/alsa/alsa-pcm-source.c @@ -566,7 +566,7 @@ static int port_set_format(void *object, uint32_t flags, const struct spa_pod *format) { struct state *this = object; - int err; + int err = 0; if (format == NULL) { if (!this->have_format) @@ -610,7 +610,7 @@ static int port_set_format(void *object, } emit_port_info(this, false); - return 0; + return err; } static int diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index de321ee3c..19c2c0a0e 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -1409,7 +1409,10 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ state->props.device, rchannels, val); if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) return -EINVAL; + if (fmt->media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; rchannels = val; + fmt->info.raw.channels = rchannels; match = false; } @@ -1429,7 +1432,10 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ state->props.device, rrate, val); if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) return -EINVAL; + if (fmt->media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; rrate = val; + fmt->info.raw.rate = rrate; match = false; } diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 19b1e1cc5..cfb8163bc 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -442,6 +442,29 @@ static int configure_format(struct impl *this, uint32_t flags, const struct spa_ if (format && spa_log_level_enabled(this->log, SPA_LOG_LEVEL_DEBUG)) spa_debug_format(0, NULL, format); + if ((res = spa_node_port_set_param(this->follower, + this->direction, 0, + SPA_PARAM_Format, flags, + format)) < 0) + return res; + if (res > 0) { + uint8_t buffer[4096]; + struct spa_pod_builder b = { 0 }; + uint32_t state = 0; + struct spa_pod *fmt; + + /* format was changed to nearest compatible format */ + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + if ((res = spa_node_port_enum_params_sync(this->follower, + this->direction, 0, + SPA_PARAM_Format, &state, + NULL, &fmt, &b)) != 1) + return -EIO; + + format = fmt; + } + if (this->target != this->follower && this->convert) { if ((res = spa_node_port_set_param(this->convert, SPA_DIRECTION_REVERSE(this->direction), 0, @@ -450,12 +473,6 @@ static int configure_format(struct impl *this, uint32_t flags, const struct spa_ return res; } - if ((res = spa_node_port_set_param(this->follower, - this->direction, 0, - SPA_PARAM_Format, flags, - format)) < 0) - return res; - this->have_format = format != NULL; if (format == NULL) { this->n_buffers = 0; @@ -513,7 +530,7 @@ static int reconfigure_mode(struct impl *this, bool passthrough, /* set new target */ this->target = passthrough ? this->follower : this->convert; - if ((res = configure_format(this, 0, format)) < 0) + if ((res = configure_format(this, SPA_NODE_PARAM_FLAG_NEAREST, format)) < 0) return res; if (this->passthrough != passthrough) { @@ -775,7 +792,7 @@ static int negotiate_format(struct impl *this) spa_pod_fixate(format); - res = configure_format(this, 0, format); + res = configure_format(this, SPA_NODE_PARAM_FLAG_NEAREST, format); done: spa_node_send_command(this->follower,