``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.
* ``QEMU_AUDIO_DRV``
Valid values are ``pa``, ``none``, ``alsa``, ``coreaudio``, ``jack``, ``oss``,
- ``sdl``, ``spice`` or ``wav``.
+ ``pipewire``, ``sdl``, ``spice`` or ``wav``.
None audio backend
^^^^^^^^^^^^^^^^^^
: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
^^^^^^^^^^^^^^^^^^^^^^^^
"spice",
"file",
"dbus",
+ "pipewire",
);
VIR_ENUM_IMPL(virDomainAudioSDLDriver,
g_free(def->streamName);
}
+static void
+virDomainAudioIOPipewireAudioFree(virDomainAudioIOPipewireAudio *def)
+{
+ g_free(def->name);
+ g_free(def->streamName);
+}
+
void
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;
}
+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,
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);
}
+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)
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);
}
+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)
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:
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;
unsigned int bufferCount;
};
+typedef struct _virDomainAudioIOPipewireAudio virDomainAudioIOPipewireAudio;
+struct _virDomainAudioIOPipewireAudio {
+ char *name;
+ char *streamName;
+ unsigned int latency;
+};
+
struct _virDomainAudioDef {
virDomainAudioType type;
struct {
char *path;
} file;
+ struct {
+ virDomainAudioIOPipewireAudio input;
+ virDomainAudioIOPipewireAudio output;
+ char *runtimeDir;
+ } pipewire;
} backend;
};
<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>
</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>
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;
}
case VIR_DOMAIN_AUDIO_TYPE_DBUS:
break;
+ case VIR_DOMAIN_AUDIO_TYPE_PIPEWIRE:
case VIR_DOMAIN_AUDIO_TYPE_LAST:
default:
virReportEnumRangeError(virDomainAudioType, def->type);
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:
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
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");
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");