}
+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)
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);
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 \
-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"}' \
<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'/>
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
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");
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");
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");
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);
<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'/>