libpipewire-module-echo-cancel ignores capture.props and playback.props in configuration file
- PipeWire version (
pipewire --version
): 0.3.63 - Distribution and distribution version (
PRETTY_NAME
from/etc/os-release
): Arch Linux - Desktop Environment: Gnome
- Kernel version (
uname -r
): 6.1.3-arch1-1
Description of Problem:
libpipewire-module-echo-cancel can be loaded from various configuration files in a similar fashion as other modules creating virtual devices (filter-chain, loopback, etc.). When loaded, it creates a virtual sink and a virtual source that can be used for echo cancellation when using speakerphone applications.
While libpipewire-module-echo-cancel does understand the module-specific properties source.props = {}
and sink.props = {}
to influence the appearance of the (application-facing) virtual source and sink it creates, it completely ignores the otherwise common (hardware-facing) capture.props = {}
and playback.props = {}
sections, even if they are present in the configuration file loading the module.
This means that the streams connecting to the physical (real) microphone and speaker will always be automatically connected to the first ports of the default playback and capture devices at the time of module load, and there is no documented way to influence that automatic behavior.
I understand why and can accept that using these ports is the default behavior since it makes initial setup easy for "standard" layouts, but in non-trivial environments with multiple and multichannel I/Os this default usually does not produce the desired audio graph.
Modifying the links after the module is loaded (using for example pw-link
) does function as a workaround, but gets cumbersome when the underlying devices disappear and re-appear mid-flight. It also poses challenges in timing, because the workaround has to be executed after every module (re-)load, but before anything starts using the echo-cancel-device.
How Reproducible:
Always. This is the implemented and documented behavior. It's not "broken", it's just "bad".
Steps to Reproduce:
- Create a working pipewire setup which has multiple input and output devices (preferably with multiple channels, too)
- include libpipewire-module-echo-cancel into one of the configuration files, for example
client.conf
, using the snipped below (taken from the official documentation), and extend it to also includecapture.props = {}
andplayback.props = {}
sections like below:
{ name = libpipewire-module-echo-cancel
args = {
library.name = aec/libspa-aec-webrtc
aec.args = {
webrtc.gain_control = true
}
source.props = {
# This is the virtual input device available to applications.
# It will route the processed microphone signal to the application recording it.
node.name = "Echo Cancellation MySource"
node.description = "Echo Cancellation MySource"
}
# module-echo-cancel seems to completely ignore this argument and just use the first two channels of the default device
capture.props = {
# This is where the raw local microphone signal is really coming from
# It should be connected to real local hardware microphones
node.name = "Echo Cancellation MyCapture"
node.description = "Echo Cancellation MyCapture"
audio.channels = 2
audio.position = [ AUX7 AUX6 ]
target.object = "alsa_input.usb-Focusrite_Scarlett_18i20_USB_03020534-00.multichannel-input"
# }
sink.props = {
# This is the virtual output device available to applications.
# It is what the communication application should use to play sound locally.
node.name = "Echo Cancellation MySink"
node.description "Echo Cancellation MySink"
}
# module-echo-cancel seems to completely ignore this argument and just use the first two channels of the default device (01/2023)
playback.props = {
# This is the local speaker we are really sending the audio to that is coming from the application.
# Note that ideally this should be a real hardware audio output, that is on the same clock as the real hardware microphones!
node.name = "Echo Cancellation MyPlayback"
node.description "Echo Cancellation MyPlayback"
node.passive = true
audio.channels = 2
audio.position = [ AUX3 AUX2 ]
target.object = "alsa_output.usb-Focusrite_Scarlett_18i20_USB_03020534-00.multichannel-output"
}
}
- update
audio.position
andtarget.object
in theplayback.props
and thecapture.props
section according to your local setup, choose something other than your default playback/capture devices - load the configuration
Actual Results:
module-echo-cancel is loaded and connects to the first ports of the default capture and playback devices, regardless of configured target in playback.props
and capture.props
.
Expected Results:
module-echo-cancel should honor the configuration in playback.props
and capture.props
and connect to the ports specified there, similar to how other modules behave, so non-trivial setups can be properly automated.