qemu: Generate cmd line for pipewire audio backend

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,
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.

Resolves: https://gitlab.com/libvirt/libvirt/-/issues/560
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
Michal Privoznik 2023-05-10 12:58:44 +02:00
parent c472ce024b
commit 10594bb311
8 changed files with 191 additions and 0 deletions

View File

@ -7833,6 +7833,68 @@ 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 *runtimeDir)
{
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):
*
* 1) set it to user defined value (@runtimeDir != NULL), or
* 2) we can add just the first existing one (basically mimic
* get_runtime_dir() logic; @runtimeDir == NULL).
*/
virCommandAddEnvPass(cmd, "PIPEWIRE_CORE");
virCommandAddEnvPass(cmd, "PIPEWIRE_REMOTE");
if (runtimeDir) {
virCommandAddEnvPair(cmd, "PIPEWIRE_RUNTIME_DIR", runtimeDir);
} else {
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)
@ -7939,6 +8001,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, def->backend.pipewire.runtimeDir);
break;
case VIR_DOMAIN_AUDIO_TYPE_LAST:
default:
virReportEnumRangeError(virDomainAudioType, def->type);

View File

@ -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 \
PIPEWIRE_RUNTIME_DIR=/run/user/1000 \
/usr/bin/qemu-system-x86_64 \
-name guest=QEMUGuest1,debug-threads=on \
-S \
@ -32,6 +33,7 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \
-audiodev '{"id":"audio1","driver":"none"}' \
-audiodev '{"id":"audio2","driver":"alsa"}' \
-audiodev '{"id":"audio3","driver":"pa"}' \
-audiodev '{"id":"audio4","driver":"pipewire"}' \
-vnc 127.0.0.1:0,audiodev=audio2 \
-device '{"driver":"cirrus-vga","id":"video0","bus":"pci.0","addr":"0x2"}' \
-device '{"driver":"AC97","id":"sound0","audiodev":"audio1","bus":"pci.0","addr":"0x3"}' \

View File

@ -51,6 +51,7 @@
<audio id='1' type='none'/>
<audio id='2' type='alsa'/>
<audio id='3' type='pulseaudio'/>
<audio id='4' type='pipewire'/>
<video>
<model type='cirrus' vram='16384' heads='1' primary='yes'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>

View File

@ -0,0 +1,36 @@
LC_ALL=C \
PATH=/bin \
HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \
USER=test \
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 \
PIPEWIRE_RUNTIME_DIR=/run/user/1000 \
/usr/bin/qemu-system-x86_64 \
-name guest=QEMUGuest1,debug-threads=on \
-S \
-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \
-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
-accel tcg \
-cpu qemu64 \
-m size=219136k \
-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
-overcommit mem-lock=off \
-smp 1,sockets=1,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
-display none \
-no-user-config \
-nodefaults \
-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
-mon chardev=charmonitor,id=monitor,mode=control \
-rtc base=utc \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
-blockdev '{"driver":"host_cdrom","filename":"/dev/cdrom","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"raw","file":"libvirt-1-storage"}' \
-device '{"driver":"ide-cd","bus":"ide.1","unit":0,"drive":"libvirt-1-format","id":"ide0-1-0","bootindex":1}' \
-audiodev '{"id":"audio1","driver":"pipewire","timer-period":50,"in":{"mixing-engine":true,"fixed-settings":true,"voices":1,"buffer-length":200,"frequency":44100,"channels":2,"format":"s16","name":"fish"},"out":{"mixing-engine":true,"fixed-settings":true,"voices":2,"buffer-length":200,"frequency":22050,"channels":4,"format":"f32","name":"fish"}}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-msg timestamp=on

View File

@ -0,0 +1,36 @@
LC_ALL=C \
PATH=/bin \
HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \
USER=test \
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 \
PIPEWIRE_RUNTIME_DIR=/run/user/1000 \
/usr/bin/qemu-system-x86_64 \
-name guest=QEMUGuest1,debug-threads=on \
-S \
-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \
-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
-accel tcg \
-cpu qemu64 \
-m size=219136k \
-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
-overcommit mem-lock=off \
-smp 1,sockets=1,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
-display none \
-no-user-config \
-nodefaults \
-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
-mon chardev=charmonitor,id=monitor,mode=control \
-rtc base=utc \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
-blockdev '{"driver":"host_cdrom","filename":"/dev/cdrom","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"raw","file":"libvirt-1-storage"}' \
-device '{"driver":"ide-cd","bus":"ide.1","unit":0,"drive":"libvirt-1-format","id":"ide0-1-0","bootindex":1}' \
-audiodev '{"id":"audio1","driver":"pipewire","timer-period":50,"in":{"mixing-engine":true,"fixed-settings":true,"voices":1,"buffer-length":100,"frequency":44100,"channels":2,"format":"s16","name":"fish","stream-name":"food","latency":100},"out":{"mixing-engine":true,"fixed-settings":true,"voices":2,"buffer-length":200,"frequency":22050,"channels":4,"format":"f32","name":"fish","stream-name":"food","latency":200}}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-msg timestamp=on

View File

@ -0,0 +1,36 @@
LC_ALL=C \
PATH=/bin \
HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \
USER=test \
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 \
PIPEWIRE_RUNTIME_DIR=/run/user/1000 \
/usr/bin/qemu-system-x86_64 \
-name guest=QEMUGuest1,debug-threads=on \
-S \
-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \
-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
-accel tcg \
-cpu qemu64 \
-m size=219136k \
-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
-overcommit mem-lock=off \
-smp 1,sockets=1,cores=1,threads=1 \
-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
-display none \
-no-user-config \
-nodefaults \
-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
-mon chardev=charmonitor,id=monitor,mode=control \
-rtc base=utc \
-no-shutdown \
-boot strict=on \
-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
-blockdev '{"driver":"host_cdrom","filename":"/dev/cdrom","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
-blockdev '{"node-name":"libvirt-1-format","read-only":true,"driver":"raw","file":"libvirt-1-storage"}' \
-device '{"driver":"ide-cd","bus":"ide.1","unit":0,"drive":"libvirt-1-format","id":"ide0-1-0","bootindex":1}' \
-audiodev '{"id":"audio1","driver":"pipewire"}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-msg timestamp=on

View File

@ -869,6 +869,9 @@ mymain(void)
g_unsetenv("DYLD_FORCE_FLAT_NAMESPACE");
g_unsetenv("QEMU_AUDIO_DRV");
g_unsetenv("SDL_AUDIODRIVER");
g_unsetenv("PIPEWIRE_CORE");
g_unsetenv("PIPEWIRE_REMOTE");
g_unsetenv("PIPEWIRE_RUNTIME_DIR");
DO_TEST_CAPS_LATEST("minimal");
DO_TEST_CAPS_LATEST_PARSE_ERROR("minimal-no-memory");
@ -909,6 +912,9 @@ mymain(void)
DO_TEST_CAPS_LATEST("audio-jack-minimal");
DO_TEST_CAPS_LATEST("audio-oss-minimal");
DO_TEST_CAPS_LATEST("audio-pulseaudio-minimal");
g_setenv("PIPEWIRE_RUNTIME_DIR", "/run/user/1000", TRUE);
DO_TEST_CAPS_LATEST("audio-pipewire-minimal");
g_unsetenv("PIPEWIRE_RUNTIME_DIR");
DO_TEST_CAPS_LATEST("audio-sdl-minimal");
DO_TEST_CAPS_LATEST("audio-spice-minimal");
DO_TEST_CAPS_LATEST("audio-file-minimal");
@ -918,6 +924,7 @@ mymain(void)
DO_TEST_CAPS_LATEST("audio-coreaudio-best");
DO_TEST_CAPS_LATEST("audio-oss-best");
DO_TEST_CAPS_LATEST("audio-pulseaudio-best");
DO_TEST_CAPS_LATEST("audio-pipewire-best");
DO_TEST_CAPS_LATEST("audio-sdl-best");
DO_TEST_CAPS_LATEST("audio-spice-best");
DO_TEST_CAPS_LATEST("audio-file-best");
@ -928,11 +935,14 @@ mymain(void)
DO_TEST_CAPS_LATEST("audio-jack-full");
DO_TEST_CAPS_LATEST("audio-oss-full");
DO_TEST_CAPS_LATEST("audio-pulseaudio-full");
DO_TEST_CAPS_LATEST("audio-pipewire-full");
DO_TEST_CAPS_LATEST("audio-sdl-full");
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);

View File

@ -51,6 +51,7 @@
<audio id='1' type='none'/>
<audio id='2' type='alsa'/>
<audio id='3' type='pulseaudio'/>
<audio id='4' type='pipewire'/>
<video>
<model type='cirrus' vram='16384' heads='1' primary='yes'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>