mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -2000,6 +2000,7 @@ void audio_create_pdos(Audiodev *dev)
|
||||
CASE(NONE, none, );
|
||||
CASE(ALSA, alsa, Alsa);
|
||||
CASE(COREAUDIO, coreaudio, Coreaudio);
|
||||
CASE(DBUS, dbus, );
|
||||
CASE(DSOUND, dsound, );
|
||||
CASE(JACK, jack, Jack);
|
||||
CASE(OSS, oss, Oss);
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
#endif
|
||||
#include "mixeng.h"
|
||||
|
||||
#ifdef CONFIG_GIO
|
||||
#include <gio/gio.h>
|
||||
#endif
|
||||
|
||||
struct audio_pcm_ops;
|
||||
|
||||
struct audio_callback {
|
||||
@@ -140,6 +144,9 @@ struct audio_driver {
|
||||
const char *descr;
|
||||
void *(*init) (Audiodev *);
|
||||
void (*fini) (void *);
|
||||
#ifdef CONFIG_GIO
|
||||
void (*set_dbus_server) (AudioState *s, GDBusObjectManagerServer *manager);
|
||||
#endif
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
int can_be_default;
|
||||
int max_voices_out;
|
||||
|
||||
@@ -327,6 +327,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
case AUDIODEV_DRIVER_COREAUDIO:
|
||||
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
|
||||
dev->u.coreaudio.TYPE);
|
||||
case AUDIODEV_DRIVER_DBUS:
|
||||
return dev->u.dbus.TYPE;
|
||||
case AUDIODEV_DRIVER_DSOUND:
|
||||
return dev->u.dsound.TYPE;
|
||||
case AUDIODEV_DRIVER_JACK:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,4 +26,10 @@ foreach m : [
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if dbus_display
|
||||
module_ss = ss.source_set()
|
||||
module_ss.add(when: gio, if_true: files('dbusaudio.c'))
|
||||
audio_modules += {'dbus': module_ss}
|
||||
endif
|
||||
|
||||
modules += {'audio': audio_modules}
|
||||
|
||||
@@ -13,6 +13,11 @@ alsa_resume_out(void) "Resuming suspended output stream"
|
||||
# ossaudio.c
|
||||
oss_version(int version) "OSS version = 0x%x"
|
||||
|
||||
# dbusaudio.c
|
||||
dbus_audio_register(const char *s, const char *dir) "sender = %s, dir = %s"
|
||||
dbus_audio_put_buffer_out(size_t len) "len = %zu"
|
||||
dbus_audio_read(size_t len) "len = %zu"
|
||||
|
||||
# audio.c
|
||||
audio_timer_start(int interval) "interval %d ms"
|
||||
audio_timer_stop(void) ""
|
||||
|
||||
+2
-1
@@ -386,7 +386,7 @@
|
||||
# Since: 4.0
|
||||
##
|
||||
{ 'enum': 'AudiodevDriver',
|
||||
'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'jack', 'oss', 'pa',
|
||||
'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', 'pa',
|
||||
'sdl', 'spice', 'wav' ] }
|
||||
|
||||
##
|
||||
@@ -412,6 +412,7 @@
|
||||
'none': 'AudiodevGenericOptions',
|
||||
'alsa': 'AudiodevAlsaOptions',
|
||||
'coreaudio': 'AudiodevCoreaudioOptions',
|
||||
'dbus': 'AudiodevGenericOptions',
|
||||
'dsound': 'AudiodevDsoundOptions',
|
||||
'jack': 'AudiodevJackOptions',
|
||||
'oss': 'AudiodevOssOptions',
|
||||
|
||||
+4
-1
@@ -1134,13 +1134,16 @@
|
||||
# @p2p: Whether to use peer-to-peer connections (accepted through
|
||||
# ``add_client``).
|
||||
#
|
||||
# @audiodev: Use the specified DBus audiodev to export audio.
|
||||
#
|
||||
# Since: 7.0
|
||||
#
|
||||
##
|
||||
{ 'struct' : 'DisplayDBus',
|
||||
'data' : { '*rendernode' : 'str',
|
||||
'*addr': 'str',
|
||||
'*p2p': 'bool' } }
|
||||
'*p2p': 'bool',
|
||||
'*audiodev': 'str' } }
|
||||
|
||||
##
|
||||
# @DisplayGLMode:
|
||||
|
||||
@@ -659,6 +659,9 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
|
||||
#endif
|
||||
#ifdef CONFIG_SPICE
|
||||
"-audiodev spice,id=id[,prop[=value][,...]]\n"
|
||||
#endif
|
||||
#ifdef CONFIG_DBUS_DISPLAY
|
||||
"-audiodev dbus,id=id[,prop[=value][,...]]\n"
|
||||
#endif
|
||||
"-audiodev wav,id=id[,prop[=value][,...]]\n"
|
||||
" path= path of wav file to record\n",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user