On 5/16/23 11:52, Martin Kletzander wrote: > On Thu, May 11, 2023 at 02:14:51PM +0200, Michal Privoznik wrote: >> This is mostly straightforward, except for a teensy-weensy >> detail: usually, there's no system wide daemon running, no system >> wide available socket that anybody could connect to. PipeWire >> uses a per user daemon approach instead. But this in turn means, > > I spent some time thinking about this even after I said "just give it > runtime dir" and now I'm wondering, is this not similar to the dbus > daemon? When we launch it with a constructed config file for graphics > type='dbus' we could also launch pipewire deamon with our config file > and let qemu connect to that daemon. I'm not sure what the audio > permissions would look like, but it seems like a less messy approach. And how would then this per-domain pipewire daemon talk to the user session daemon that's actually doing all the mixing? I mean, how would sound from a domain get to speakers/headphones? For dbus it's fairly easy to use, because the socket (and config file) is located under configurable location (cfg->dbusStateDir) and with predictable name (domain short name) AND (more importantly) we have virDomainOpenGraphics() API which then handles virt-viewer connection requests. > >> that the socket location floats between various locations and is >> derived from various environment variables (just like the actual >> socket name) and thus we must pass the variables to QEMU. >> >> Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> >> --- >> src/qemu/qemu_command.c | 61 +++++++++++++++++++ >> .../audio-many-backends.x86_64-latest.args | 2 + >> .../qemuxml2argvdata/audio-many-backends.xml | 1 + >> .../audio-pipewire-best.x86_64-latest.args | 36 +++++++++++ >> .../audio-pipewire-full.x86_64-latest.args | 36 +++++++++++ >> .../audio-pipewire-minimal.x86_64-latest.args | 36 +++++++++++ >> tests/qemuxml2argvtest.c | 12 ++++ >> 7 files changed, 184 insertions(+) >> create mode 100644 >> tests/qemuxml2argvdata/audio-pipewire-best.x86_64-latest.args >> create mode 100644 >> tests/qemuxml2argvdata/audio-pipewire-full.x86_64-latest.args >> create mode 100644 >> tests/qemuxml2argvdata/audio-pipewire-minimal.x86_64-latest.args >> >> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c >> index 3a08cac870..b31f4db6b1 100644 >> --- a/src/qemu/qemu_command.c >> +++ b/src/qemu/qemu_command.c >> @@ -7783,6 +7783,60 @@ qemuBuildAudioSDLProps(virDomainAudioIOSDL *def, >> } >> >> >> +static int >> +qemuBuildAudioPipewireAudioProps(virDomainAudioIOPipewireAudio *def, >> + virJSONValue **props) >> +{ >> + return virJSONValueObjectAdd(props, >> + "S:name", def->name, >> + "S:stream-name", def->streamName, >> + "p:latency", def->latency, >> + NULL); >> +} >> + >> + >> +static void >> +qemuBuildAudioPipewireAudioEnv(virCommand *cmd) >> +{ >> + const char *envVars[] = { "PIPEWIRE_RUNTIME_DIR", "XDG_RUNTIME_DIR", >> + "USERPROFILE" }; >> + size_t i; >> + >> + /* PipeWire needs access to its daemon socket. The socket name is >> + * configurable (core.name in pipewire.conf, or PIPEWIRE_CORE and >> + * PIPEWIRE_REMOTE env vars). If the socket name is not an absolute >> + * path, then the socket is looked for in the following directories >> + * (in order): >> + * >> + * - PIPEWIRE_RUNTIME_DIR >> + * - XDG_RUNTIME_DIR >> + * - USERPROFILE >> + * >> + * This order is defined in get_runtime_dir() from >> + * src/modules/module-protocol-native/local-socket.c from PipeWire's >> + * codebase. >> + * >> + * Now, PIPEWIRE_CORE and/or PIPEWIRE_REMOTE should be passed >> + * whenever present in the environment. But for the other three >> + * (socket location dirs), we can add just the first existing one >> + * (basically mimic get_runtime_dir() logic). >> + */ >> + >> + virCommandAddEnvPass(cmd, "PIPEWIRE_CORE"); >> + virCommandAddEnvPass(cmd, "PIPEWIRE_REMOTE"); >> + >> + for (i = 0; i < G_N_ELEMENTS(envVars); i++) { >> + const char *value = getenv(envVars[i]); >> + >> + if (!value) >> + continue; >> + >> + virCommandAddEnvPair(cmd, envVars[i], value); >> + break; >> + } >> +} >> + >> + >> static int >> qemuBuildAudioCommandLineArg(virCommand *cmd, >> virDomainAudioDef *def) >> @@ -7889,6 +7943,13 @@ qemuBuildAudioCommandLineArg(virCommand *cmd, >> break; >> >> case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: >> + if >> (qemuBuildAudioPipewireAudioProps(&def->backend.pipewire.input, &in) < >> 0 || >> + >> qemuBuildAudioPipewireAudioProps(&def->backend.pipewire.output, &out) >> < 0) >> + return -1; >> + >> + qemuBuildAudioPipewireAudioEnv(cmd); >> + break; >> + >> case VIR_DOMAIN_AUDIO_TYPE_LAST: >> default: >> virReportEnumRangeError(virDomainAudioType, def->type); >> diff --git >> a/tests/qemuxml2argvdata/audio-many-backends.x86_64-latest.args >> b/tests/qemuxml2argvdata/audio-many-backends.x86_64-latest.args >> index 9caf591daf..13dd55054e 100644 >> --- a/tests/qemuxml2argvdata/audio-many-backends.x86_64-latest.args >> +++ b/tests/qemuxml2argvdata/audio-many-backends.x86_64-latest.args >> @@ -6,6 +6,7 @@ LOGNAME=test \ >> XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \ >> XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \ >> XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ >> +XDG_RUNTIME_DIR=/bad-test-used-env-xdg-runtime-dir \ > > This should fail the test suite IIRC. I'm failing to see why. The poisoning was done so that things like: virDirCreate("${XGD_RUNTIME_DIR}/something/something"); fail. It's okay if it's in environment. But for everybody's peace of mind, I can squash this in: diff --git i/tests/qemuxml2argvtest.c w/tests/qemuxml2argvtest.c index f29597a19f..37c2ab0826 100644 --- i/tests/qemuxml2argvtest.c +++ w/tests/qemuxml2argvtest.c @@ -1002,7 +1002,9 @@ mymain(void) DO_TEST_CAPS_LATEST("audio-spice-full"); DO_TEST_CAPS_LATEST("audio-file-full"); + g_setenv("PIPEWIRE_RUNTIME_DIR", "/run/user/1000", TRUE); DO_TEST_CAPS_LATEST("audio-many-backends"); + g_unsetenv("PIPEWIRE_RUNTIME_DIR"); /* Validate auto-creation of <audio> for legacy compat */ g_setenv("QEMU_AUDIO_DRV", "sdl", TRUE); Michal