mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
audioadapter: improve fixation of the format
Keep track of the format as given in the PortConfig. Instead of blindly fixating the negotiated format to whatever default, use the PortConfig format to fixate to something better. This makes the channels/position, rate or format match the PortConfig format when this is possible and results in the least amount of conversions. It mostly improves the handling of wildcard formats, were a stream only specifies some fields and leaves the other free. A concrete case is WINE that uses the pulseaudio FIX flags to omit the number of channels and rate. With this change, the stream will negotiate to the format of the linked sink and obtain the channelmap from it. See #876
This commit is contained in:
parent
e420b9c93c
commit
c0727e1efc
1 changed files with 74 additions and 2 deletions
|
|
@ -68,6 +68,7 @@ struct impl {
|
|||
struct spa_hook follower_listener;
|
||||
uint32_t follower_flags;
|
||||
struct spa_audio_info follower_current_format;
|
||||
struct spa_audio_info default_format;
|
||||
|
||||
struct spa_handle *hnd_convert;
|
||||
struct spa_node *convert;
|
||||
|
|
@ -522,6 +523,31 @@ static int reconfigure_mode(struct impl *this, bool passthrough,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int format_audio_raw_parse_opt(const struct spa_pod *format, struct spa_audio_info_raw *info)
|
||||
{
|
||||
struct spa_pod *position = NULL;
|
||||
uint32_t media_type, media_subtype;
|
||||
int res;
|
||||
if ((res = spa_format_parse(format, &media_type, &media_subtype)) < 0)
|
||||
return res;
|
||||
if (media_type != SPA_MEDIA_TYPE_audio ||
|
||||
media_subtype != SPA_MEDIA_SUBTYPE_raw)
|
||||
return -ENOTSUP;
|
||||
|
||||
spa_zero(*info);
|
||||
res = spa_pod_parse_object(format,
|
||||
SPA_TYPE_OBJECT_Format, NULL,
|
||||
SPA_FORMAT_AUDIO_format, SPA_POD_OPT_Id(&info->format),
|
||||
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 (position == NULL ||
|
||||
!spa_pod_copy_array(position, SPA_TYPE_Id, info->position, SPA_AUDIO_MAX_CHANNELS))
|
||||
SPA_FLAG_SET(info->flags, SPA_AUDIO_FLAG_UNPOSITIONED);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
|
|
@ -569,6 +595,12 @@ 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)
|
||||
return -EINVAL;
|
||||
|
||||
if (format) {
|
||||
struct spa_audio_info info;
|
||||
if (format_audio_raw_parse_opt(format, &info.info.raw) >= 0)
|
||||
this->default_format = info;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case SPA_PARAM_PORT_CONFIG_MODE_none:
|
||||
return -ENOTSUP;
|
||||
|
|
@ -634,10 +666,44 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
|
|||
return res;
|
||||
}
|
||||
|
||||
static struct spa_pod *merge_objects(struct impl *this, struct spa_pod_builder *b, uint32_t id,
|
||||
struct spa_pod_object *o1, struct spa_pod_object *o2)
|
||||
{
|
||||
const struct spa_pod_prop *p1, *p2;
|
||||
struct spa_pod_frame f;
|
||||
struct spa_pod_builder_state state;
|
||||
int res = 0;
|
||||
|
||||
if (o2 == NULL || SPA_POD_TYPE(o1) != SPA_POD_TYPE(o2))
|
||||
return (struct spa_pod*)o1;
|
||||
|
||||
spa_pod_builder_push_object(b, &f, o1->body.type, o1->body.id);
|
||||
p2 = NULL;
|
||||
SPA_POD_OBJECT_FOREACH(o1, p1) {
|
||||
p2 = spa_pod_object_find_prop(o2, p2, p1->key);
|
||||
if (p2 != NULL) {
|
||||
spa_pod_builder_get_state(b, &state);
|
||||
res = spa_pod_filter_prop(b, p1, p2);
|
||||
if (res < 0)
|
||||
spa_pod_builder_reset(b, &state);
|
||||
}
|
||||
if (p2 == NULL || res < 0)
|
||||
spa_pod_builder_raw_padded(b, p1, SPA_POD_PROP_SIZE(p1));
|
||||
}
|
||||
p1 = NULL;
|
||||
SPA_POD_OBJECT_FOREACH(o2, p2) {
|
||||
p1 = spa_pod_object_find_prop(o1, p1, p2->key);
|
||||
if (p1 != NULL)
|
||||
continue;
|
||||
spa_pod_builder_raw_padded(b, p2, SPA_POD_PROP_SIZE(p2));
|
||||
}
|
||||
return spa_pod_builder_pop(b, &f);
|
||||
}
|
||||
|
||||
static int negotiate_format(struct impl *this)
|
||||
{
|
||||
uint32_t state;
|
||||
struct spa_pod *format;
|
||||
struct spa_pod *format, *def;
|
||||
uint8_t buffer[4096];
|
||||
struct spa_pod_builder b = { 0 };
|
||||
int res;
|
||||
|
|
@ -669,7 +735,6 @@ static int negotiate_format(struct impl *this)
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->convert) {
|
||||
state = 0;
|
||||
if ((res = spa_node_port_enum_params_sync(this->convert,
|
||||
|
|
@ -688,6 +753,13 @@ static int negotiate_format(struct impl *this)
|
|||
goto done;
|
||||
}
|
||||
|
||||
def = spa_format_audio_raw_build(&b,
|
||||
SPA_PARAM_Format, &this->default_format.info.raw);
|
||||
|
||||
format = merge_objects(this, &b, SPA_PARAM_Format,
|
||||
(struct spa_pod_object*)format,
|
||||
(struct spa_pod_object*)def);
|
||||
|
||||
spa_pod_fixate(format);
|
||||
|
||||
res = configure_format(this, 0, format);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue