[PATCH v2 0/3] Introduce pipewire audio backend

v2 of: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/4M7OB... diff to v1: - Introduced 'runtimeDir' attribute to <audio/> so that users do not have use <qemu:env/> hack to set PIPEWIRE_RUNTIME_DIR. - Worked in the rest of Peter's review suggestions. Michal Prívozník (3): conf: Introduce pipewire audio backend qemu: Generate cmd line for pipewire audio backend NEWS: Document pipewire audio backend NEWS.rst | 5 ++ docs/formatdomain.rst | 46 +++++++++++- src/conf/domain_conf.c | 73 +++++++++++++++++++ src/conf/domain_conf.h | 13 ++++ src/conf/schemas/domaincommon.rng | 42 +++++++++++ src/qemu/qemu_command.c | 71 ++++++++++++++++++ src/qemu/qemu_validate.c | 1 + .../audio-many-backends.x86_64-latest.args | 2 + .../qemuxml2argvdata/audio-many-backends.xml | 1 + .../audio-pipewire-best.x86_64-latest.args | 36 +++++++++ .../qemuxml2argvdata/audio-pipewire-best.xml | 43 +++++++++++ .../audio-pipewire-full.x86_64-latest.args | 36 +++++++++ .../qemuxml2argvdata/audio-pipewire-full.xml | 43 +++++++++++ .../audio-pipewire-minimal.x86_64-latest.args | 36 +++++++++ .../audio-pipewire-minimal.xml | 36 +++++++++ tests/qemuxml2argvtest.c | 10 +++ .../audio-many-backends.x86_64-latest.xml | 1 + .../audio-pipewire-best.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-full.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-minimal.x86_64-latest.xml | 39 ++++++++++ tests/qemuxml2xmltest.c | 3 + 21 files changed, 627 insertions(+), 2 deletions(-) create mode 100644 tests/qemuxml2argvdata/audio-pipewire-best.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/audio-pipewire-best.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-full.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/audio-pipewire-full.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-minimal.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/audio-pipewire-minimal.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-best.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-full.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-minimal.x86_64-latest.xml -- 2.41.0

QEMU gained support for PipeWire audio backend (see QEMU commit of v8.0.0-403-gc2d3d1c294). Its configuration knobs are basically the same as pulseaudio's, except for PA's server name. Therefore, a lot of code is copied over from pulseadio and fixed by s/Pulse/Pipewire/ or s/pulseaudio/pipewire/. There's one ley difference to PA though: pipewire daemon is usually on per user basis (just like our qemu:///session). Therefore, introduce this 'runtimeDir' attribute, which allows specifying path to pipewire daemon socket (useful for qemu:///system for instance). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.rst | 46 +++++++++++- src/conf/domain_conf.c | 73 +++++++++++++++++++ src/conf/domain_conf.h | 13 ++++ src/conf/schemas/domaincommon.rng | 42 +++++++++++ src/qemu/qemu_command.c | 2 + src/qemu/qemu_validate.c | 1 + .../qemuxml2argvdata/audio-pipewire-best.xml | 43 +++++++++++ .../qemuxml2argvdata/audio-pipewire-full.xml | 43 +++++++++++ .../audio-pipewire-minimal.xml | 36 +++++++++ .../audio-pipewire-best.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-full.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-minimal.x86_64-latest.xml | 39 ++++++++++ tests/qemuxml2xmltest.c | 3 + 13 files changed, 431 insertions(+), 2 deletions(-) create mode 100644 tests/qemuxml2argvdata/audio-pipewire-best.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-full.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-minimal.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-best.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-full.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-minimal.x86_64-latest.xml diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 459815d2b5..310d2bc427 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -7368,7 +7368,8 @@ to the guest sound device. ``type`` The required ``type`` attribute specifies audio backend type. Currently, the supported values are ``none``, ``alsa``, ``coreaudio``, - ``dbus``, ``jack``, ``oss``, ``pulseaudio``, ``sdl``, ``spice``, ``file``. + ``dbus``, ``jack``, ``oss``, ``pipewire``, ``pulseaudio``, ``sdl``, + ``spice``, ``file``. ``id`` Integer id of the audio device. Must be greater than 0. @@ -7452,7 +7453,7 @@ following environment variables: * ``QEMU_AUDIO_DRV`` Valid values are ``pa``, ``none``, ``alsa``, ``coreaudio``, ``jack``, ``oss``, - ``sdl``, ``spice`` or ``wav``. + ``pipewire``, ``sdl``, ``spice`` or ``wav``. None audio backend ^^^^^^^^^^^^^^^^^^ @@ -7601,6 +7602,47 @@ and ``<output>`` elements :since:`Since 6.7.0, bhyve; Since 7.2.0, qemu` +PipeWire audio backend +^^^^^^^^^^^^^^^^^^^^^^ + +The ``pipewire`` audio backend delegates to a PipeWire daemon audio input and +output. + +The following additional attributes are permitted on the ``<input/>`` and +``<output/>`` elements: + +* ``name`` + + The sink/source name to use + +* ``streamName`` + + The name to identify the stream associated with the VM + +* ``latency`` + + Desired latency for the server to target in microseconds + +:: + + <audio id="1" type="pipewire"> + <input name="fish" streamName="food" latency="100"/> + <output name="fish" streamName="food" latency="200"/> + </audio> + +Optionally, path to pipewire daemon socket (aka ``PIPEWIRE_RUNTIME_DIR``) can +be specified via ``runtimeDir`` attribute. This is useful when a domain under +``qemu:///system`` wants to use session pipewire daemon, or vice versa. + +:: + + <audio id="1" type="pipewire" runtimeDir='/run/user/1000'> + <input name="fish" streamName="food" latency="100"/> + <output name="fish" streamName="food" latency="200"/> + </audio> + +:since:`Since 9.10.0, qemu` + PulseAudio audio backend ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f7d7244738..22ad43e1d7 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -791,6 +791,7 @@ VIR_ENUM_IMPL(virDomainAudioType, "spice", "file", "dbus", + "pipewire", ); VIR_ENUM_IMPL(virDomainAudioSDLDriver, @@ -3230,6 +3231,13 @@ virDomainAudioIOPulseAudioFree(virDomainAudioIOPulseAudio *def) g_free(def->streamName); } +static void +virDomainAudioIOPipewireAudioFree(virDomainAudioIOPipewireAudio *def) +{ + g_free(def->name); + g_free(def->streamName); +} + void virDomainAudioDefFree(virDomainAudioDef *def) { @@ -3274,6 +3282,12 @@ virDomainAudioDefFree(virDomainAudioDef *def) g_free(def->backend.file.path); break; + case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: + virDomainAudioIOPipewireAudioFree(&def->backend.pipewire.input); + virDomainAudioIOPipewireAudioFree(&def->backend.pipewire.output); + g_free(def->backend.pipewire.runtimeDir); + break; + case VIR_DOMAIN_AUDIO_TYPE_DBUS: case VIR_DOMAIN_AUDIO_TYPE_LAST: break; @@ -11920,6 +11934,21 @@ virDomainAudioSDLParse(virDomainAudioIOSDL *def, } +static int +virDomainAudioPipewireAudioParse(virDomainAudioIOPipewireAudio *def, + xmlNodePtr node) +{ + def->name = virXMLPropString(node, "name"); + def->streamName = virXMLPropString(node, "streamName"); + + if (virXMLPropUInt(node, "latency", 10, VIR_XML_PROP_NONZERO, + &def->latency) < 0) + return -1; + + return 0; +} + + static virDomainAudioDef * virDomainAudioDefParseXML(virDomainXMLOption *xmlopt G_GNUC_UNUSED, xmlNodePtr node, @@ -12050,6 +12079,16 @@ virDomainAudioDefParseXML(virDomainXMLOption *xmlopt G_GNUC_UNUSED, case VIR_DOMAIN_AUDIO_TYPE_DBUS: break; + case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: + if (inputNode && + virDomainAudioPipewireAudioParse(&def->backend.pipewire.input, inputNode) < 0) + goto error; + if (outputNode && + virDomainAudioPipewireAudioParse(&def->backend.pipewire.output, outputNode) < 0) + goto error; + def->backend.pipewire.runtimeDir = virXMLPropString(node, "runtimeDir"); + break; + case VIR_DOMAIN_AUDIO_TYPE_LAST: default: virReportEnumRangeError(virDomainAudioType, def->type); @@ -24839,6 +24878,18 @@ virDomainAudioSDLFormat(virDomainAudioIOSDL *def, } +static void +virDomainAudioPipewireAudioFormat(virDomainAudioIOPipewireAudio *def, + virBuffer *buf) +{ + virBufferEscapeString(buf, " name='%s'", def->name); + virBufferEscapeString(buf, " streamName='%s'", def->streamName); + if (def->latency) + virBufferAsprintf(buf, " latency='%u'", def->latency); + +} + + static int virDomainAudioDefFormat(virBuffer *buf, virDomainAudioDef *def) @@ -24921,6 +24972,12 @@ virDomainAudioDefFormat(virBuffer *buf, case VIR_DOMAIN_AUDIO_TYPE_DBUS: break; + case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: + virDomainAudioPipewireAudioFormat(&def->backend.pipewire.input, &inputBuf); + virDomainAudioPipewireAudioFormat(&def->backend.pipewire.output, &outputBuf); + virBufferEscapeString(&attrBuf, " runtimeDir='%s'", def->backend.pipewire.runtimeDir); + break; + case VIR_DOMAIN_AUDIO_TYPE_LAST: default: virReportEnumRangeError(virDomainAudioType, def->type); @@ -29273,6 +29330,16 @@ virDomainAudioIOSDLIsEqual(virDomainAudioIOSDL *this, } +static bool +virDomainAudioIOPipewireAudioIsEqual(virDomainAudioIOPipewireAudio *this, + virDomainAudioIOPipewireAudio *that) +{ + return STREQ_NULLABLE(this->name, that->name) && + STREQ_NULLABLE(this->streamName, that->streamName) && + this->latency == that->latency; +} + + static bool virDomainAudioBackendIsEqual(virDomainAudioDef *this, virDomainAudioDef *that) @@ -29333,6 +29400,12 @@ virDomainAudioBackendIsEqual(virDomainAudioDef *this, case VIR_DOMAIN_AUDIO_TYPE_FILE: return STREQ_NULLABLE(this->backend.file.path, that->backend.file.path); + case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: + return virDomainAudioIOPipewireAudioIsEqual(&this->backend.pipewire.input, + &that->backend.pipewire.input) && + virDomainAudioIOPipewireAudioIsEqual(&this->backend.pipewire.output, + &that->backend.pipewire.output); + case VIR_DOMAIN_AUDIO_TYPE_DBUS: case VIR_DOMAIN_AUDIO_TYPE_LAST: default: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 7c0e017038..ed07859bc5 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1579,6 +1579,7 @@ typedef enum { VIR_DOMAIN_AUDIO_TYPE_SPICE, VIR_DOMAIN_AUDIO_TYPE_FILE, VIR_DOMAIN_AUDIO_TYPE_DBUS, + VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE, VIR_DOMAIN_AUDIO_TYPE_LAST } virDomainAudioType; @@ -1654,6 +1655,13 @@ struct _virDomainAudioIOSDL { unsigned int bufferCount; }; +typedef struct _virDomainAudioIOPipewireAudio virDomainAudioIOPipewireAudio; +struct _virDomainAudioIOPipewireAudio { + char *name; + char *streamName; + unsigned int latency; +}; + struct _virDomainAudioDef { virDomainAudioType type; @@ -1697,6 +1705,11 @@ struct _virDomainAudioDef { struct { char *path; } file; + struct { + virDomainAudioIOPipewireAudio input; + virDomainAudioIOPipewireAudio output; + char *runtimeDir; + } pipewire; } backend; }; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index a26986b5ce..b98a2ae602 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -5186,6 +5186,26 @@ <ref name="audiocommonchild"/> </define> + <define name="audiopipewire"> + <ref name="audiocommonattr"/> + <optional> + <attribute name="name"> + <data type="string"/> + </attribute> + </optional> + <optional> + <attribute name="streamName"> + <data type="string"/> + </attribute> + </optional> + <optional> + <attribute name="latency"> + <ref name="uint32"/> + </attribute> + </optional> + <ref name="audiocommonchild"/> + </define> + <define name="audiopulseaudio"> <ref name="audiocommonattr"/> <optional> @@ -5361,6 +5381,28 @@ </optional> </interleave> </group> + <group> + <attribute name="type"> + <value>pipewire</value> + </attribute> + <interleave> + <optional> + <element name="input"> + <ref name="audiopipewire"/> + </element> + </optional> + <optional> + <element name="output"> + <ref name="audiopipewire"/> + </element> + </optional> + <optional> + <attribute name="runtimeDir"> + <ref name="filePath"/> + </attribute> + </optional> + </interleave> + </group> <group> <attribute name="type"> <value>pulseaudio</value> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 2674dd6959..173639277c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -142,6 +142,7 @@ qemuAudioDriverTypeToString(virDomainAudioType type) case VIR_DOMAIN_AUDIO_TYPE_SDL: case VIR_DOMAIN_AUDIO_TYPE_SPICE: case VIR_DOMAIN_AUDIO_TYPE_DBUS: + case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: case VIR_DOMAIN_AUDIO_TYPE_LAST: break; } @@ -7937,6 +7938,7 @@ qemuBuildAudioCommandLineArg(virCommand *cmd, case VIR_DOMAIN_AUDIO_TYPE_DBUS: break; + case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: case VIR_DOMAIN_AUDIO_TYPE_LAST: default: virReportEnumRangeError(virDomainAudioType, def->type); diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 2afd7e128a..e475ad035e 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4403,6 +4403,7 @@ qemuValidateDomainDeviceDefAudio(virDomainAudioDef *audio, case VIR_DOMAIN_AUDIO_TYPE_PULSEAUDIO: case VIR_DOMAIN_AUDIO_TYPE_SDL: case VIR_DOMAIN_AUDIO_TYPE_FILE: + case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE: break; case VIR_DOMAIN_AUDIO_TYPE_SPICE: diff --git a/tests/qemuxml2argvdata/audio-pipewire-best.xml b/tests/qemuxml2argvdata/audio-pipewire-best.xml new file mode 100644 index 0000000000..487f280c9b --- /dev/null +++ b/tests/qemuxml2argvdata/audio-pipewire-best.xml @@ -0,0 +1,43 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='cdrom'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <source dev='/dev/cdrom'/> + <target dev='hdc' bus='ide'/> + <readonly/> + <address type='drive' controller='0' bus='1' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='pipewire' timerPeriod='50' runtimeDir='/run/user/1000'> + <input mixingEngine='yes' fixedSettings='yes' voices='1' bufferLength='200' name='fish'> + <settings frequency='44100' channels='2' format='s16'/> + </input> + <output mixingEngine='yes' fixedSettings='yes' voices='2' bufferLength='200' name='fish'> + <settings frequency='22050' channels='4' format='f32'/> + </output> + </audio> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/audio-pipewire-full.xml b/tests/qemuxml2argvdata/audio-pipewire-full.xml new file mode 100644 index 0000000000..e0df5a04d9 --- /dev/null +++ b/tests/qemuxml2argvdata/audio-pipewire-full.xml @@ -0,0 +1,43 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='cdrom'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <source dev='/dev/cdrom'/> + <target dev='hdc' bus='ide'/> + <readonly/> + <address type='drive' controller='0' bus='1' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='pipewire' timerPeriod='50' runtimeDir='/run/user/1000'> + <input mixingEngine='yes' fixedSettings='yes' voices='1' bufferLength='100' name='fish' streamName='food' latency='100'> + <settings frequency='44100' channels='2' format='s16'/> + </input> + <output mixingEngine='yes' fixedSettings='yes' voices='2' bufferLength='200' name='fish' streamName='food' latency='200'> + <settings frequency='22050' channels='4' format='f32'/> + </output> + </audio> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/audio-pipewire-minimal.xml b/tests/qemuxml2argvdata/audio-pipewire-minimal.xml new file mode 100644 index 0000000000..2085225722 --- /dev/null +++ b/tests/qemuxml2argvdata/audio-pipewire-minimal.xml @@ -0,0 +1,36 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='cdrom'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <source dev='/dev/cdrom'/> + <target dev='hdc' bus='ide'/> + <readonly/> + <address type='drive' controller='0' bus='1' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='pipewire'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/audio-pipewire-best.x86_64-latest.xml b/tests/qemuxml2xmloutdata/audio-pipewire-best.x86_64-latest.xml new file mode 100644 index 0000000000..c73a9abb0e --- /dev/null +++ b/tests/qemuxml2xmloutdata/audio-pipewire-best.x86_64-latest.xml @@ -0,0 +1,46 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='cdrom'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <source dev='/dev/cdrom'/> + <target dev='hdc' bus='ide'/> + <readonly/> + <address type='drive' controller='0' bus='1' target='0' unit='0'/> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='pipewire' timerPeriod='50' runtimeDir='/run/user/1000'> + <input mixingEngine='yes' fixedSettings='yes' voices='1' bufferLength='200' name='fish'> + <settings frequency='44100' channels='2' format='s16'/> + </input> + <output mixingEngine='yes' fixedSettings='yes' voices='2' bufferLength='200' name='fish'> + <settings frequency='22050' channels='4' format='f32'/> + </output> + </audio> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/audio-pipewire-full.x86_64-latest.xml b/tests/qemuxml2xmloutdata/audio-pipewire-full.x86_64-latest.xml new file mode 100644 index 0000000000..0b06d614c0 --- /dev/null +++ b/tests/qemuxml2xmloutdata/audio-pipewire-full.x86_64-latest.xml @@ -0,0 +1,46 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='cdrom'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <source dev='/dev/cdrom'/> + <target dev='hdc' bus='ide'/> + <readonly/> + <address type='drive' controller='0' bus='1' target='0' unit='0'/> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='pipewire' timerPeriod='50' runtimeDir='/run/user/1000'> + <input mixingEngine='yes' fixedSettings='yes' voices='1' bufferLength='100' name='fish' streamName='food' latency='100'> + <settings frequency='44100' channels='2' format='s16'/> + </input> + <output mixingEngine='yes' fixedSettings='yes' voices='2' bufferLength='200' name='fish' streamName='food' latency='200'> + <settings frequency='22050' channels='4' format='f32'/> + </output> + </audio> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmloutdata/audio-pipewire-minimal.x86_64-latest.xml b/tests/qemuxml2xmloutdata/audio-pipewire-minimal.x86_64-latest.xml new file mode 100644 index 0000000000..7fc0a65a68 --- /dev/null +++ b/tests/qemuxml2xmloutdata/audio-pipewire-minimal.x86_64-latest.xml @@ -0,0 +1,39 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='cdrom'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='cdrom'> + <driver name='qemu' type='raw'/> + <source dev='/dev/cdrom'/> + <target dev='hdc' bus='ide'/> + <readonly/> + <address type='drive' controller='0' bus='1' target='0' unit='0'/> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='pipewire'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 72f976358f..1010b68ebc 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -957,6 +957,7 @@ mymain(void) DO_TEST_CAPS_LATEST("audio-coreaudio-minimal"); DO_TEST_CAPS_LATEST("audio-oss-minimal"); DO_TEST_CAPS_LATEST("audio-pulseaudio-minimal"); + DO_TEST_CAPS_LATEST("audio-pipewire-minimal"); DO_TEST_CAPS_LATEST("audio-sdl-minimal"); DO_TEST_CAPS_LATEST("audio-spice-minimal"); DO_TEST_CAPS_LATEST("audio-file-minimal"); @@ -967,6 +968,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"); @@ -978,6 +980,7 @@ 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"); -- 2.41.0

On a Friday in 2023, Michal Privoznik wrote:
QEMU gained support for PipeWire audio backend (see QEMU commit of v8.0.0-403-gc2d3d1c294). Its configuration knobs are basically the same as pulseaudio's, except for PA's server name. Therefore, a lot of code is copied over from pulseadio and fixed by s/Pulse/Pipewire/ or s/pulseaudio/pipewire/.
There's one ley difference to PA though: pipewire daemon is
s/ley/key/ Jano
usually on per user basis (just like our qemu:///session). Therefore, introduce this 'runtimeDir' attribute, which allows specifying path to pipewire daemon socket (useful for qemu:///system for instance).
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- docs/formatdomain.rst | 46 +++++++++++- src/conf/domain_conf.c | 73 +++++++++++++++++++ src/conf/domain_conf.h | 13 ++++ src/conf/schemas/domaincommon.rng | 42 +++++++++++ src/qemu/qemu_command.c | 2 + src/qemu/qemu_validate.c | 1 + .../qemuxml2argvdata/audio-pipewire-best.xml | 43 +++++++++++ .../qemuxml2argvdata/audio-pipewire-full.xml | 43 +++++++++++ .../audio-pipewire-minimal.xml | 36 +++++++++ .../audio-pipewire-best.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-full.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-minimal.x86_64-latest.xml | 39 ++++++++++ tests/qemuxml2xmltest.c | 3 + 13 files changed, 431 insertions(+), 2 deletions(-) create mode 100644 tests/qemuxml2argvdata/audio-pipewire-best.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-full.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-minimal.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-best.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-full.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-minimal.x86_64-latest.xml

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> --- src/qemu/qemu_command.c | 69 +++++++++++++++++++ .../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 | 10 +++ .../audio-many-backends.x86_64-latest.xml | 1 + 8 files changed, 191 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 173639277c..d54149ed2d 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -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); diff --git a/tests/qemuxml2argvdata/audio-many-backends.x86_64-latest.args b/tests/qemuxml2argvdata/audio-many-backends.x86_64-latest.args index 4c6c925ccd..0bdc75689f 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 \ +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"}' \ diff --git a/tests/qemuxml2argvdata/audio-many-backends.xml b/tests/qemuxml2argvdata/audio-many-backends.xml index c681784526..1659723f91 100644 --- a/tests/qemuxml2argvdata/audio-many-backends.xml +++ b/tests/qemuxml2argvdata/audio-many-backends.xml @@ -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'/> diff --git a/tests/qemuxml2argvdata/audio-pipewire-best.x86_64-latest.args b/tests/qemuxml2argvdata/audio-pipewire-best.x86_64-latest.args new file mode 100644 index 0000000000..d94bd6a09e --- /dev/null +++ b/tests/qemuxml2argvdata/audio-pipewire-best.x86_64-latest.args @@ -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 diff --git a/tests/qemuxml2argvdata/audio-pipewire-full.x86_64-latest.args b/tests/qemuxml2argvdata/audio-pipewire-full.x86_64-latest.args new file mode 100644 index 0000000000..dd97b462fb --- /dev/null +++ b/tests/qemuxml2argvdata/audio-pipewire-full.x86_64-latest.args @@ -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 diff --git a/tests/qemuxml2argvdata/audio-pipewire-minimal.x86_64-latest.args b/tests/qemuxml2argvdata/audio-pipewire-minimal.x86_64-latest.args new file mode 100644 index 0000000000..90951a3219 --- /dev/null +++ b/tests/qemuxml2argvdata/audio-pipewire-minimal.x86_64-latest.args @@ -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 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 24997c0aaa..b2ea2191dc 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -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); diff --git a/tests/qemuxml2xmloutdata/audio-many-backends.x86_64-latest.xml b/tests/qemuxml2xmloutdata/audio-many-backends.x86_64-latest.xml index c681784526..1659723f91 100644 --- a/tests/qemuxml2xmloutdata/audio-many-backends.x86_64-latest.xml +++ b/tests/qemuxml2xmloutdata/audio-many-backends.x86_64-latest.xml @@ -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'/> -- 2.41.0

Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- NEWS.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 08e5a3d04a..f12734c2a1 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -17,6 +17,11 @@ v9.10.0 (unreleased) * **New features** + * Introduce pipewire audio backend + + The QEMU hypervisor driver now allows setting ``pipewire`` backend for + ``<audio/>`` device. + * **Improvements** * **Bug fixes** -- 2.41.0

On a Friday in 2023, Michal Privoznik wrote:
v2 of:
https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/4M7OB...
diff to v1: - Introduced 'runtimeDir' attribute to <audio/> so that users do not have use <qemu:env/> hack to set PIPEWIRE_RUNTIME_DIR. - Worked in the rest of Peter's review suggestions.
Michal Prívozník (3): conf: Introduce pipewire audio backend qemu: Generate cmd line for pipewire audio backend NEWS: Document pipewire audio backend
NEWS.rst | 5 ++ docs/formatdomain.rst | 46 +++++++++++- src/conf/domain_conf.c | 73 +++++++++++++++++++ src/conf/domain_conf.h | 13 ++++ src/conf/schemas/domaincommon.rng | 42 +++++++++++ src/qemu/qemu_command.c | 71 ++++++++++++++++++ src/qemu/qemu_validate.c | 1 + .../audio-many-backends.x86_64-latest.args | 2 + .../qemuxml2argvdata/audio-many-backends.xml | 1 + .../audio-pipewire-best.x86_64-latest.args | 36 +++++++++ .../qemuxml2argvdata/audio-pipewire-best.xml | 43 +++++++++++ .../audio-pipewire-full.x86_64-latest.args | 36 +++++++++ .../qemuxml2argvdata/audio-pipewire-full.xml | 43 +++++++++++ .../audio-pipewire-minimal.x86_64-latest.args | 36 +++++++++ .../audio-pipewire-minimal.xml | 36 +++++++++ tests/qemuxml2argvtest.c | 10 +++ .../audio-many-backends.x86_64-latest.xml | 1 + .../audio-pipewire-best.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-full.x86_64-latest.xml | 46 ++++++++++++ .../audio-pipewire-minimal.x86_64-latest.xml | 39 ++++++++++ tests/qemuxml2xmltest.c | 3 + 21 files changed, 627 insertions(+), 2 deletions(-) create mode 100644 tests/qemuxml2argvdata/audio-pipewire-best.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/audio-pipewire-best.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-full.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/audio-pipewire-full.xml create mode 100644 tests/qemuxml2argvdata/audio-pipewire-minimal.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/audio-pipewire-minimal.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-best.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-full.x86_64-latest.xml create mode 100644 tests/qemuxml2xmloutdata/audio-pipewire-minimal.x86_64-latest.xml
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
participants (2)
-
Ján Tomko
-
Michal Privoznik