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>
This commit is contained in:
Marc-André Lureau 2021-03-09 17:15:28 +04:00
parent b4dd5b6a60
commit 739362d420
12 changed files with 931 additions and 2 deletions

View file

@ -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>

View file

@ -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);

View file

@ -36,6 +36,7 @@ struct DBusDisplay {
DisplayGLMode gl_mode;
bool p2p;
char *dbus_addr;
char *audiodev;
DisplayGLCtx glctx;
GDBusConnection *bus;