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
This commit is contained in:
Wim Taymans 2022-06-23 10:20:49 +02:00
parent ecc0eecf0f
commit 0f62d3442c
4 changed files with 35 additions and 12 deletions

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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,