module-echo-cancel: add capture and playback props

Add a capture.props and playback.props config section to set properties
for the capture and playback stream.

Fixes #2939
This commit is contained in:
Wim Taymans 2023-01-07 13:58:30 +01:00
parent 6fb543a514
commit feac9f1c70

View file

@ -69,8 +69,10 @@
* *
* Options specific to the behavior of this module * Options specific to the behavior of this module
* *
* - `capture.props = {}`: properties to be passed to the capture stream
* - `source.props = {}`: properties to be passed to the source stream * - `source.props = {}`: properties to be passed to the source stream
* - `sink.props = {}`: properties to be passed to the sink stream * - `sink.props = {}`: properties to be passed to the sink stream
* - `playback.props = {}`: properties to be passed to the playback stream
* - `library.name = <str>`: the echo cancellation library Currently supported: * - `library.name = <str>`: the echo cancellation library Currently supported:
* `aec/libspa-aec-webrtc`. Leave unset to use the default method (`aec/libspa-aec-webrtc`). * `aec/libspa-aec-webrtc`. Leave unset to use the default method (`aec/libspa-aec-webrtc`).
* - `aec.args = <str>`: arguments to pass to the echo cancellation method * - `aec.args = <str>`: arguments to pass to the echo cancellation method
@ -99,12 +101,18 @@
* args = { * args = {
* # library.name = aec/libspa-aec-webrtc * # library.name = aec/libspa-aec-webrtc
* # node.latency = 1024/48000 * # node.latency = 1024/48000
* capture.props = {
* node.name = "Echo Cancellation Capture"
* }
* source.props = { * source.props = {
* node.name = "Echo Cancellation Source" * node.name = "Echo Cancellation Source"
* } * }
* sink.props = { * sink.props = {
* node.name = "Echo Cancellation Sink" * node.name = "Echo Cancellation Sink"
* } * }
* playback.props = {
* node.name = "Echo Cancellation Playback"
* }
* } * }
* } * }
*] *]
@ -147,8 +155,10 @@ static const struct spa_dict_item module_props[] = {
"[ buffer.play_delay=<delay as fraction> ] " "[ buffer.play_delay=<delay as fraction> ] "
"[ library.name =<library name> ] " "[ library.name =<library name> ] "
"[ aec.args=<aec arguments> ] " "[ aec.args=<aec arguments> ] "
"[ capture.props=<properties> ] "
"[ source.props=<properties> ] " "[ source.props=<properties> ] "
"[ sink.props=<properties> ] " }, "[ sink.props=<properties> ] "
"[ playback.props=<properties> ] " },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
}; };
@ -164,17 +174,22 @@ struct impl {
struct spa_audio_info_raw info; struct spa_audio_info_raw info;
struct pw_properties *capture_props;
struct pw_stream *capture; struct pw_stream *capture;
struct spa_hook capture_listener; struct spa_hook capture_listener;
struct pw_properties *source_props; struct pw_properties *source_props;
struct pw_stream *source; struct pw_stream *source;
struct spa_hook source_listener; struct spa_hook source_listener;
void *rec_buffer[SPA_AUDIO_MAX_CHANNELS]; void *rec_buffer[SPA_AUDIO_MAX_CHANNELS];
uint32_t rec_ringsize; uint32_t rec_ringsize;
struct spa_ringbuffer rec_ring; struct spa_ringbuffer rec_ring;
struct pw_properties *playback_props;
struct pw_stream *playback; struct pw_stream *playback;
struct spa_hook playback_listener; struct spa_hook playback_listener;
struct pw_properties *sink_props; struct pw_properties *sink_props;
struct pw_stream *sink; struct pw_stream *sink;
struct spa_hook sink_listener; struct spa_hook sink_listener;
@ -761,32 +776,11 @@ static int setup_streams(struct impl *impl)
uint32_t offsets[512]; uint32_t offsets[512];
const struct spa_pod *params[512]; const struct spa_pod *params[512];
struct spa_pod_dynamic_builder b; struct spa_pod_dynamic_builder b;
struct pw_properties *props;
const char *str;
uint32_t index; uint32_t index;
props = pw_properties_new(
PW_KEY_NODE_NAME, "echo-cancel-capture",
PW_KEY_NODE_VIRTUAL, "true",
PW_KEY_NODE_PASSIVE, "true",
NULL);
if ((str = pw_properties_get(impl->source_props, PW_KEY_NODE_GROUP)) != NULL)
pw_properties_set(props, PW_KEY_NODE_GROUP, str);
if ((str = pw_properties_get(impl->source_props, PW_KEY_NODE_LINK_GROUP)) != NULL)
pw_properties_set(props, PW_KEY_NODE_LINK_GROUP, str);
if ((str = pw_properties_get(impl->source_props, PW_KEY_NODE_LATENCY)) != NULL)
pw_properties_set(props, PW_KEY_NODE_LATENCY, str);
else if (impl->aec->latency)
pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency);
if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_CHANNELS)) != NULL)
pw_properties_set(props, SPA_KEY_AUDIO_CHANNELS, str);
if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL)
pw_properties_set(props, SPA_KEY_AUDIO_POSITION, str);
if ((str = pw_properties_get(impl->source_props, "resample.prefill")) != NULL)
pw_properties_set(props, "resample.prefill", str);
impl->capture = pw_stream_new(impl->core, impl->capture = pw_stream_new(impl->core,
"Echo-Cancel Capture", props); "Echo-Cancel Capture", impl->capture_props);
impl->capture_props = NULL;
if (impl->capture == NULL) if (impl->capture == NULL)
return -errno; return -errno;
@ -807,28 +801,9 @@ static int setup_streams(struct impl *impl)
if (impl->monitor_mode) { if (impl->monitor_mode) {
impl->playback = NULL; impl->playback = NULL;
} else { } else {
props = pw_properties_new(
PW_KEY_NODE_NAME, "echo-cancel-playback",
PW_KEY_NODE_VIRTUAL, "true",
PW_KEY_NODE_PASSIVE, "true",
NULL);
if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_GROUP)) != NULL)
pw_properties_set(props, PW_KEY_NODE_GROUP, str);
if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_LINK_GROUP)) != NULL)
pw_properties_set(props, PW_KEY_NODE_LINK_GROUP, str);
if ((str = pw_properties_get(impl->sink_props, PW_KEY_NODE_LATENCY)) != NULL)
pw_properties_set(props, PW_KEY_NODE_LATENCY, str);
else if (impl->aec->latency)
pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency);
if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_CHANNELS)) != NULL)
pw_properties_set(props, SPA_KEY_AUDIO_CHANNELS, str);
if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL)
pw_properties_set(props, SPA_KEY_AUDIO_POSITION, str);
if ((str = pw_properties_get(impl->sink_props, "resample.prefill")) != NULL)
pw_properties_set(props, "resample.prefill", str);
impl->playback = pw_stream_new(impl->core, impl->playback = pw_stream_new(impl->core,
"Echo-Cancel Playback", props); "Echo-Cancel Playback", impl->playback_props);
impl->playback_props = NULL;
if (impl->playback == NULL) if (impl->playback == NULL)
return -errno; return -errno;
@ -980,7 +955,9 @@ static void impl_destroy(struct impl *impl)
pw_core_disconnect(impl->core); pw_core_disconnect(impl->core);
if (impl->spa_handle) if (impl->spa_handle)
spa_plugin_loader_unload(impl->loader, impl->spa_handle); spa_plugin_loader_unload(impl->loader, impl->spa_handle);
pw_properties_free(impl->capture_props);
pw_properties_free(impl->source_props); pw_properties_free(impl->source_props);
pw_properties_free(impl->playback_props);
pw_properties_free(impl->sink_props); pw_properties_free(impl->sink_props);
for (i = 0; i < impl->info.channels; i++) { for (i = 0; i < impl->info.channels; i++) {
@ -1055,8 +1032,12 @@ static void copy_props(struct impl *impl, struct pw_properties *props, const cha
{ {
const char *str; const char *str;
if ((str = pw_properties_get(props, key)) != NULL) { if ((str = pw_properties_get(props, key)) != NULL) {
if (pw_properties_get(impl->capture_props, key) == NULL)
pw_properties_set(impl->capture_props, key, str);
if (pw_properties_get(impl->source_props, key) == NULL) if (pw_properties_get(impl->source_props, key) == NULL)
pw_properties_set(impl->source_props, key, str); pw_properties_set(impl->source_props, key, str);
if (pw_properties_get(impl->playback_props, key) == NULL)
pw_properties_set(impl->playback_props, key, str);
if (pw_properties_get(impl->sink_props, key) == NULL) if (pw_properties_get(impl->sink_props, key) == NULL)
pw_properties_set(impl->sink_props, key, str); pw_properties_set(impl->sink_props, key, str);
} }
@ -1094,9 +1075,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
goto error; goto error;
} }
impl->capture_props = pw_properties_new(NULL, NULL);
impl->source_props = pw_properties_new(NULL, NULL); impl->source_props = pw_properties_new(NULL, NULL);
impl->playback_props = pw_properties_new(NULL, NULL);
impl->sink_props = pw_properties_new(NULL, NULL); impl->sink_props = pw_properties_new(NULL, NULL);
if (impl->source_props == NULL || impl->sink_props == NULL) { if (impl->source_props == NULL || impl->sink_props == NULL ||
impl->capture_props == NULL || impl->playback_props == NULL) {
res = -errno; res = -errno;
pw_log_error( "can't create properties: %m"); pw_log_error( "can't create properties: %m");
goto error; goto error;
@ -1115,13 +1099,26 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
pw_properties_setf(props, PW_KEY_NODE_LINK_GROUP, "echo-cancel-%u-%u", pid, id); pw_properties_setf(props, PW_KEY_NODE_LINK_GROUP, "echo-cancel-%u-%u", pid, id);
if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
if (pw_properties_get(props, "resample.prefill") == NULL)
pw_properties_set(props, "resample.prefill", "true");
parse_audio_info(props, &impl->info); parse_audio_info(props, &impl->info);
if ((str = pw_properties_get(props, "capture.props")) != NULL)
pw_properties_update_string(impl->capture_props, str, strlen(str));
if ((str = pw_properties_get(props, "source.props")) != NULL) if ((str = pw_properties_get(props, "source.props")) != NULL)
pw_properties_update_string(impl->source_props, str, strlen(str)); pw_properties_update_string(impl->source_props, str, strlen(str));
if ((str = pw_properties_get(props, "sink.props")) != NULL) if ((str = pw_properties_get(props, "sink.props")) != NULL)
pw_properties_update_string(impl->sink_props, str, strlen(str)); pw_properties_update_string(impl->sink_props, str, strlen(str));
if ((str = pw_properties_get(props, "playback.props")) != NULL)
pw_properties_update_string(impl->playback_props, str, strlen(str));
if (pw_properties_get(impl->capture_props, PW_KEY_NODE_NAME) == NULL)
pw_properties_set(impl->capture_props, PW_KEY_NODE_NAME, "echo-cancel-capture");
if (pw_properties_get(impl->capture_props, PW_KEY_NODE_DESCRIPTION) == NULL)
pw_properties_set(impl->capture_props, PW_KEY_NODE_DESCRIPTION, "Echo-Cancel Capture");
if (pw_properties_get(impl->capture_props, PW_KEY_NODE_PASSIVE) == NULL)
pw_properties_set(impl->capture_props, PW_KEY_NODE_PASSIVE, "true");
if (pw_properties_get(impl->source_props, PW_KEY_NODE_NAME) == NULL) if (pw_properties_get(impl->source_props, PW_KEY_NODE_NAME) == NULL)
pw_properties_set(impl->source_props, PW_KEY_NODE_NAME, "echo-cancel-source"); pw_properties_set(impl->source_props, PW_KEY_NODE_NAME, "echo-cancel-source");
@ -1129,8 +1126,13 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
pw_properties_set(impl->source_props, PW_KEY_NODE_DESCRIPTION, "Echo-Cancel Source"); pw_properties_set(impl->source_props, PW_KEY_NODE_DESCRIPTION, "Echo-Cancel Source");
if (pw_properties_get(impl->source_props, PW_KEY_MEDIA_CLASS) == NULL) if (pw_properties_get(impl->source_props, PW_KEY_MEDIA_CLASS) == NULL)
pw_properties_set(impl->source_props, PW_KEY_MEDIA_CLASS, "Audio/Source"); pw_properties_set(impl->source_props, PW_KEY_MEDIA_CLASS, "Audio/Source");
if (pw_properties_get(impl->source_props, "resample.prefill") == NULL)
pw_properties_set(impl->source_props, "resample.prefill", "true"); if (pw_properties_get(impl->playback_props, PW_KEY_NODE_NAME) == NULL)
pw_properties_set(impl->playback_props, PW_KEY_NODE_NAME, "echo-cancel-playback");
if (pw_properties_get(impl->playback_props, PW_KEY_NODE_DESCRIPTION) == NULL)
pw_properties_set(impl->playback_props, PW_KEY_NODE_DESCRIPTION, "Echo-Cancel Playback");
if (pw_properties_get(impl->playback_props, PW_KEY_NODE_PASSIVE) == NULL)
pw_properties_set(impl->playback_props, PW_KEY_NODE_PASSIVE, "true");
if (pw_properties_get(impl->sink_props, PW_KEY_NODE_NAME) == NULL) if (pw_properties_get(impl->sink_props, PW_KEY_NODE_NAME) == NULL)
pw_properties_set(impl->sink_props, PW_KEY_NODE_NAME, "echo-cancel-sink"); pw_properties_set(impl->sink_props, PW_KEY_NODE_NAME, "echo-cancel-sink");
@ -1139,11 +1141,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (pw_properties_get(impl->sink_props, PW_KEY_MEDIA_CLASS) == NULL) if (pw_properties_get(impl->sink_props, PW_KEY_MEDIA_CLASS) == NULL)
pw_properties_set(impl->sink_props, PW_KEY_MEDIA_CLASS, pw_properties_set(impl->sink_props, PW_KEY_MEDIA_CLASS,
impl->monitor_mode ? "Stream/Input/Audio" : "Audio/Sink"); impl->monitor_mode ? "Stream/Input/Audio" : "Audio/Sink");
if (pw_properties_get(impl->sink_props, "resample.prefill") == NULL)
pw_properties_set(impl->sink_props, "resample.prefill", "true");
if (impl->monitor_mode) { if (impl->monitor_mode) {
if (pw_properties_get(impl->sink_props, PW_KEY_NODE_PASSIVE) == NULL)
pw_properties_set(impl->sink_props, PW_KEY_NODE_PASSIVE, "true"); pw_properties_set(impl->sink_props, PW_KEY_NODE_PASSIVE, "true");
if (pw_properties_get(impl->sink_props, PW_KEY_STREAM_MONITOR) == NULL)
pw_properties_set(impl->sink_props, PW_KEY_STREAM_MONITOR, "true"); pw_properties_set(impl->sink_props, PW_KEY_STREAM_MONITOR, "true");
if (pw_properties_get(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK) == NULL)
pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true"); pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true");
} }
@ -1252,6 +1255,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_NODE_LATENCY); copy_props(impl, props, PW_KEY_NODE_LATENCY);
copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS); copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS);
copy_props(impl, props, SPA_KEY_AUDIO_POSITION); copy_props(impl, props, SPA_KEY_AUDIO_POSITION);
copy_props(impl, props, "resample.prefill");
impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS); impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS);