summary refs log tree commit diff stats
path: root/ui
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2021-03-09 17:15:28 +0400
committerMarc-André Lureau <marcandre.lureau@redhat.com>2021-12-21 10:50:22 +0400
commit739362d4205cd90686118fe5af3e236c2f8c6be9 (patch)
tree10d27d3efc227f29ffee0dd02d345e0faa707623 /ui
parentb4dd5b6a60eb525437c8d315d0d59dc25d4e4cb1 (diff)
downloadfocaccia-qemu-739362d4205cd90686118fe5af3e236c2f8c6be9.tar.gz
focaccia-qemu-739362d4205cd90686118fe5af3e236c2f8c6be9.zip
audio: add "dbus" audio backend
Add a new -audio backend that accepts D-Bus clients/listeners to handle
playback & recording, to be exported via the -display dbus.

Example usage:
-audiodev dbus,in.mixing-engine=off,out.mixing-engine=off,id=dbus
-display dbus,audiodev=dbus

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'ui')
-rw-r--r--ui/dbus-display1.xml211
-rw-r--r--ui/dbus.c35
-rw-r--r--ui/dbus.h1
3 files changed, 247 insertions, 0 deletions
diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml
index 0f0ae92e4d..aff645220c 100644
--- a/ui/dbus-display1.xml
+++ b/ui/dbus-display1.xml
@@ -375,4 +375,215 @@
       </arg>
     </method>
   </interface>
+
+  <!--
+      org.qemu.Display1.Audio:
+
+      Audio backend may be available on ``/org/qemu/Display1/Audio``.
+  -->
+  <interface name="org.qemu.Display1.Audio">
+    <!--
+        RegisterOutListener:
+        @listener: a Unix socket FD, for peer-to-peer D-Bus communication.
+
+        Register an audio backend playback handler.
+
+        Multiple listeners may be registered simultaneously.
+
+        The listener is expected to implement the
+        :dbus:iface:`org.qemu.Display1.AudioOutListener` interface.
+    -->
+    <method name="RegisterOutListener">
+      <arg type="h" name="listener" direction="in"/>
+    </method>
+
+    <!--
+        RegisterInListener:
+        @listener: a Unix socket FD, for peer-to-peer D-Bus communication.
+
+        Register an audio backend record handler.
+
+        Multiple listeners may be registered simultaneously.
+
+        The listener is expected to implement the
+        :dbus:iface:`org.qemu.Display1.AudioInListener` interface.
+    -->
+    <method name="RegisterInListener">
+      <arg type="h" name="listener" direction="in"/>
+    </method>
+  </interface>
+
+  <!--
+      org.qemu.Display1.AudioOutListener:
+
+      This client-side interface must be available on
+      ``/org/qemu/Display1/AudioOutListener`` when registering the peer-to-peer
+      connection with :dbus:meth:`~org.qemu.Display1.Audio.RegisterOutListener`.
+  -->
+  <interface name="org.qemu.Display1.AudioOutListener">
+    <!--
+        Init:
+        @id: the stream ID.
+        @bits: PCM bits per sample.
+        @is_signed: whether the PCM data is signed.
+        @is_float: PCM floating point format.
+        @freq: the PCM frequency in Hz.
+        @nchannels: the number of channels.
+        @bytes_per_frame: the bytes per frame.
+        @bytes_per_second: the bytes per second.
+        @be: whether using big-endian format.
+
+        Initializes a PCM playback stream.
+    -->
+    <method name="Init">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="bits" type="y" direction="in"/>
+      <arg name="is_signed" type="b" direction="in"/>
+      <arg name="is_float" type="b" direction="in"/>
+      <arg name="freq" type="u" direction="in"/>
+      <arg name="nchannels" type="y" direction="in"/>
+      <arg name="bytes_per_frame" type="u" direction="in"/>
+      <arg name="bytes_per_second" type="u" direction="in"/>
+      <arg name="be" type="b" direction="in"/>
+    </method>
+
+    <!--
+        Fini:
+        @id: the stream ID.
+
+        Finish & close a playback stream.
+    -->
+    <method name="Fini">
+      <arg name="id" type="t" direction="in"/>
+    </method>
+
+    <!--
+        SetEnabled:
+        @id: the stream ID.
+
+        Resume or suspend the playback stream.
+    -->
+    <method name="SetEnabled">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="enabled" type="b" direction="in"/>
+    </method>
+
+    <!--
+        SetVolume:
+        @id: the stream ID.
+        @mute: whether the stream is muted.
+        @volume: the volume per-channel.
+
+        Set the stream volume and mute state (volume without unit, 0-255).
+    -->
+    <method name="SetVolume">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="mute" type="b" direction="in"/>
+      <arg name="volume" type="ay" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+
+    <!--
+        Write:
+        @id: the stream ID.
+        @data: the PCM data.
+
+        PCM stream to play.
+    -->
+    <method name="Write">
+      <arg name="id" type="t" direction="in"/>
+      <arg type="ay" name="data" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+  </interface>
+
+  <!--
+      org.qemu.Display1.AudioInListener:
+
+      This client-side interface must be available on
+      ``/org/qemu/Display1/AudioInListener`` when registering the peer-to-peer
+      connection with :dbus:meth:`~org.qemu.Display1.Audio.RegisterInListener`.
+  -->
+  <interface name="org.qemu.Display1.AudioInListener">
+    <!--
+        Init:
+        @id: the stream ID.
+        @bits: PCM bits per sample.
+        @is_signed: whether the PCM data is signed.
+        @is_float: PCM floating point format.
+        @freq: the PCM frequency in Hz.
+        @nchannels: the number of channels.
+        @bytes_per_frame: the bytes per frame.
+        @bytes_per_second: the bytes per second.
+        @be: whether using big-endian format.
+
+        Initializes a PCM record stream.
+    -->
+    <method name="Init">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="bits" type="y" direction="in"/>
+      <arg name="is_signed" type="b" direction="in"/>
+      <arg name="is_float" type="b" direction="in"/>
+      <arg name="freq" type="u" direction="in"/>
+      <arg name="nchannels" type="y" direction="in"/>
+      <arg name="bytes_per_frame" type="u" direction="in"/>
+      <arg name="bytes_per_second" type="u" direction="in"/>
+      <arg name="be" type="b" direction="in"/>
+    </method>
+
+    <!--
+        Fini:
+        @id: the stream ID.
+
+        Finish & close a record stream.
+    -->
+    <method name="Fini">
+      <arg name="id" type="t" direction="in"/>
+    </method>
+
+    <!--
+        SetEnabled:
+        @id: the stream ID.
+
+        Resume or suspend the record stream.
+    -->
+    <method name="SetEnabled">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="enabled" type="b" direction="in"/>
+    </method>
+
+    <!--
+        SetVolume:
+        @id: the stream ID.
+        @mute: whether the stream is muted.
+        @volume: the volume per-channel.
+
+        Set the stream volume and mute state (volume without unit, 0-255).
+    -->
+    <method name="SetVolume">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="mute" type="b" direction="in"/>
+      <arg name="volume" type="ay" direction="in">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+
+    <!--
+        Read:
+        @id: the stream ID.
+        @size: the amount to read, in bytes.
+        @data: the recorded data (which may be less than requested).
+
+        Read "size" bytes from the record stream.
+    -->
+    <method name="Read">
+      <arg name="id" type="t" direction="in"/>
+      <arg name="size" type="t" direction="in"/>
+      <arg type="ay" name="data" direction="out">
+        <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+      </arg>
+    </method>
+  </interface>
 </node>
diff --git a/ui/dbus.c b/ui/dbus.c
index 847a667821..d24f704d46 100644
--- a/ui/dbus.c
+++ b/ui/dbus.c
@@ -30,6 +30,8 @@
 #include "ui/dbus-module.h"
 #include "ui/egl-helpers.h"
 #include "ui/egl-context.h"
+#include "audio/audio.h"
+#include "audio/audio_int.h"
 #include "qapi/error.h"
 #include "trace.h"
 
@@ -84,6 +86,7 @@ dbus_display_finalize(Object *o)
     g_clear_object(&dd->bus);
     g_clear_object(&dd->iface);
     g_free(dd->dbus_addr);
+    g_free(dd->audiodev);
     dbus_display = NULL;
 }
 
@@ -140,6 +143,19 @@ dbus_display_complete(UserCreatable *uc, Error **errp)
         return;
     }
 
+    if (dd->audiodev && *dd->audiodev) {
+        AudioState *audio_state = audio_state_by_name(dd->audiodev);
+        if (!audio_state) {
+            error_setg(errp, "Audiodev '%s' not found", dd->audiodev);
+            return;
+        }
+        if (!g_str_equal(audio_state->drv->name, "dbus")) {
+            error_setg(errp, "Audiodev '%s' is not compatible with DBus",
+                       dd->audiodev);
+            return;
+        }
+        audio_state->drv->set_dbus_server(audio_state, dd->server);
+    }
 
     consoles = g_array_new(FALSE, FALSE, sizeof(guint32));
     for (idx = 0;; idx++) {
@@ -261,6 +277,23 @@ set_dbus_addr(Object *o, const char *str, Error **errp)
     dd->dbus_addr = g_strdup(str);
 }
 
+static char *
+get_audiodev(Object *o, Error **errp)
+{
+    DBusDisplay *dd = DBUS_DISPLAY(o);
+
+    return g_strdup(dd->audiodev);
+}
+
+static void
+set_audiodev(Object *o, const char *str, Error **errp)
+{
+    DBusDisplay *dd = DBUS_DISPLAY(o);
+
+    g_free(dd->audiodev);
+    dd->audiodev = g_strdup(str);
+}
+
 static int
 get_gl_mode(Object *o, Error **errp)
 {
@@ -285,6 +318,7 @@ dbus_display_class_init(ObjectClass *oc, void *data)
     ucc->complete = dbus_display_complete;
     object_class_property_add_bool(oc, "p2p", get_dbus_p2p, set_dbus_p2p);
     object_class_property_add_str(oc, "addr", get_dbus_addr, set_dbus_addr);
+    object_class_property_add_str(oc, "audiodev", get_audiodev, set_audiodev);
     object_class_property_add_enum(oc, "gl-mode",
                                    "DisplayGLMode", &DisplayGLMode_lookup,
                                    get_gl_mode, set_gl_mode);
@@ -321,6 +355,7 @@ dbus_init(DisplayState *ds, DisplayOptions *opts)
                           object_get_objects_root(),
                           "dbus-display", &error_fatal,
                           "addr", opts->u.dbus.addr ?: "",
+                          "audiodev", opts->u.dbus.audiodev ?: "",
                           "gl-mode", DisplayGLMode_str(mode),
                           "p2p", yes_no(opts->u.dbus.p2p),
                           NULL);
diff --git a/ui/dbus.h b/ui/dbus.h
index 4698d32463..ca1f0f4ab9 100644
--- a/ui/dbus.h
+++ b/ui/dbus.h
@@ -36,6 +36,7 @@ struct DBusDisplay {
     DisplayGLMode gl_mode;
     bool p2p;
     char *dbus_addr;
+    char *audiodev;
     DisplayGLCtx glctx;
 
     GDBusConnection *bus;