get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/1571492/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1571492,
    "url": "http://patchwork.ozlabs.org/api/patches/1571492/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20211221065855.142578-30-marcandre.lureau@redhat.com/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20211221065855.142578-30-marcandre.lureau@redhat.com>",
    "list_archive_url": null,
    "date": "2021-12-21T06:58:48",
    "name": "[PULL,v2,29/36] audio: add \"dbus\" audio backend",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "f5583701252c83b96799c4eaffeee1dd83de6910",
    "submitter": {
        "id": 66774,
        "url": "http://patchwork.ozlabs.org/api/people/66774/?format=api",
        "name": "Marc-André Lureau",
        "email": "marcandre.lureau@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20211221065855.142578-30-marcandre.lureau@redhat.com/mbox/",
    "series": [
        {
            "id": 277865,
            "url": "http://patchwork.ozlabs.org/api/series/277865/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=277865",
            "date": "2021-12-21T06:58:19",
            "name": "[PULL,v2,01/36] ui/vdagent: add CHECK_SPICE_PROTOCOL_VERSION",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/277865/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/1571492/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/1571492/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org",
        "Authentication-Results": [
            "bilbo.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=b08Jk3+E;\n\tdkim-atps=neutral",
            "ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=<UNKNOWN>)",
            "relay.mimecast.com;\n auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=marcandre.lureau@redhat.com"
        ],
        "Received": [
            "from lists.gnu.org (lists.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby bilbo.ozlabs.org (Postfix) with ESMTPS id 4JJ7jz4TmQz9s3q\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Dec 2021 18:43:35 +1100 (AEDT)",
            "from localhost ([::1]:55086 helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>)\n\tid 1mzZo5-00013A-Du\n\tfor incoming@patchwork.ozlabs.org; Tue, 21 Dec 2021 02:43:33 -0500",
            "from eggs.gnu.org ([209.51.188.92]:59150)\n by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1mzZFI-0002S0-O4\n for qemu-devel@nongnu.org; Tue, 21 Dec 2021 02:07:37 -0500",
            "from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:25983)\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1mzZFF-0002Ub-9q\n for qemu-devel@nongnu.org; Tue, 21 Dec 2021 02:07:36 -0500",
            "from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com\n [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n us-mta-65-Ybsm6vWLPCyKzOr4D5FHIQ-1; Tue, 21 Dec 2021 02:07:30 -0500",
            "from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com\n [10.5.11.22])\n (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n (No client certificate requested)\n by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 71EC48042E2;\n Tue, 21 Dec 2021 07:07:29 +0000 (UTC)",
            "from localhost (unknown [10.39.208.37])\n by smtp.corp.redhat.com (Postfix) with ESMTP id 69A79105B20B;\n Tue, 21 Dec 2021 07:07:02 +0000 (UTC)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1640070452;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=uZmHqPJwF3GSSjNUX5ebugjGeisqMy1vI7MNbYpokPw=;\n b=b08Jk3+Ex3GJoMbuunpT9BCZkLyJitK6i9DaWi4E0BgErtF3aFxOGQizVDW0/5okWdvWSm\n D9RkQ74L1b7Gxhe9ujEt9uCjj4x663C5nHxsaBm+Vi9MSM6eIzUrlo8CuAVqQNIxqcnViQ\n o1oKbtJwxlhngvBnaDdyHdOxEQIMH1w=",
        "X-MC-Unique": "Ybsm6vWLPCyKzOr4D5FHIQ-1",
        "From": "marcandre.lureau@redhat.com",
        "To": "qemu-devel@nongnu.org",
        "Subject": "[PULL v2 29/36] audio: add \"dbus\" audio backend",
        "Date": "Tue, 21 Dec 2021 10:58:48 +0400",
        "Message-Id": "<20211221065855.142578-30-marcandre.lureau@redhat.com>",
        "In-Reply-To": "<20211221065855.142578-1-marcandre.lureau@redhat.com>",
        "References": "<20211221065855.142578-1-marcandre.lureau@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 2.84 on 10.5.11.22",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-Originator": "redhat.com",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Received-SPF": "pass client-ip=170.10.129.124;\n envelope-from=marcandre.lureau@redhat.com;\n helo=us-smtp-delivery-124.mimecast.com",
        "X-Spam_score_int": "-29",
        "X-Spam_score": "-3.0",
        "X-Spam_bar": "---",
        "X-Spam_report": "(-3.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.203,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001,\n SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no",
        "X-Spam_action": "no action",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Cc": "peter.maydell@linaro.org, richard.henderson@linaro.org, =?utf-8?q?Marc-A?=\n\t=?utf-8?q?ndr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "\"Qemu-devel\"\n <qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>"
    },
    "content": "From: Marc-André Lureau <marcandre.lureau@redhat.com>\n\nAdd a new -audio backend that accepts D-Bus clients/listeners to handle\nplayback & recording, to be exported via the -display dbus.\n\nExample usage:\n-audiodev dbus,in.mixing-engine=off,out.mixing-engine=off,id=dbus\n-display dbus,audiodev=dbus\n\nSigned-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>\nAcked-by: Gerd Hoffmann <kraxel@redhat.com>\n---\n qapi/audio.json        |   3 +-\n qapi/ui.json           |   5 +-\n audio/audio_int.h      |   7 +\n audio/audio_template.h |   2 +\n ui/dbus.h              |   1 +\n audio/audio.c          |   1 +\n audio/dbusaudio.c      | 654 +++++++++++++++++++++++++++++++++++++++++\n ui/dbus.c              |  35 +++\n audio/meson.build      |   6 +\n audio/trace-events     |   5 +\n qemu-options.hx        |   3 +\n ui/dbus-display1.xml   | 211 +++++++++++++\n 12 files changed, 931 insertions(+), 2 deletions(-)\n create mode 100644 audio/dbusaudio.c",
    "diff": "diff --git a/qapi/audio.json b/qapi/audio.json\nindex 9cba0df8a4e9..693e327c6b6a 100644\n--- a/qapi/audio.json\n+++ b/qapi/audio.json\n@@ -386,7 +386,7 @@\n # Since: 4.0\n ##\n { 'enum': 'AudiodevDriver',\n-  'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'jack', 'oss', 'pa',\n+  'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', 'pa',\n             'sdl', 'spice', 'wav' ] }\n \n ##\n@@ -412,6 +412,7 @@\n     'none':      'AudiodevGenericOptions',\n     'alsa':      'AudiodevAlsaOptions',\n     'coreaudio': 'AudiodevCoreaudioOptions',\n+    'dbus':      'AudiodevGenericOptions',\n     'dsound':    'AudiodevDsoundOptions',\n     'jack':      'AudiodevJackOptions',\n     'oss':       'AudiodevOssOptions',\ndiff --git a/qapi/ui.json b/qapi/ui.json\nindex d435e9472264..2b4371da3777 100644\n--- a/qapi/ui.json\n+++ b/qapi/ui.json\n@@ -1134,13 +1134,16 @@\n # @p2p: Whether to use peer-to-peer connections (accepted through\n #       ``add_client``).\n #\n+# @audiodev: Use the specified DBus audiodev to export audio.\n+#\n # Since: 7.0\n #\n ##\n { 'struct'  : 'DisplayDBus',\n   'data'    : { '*rendernode' : 'str',\n                 '*addr': 'str',\n-                '*p2p': 'bool' } }\n+                '*p2p': 'bool',\n+                '*audiodev': 'str' } }\n \n  ##\n  # @DisplayGLMode:\ndiff --git a/audio/audio_int.h b/audio/audio_int.h\nindex 6d685e24a388..428a091d05e5 100644\n--- a/audio/audio_int.h\n+++ b/audio/audio_int.h\n@@ -31,6 +31,10 @@\n #endif\n #include \"mixeng.h\"\n \n+#ifdef CONFIG_GIO\n+#include <gio/gio.h>\n+#endif\n+\n struct audio_pcm_ops;\n \n struct audio_callback {\n@@ -140,6 +144,9 @@ struct audio_driver {\n     const char *descr;\n     void *(*init) (Audiodev *);\n     void (*fini) (void *);\n+#ifdef CONFIG_GIO\n+    void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager);\n+#endif\n     struct audio_pcm_ops *pcm_ops;\n     int can_be_default;\n     int max_voices_out;\ndiff --git a/audio/audio_template.h b/audio/audio_template.h\nindex c6714946aaed..d2d348638b8c 100644\n--- a/audio/audio_template.h\n+++ b/audio/audio_template.h\n@@ -327,6 +327,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)\n     case AUDIODEV_DRIVER_COREAUDIO:\n         return qapi_AudiodevCoreaudioPerDirectionOptions_base(\n             dev->u.coreaudio.TYPE);\n+    case AUDIODEV_DRIVER_DBUS:\n+        return dev->u.dbus.TYPE;\n     case AUDIODEV_DRIVER_DSOUND:\n         return dev->u.dsound.TYPE;\n     case AUDIODEV_DRIVER_JACK:\ndiff --git a/ui/dbus.h b/ui/dbus.h\nindex 4698d324632e..ca1f0f4ab94f 100644\n--- a/ui/dbus.h\n+++ b/ui/dbus.h\n@@ -36,6 +36,7 @@ struct DBusDisplay {\n     DisplayGLMode gl_mode;\n     bool p2p;\n     char *dbus_addr;\n+    char *audiodev;\n     DisplayGLCtx glctx;\n \n     GDBusConnection *bus;\ndiff --git a/audio/audio.c b/audio/audio.c\nindex 54a153c0ef07..dc28685d226d 100644\n--- a/audio/audio.c\n+++ b/audio/audio.c\n@@ -2000,6 +2000,7 @@ void audio_create_pdos(Audiodev *dev)\n         CASE(NONE, none, );\n         CASE(ALSA, alsa, Alsa);\n         CASE(COREAUDIO, coreaudio, Coreaudio);\n+        CASE(DBUS, dbus, );\n         CASE(DSOUND, dsound, );\n         CASE(JACK, jack, Jack);\n         CASE(OSS, oss, Oss);\ndiff --git a/audio/dbusaudio.c b/audio/dbusaudio.c\nnew file mode 100644\nindex 000000000000..f178b47deec1\n--- /dev/null\n+++ b/audio/dbusaudio.c\n@@ -0,0 +1,654 @@\n+/*\n+ * QEMU DBus audio\n+ *\n+ * Copyright (c) 2021 Red Hat, Inc.\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy\n+ * of this software and associated documentation files (the \"Software\"), to deal\n+ * in the Software without restriction, including without limitation the rights\n+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n+ * copies of the Software, and to permit persons to whom the Software is\n+ * furnished to do so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n+ * THE SOFTWARE.\n+ */\n+\n+#include \"qemu/osdep.h\"\n+#include \"qemu/error-report.h\"\n+#include \"qemu/host-utils.h\"\n+#include \"qemu/module.h\"\n+#include \"qemu/timer.h\"\n+#include \"qemu/dbus.h\"\n+\n+#include <gio/gunixfdlist.h>\n+#include \"ui/dbus-display1.h\"\n+\n+#define AUDIO_CAP \"dbus\"\n+#include \"audio.h\"\n+#include \"audio_int.h\"\n+#include \"trace.h\"\n+\n+#define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT \"/Audio\"\n+\n+#define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */\n+\n+typedef struct DBusAudio {\n+    GDBusObjectManagerServer *server;\n+    GDBusObjectSkeleton *audio;\n+    QemuDBusDisplay1Audio *iface;\n+    GHashTable *out_listeners;\n+    GHashTable *in_listeners;\n+} DBusAudio;\n+\n+typedef struct DBusVoiceOut {\n+    HWVoiceOut hw;\n+    bool enabled;\n+    RateCtl rate;\n+\n+    void *buf;\n+    size_t buf_pos;\n+    size_t buf_size;\n+\n+    bool has_volume;\n+    Volume volume;\n+} DBusVoiceOut;\n+\n+typedef struct DBusVoiceIn {\n+    HWVoiceIn hw;\n+    bool enabled;\n+    RateCtl rate;\n+\n+    bool has_volume;\n+    Volume volume;\n+} DBusVoiceIn;\n+\n+static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)\n+{\n+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+\n+    if (!vo->buf) {\n+        vo->buf_size = hw->samples * hw->info.bytes_per_frame;\n+        vo->buf = g_malloc(vo->buf_size);\n+        vo->buf_pos = 0;\n+    }\n+\n+    *size = MIN(vo->buf_size - vo->buf_pos, *size);\n+    *size = audio_rate_get_bytes(&hw->info, &vo->rate, *size);\n+\n+    return vo->buf + vo->buf_pos;\n+\n+}\n+\n+static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioOutListener *listener = NULL;\n+    g_autoptr(GBytes) bytes = NULL;\n+    g_autoptr(GVariant) v_data = NULL;\n+\n+    assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);\n+    vo->buf_pos += size;\n+\n+    trace_dbus_audio_put_buffer_out(size);\n+\n+    if (vo->buf_pos < vo->buf_size) {\n+        return size;\n+    }\n+\n+    bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);\n+    v_data = g_variant_new_from_bytes(G_VARIANT_TYPE(\"ay\"), bytes, TRUE);\n+    g_variant_ref_sink(v_data);\n+\n+    g_hash_table_iter_init(&iter, da->out_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        qemu_dbus_display1_audio_out_listener_call_write(\n+            listener,\n+            (uintptr_t)hw,\n+            v_data,\n+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+    }\n+\n+    return size;\n+}\n+\n+#ifdef HOST_WORDS_BIGENDIAN\n+#define AUDIO_HOST_BE TRUE\n+#else\n+#define AUDIO_HOST_BE FALSE\n+#endif\n+\n+static void\n+dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,\n+                       HWVoiceOut *hw)\n+{\n+    qemu_dbus_display1_audio_out_listener_call_init(\n+        listener,\n+        (uintptr_t)hw,\n+        hw->info.bits,\n+        hw->info.is_signed,\n+        hw->info.is_float,\n+        hw->info.freq,\n+        hw->info.nchannels,\n+        hw->info.bytes_per_frame,\n+        hw->info.bytes_per_second,\n+        hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,\n+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+}\n+\n+static int\n+dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioOutListener *listener = NULL;\n+\n+    audio_pcm_init_info(&hw->info, as);\n+    hw->samples = DBUS_AUDIO_NSAMPLES;\n+    audio_rate_start(&vo->rate);\n+\n+    g_hash_table_iter_init(&iter, da->out_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        dbus_init_out_listener(listener, hw);\n+    }\n+    return 0;\n+}\n+\n+static void\n+dbus_fini_out(HWVoiceOut *hw)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioOutListener *listener = NULL;\n+\n+    g_hash_table_iter_init(&iter, da->out_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        qemu_dbus_display1_audio_out_listener_call_fini(\n+            listener,\n+            (uintptr_t)hw,\n+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+    }\n+\n+    g_clear_pointer(&vo->buf, g_free);\n+}\n+\n+static void\n+dbus_enable_out(HWVoiceOut *hw, bool enable)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioOutListener *listener = NULL;\n+\n+    vo->enabled = enable;\n+    if (enable) {\n+        audio_rate_start(&vo->rate);\n+    }\n+\n+    g_hash_table_iter_init(&iter, da->out_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        qemu_dbus_display1_audio_out_listener_call_set_enabled(\n+            listener, (uintptr_t)hw, enable,\n+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+    }\n+}\n+\n+static void\n+dbus_volume_out_listener(HWVoiceOut *hw,\n+                         QemuDBusDisplay1AudioOutListener *listener)\n+{\n+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+    Volume *vol = &vo->volume;\n+    g_autoptr(GBytes) bytes = NULL;\n+    GVariant *v_vol = NULL;\n+\n+    if (!vo->has_volume) {\n+        return;\n+    }\n+\n+    assert(vol->channels < sizeof(vol->vol));\n+    bytes = g_bytes_new(vol->vol, vol->channels);\n+    v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE(\"ay\"), bytes, TRUE);\n+    qemu_dbus_display1_audio_out_listener_call_set_volume(\n+        listener, (uintptr_t)hw, vol->mute, v_vol,\n+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+}\n+\n+static void\n+dbus_volume_out(HWVoiceOut *hw, Volume *vol)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioOutListener *listener = NULL;\n+\n+    vo->has_volume = true;\n+    vo->volume = *vol;\n+\n+    g_hash_table_iter_init(&iter, da->out_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        dbus_volume_out_listener(hw, listener);\n+    }\n+}\n+\n+static void\n+dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)\n+{\n+    qemu_dbus_display1_audio_in_listener_call_init(\n+        listener,\n+        (uintptr_t)hw,\n+        hw->info.bits,\n+        hw->info.is_signed,\n+        hw->info.is_float,\n+        hw->info.freq,\n+        hw->info.nchannels,\n+        hw->info.bytes_per_frame,\n+        hw->info.bytes_per_second,\n+        hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,\n+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+}\n+\n+static int\n+dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioInListener *listener = NULL;\n+\n+    audio_pcm_init_info(&hw->info, as);\n+    hw->samples = DBUS_AUDIO_NSAMPLES;\n+    audio_rate_start(&vo->rate);\n+\n+    g_hash_table_iter_init(&iter, da->in_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        dbus_init_in_listener(listener, hw);\n+    }\n+    return 0;\n+}\n+\n+static void\n+dbus_fini_in(HWVoiceIn *hw)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioInListener *listener = NULL;\n+\n+    g_hash_table_iter_init(&iter, da->in_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        qemu_dbus_display1_audio_in_listener_call_fini(\n+            listener,\n+            (uintptr_t)hw,\n+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+    }\n+}\n+\n+static void\n+dbus_volume_in_listener(HWVoiceIn *hw,\n+                         QemuDBusDisplay1AudioInListener *listener)\n+{\n+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);\n+    Volume *vol = &vo->volume;\n+    g_autoptr(GBytes) bytes = NULL;\n+    GVariant *v_vol = NULL;\n+\n+    if (!vo->has_volume) {\n+        return;\n+    }\n+\n+    assert(vol->channels < sizeof(vol->vol));\n+    bytes = g_bytes_new(vol->vol, vol->channels);\n+    v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE(\"ay\"), bytes, TRUE);\n+    qemu_dbus_display1_audio_in_listener_call_set_volume(\n+        listener, (uintptr_t)hw, vol->mute, v_vol,\n+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+}\n+\n+static void\n+dbus_volume_in(HWVoiceIn *hw, Volume *vol)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioInListener *listener = NULL;\n+\n+    vo->has_volume = true;\n+    vo->volume = *vol;\n+\n+    g_hash_table_iter_init(&iter, da->in_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        dbus_volume_in_listener(hw, listener);\n+    }\n+}\n+\n+static size_t\n+dbus_read(HWVoiceIn *hw, void *buf, size_t size)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioInListener *listener = NULL;\n+\n+    trace_dbus_audio_read(size);\n+\n+    /* size = audio_rate_get_bytes(&hw->info, &vo->rate, size); */\n+\n+    g_hash_table_iter_init(&iter, da->in_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        g_autoptr(GVariant) v_data = NULL;\n+        const char *data;\n+        gsize n = 0;\n+\n+        if (qemu_dbus_display1_audio_in_listener_call_read_sync(\n+                listener,\n+                (uintptr_t)hw,\n+                size,\n+                G_DBUS_CALL_FLAGS_NONE, -1,\n+                &v_data, NULL, NULL)) {\n+            data = g_variant_get_fixed_array(v_data, &n, 1);\n+            g_warn_if_fail(n <= size);\n+            size = MIN(n, size);\n+            memcpy(buf, data, size);\n+            break;\n+        }\n+    }\n+\n+    return size;\n+}\n+\n+static void\n+dbus_enable_in(HWVoiceIn *hw, bool enable)\n+{\n+    DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;\n+    DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);\n+    GHashTableIter iter;\n+    QemuDBusDisplay1AudioInListener *listener = NULL;\n+\n+    vo->enabled = enable;\n+    if (enable) {\n+        audio_rate_start(&vo->rate);\n+    }\n+\n+    g_hash_table_iter_init(&iter, da->in_listeners);\n+    while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {\n+        qemu_dbus_display1_audio_in_listener_call_set_enabled(\n+            listener, (uintptr_t)hw, enable,\n+            G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+    }\n+}\n+\n+static void *\n+dbus_audio_init(Audiodev *dev)\n+{\n+    DBusAudio *da = g_new0(DBusAudio, 1);\n+\n+    da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,\n+                                                g_free, g_object_unref);\n+    da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,\n+                                               g_free, g_object_unref);\n+    return da;\n+}\n+\n+static void\n+dbus_audio_fini(void *opaque)\n+{\n+    DBusAudio *da = opaque;\n+\n+    if (da->server) {\n+        g_dbus_object_manager_server_unexport(da->server,\n+                                              DBUS_DISPLAY1_AUDIO_PATH);\n+    }\n+    g_clear_object(&da->audio);\n+    g_clear_object(&da->iface);\n+    g_clear_pointer(&da->in_listeners, g_hash_table_unref);\n+    g_clear_pointer(&da->out_listeners, g_hash_table_unref);\n+    g_clear_object(&da->server);\n+    g_free(da);\n+}\n+\n+static void\n+listener_out_vanished_cb(GDBusConnection *connection,\n+                         gboolean remote_peer_vanished,\n+                         GError *error,\n+                         DBusAudio *da)\n+{\n+    char *name = g_object_get_data(G_OBJECT(connection), \"name\");\n+\n+    g_hash_table_remove(da->out_listeners, name);\n+}\n+\n+static void\n+listener_in_vanished_cb(GDBusConnection *connection,\n+                        gboolean remote_peer_vanished,\n+                        GError *error,\n+                        DBusAudio *da)\n+{\n+    char *name = g_object_get_data(G_OBJECT(connection), \"name\");\n+\n+    g_hash_table_remove(da->in_listeners, name);\n+}\n+\n+static gboolean\n+dbus_audio_register_listener(AudioState *s,\n+                             GDBusMethodInvocation *invocation,\n+                             GUnixFDList *fd_list,\n+                             GVariant *arg_listener,\n+                             bool out)\n+{\n+    DBusAudio *da = s->drv_opaque;\n+    const char *sender = g_dbus_method_invocation_get_sender(invocation);\n+    g_autoptr(GDBusConnection) listener_conn = NULL;\n+    g_autoptr(GError) err = NULL;\n+    g_autoptr(GSocket) socket = NULL;\n+    g_autoptr(GSocketConnection) socket_conn = NULL;\n+    g_autofree char *guid = g_dbus_generate_guid();\n+    GHashTable *listeners = out ? da->out_listeners : da->in_listeners;\n+    GObject *listener;\n+    int fd;\n+\n+    trace_dbus_audio_register(sender, out ? \"out\" : \"in\");\n+\n+    if (g_hash_table_contains(listeners, sender)) {\n+        g_dbus_method_invocation_return_error(invocation,\n+                                              DBUS_DISPLAY_ERROR,\n+                                              DBUS_DISPLAY_ERROR_INVALID,\n+                                              \"`%s` is already registered!\",\n+                                              sender);\n+        return DBUS_METHOD_INVOCATION_HANDLED;\n+    }\n+\n+    fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);\n+    if (err) {\n+        g_dbus_method_invocation_return_error(invocation,\n+                                              DBUS_DISPLAY_ERROR,\n+                                              DBUS_DISPLAY_ERROR_FAILED,\n+                                              \"Couldn't get peer fd: %s\",\n+                                              err->message);\n+        return DBUS_METHOD_INVOCATION_HANDLED;\n+    }\n+\n+    socket = g_socket_new_from_fd(fd, &err);\n+    if (err) {\n+        g_dbus_method_invocation_return_error(invocation,\n+                                              DBUS_DISPLAY_ERROR,\n+                                              DBUS_DISPLAY_ERROR_FAILED,\n+                                              \"Couldn't make a socket: %s\",\n+                                              err->message);\n+        return DBUS_METHOD_INVOCATION_HANDLED;\n+    }\n+    socket_conn = g_socket_connection_factory_create_connection(socket);\n+    if (out) {\n+        qemu_dbus_display1_audio_complete_register_out_listener(\n+            da->iface, invocation, NULL);\n+    } else {\n+        qemu_dbus_display1_audio_complete_register_in_listener(\n+            da->iface, invocation, NULL);\n+    }\n+\n+    listener_conn =\n+        g_dbus_connection_new_sync(\n+            G_IO_STREAM(socket_conn),\n+            guid,\n+            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,\n+            NULL, NULL, &err);\n+    if (err) {\n+        error_report(\"Failed to setup peer connection: %s\", err->message);\n+        return DBUS_METHOD_INVOCATION_HANDLED;\n+    }\n+\n+    listener = out ?\n+        G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(\n+            listener_conn,\n+            G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,\n+            NULL,\n+            \"/org/qemu/Display1/AudioOutListener\",\n+            NULL,\n+            &err)) :\n+        G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(\n+            listener_conn,\n+            G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,\n+            NULL,\n+            \"/org/qemu/Display1/AudioInListener\",\n+            NULL,\n+            &err));\n+    if (!listener) {\n+        error_report(\"Failed to setup proxy: %s\", err->message);\n+        return DBUS_METHOD_INVOCATION_HANDLED;\n+    }\n+\n+    if (out) {\n+        HWVoiceOut *hw;\n+\n+        QLIST_FOREACH(hw, &s->hw_head_out, entries) {\n+            DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);\n+            QemuDBusDisplay1AudioOutListener *l =\n+                QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);\n+\n+            dbus_init_out_listener(l, hw);\n+            qemu_dbus_display1_audio_out_listener_call_set_enabled(\n+                l, (uintptr_t)hw, vo->enabled,\n+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+        }\n+    } else {\n+        HWVoiceIn *hw;\n+\n+        QLIST_FOREACH(hw, &s->hw_head_in, entries) {\n+            DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);\n+            QemuDBusDisplay1AudioInListener *l =\n+                QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);\n+\n+            dbus_init_in_listener(\n+                QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);\n+            qemu_dbus_display1_audio_in_listener_call_set_enabled(\n+                l, (uintptr_t)hw, vo->enabled,\n+                G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);\n+        }\n+    }\n+\n+    g_object_set_data_full(G_OBJECT(listener_conn), \"name\",\n+                           g_strdup(sender), g_free);\n+    g_hash_table_insert(listeners, g_strdup(sender), listener);\n+    g_object_connect(listener_conn,\n+                     \"signal::closed\",\n+                     out ? listener_out_vanished_cb : listener_in_vanished_cb,\n+                     da,\n+                     NULL);\n+\n+    return DBUS_METHOD_INVOCATION_HANDLED;\n+}\n+\n+static gboolean\n+dbus_audio_register_out_listener(AudioState *s,\n+                                 GDBusMethodInvocation *invocation,\n+                                 GUnixFDList *fd_list,\n+                                 GVariant *arg_listener)\n+{\n+    return dbus_audio_register_listener(s, invocation,\n+                                        fd_list, arg_listener, true);\n+\n+}\n+\n+static gboolean\n+dbus_audio_register_in_listener(AudioState *s,\n+                                GDBusMethodInvocation *invocation,\n+                                GUnixFDList *fd_list,\n+                                GVariant *arg_listener)\n+{\n+    return dbus_audio_register_listener(s, invocation,\n+                                        fd_list, arg_listener, false);\n+}\n+\n+static void\n+dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server)\n+{\n+    DBusAudio *da = s->drv_opaque;\n+\n+    g_assert(da);\n+    g_assert(!da->server);\n+\n+    da->server = g_object_ref(server);\n+\n+    da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);\n+    da->iface = qemu_dbus_display1_audio_skeleton_new();\n+    g_object_connect(da->iface,\n+                     \"swapped-signal::handle-register-in-listener\",\n+                     dbus_audio_register_in_listener, s,\n+                     \"swapped-signal::handle-register-out-listener\",\n+                     dbus_audio_register_out_listener, s,\n+                     NULL);\n+\n+    g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),\n+                                         G_DBUS_INTERFACE_SKELETON(da->iface));\n+    g_dbus_object_manager_server_export(da->server, da->audio);\n+}\n+\n+static struct audio_pcm_ops dbus_pcm_ops = {\n+    .init_out = dbus_init_out,\n+    .fini_out = dbus_fini_out,\n+    .write    = audio_generic_write,\n+    .get_buffer_out = dbus_get_buffer_out,\n+    .put_buffer_out = dbus_put_buffer_out,\n+    .enable_out = dbus_enable_out,\n+    .volume_out = dbus_volume_out,\n+\n+    .init_in  = dbus_init_in,\n+    .fini_in  = dbus_fini_in,\n+    .read     = dbus_read,\n+    .run_buffer_in = audio_generic_run_buffer_in,\n+    .enable_in = dbus_enable_in,\n+    .volume_in = dbus_volume_in,\n+};\n+\n+static struct audio_driver dbus_audio_driver = {\n+    .name            = \"dbus\",\n+    .descr           = \"Timer based audio exposed with DBus interface\",\n+    .init            = dbus_audio_init,\n+    .fini            = dbus_audio_fini,\n+    .set_dbus_server = dbus_audio_set_server,\n+    .pcm_ops         = &dbus_pcm_ops,\n+    .can_be_default  = 1,\n+    .max_voices_out  = INT_MAX,\n+    .max_voices_in   = INT_MAX,\n+    .voice_size_out  = sizeof(DBusVoiceOut),\n+    .voice_size_in   = sizeof(DBusVoiceIn)\n+};\n+\n+static void register_audio_dbus(void)\n+{\n+    audio_driver_register(&dbus_audio_driver);\n+}\n+type_init(register_audio_dbus);\n+\n+module_dep(\"ui-dbus\")\ndiff --git a/ui/dbus.c b/ui/dbus.c\nindex 847a66782116..d24f704d4662 100644\n--- a/ui/dbus.c\n+++ b/ui/dbus.c\n@@ -30,6 +30,8 @@\n #include \"ui/dbus-module.h\"\n #include \"ui/egl-helpers.h\"\n #include \"ui/egl-context.h\"\n+#include \"audio/audio.h\"\n+#include \"audio/audio_int.h\"\n #include \"qapi/error.h\"\n #include \"trace.h\"\n \n@@ -84,6 +86,7 @@ dbus_display_finalize(Object *o)\n     g_clear_object(&dd->bus);\n     g_clear_object(&dd->iface);\n     g_free(dd->dbus_addr);\n+    g_free(dd->audiodev);\n     dbus_display = NULL;\n }\n \n@@ -140,6 +143,19 @@ dbus_display_complete(UserCreatable *uc, Error **errp)\n         return;\n     }\n \n+    if (dd->audiodev && *dd->audiodev) {\n+        AudioState *audio_state = audio_state_by_name(dd->audiodev);\n+        if (!audio_state) {\n+            error_setg(errp, \"Audiodev '%s' not found\", dd->audiodev);\n+            return;\n+        }\n+        if (!g_str_equal(audio_state->drv->name, \"dbus\")) {\n+            error_setg(errp, \"Audiodev '%s' is not compatible with DBus\",\n+                       dd->audiodev);\n+            return;\n+        }\n+        audio_state->drv->set_dbus_server(audio_state, dd->server);\n+    }\n \n     consoles = g_array_new(FALSE, FALSE, sizeof(guint32));\n     for (idx = 0;; idx++) {\n@@ -261,6 +277,23 @@ set_dbus_addr(Object *o, const char *str, Error **errp)\n     dd->dbus_addr = g_strdup(str);\n }\n \n+static char *\n+get_audiodev(Object *o, Error **errp)\n+{\n+    DBusDisplay *dd = DBUS_DISPLAY(o);\n+\n+    return g_strdup(dd->audiodev);\n+}\n+\n+static void\n+set_audiodev(Object *o, const char *str, Error **errp)\n+{\n+    DBusDisplay *dd = DBUS_DISPLAY(o);\n+\n+    g_free(dd->audiodev);\n+    dd->audiodev = g_strdup(str);\n+}\n+\n static int\n get_gl_mode(Object *o, Error **errp)\n {\n@@ -285,6 +318,7 @@ dbus_display_class_init(ObjectClass *oc, void *data)\n     ucc->complete = dbus_display_complete;\n     object_class_property_add_bool(oc, \"p2p\", get_dbus_p2p, set_dbus_p2p);\n     object_class_property_add_str(oc, \"addr\", get_dbus_addr, set_dbus_addr);\n+    object_class_property_add_str(oc, \"audiodev\", get_audiodev, set_audiodev);\n     object_class_property_add_enum(oc, \"gl-mode\",\n                                    \"DisplayGLMode\", &DisplayGLMode_lookup,\n                                    get_gl_mode, set_gl_mode);\n@@ -321,6 +355,7 @@ dbus_init(DisplayState *ds, DisplayOptions *opts)\n                           object_get_objects_root(),\n                           \"dbus-display\", &error_fatal,\n                           \"addr\", opts->u.dbus.addr ?: \"\",\n+                          \"audiodev\", opts->u.dbus.audiodev ?: \"\",\n                           \"gl-mode\", DisplayGLMode_str(mode),\n                           \"p2p\", yes_no(opts->u.dbus.p2p),\n                           NULL);\ndiff --git a/audio/meson.build b/audio/meson.build\nindex 462533bb8c22..0ac3791d0bd1 100644\n--- a/audio/meson.build\n+++ b/audio/meson.build\n@@ -26,4 +26,10 @@ foreach m : [\n   endif\n endforeach\n \n+if dbus_display\n+    module_ss = ss.source_set()\n+    module_ss.add(when: gio, if_true: files('dbusaudio.c'))\n+    audio_modules += {'dbus': module_ss}\n+endif\n+\n modules += {'audio': audio_modules}\ndiff --git a/audio/trace-events b/audio/trace-events\nindex 957c92337bee..e1ab643add39 100644\n--- a/audio/trace-events\n+++ b/audio/trace-events\n@@ -13,6 +13,11 @@ alsa_resume_out(void) \"Resuming suspended output stream\"\n # ossaudio.c\n oss_version(int version) \"OSS version = 0x%x\"\n \n+# dbusaudio.c\n+dbus_audio_register(const char *s, const char *dir) \"sender = %s, dir = %s\"\n+dbus_audio_put_buffer_out(size_t len) \"len = %zu\"\n+dbus_audio_read(size_t len) \"len = %zu\"\n+\n # audio.c\n audio_timer_start(int interval) \"interval %d ms\"\n audio_timer_stop(void) \"\"\ndiff --git a/qemu-options.hx b/qemu-options.hx\nindex 977e0873a12c..7d4751094783 100644\n--- a/qemu-options.hx\n+++ b/qemu-options.hx\n@@ -659,6 +659,9 @@ DEF(\"audiodev\", HAS_ARG, QEMU_OPTION_audiodev,\n #endif\n #ifdef CONFIG_SPICE\n     \"-audiodev spice,id=id[,prop[=value][,...]]\\n\"\n+#endif\n+#ifdef CONFIG_DBUS_DISPLAY\n+    \"-audiodev dbus,id=id[,prop[=value][,...]]\\n\"\n #endif\n     \"-audiodev wav,id=id[,prop[=value][,...]]\\n\"\n     \"                path= path of wav file to record\\n\",\ndiff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml\nindex 0f0ae92e4d57..aff645220c88 100644\n--- a/ui/dbus-display1.xml\n+++ b/ui/dbus-display1.xml\n@@ -375,4 +375,215 @@\n       </arg>\n     </method>\n   </interface>\n+\n+  <!--\n+      org.qemu.Display1.Audio:\n+\n+      Audio backend may be available on ``/org/qemu/Display1/Audio``.\n+  -->\n+  <interface name=\"org.qemu.Display1.Audio\">\n+    <!--\n+        RegisterOutListener:\n+        @listener: a Unix socket FD, for peer-to-peer D-Bus communication.\n+\n+        Register an audio backend playback handler.\n+\n+        Multiple listeners may be registered simultaneously.\n+\n+        The listener is expected to implement the\n+        :dbus:iface:`org.qemu.Display1.AudioOutListener` interface.\n+    -->\n+    <method name=\"RegisterOutListener\">\n+      <arg type=\"h\" name=\"listener\" direction=\"in\"/>\n+    </method>\n+\n+    <!--\n+        RegisterInListener:\n+        @listener: a Unix socket FD, for peer-to-peer D-Bus communication.\n+\n+        Register an audio backend record handler.\n+\n+        Multiple listeners may be registered simultaneously.\n+\n+        The listener is expected to implement the\n+        :dbus:iface:`org.qemu.Display1.AudioInListener` interface.\n+    -->\n+    <method name=\"RegisterInListener\">\n+      <arg type=\"h\" name=\"listener\" direction=\"in\"/>\n+    </method>\n+  </interface>\n+\n+  <!--\n+      org.qemu.Display1.AudioOutListener:\n+\n+      This client-side interface must be available on\n+      ``/org/qemu/Display1/AudioOutListener`` when registering the peer-to-peer\n+      connection with :dbus:meth:`~org.qemu.Display1.Audio.RegisterOutListener`.\n+  -->\n+  <interface name=\"org.qemu.Display1.AudioOutListener\">\n+    <!--\n+        Init:\n+        @id: the stream ID.\n+        @bits: PCM bits per sample.\n+        @is_signed: whether the PCM data is signed.\n+        @is_float: PCM floating point format.\n+        @freq: the PCM frequency in Hz.\n+        @nchannels: the number of channels.\n+        @bytes_per_frame: the bytes per frame.\n+        @bytes_per_second: the bytes per second.\n+        @be: whether using big-endian format.\n+\n+        Initializes a PCM playback stream.\n+    -->\n+    <method name=\"Init\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg name=\"bits\" type=\"y\" direction=\"in\"/>\n+      <arg name=\"is_signed\" type=\"b\" direction=\"in\"/>\n+      <arg name=\"is_float\" type=\"b\" direction=\"in\"/>\n+      <arg name=\"freq\" type=\"u\" direction=\"in\"/>\n+      <arg name=\"nchannels\" type=\"y\" direction=\"in\"/>\n+      <arg name=\"bytes_per_frame\" type=\"u\" direction=\"in\"/>\n+      <arg name=\"bytes_per_second\" type=\"u\" direction=\"in\"/>\n+      <arg name=\"be\" type=\"b\" direction=\"in\"/>\n+    </method>\n+\n+    <!--\n+        Fini:\n+        @id: the stream ID.\n+\n+        Finish & close a playback stream.\n+    -->\n+    <method name=\"Fini\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+    </method>\n+\n+    <!--\n+        SetEnabled:\n+        @id: the stream ID.\n+\n+        Resume or suspend the playback stream.\n+    -->\n+    <method name=\"SetEnabled\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg name=\"enabled\" type=\"b\" direction=\"in\"/>\n+    </method>\n+\n+    <!--\n+        SetVolume:\n+        @id: the stream ID.\n+        @mute: whether the stream is muted.\n+        @volume: the volume per-channel.\n+\n+        Set the stream volume and mute state (volume without unit, 0-255).\n+    -->\n+    <method name=\"SetVolume\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg name=\"mute\" type=\"b\" direction=\"in\"/>\n+      <arg name=\"volume\" type=\"ay\" direction=\"in\">\n+        <annotation name=\"org.gtk.GDBus.C.ForceGVariant\" value=\"true\"/>\n+      </arg>\n+    </method>\n+\n+    <!--\n+        Write:\n+        @id: the stream ID.\n+        @data: the PCM data.\n+\n+        PCM stream to play.\n+    -->\n+    <method name=\"Write\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg type=\"ay\" name=\"data\" direction=\"in\">\n+        <annotation name=\"org.gtk.GDBus.C.ForceGVariant\" value=\"true\"/>\n+      </arg>\n+    </method>\n+  </interface>\n+\n+  <!--\n+      org.qemu.Display1.AudioInListener:\n+\n+      This client-side interface must be available on\n+      ``/org/qemu/Display1/AudioInListener`` when registering the peer-to-peer\n+      connection with :dbus:meth:`~org.qemu.Display1.Audio.RegisterInListener`.\n+  -->\n+  <interface name=\"org.qemu.Display1.AudioInListener\">\n+    <!--\n+        Init:\n+        @id: the stream ID.\n+        @bits: PCM bits per sample.\n+        @is_signed: whether the PCM data is signed.\n+        @is_float: PCM floating point format.\n+        @freq: the PCM frequency in Hz.\n+        @nchannels: the number of channels.\n+        @bytes_per_frame: the bytes per frame.\n+        @bytes_per_second: the bytes per second.\n+        @be: whether using big-endian format.\n+\n+        Initializes a PCM record stream.\n+    -->\n+    <method name=\"Init\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg name=\"bits\" type=\"y\" direction=\"in\"/>\n+      <arg name=\"is_signed\" type=\"b\" direction=\"in\"/>\n+      <arg name=\"is_float\" type=\"b\" direction=\"in\"/>\n+      <arg name=\"freq\" type=\"u\" direction=\"in\"/>\n+      <arg name=\"nchannels\" type=\"y\" direction=\"in\"/>\n+      <arg name=\"bytes_per_frame\" type=\"u\" direction=\"in\"/>\n+      <arg name=\"bytes_per_second\" type=\"u\" direction=\"in\"/>\n+      <arg name=\"be\" type=\"b\" direction=\"in\"/>\n+    </method>\n+\n+    <!--\n+        Fini:\n+        @id: the stream ID.\n+\n+        Finish & close a record stream.\n+    -->\n+    <method name=\"Fini\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+    </method>\n+\n+    <!--\n+        SetEnabled:\n+        @id: the stream ID.\n+\n+        Resume or suspend the record stream.\n+    -->\n+    <method name=\"SetEnabled\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg name=\"enabled\" type=\"b\" direction=\"in\"/>\n+    </method>\n+\n+    <!--\n+        SetVolume:\n+        @id: the stream ID.\n+        @mute: whether the stream is muted.\n+        @volume: the volume per-channel.\n+\n+        Set the stream volume and mute state (volume without unit, 0-255).\n+    -->\n+    <method name=\"SetVolume\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg name=\"mute\" type=\"b\" direction=\"in\"/>\n+      <arg name=\"volume\" type=\"ay\" direction=\"in\">\n+        <annotation name=\"org.gtk.GDBus.C.ForceGVariant\" value=\"true\"/>\n+      </arg>\n+    </method>\n+\n+    <!--\n+        Read:\n+        @id: the stream ID.\n+        @size: the amount to read, in bytes.\n+        @data: the recorded data (which may be less than requested).\n+\n+        Read \"size\" bytes from the record stream.\n+    -->\n+    <method name=\"Read\">\n+      <arg name=\"id\" type=\"t\" direction=\"in\"/>\n+      <arg name=\"size\" type=\"t\" direction=\"in\"/>\n+      <arg type=\"ay\" name=\"data\" direction=\"out\">\n+        <annotation name=\"org.gtk.GDBus.C.ForceGVariant\" value=\"true\"/>\n+      </arg>\n+    </method>\n+  </interface>\n </node>\n",
    "prefixes": [
        "PULL",
        "v2",
        "29/36"
    ]
}