diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index cecb27e744..87f9c8d04c 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -6851,6 +6851,69 @@ to the guest sound device. ``id`` Integer id of the audio device. Must be greater than 0. +All the backends support child element for configuring input and +output properties + +:: + + ... + + + + ... + +The input and output elements support the same set of attributes and +elements + +* ``mixingEngine`` + + Control whether the host mixing engine is used to convert between + different audio formats and sampling rates. When the mixing engine + is disabled it is possible to make use of improved audio formats + such as 5.1/7.1. If not specified, a hypervisor default applies. + +* ``fixedSettings`` + + Control whether the mixing engine can dynamically choose settings + to mimimize format conversion. This is only valid when the + mixing engine is explicitly enabled. + +* ``voices`` + + The number of voices voices to use, usually defaults to 1 + +* ``bufferLength`` + + The length of the audio buffer in microseconds. Default is + backend specific. + +The ```` and ```` elements may also permit backend +specific options. + +When fixed settings are enabled, the ```` child element +is permitted with the following attributes. + +* ``frequency`` + + The frequency in HZ, usually defaulting to 44100 + +* ``channels`` + + The number of channels, usually defaulting to 2. The permitted + max number of channels is hypervisor specific. + +* ``format`` + + The audio format, one of ``s8``, ``u8``, ``s16``, ``u16``, + ``s32``, ``u32``, ``f32``. The defalt is hypervisor specific. + None audio backend ^^^^^^^^^^^^^^^^^^ diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 4729590871..8dbfdbb8e2 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4528,12 +4528,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + s8 + u8 + s16 + u16 + s32 + u32 + f32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4546,21 +4651,69 @@ none + + + + + + + + + + + + alsa + + + + + + + + + + + + coreaudio + + + + + + + + + + + + jack + + + + + + + + + + + + @@ -4585,6 +4738,18 @@ pulseaudio + + + + + + + + + + + + @@ -4600,16 +4765,52 @@ + + + + + + + + + + + + spice + + + + + + + + + + + + file + + + + + + + + + + + + diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 6788a0a4c3..c4c64788b9 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -514,6 +514,13 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED, if (audio) { switch ((virDomainAudioType) audio->type) { case VIR_DOMAIN_AUDIO_TYPE_OSS: + if (virDomainAudioIOCommonIsSet(&audio->input) || + virDomainAudioIOCommonIsSet(&audio->output)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("cannot set common audio backend settings")); + return -1; + } + if (audio->backend.oss.input.dev) virBufferAsprintf(¶ms, ",play=%s", audio->backend.oss.input.dev); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 67b905fe98..a07c7eccf3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -756,6 +756,18 @@ VIR_ENUM_IMPL(virDomainAudioSDLDriver, "pulseaudio", ); +VIR_ENUM_IMPL(virDomainAudioFormat, + VIR_DOMAIN_AUDIO_FORMAT_LAST, + "", + "u8", + "s8", + "u16", + "s16", + "u32", + "s32", + "f32", +); + VIR_ENUM_IMPL(virDomainKeyWrapCipherName, VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST, "aes", @@ -13941,6 +13953,99 @@ virDomainSoundDefFind(const virDomainDef *def, } +static int +virDomainAudioCommonParse(virDomainAudioIOCommonPtr def, + xmlNodePtr node, + xmlXPathContextPtr ctxt) +{ + g_autofree char *mixingEngine = virXMLPropString(node, "mixingEngine"); + g_autofree char *fixedSettings = virXMLPropString(node, "fixedSettings"); + g_autofree char *voices = virXMLPropString(node, "voices"); + g_autofree char *bufferLength = virXMLPropString(node, "bufferLength"); + xmlNodePtr settings; + VIR_XPATH_NODE_AUTORESTORE(ctxt); + + ctxt->node = node; + settings = virXPathNode("./settings", ctxt); + + if (mixingEngine && + ((def->mixingEngine = + virTristateBoolTypeFromString(mixingEngine)) <= 0)) { + virReportError(VIR_ERR_XML_ERROR, + _("unknown 'mixingEngine' value '%s'"), mixingEngine); + return -1; + } + + if (fixedSettings && + ((def->fixedSettings = + virTristateBoolTypeFromString(fixedSettings)) <= 0)) { + virReportError(VIR_ERR_XML_ERROR, + _("unknown 'fixedSettings' value '%s'"), fixedSettings); + return -1; + } + + if (def->fixedSettings == VIR_TRISTATE_BOOL_YES && + def->mixingEngine != VIR_TRISTATE_BOOL_YES) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("fixed audio settings requires mixing engine")); + return -1; + } + + if (voices && + (virStrToLong_ui(voices, NULL, 10, &def->voices) < 0 || + !def->voices)) { + virReportError(VIR_ERR_XML_ERROR, + _("cannot parse 'voices' value '%s'"), voices); + return -1; + } + + if (bufferLength && + (virStrToLong_ui(bufferLength, NULL, 10, &def->bufferLength) < 0 || + !def->bufferLength)) { + virReportError(VIR_ERR_XML_ERROR, + _("cannot parse 'bufferLength' value '%s'"), bufferLength); + return -1; + } + + if (settings) { + g_autofree char *frequency = virXMLPropString(settings, "frequency"); + g_autofree char *channels = virXMLPropString(settings, "channels"); + g_autofree char *format = virXMLPropString(settings, "format"); + + if (def->fixedSettings != VIR_TRISTATE_BOOL_YES) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("audio settings specified without fixed settings flag")); + return -1; + } + + if (frequency && + (virStrToLong_ui(frequency, NULL, 10, &def->frequency) < 0 || + !def->frequency)) { + virReportError(VIR_ERR_XML_ERROR, + _("cannot parse 'frequency' value '%s'"), frequency); + return -1; + } + + if (channels && + (virStrToLong_ui(channels, NULL, 10, &def->channels) < 0 || + !def->channels)) { + virReportError(VIR_ERR_XML_ERROR, + _("cannot parse 'channels' value '%s'"), channels); + return -1; + } + + if (format && + (def->format = virDomainAudioFormatTypeFromString(format)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("cannot parse 'format' value '%s'"), format); + return -1; + } + } + + return 0; +} + + static int virDomainAudioOSSParse(virDomainAudioIOOSSPtr def, xmlNodePtr node) @@ -13953,8 +14058,8 @@ virDomainAudioOSSParse(virDomainAudioIOOSSPtr def, static virDomainAudioDefPtr virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED, - xmlNodePtr node G_GNUC_UNUSED, - xmlXPathContextPtr ctxt G_GNUC_UNUSED) + xmlNodePtr node, + xmlXPathContextPtr ctxt) { virDomainAudioDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt) @@ -13994,6 +14099,11 @@ virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED, inputNode = virXPathNode("./input", ctxt); outputNode = virXPathNode("./output", ctxt); + if (inputNode && virDomainAudioCommonParse(&def->input, inputNode, ctxt) < 0) + goto error; + if (outputNode && virDomainAudioCommonParse(&def->output, outputNode, ctxt) < 0) + goto error; + switch ((virDomainAudioType) def->type) { case VIR_DOMAIN_AUDIO_TYPE_NONE: break; @@ -14030,6 +14140,8 @@ virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED, } case VIR_DOMAIN_AUDIO_TYPE_SPICE: + break; + case VIR_DOMAIN_AUDIO_TYPE_FILE: break; @@ -26470,18 +26582,59 @@ virDomainSoundDefFormat(virBufferPtr buf, static void -virDomainAudioCommonFormat(virBufferPtr childBuf, +virDomainAudioCommonFormat(virDomainAudioIOCommonPtr def, + virBufferPtr childBuf, virBufferPtr backendAttrBuf, const char *direction) { - if (virBufferUse(backendAttrBuf)) { + g_auto(virBuffer) settingsBuf = VIR_BUFFER_INITIALIZER; + + if (def->fixedSettings == VIR_TRISTATE_BOOL_YES) { + if (def->frequency) + virBufferAsprintf(&settingsBuf, " frequency='%u'", + def->frequency); + if (def->channels) + virBufferAsprintf(&settingsBuf, " channels='%u'", + def->channels); + if (def->format) + virBufferAsprintf(&settingsBuf, " format='%s'", + virDomainAudioFormatTypeToString(def->format)); + } + + if (def->mixingEngine || def->fixedSettings || + def->voices || def->bufferLength || + virBufferUse(backendAttrBuf)) { virBufferAsprintf(childBuf, "<%s", direction); - virBufferAdd(childBuf, virBufferCurrentContent(backendAttrBuf), -1); - virBufferAddLit(childBuf, "/>\n"); + if (def->mixingEngine) + virBufferAsprintf(childBuf, " mixingEngine='%s'", + virTristateBoolTypeToString(def->mixingEngine)); + if (def->fixedSettings) + virBufferAsprintf(childBuf, " fixedSettings='%s'", + virTristateBoolTypeToString(def->fixedSettings)); + if (def->voices) + virBufferAsprintf(childBuf, " voices='%u'", + def->voices); + if (def->bufferLength) + virBufferAsprintf(childBuf, " bufferLength='%u'", + def->bufferLength); + if (virBufferUse(backendAttrBuf)) + virBufferAdd(childBuf, virBufferCurrentContent(backendAttrBuf), -1); + if (def->fixedSettings == VIR_TRISTATE_BOOL_YES) { + virBufferAddLit(childBuf, ">\n"); + virBufferAdjustIndent(childBuf, 2); + virBufferAddLit(childBuf, "\n"); + virBufferAdjustIndent(childBuf, -2); + virBufferAsprintf(childBuf, "\n", direction); + } else { + virBufferAddLit(childBuf, "/>\n"); + } } } - static void virDomainAudioOSSFormat(virDomainAudioIOOSSPtr def, virBufferPtr buf) @@ -26547,8 +26700,8 @@ virDomainAudioDefFormat(virBufferPtr buf, return -1; } - virDomainAudioCommonFormat(&childBuf, &inputBuf, "input"); - virDomainAudioCommonFormat(&childBuf, &outputBuf, "output"); + virDomainAudioCommonFormat(&def->input, &childBuf, &inputBuf, "input"); + virDomainAudioCommonFormat(&def->output, &childBuf, &outputBuf, "output"); if (virBufferUse(&childBuf)) { virBufferAddLit(buf, ">\n"); @@ -30548,6 +30701,17 @@ virDomainSoundModelSupportsCodecs(virDomainSoundDefPtr def) def->model == VIR_DOMAIN_SOUND_MODEL_ICH9; } +bool +virDomainAudioIOCommonIsSet(virDomainAudioIOCommonPtr common) +{ + return common->mixingEngine || + common->fixedSettings || + common->frequency || + common->channels || + common->voices || + common->format || + common->bufferLength; +} char * virDomainObjGetMetadata(virDomainObjPtr vm, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 62c78ba988..93bab14cb1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1469,6 +1469,32 @@ typedef enum { VIR_DOMAIN_AUDIO_SDL_DRIVER_LAST } virDomainAudioSDLDriver; +typedef enum { + VIR_DOMAIN_AUDIO_FORMAT_DEFAULT, + VIR_DOMAIN_AUDIO_FORMAT_U8, + VIR_DOMAIN_AUDIO_FORMAT_S8, + VIR_DOMAIN_AUDIO_FORMAT_U16, + VIR_DOMAIN_AUDIO_FORMAT_S16, + VIR_DOMAIN_AUDIO_FORMAT_U32, + VIR_DOMAIN_AUDIO_FORMAT_S32, + VIR_DOMAIN_AUDIO_FORMAT_F32, + + VIR_DOMAIN_AUDIO_FORMAT_LAST +} virDomainAudioFormat; + +typedef struct _virDomainAudioIOCommon virDomainAudioIOCommon; +typedef virDomainAudioIOCommon *virDomainAudioIOCommonPtr; +struct _virDomainAudioIOCommon { + virTristateBool mixingEngine; + virTristateBool fixedSettings; + unsigned int frequency; + unsigned int channels; + unsigned int voices; + int format; /* virDomainAudioFormat */ + unsigned int bufferLength; /* milliseconds */ +}; + + typedef struct _virDomainAudioIOOSS virDomainAudioIOOSS; typedef virDomainAudioIOOSS *virDomainAudioIOOSSPtr; struct _virDomainAudioIOOSS { @@ -1480,6 +1506,8 @@ struct _virDomainAudioDef { unsigned int id; + virDomainAudioIOCommon input; + virDomainAudioIOCommon output; union { struct { virDomainAudioIOOSS input; @@ -3717,6 +3745,7 @@ VIR_ENUM_DECL(virDomainChrTcpProtocol); VIR_ENUM_DECL(virDomainChrSpicevmc); VIR_ENUM_DECL(virDomainSoundCodec); VIR_ENUM_DECL(virDomainSoundModel); +VIR_ENUM_DECL(virDomainAudioFormat); VIR_ENUM_DECL(virDomainAudioType); VIR_ENUM_DECL(virDomainAudioSDLDriver); VIR_ENUM_DECL(virDomainKeyWrapCipherName); @@ -3817,6 +3846,8 @@ virDomainDefFindAudioByID(const virDomainDef *def, int id); bool virDomainSoundModelSupportsCodecs(virDomainSoundDefPtr def); +bool +virDomainAudioIOCommonIsSet(virDomainAudioIOCommonPtr common); const char *virDomainChrSourceDefGetPath(virDomainChrSourceDefPtr chr); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1d837dd34a..c4052aa41b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -226,6 +226,9 @@ virDiskNameParse; virDiskNameToBusDeviceIndex; virDiskNameToIndex; virDomainActualNetDefFree; +virDomainAudioFormatTypeFromString; +virDomainAudioFormatTypeToString; +virDomainAudioIOCommonIsSet; virDomainAudioSDLDriverTypeFromString; virDomainAudioSDLDriverTypeToString; virDomainAudioTypeTypeFromString;