From eb797cac48d1538ba159216497016c8d57cf1633 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 23 Feb 2023 12:41:37 +0100 Subject: [PATCH] pulse-server: improve FIX_ flag handling When a stream uses the FIX_ flags it should negotiate to the format of the sink or source it connects to. To do this, look up the sink or source and look at the format, use this as the allowed format for the stream when the FIX flags are set. Make it still possible to override with with properties. Use audio.position to make it possible to set a channelmap override. --- src/modules/module-protocol-pulse/format.c | 30 +++++++---- src/modules/module-protocol-pulse/format.h | 5 +- .../module-protocol-pulse/pulse-server.c | 54 ++++++++++++++++--- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 3477ea01c..2efe45527 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -225,27 +225,35 @@ bool sample_spec_valid(const struct sample_spec *ss) ss->channels > 0 && ss->channels <= CHANNELS_MAX); } -void sample_spec_fix(struct sample_spec *ss, struct spa_dict *props, - bool fix_format, bool fix_rate, bool fix_channels) +void sample_spec_fix(struct sample_spec *ss, struct channel_map *map, + const struct sample_spec *fix_ss, const struct channel_map *fix_map, + struct spa_dict *props) { const char *str; - if (fix_format) { + if (fix_ss->format != 0) { if ((str = spa_dict_lookup(props, "pulse.fix.format")) != NULL) ss->format = format_name2id(str); else - ss->format = SPA_AUDIO_FORMAT_UNKNOWN; + ss->format = fix_ss->format; + /* convert back and forth to convert potential planar to packed */ + ss->format = format_pa2id(format_id2pa(ss->format)); } - if (fix_rate) { + if (fix_ss->rate != 0) { if ((str = spa_dict_lookup(props, "pulse.fix.rate")) != NULL) ss->rate = atoi(str); else - ss->rate = 0; + ss->rate = fix_ss->rate; + ss->rate = SPA_CLAMP(ss->rate, 0u, RATE_MAX); } - if (fix_channels) { - if ((str = spa_dict_lookup(props, "pulse.fix.channels")) != NULL) - ss->channels = atoi(str); - else - ss->channels = 0; + if (fix_ss->channels != 0) { + if ((str = spa_dict_lookup(props, "pulse.fix.position")) != NULL) { + channel_map_parse_position(str, map); + ss->channels = map->channels; + } else { + ss->channels = fix_ss->channels; + *map = *fix_map; + } + ss->channels = SPA_CLAMP(ss->channels, 0u, CHANNELS_MAX); } } diff --git a/src/modules/module-protocol-pulse/format.h b/src/modules/module-protocol-pulse/format.h index 77795a5e4..6818ec179 100644 --- a/src/modules/module-protocol-pulse/format.h +++ b/src/modules/module-protocol-pulse/format.h @@ -174,8 +174,9 @@ uint32_t format_encoding2id(enum encoding enc); uint32_t sample_spec_frame_size(const struct sample_spec *ss); bool sample_spec_valid(const struct sample_spec *ss); -void sample_spec_fix(struct sample_spec *ss, struct spa_dict *props, - bool fix_format, bool fix_rate, bool fix_channels); +void sample_spec_fix(struct sample_spec *ss, struct channel_map *map, + const struct sample_spec *fix_ss, const struct channel_map *fix_map, + struct spa_dict *props); uint32_t channel_pa2id(enum channel_position channel); const char *channel_id2name(uint32_t channel); diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index fbfe7a6f1..a2a9f6cdb 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -1549,8 +1549,8 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui struct impl *impl = client->impl; const char *name = NULL; int res; - struct sample_spec ss; - struct channel_map map; + struct sample_spec ss, fix_ss; + struct channel_map map, fix_map; uint32_t sink_index, syncid, rate = 0; const char *sink_name; struct buffer_attr attr = { 0 }; @@ -1625,6 +1625,24 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui TAG_INVALID) < 0) goto error_protocol; } + + spa_zero(fix_ss); + spa_zero(fix_map); + if (fix_format || fix_rate || fix_channels) { + struct pw_manager_object *o; + bool is_monitor; + + o = find_device(client, sink_index, sink_name, true, &is_monitor); + if (o != NULL) { + struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_OUTPUT); + collect_device_info(o, NULL, &dev_info, is_monitor, &impl->defs); + fix_ss.format = fix_format ? dev_info.ss.format : 0; + fix_ss.rate = fix_rate ? dev_info.ss.rate : 0; + fix_ss.channels = fix_channels ? dev_info.ss.channels : 0; + fix_map = dev_info.map; + } + } + if (client->version >= 13) { if (message_get(m, TAG_BOOLEAN, &muted, @@ -1694,15 +1712,16 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui } if (sample_spec_valid(&ss)) { struct sample_spec sfix = ss; + struct channel_map mfix = map; rate = ss.rate; - sample_spec_fix(&sfix, &props->dict, fix_format, fix_rate, fix_channels); + sample_spec_fix(&sfix, &mfix, &fix_ss, &fix_map, &props->dict); if (n_params < MAX_FORMATS && (params[n_params] = format_build_param(&b, SPA_PARAM_EnumFormat, &sfix, - sfix.channels > 0 ? &map : NULL)) != NULL) { + sfix.channels > 0 ? &mfix : NULL)) != NULL) { n_params++; n_valid_formats++; } else { @@ -1800,8 +1819,8 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint struct impl *impl = client->impl; const char *name = NULL; int res; - struct sample_spec ss; - struct channel_map map; + struct sample_spec ss, fix_ss; + struct channel_map map, fix_map; uint32_t source_index; const char *source_name; struct buffer_attr attr = { 0 }; @@ -1896,6 +1915,24 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint TAG_INVALID) < 0) goto error_protocol; } + + spa_zero(fix_ss); + spa_zero(fix_map); + if (fix_format || fix_rate || fix_channels) { + struct pw_manager_object *o; + bool is_monitor; + + o = find_device(client, source_index, source_name, false, &is_monitor); + if (o != NULL) { + struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_INPUT); + collect_device_info(o, NULL, &dev_info, is_monitor, &impl->defs); + fix_ss.format = fix_format ? dev_info.ss.format : 0; + fix_ss.rate = fix_rate ? dev_info.ss.rate : 0; + fix_ss.channels = fix_channels ? dev_info.ss.channels : 0; + fix_map = dev_info.map; + } + } + if (client->version >= 22) { if (message_get(m, TAG_U8, &n_formats, @@ -1940,15 +1977,16 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint } if (sample_spec_valid(&ss)) { struct sample_spec sfix = ss; + struct channel_map mfix = map; rate = ss.rate; - sample_spec_fix(&sfix, &props->dict, fix_format, fix_rate, fix_channels); + sample_spec_fix(&sfix, &mfix, &fix_ss, &fix_map, &props->dict); if (n_params < MAX_FORMATS && (params[n_params] = format_build_param(&b, SPA_PARAM_EnumFormat, &sfix, - sfix.channels > 0 ? &map : NULL)) != NULL) { + sfix.channels > 0 ? &mfix : NULL)) != NULL) { n_params++; n_valid_formats++; } else {