master
c 736 lines 22 KB
Raw
1 /*
2 * QEMU DBus audio
3 *
4 * Copyright (c) 2021 Red Hat, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 #include "qemu/osdep.h"
26 #include "qemu/error-report.h"
27 #include "qemu/module.h"
28 #include "qemu/dbus.h"
29 #include "qom/object.h"
30
31 #ifdef G_OS_UNIX
32 #include <gio/gunixfdlist.h>
33 #endif
34
35 #include "ui/dbus.h"
36 #include "ui/dbus-display.h"
37 #include "ui/dbus-display1.h"
38
39 #include "qemu/audio.h"
40 #include "audio_int.h"
41 #include "trace.h"
42
43 #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
44
45 #define DBUS_DEFAULT_AUDIO_NSAMPLES 480
46
47 #define TYPE_AUDIO_DBUS "audio-dbus"
48 OBJECT_DECLARE_SIMPLE_TYPE(AudioDbus, AUDIO_DBUS)
49
50 static AudioBackendClass *audio_dbus_parent_class;
51
52 struct AudioDbus {
53 AudioMixengBackend parent_obj;
54
55 GDBusObjectManagerServer *server;
56 bool p2p;
57 GDBusObjectSkeleton *audio;
58 QemuDBusDisplay1Audio *iface;
59 GHashTable *out_listeners;
60 GHashTable *in_listeners;
61 };
62
63
64 typedef struct DBusVoiceOut {
65 HWVoiceOut hw;
66 bool enabled;
67 RateCtl rate;
68
69 void *buf;
70 size_t buf_pos;
71 size_t buf_size;
72
73 bool has_volume;
74 Volume volume;
75 } DBusVoiceOut;
76
77 typedef struct DBusVoiceIn {
78 HWVoiceIn hw;
79 bool enabled;
80 RateCtl rate;
81
82 bool has_volume;
83 Volume volume;
84 } DBusVoiceIn;
85
86 static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
87 {
88 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
89
90 if (!vo->buf) {
91 vo->buf_size = hw->samples * hw->info.bytes_per_frame;
92 vo->buf = g_malloc(vo->buf_size);
93 vo->buf_pos = 0;
94 }
95
96 *size = MIN(vo->buf_size - vo->buf_pos, *size);
97 *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
98
99 return vo->buf + vo->buf_pos;
100
101 }
102
103 static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
104 {
105 AudioDbus *da = AUDIO_DBUS(hw->s);
106 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
107 GHashTableIter iter;
108 QemuDBusDisplay1AudioOutListener *listener = NULL;
109 g_autoptr(GBytes) bytes = NULL;
110 g_autoptr(GVariant) v_data = NULL;
111
112 assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
113 vo->buf_pos += size;
114
115 trace_dbus_audio_put_buffer_out(vo->buf_pos, vo->buf_size);
116
117 if (vo->buf_pos < vo->buf_size) {
118 return size;
119 }
120
121 bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
122 v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
123 g_variant_ref_sink(v_data);
124
125 g_hash_table_iter_init(&iter, da->out_listeners);
126 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
127 qemu_dbus_display1_audio_out_listener_call_write(
128 listener,
129 (uintptr_t)hw,
130 v_data,
131 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
132 }
133
134 return size;
135 }
136
137 #if HOST_BIG_ENDIAN
138 #define AUDIO_HOST_BE TRUE
139 #else
140 #define AUDIO_HOST_BE FALSE
141 #endif
142
143 static void
144 dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,
145 HWVoiceOut *hw)
146 {
147 qemu_dbus_display1_audio_out_listener_call_init(
148 listener,
149 (uintptr_t)hw,
150 audio_format_bits(hw->info.af),
151 audio_format_is_signed(hw->info.af),
152 audio_format_is_float(hw->info.af),
153 hw->info.freq,
154 hw->info.nchannels,
155 hw->info.bytes_per_frame,
156 hw->info.bytes_per_second,
157 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
158 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
159 }
160
161 static guint
162 dbus_audio_get_nsamples(AudioDbus *da)
163 {
164 AudiodevDBusOptions *opts = &AUDIO_MIXENG_BACKEND(da)->dev->u.dbus;
165
166 if (opts->has_nsamples && opts->nsamples) {
167 return opts->nsamples;
168 } else {
169 return DBUS_DEFAULT_AUDIO_NSAMPLES;
170 }
171 }
172
173 static int
174 dbus_init_out(HWVoiceOut *hw, struct audsettings *as)
175 {
176 AudioDbus *da = AUDIO_DBUS(hw->s);
177 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
178 GHashTableIter iter;
179 QemuDBusDisplay1AudioOutListener *listener = NULL;
180
181 audio_pcm_init_info(&hw->info, as);
182 hw->samples = dbus_audio_get_nsamples(da);
183 audio_rate_start(&vo->rate);
184
185 g_hash_table_iter_init(&iter, da->out_listeners);
186 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
187 dbus_init_out_listener(listener, hw);
188 }
189 return 0;
190 }
191
192 static void
193 dbus_fini_out(HWVoiceOut *hw)
194 {
195 AudioDbus *da = AUDIO_DBUS(hw->s);
196 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
197 GHashTableIter iter;
198 QemuDBusDisplay1AudioOutListener *listener = NULL;
199
200 g_hash_table_iter_init(&iter, da->out_listeners);
201 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
202 qemu_dbus_display1_audio_out_listener_call_fini(
203 listener,
204 (uintptr_t)hw,
205 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
206 }
207
208 g_clear_pointer(&vo->buf, g_free);
209 }
210
211 static void
212 dbus_enable_out(HWVoiceOut *hw, bool enable)
213 {
214 AudioDbus *da = AUDIO_DBUS(hw->s);
215 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
216 GHashTableIter iter;
217 QemuDBusDisplay1AudioOutListener *listener = NULL;
218
219 vo->enabled = enable;
220 if (enable) {
221 audio_rate_start(&vo->rate);
222 }
223
224 g_hash_table_iter_init(&iter, da->out_listeners);
225 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
226 qemu_dbus_display1_audio_out_listener_call_set_enabled(
227 listener, (uintptr_t)hw, enable,
228 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
229 }
230 }
231
232 static void
233 dbus_volume_out_listener(HWVoiceOut *hw,
234 QemuDBusDisplay1AudioOutListener *listener)
235 {
236 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
237 Volume *vol = &vo->volume;
238 g_autoptr(GBytes) bytes = NULL;
239 GVariant *v_vol = NULL;
240
241 if (!vo->has_volume) {
242 return;
243 }
244
245 assert(vol->channels < sizeof(vol->vol));
246 bytes = g_bytes_new(vol->vol, vol->channels);
247 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
248 qemu_dbus_display1_audio_out_listener_call_set_volume(
249 listener, (uintptr_t)hw, vol->mute, v_vol,
250 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
251 }
252
253 static void
254 dbus_volume_out(HWVoiceOut *hw, Volume *vol)
255 {
256 AudioDbus *da = AUDIO_DBUS(hw->s);
257 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
258 GHashTableIter iter;
259 QemuDBusDisplay1AudioOutListener *listener = NULL;
260
261 vo->has_volume = true;
262 vo->volume = *vol;
263
264 g_hash_table_iter_init(&iter, da->out_listeners);
265 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
266 dbus_volume_out_listener(hw, listener);
267 }
268 }
269
270 static void
271 dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)
272 {
273 qemu_dbus_display1_audio_in_listener_call_init(
274 listener,
275 (uintptr_t)hw,
276 audio_format_bits(hw->info.af),
277 audio_format_is_signed(hw->info.af),
278 audio_format_is_float(hw->info.af),
279 hw->info.freq,
280 hw->info.nchannels,
281 hw->info.bytes_per_frame,
282 hw->info.bytes_per_second,
283 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
284 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
285 }
286
287 static int
288 dbus_init_in(HWVoiceIn *hw, struct audsettings *as)
289 {
290 AudioDbus *da = AUDIO_DBUS(hw->s);
291 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
292 GHashTableIter iter;
293 QemuDBusDisplay1AudioInListener *listener = NULL;
294
295 audio_pcm_init_info(&hw->info, as);
296 hw->samples = dbus_audio_get_nsamples(da);
297 audio_rate_start(&vo->rate);
298
299 g_hash_table_iter_init(&iter, da->in_listeners);
300 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
301 dbus_init_in_listener(listener, hw);
302 }
303 return 0;
304 }
305
306 static void
307 dbus_fini_in(HWVoiceIn *hw)
308 {
309 AudioDbus *da = AUDIO_DBUS(hw->s);
310 GHashTableIter iter;
311 QemuDBusDisplay1AudioInListener *listener = NULL;
312
313 g_hash_table_iter_init(&iter, da->in_listeners);
314 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
315 qemu_dbus_display1_audio_in_listener_call_fini(
316 listener,
317 (uintptr_t)hw,
318 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
319 }
320 }
321
322 static void
323 dbus_volume_in_listener(HWVoiceIn *hw,
324 QemuDBusDisplay1AudioInListener *listener)
325 {
326 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
327 Volume *vol = &vo->volume;
328 g_autoptr(GBytes) bytes = NULL;
329 GVariant *v_vol = NULL;
330
331 if (!vo->has_volume) {
332 return;
333 }
334
335 assert(vol->channels < sizeof(vol->vol));
336 bytes = g_bytes_new(vol->vol, vol->channels);
337 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
338 qemu_dbus_display1_audio_in_listener_call_set_volume(
339 listener, (uintptr_t)hw, vol->mute, v_vol,
340 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
341 }
342
343 static void
344 dbus_volume_in(HWVoiceIn *hw, Volume *vol)
345 {
346 AudioDbus *da = AUDIO_DBUS(hw->s);
347 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
348 GHashTableIter iter;
349 QemuDBusDisplay1AudioInListener *listener = NULL;
350
351 vo->has_volume = true;
352 vo->volume = *vol;
353
354 g_hash_table_iter_init(&iter, da->in_listeners);
355 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
356 dbus_volume_in_listener(hw, listener);
357 }
358 }
359
360 static size_t
361 dbus_read(HWVoiceIn *hw, void *buf, size_t size)
362 {
363 AudioDbus *da = AUDIO_DBUS(hw->s);
364 /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
365 GHashTableIter iter;
366 QemuDBusDisplay1AudioInListener *listener = NULL;
367
368 trace_dbus_audio_read(size);
369
370 /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
371
372 g_hash_table_iter_init(&iter, da->in_listeners);
373 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
374 g_autoptr(GVariant) v_data = NULL;
375 const char *data;
376 gsize n = 0;
377
378 if (qemu_dbus_display1_audio_in_listener_call_read_sync(
379 listener,
380 (uintptr_t)hw,
381 size,
382 G_DBUS_CALL_FLAGS_NONE, -1,
383 &v_data, NULL, NULL)) {
384 data = g_variant_get_fixed_array(v_data, &n, 1);
385 g_warn_if_fail(n <= size);
386 size = MIN(n, size);
387 memcpy(buf, data, size);
388 break;
389 }
390 }
391
392 return size;
393 }
394
395 static void
396 dbus_enable_in(HWVoiceIn *hw, bool enable)
397 {
398 AudioDbus *da = AUDIO_DBUS(hw->s);
399 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
400 GHashTableIter iter;
401 QemuDBusDisplay1AudioInListener *listener = NULL;
402
403 vo->enabled = enable;
404 if (enable) {
405 audio_rate_start(&vo->rate);
406 }
407
408 g_hash_table_iter_init(&iter, da->in_listeners);
409 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
410 qemu_dbus_display1_audio_in_listener_call_set_enabled(
411 listener, (uintptr_t)hw, enable,
412 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
413 }
414 }
415
416 static bool
417 audio_dbus_realize(AudioBackend *abe, Audiodev *dev, Error **errp)
418 {
419 AudioDbus *da = AUDIO_DBUS(abe);
420
421 if (!qemu_using_dbus_display(errp)) {
422 qapi_free_Audiodev(dev);
423 return false;
424 }
425
426 if (!audio_dbus_parent_class->realize(abe, dev, errp)) {
427 return false;
428 }
429
430 da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
431 g_free, g_object_unref);
432 da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
433 g_free, g_object_unref);
434 return true;
435 }
436
437 static void
438 audio_dbus_finalize(Object *obj)
439 {
440 AudioDbus *da = AUDIO_DBUS(obj);
441
442 if (da->server) {
443 g_dbus_object_manager_server_unexport(da->server,
444 DBUS_DISPLAY1_AUDIO_PATH);
445 }
446 g_clear_object(&da->audio);
447 g_clear_object(&da->iface);
448 g_clear_pointer(&da->in_listeners, g_hash_table_unref);
449 g_clear_pointer(&da->out_listeners, g_hash_table_unref);
450 g_clear_object(&da->server);
451 }
452
453 static void
454 listener_out_vanished_cb(GDBusConnection *connection,
455 gboolean remote_peer_vanished,
456 GError *error,
457 AudioDbus *da)
458 {
459 char *name = g_object_get_data(G_OBJECT(connection), "name");
460
461 g_hash_table_remove(da->out_listeners, name);
462 }
463
464 static void
465 listener_in_vanished_cb(GDBusConnection *connection,
466 gboolean remote_peer_vanished,
467 GError *error,
468 AudioDbus *da)
469 {
470 char *name = g_object_get_data(G_OBJECT(connection), "name");
471
472 g_hash_table_remove(da->in_listeners, name);
473 }
474
475 static gboolean
476 dbus_audio_register_listener(AudioMixengBackend *s,
477 GDBusMethodInvocation *invocation,
478 #ifdef G_OS_UNIX
479 GUnixFDList *fd_list,
480 #endif
481 GVariant *arg_listener,
482 bool out)
483 {
484 AudioDbus *da = AUDIO_DBUS(s);
485 const char *sender =
486 da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation);
487 g_autoptr(GDBusConnection) listener_conn = NULL;
488 g_autoptr(GError) err = NULL;
489 g_autoptr(GSocket) socket = NULL;
490 g_autoptr(GSocketConnection) socket_conn = NULL;
491 g_autofree char *guid = g_dbus_generate_guid();
492 GHashTable *listeners = out ? da->out_listeners : da->in_listeners;
493 GObject *listener;
494 int fd;
495
496 trace_dbus_audio_register(sender, out ? "out" : "in");
497
498 if (g_hash_table_contains(listeners, sender)) {
499 g_dbus_method_invocation_return_error(invocation,
500 DBUS_DISPLAY_ERROR,
501 DBUS_DISPLAY_ERROR_INVALID,
502 "`%s` is already registered!",
503 sender);
504 return DBUS_METHOD_INVOCATION_HANDLED;
505 }
506
507 #ifdef G_OS_WIN32
508 if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
509 return DBUS_METHOD_INVOCATION_HANDLED;
510 }
511 #else
512 fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
513 if (err) {
514 g_dbus_method_invocation_return_error(invocation,
515 DBUS_DISPLAY_ERROR,
516 DBUS_DISPLAY_ERROR_FAILED,
517 "Couldn't get peer fd: %s",
518 err->message);
519 return DBUS_METHOD_INVOCATION_HANDLED;
520 }
521 #endif
522
523 socket = g_socket_new_from_fd(fd, &err);
524 if (err) {
525 g_dbus_method_invocation_return_error(invocation,
526 DBUS_DISPLAY_ERROR,
527 DBUS_DISPLAY_ERROR_FAILED,
528 "Couldn't make a socket: %s",
529 err->message);
530 #ifdef G_OS_WIN32
531 closesocket(fd);
532 #else
533 close(fd);
534 #endif
535 return DBUS_METHOD_INVOCATION_HANDLED;
536 }
537 socket_conn = g_socket_connection_factory_create_connection(socket);
538 if (out) {
539 qemu_dbus_display1_audio_complete_register_out_listener(
540 da->iface, invocation
541 #ifdef G_OS_UNIX
542 , NULL
543 #endif
544 );
545 } else {
546 qemu_dbus_display1_audio_complete_register_in_listener(
547 da->iface, invocation
548 #ifdef G_OS_UNIX
549 , NULL
550 #endif
551 );
552 }
553
554 GDBusConnectionFlags flags =
555 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER;
556 #ifdef WIN32
557 flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
558 #endif
559
560 listener_conn =
561 g_dbus_connection_new_sync(
562 G_IO_STREAM(socket_conn),
563 guid,
564 flags,
565 NULL, NULL, &err);
566 if (err) {
567 error_report("Failed to setup peer connection: %s", err->message);
568 return DBUS_METHOD_INVOCATION_HANDLED;
569 }
570
571 listener = out ?
572 G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(
573 listener_conn,
574 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
575 NULL,
576 "/org/qemu/Display1/AudioOutListener",
577 NULL,
578 &err)) :
579 G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(
580 listener_conn,
581 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
582 NULL,
583 "/org/qemu/Display1/AudioInListener",
584 NULL,
585 &err));
586 if (!listener) {
587 error_report("Failed to setup proxy: %s", err->message);
588 return DBUS_METHOD_INVOCATION_HANDLED;
589 }
590
591 if (out) {
592 HWVoiceOut *hw;
593
594 QLIST_FOREACH(hw, &s->hw_head_out, entries) {
595 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
596 QemuDBusDisplay1AudioOutListener *l =
597 QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);
598
599 dbus_init_out_listener(l, hw);
600 qemu_dbus_display1_audio_out_listener_call_set_enabled(
601 l, (uintptr_t)hw, vo->enabled,
602 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
603 }
604 } else {
605 HWVoiceIn *hw;
606
607 QLIST_FOREACH(hw, &s->hw_head_in, entries) {
608 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
609 QemuDBusDisplay1AudioInListener *l =
610 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);
611
612 dbus_init_in_listener(
613 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
614 qemu_dbus_display1_audio_in_listener_call_set_enabled(
615 l, (uintptr_t)hw, vo->enabled,
616 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
617 }
618 }
619
620 g_object_set_data_full(G_OBJECT(listener_conn), "name",
621 g_strdup(sender), g_free);
622 g_hash_table_insert(listeners, g_strdup(sender), listener);
623 g_object_connect(listener_conn,
624 "signal::closed",
625 out ? listener_out_vanished_cb : listener_in_vanished_cb,
626 da,
627 NULL);
628
629 return DBUS_METHOD_INVOCATION_HANDLED;
630 }
631
632 static gboolean
633 dbus_audio_register_out_listener(AudioMixengBackend *s,
634 GDBusMethodInvocation *invocation,
635 #ifdef G_OS_UNIX
636 GUnixFDList *fd_list,
637 #endif
638 GVariant *arg_listener)
639 {
640 return dbus_audio_register_listener(s, invocation,
641 #ifdef G_OS_UNIX
642 fd_list,
643 #endif
644 arg_listener, true);
645
646 }
647
648 static gboolean
649 dbus_audio_register_in_listener(AudioMixengBackend *s,
650 GDBusMethodInvocation *invocation,
651 #ifdef G_OS_UNIX
652 GUnixFDList *fd_list,
653 #endif
654 GVariant *arg_listener)
655 {
656 return dbus_audio_register_listener(s, invocation,
657 #ifdef G_OS_UNIX
658 fd_list,
659 #endif
660 arg_listener, false);
661 }
662
663 static bool
664 dbus_audio_set_server(AudioBackend *s,
665 GDBusObjectManagerServer *server,
666 bool p2p,
667 Error **errp)
668 {
669 AudioDbus *da = AUDIO_DBUS(s);
670
671 g_assert(!da->server);
672
673 da->server = g_object_ref(server);
674 da->p2p = p2p;
675
676 da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
677 da->iface = qemu_dbus_display1_audio_skeleton_new();
678 g_object_connect(da->iface,
679 "swapped-signal::handle-register-in-listener",
680 dbus_audio_register_in_listener, s,
681 "swapped-signal::handle-register-out-listener",
682 dbus_audio_register_out_listener, s,
683 NULL);
684 qemu_dbus_display1_audio_set_nsamples(da->iface, dbus_audio_get_nsamples(da));
685
686 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
687 G_DBUS_INTERFACE_SKELETON(da->iface));
688 g_dbus_object_manager_server_export(da->server, da->audio);
689
690 return true;
691 }
692
693 static void audio_dbus_class_init(ObjectClass *klass, const void *data)
694 {
695 AudioBackendClass *b = AUDIO_BACKEND_CLASS(klass);
696 AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
697
698 audio_dbus_parent_class = AUDIO_BACKEND_CLASS(object_class_get_parent(klass));
699
700 b->realize = audio_dbus_realize;
701 b->set_dbus_server = dbus_audio_set_server;
702 k->max_voices_out = INT_MAX;
703 k->max_voices_in = INT_MAX;
704 k->voice_size_out = sizeof(DBusVoiceOut);
705 k->voice_size_in = sizeof(DBusVoiceIn);
706
707 k->init_out = dbus_init_out;
708 k->fini_out = dbus_fini_out;
709 k->write = audio_generic_write;
710 k->get_buffer_out = dbus_get_buffer_out;
711 k->put_buffer_out = dbus_put_buffer_out;
712 k->enable_out = dbus_enable_out;
713 k->volume_out = dbus_volume_out;
714
715 k->init_in = dbus_init_in;
716 k->fini_in = dbus_fini_in;
717 k->read = dbus_read;
718 k->run_buffer_in = audio_generic_run_buffer_in;
719 k->enable_in = dbus_enable_in;
720 k->volume_in = dbus_volume_in;
721 }
722
723 static const TypeInfo audio_types[] = {
724 {
725 .name = TYPE_AUDIO_DBUS,
726 .parent = TYPE_AUDIO_MIXENG_BACKEND,
727 .instance_size = sizeof(AudioDbus),
728 .instance_finalize = audio_dbus_finalize,
729 .class_init = audio_dbus_class_init,
730 },
731 };
732
733 DEFINE_TYPES(audio_types)
734
735 module_dep("ui-dbus")
736 module_obj(TYPE_AUDIO_DBUS)