dbusaudio.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  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. #include "qemu/osdep.h"
  25. #include "qemu/error-report.h"
  26. #include "qemu/host-utils.h"
  27. #include "qemu/module.h"
  28. #include "qemu/timer.h"
  29. #include "qemu/dbus.h"
  30. #ifdef G_OS_UNIX
  31. #include <gio/gunixfdlist.h>
  32. #endif
  33. #include "ui/dbus.h"
  34. #include "ui/dbus-display1.h"
  35. #define AUDIO_CAP "dbus"
  36. #include "audio.h"
  37. #include "audio_int.h"
  38. #include "trace.h"
  39. #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
  40. #define DBUS_DEFAULT_AUDIO_NSAMPLES 480
  41. typedef struct DBusAudio {
  42. Audiodev *dev;
  43. GDBusObjectManagerServer *server;
  44. bool p2p;
  45. GDBusObjectSkeleton *audio;
  46. QemuDBusDisplay1Audio *iface;
  47. GHashTable *out_listeners;
  48. GHashTable *in_listeners;
  49. } DBusAudio;
  50. typedef struct DBusVoiceOut {
  51. HWVoiceOut hw;
  52. bool enabled;
  53. RateCtl rate;
  54. void *buf;
  55. size_t buf_pos;
  56. size_t buf_size;
  57. bool has_volume;
  58. Volume volume;
  59. } DBusVoiceOut;
  60. typedef struct DBusVoiceIn {
  61. HWVoiceIn hw;
  62. bool enabled;
  63. RateCtl rate;
  64. bool has_volume;
  65. Volume volume;
  66. } DBusVoiceIn;
  67. static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
  68. {
  69. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  70. if (!vo->buf) {
  71. vo->buf_size = hw->samples * hw->info.bytes_per_frame;
  72. vo->buf = g_malloc(vo->buf_size);
  73. vo->buf_pos = 0;
  74. }
  75. *size = MIN(vo->buf_size - vo->buf_pos, *size);
  76. *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
  77. return vo->buf + vo->buf_pos;
  78. }
  79. static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
  80. {
  81. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  82. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  83. GHashTableIter iter;
  84. QemuDBusDisplay1AudioOutListener *listener = NULL;
  85. g_autoptr(GBytes) bytes = NULL;
  86. g_autoptr(GVariant) v_data = NULL;
  87. assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
  88. vo->buf_pos += size;
  89. trace_dbus_audio_put_buffer_out(vo->buf_pos, vo->buf_size);
  90. if (vo->buf_pos < vo->buf_size) {
  91. return size;
  92. }
  93. bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
  94. v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
  95. g_variant_ref_sink(v_data);
  96. g_hash_table_iter_init(&iter, da->out_listeners);
  97. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  98. qemu_dbus_display1_audio_out_listener_call_write(
  99. listener,
  100. (uintptr_t)hw,
  101. v_data,
  102. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  103. }
  104. return size;
  105. }
  106. #if HOST_BIG_ENDIAN
  107. #define AUDIO_HOST_BE TRUE
  108. #else
  109. #define AUDIO_HOST_BE FALSE
  110. #endif
  111. static void
  112. dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,
  113. HWVoiceOut *hw)
  114. {
  115. qemu_dbus_display1_audio_out_listener_call_init(
  116. listener,
  117. (uintptr_t)hw,
  118. hw->info.bits,
  119. hw->info.is_signed,
  120. hw->info.is_float,
  121. hw->info.freq,
  122. hw->info.nchannels,
  123. hw->info.bytes_per_frame,
  124. hw->info.bytes_per_second,
  125. hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
  126. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  127. }
  128. static guint
  129. dbus_audio_get_nsamples(DBusAudio *da)
  130. {
  131. AudiodevDBusOptions *opts = &da->dev->u.dbus;
  132. if (opts->has_nsamples && opts->nsamples) {
  133. return opts->nsamples;
  134. } else {
  135. return DBUS_DEFAULT_AUDIO_NSAMPLES;
  136. }
  137. }
  138. static int
  139. dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
  140. {
  141. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  142. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  143. GHashTableIter iter;
  144. QemuDBusDisplay1AudioOutListener *listener = NULL;
  145. audio_pcm_init_info(&hw->info, as);
  146. hw->samples = dbus_audio_get_nsamples(da);
  147. audio_rate_start(&vo->rate);
  148. g_hash_table_iter_init(&iter, da->out_listeners);
  149. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  150. dbus_init_out_listener(listener, hw);
  151. }
  152. return 0;
  153. }
  154. static void
  155. dbus_fini_out(HWVoiceOut *hw)
  156. {
  157. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  158. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  159. GHashTableIter iter;
  160. QemuDBusDisplay1AudioOutListener *listener = NULL;
  161. g_hash_table_iter_init(&iter, da->out_listeners);
  162. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  163. qemu_dbus_display1_audio_out_listener_call_fini(
  164. listener,
  165. (uintptr_t)hw,
  166. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  167. }
  168. g_clear_pointer(&vo->buf, g_free);
  169. }
  170. static void
  171. dbus_enable_out(HWVoiceOut *hw, bool enable)
  172. {
  173. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  174. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  175. GHashTableIter iter;
  176. QemuDBusDisplay1AudioOutListener *listener = NULL;
  177. vo->enabled = enable;
  178. if (enable) {
  179. audio_rate_start(&vo->rate);
  180. }
  181. g_hash_table_iter_init(&iter, da->out_listeners);
  182. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  183. qemu_dbus_display1_audio_out_listener_call_set_enabled(
  184. listener, (uintptr_t)hw, enable,
  185. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  186. }
  187. }
  188. static void
  189. dbus_volume_out_listener(HWVoiceOut *hw,
  190. QemuDBusDisplay1AudioOutListener *listener)
  191. {
  192. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  193. Volume *vol = &vo->volume;
  194. g_autoptr(GBytes) bytes = NULL;
  195. GVariant *v_vol = NULL;
  196. if (!vo->has_volume) {
  197. return;
  198. }
  199. assert(vol->channels < sizeof(vol->vol));
  200. bytes = g_bytes_new(vol->vol, vol->channels);
  201. v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
  202. qemu_dbus_display1_audio_out_listener_call_set_volume(
  203. listener, (uintptr_t)hw, vol->mute, v_vol,
  204. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  205. }
  206. static void
  207. dbus_volume_out(HWVoiceOut *hw, Volume *vol)
  208. {
  209. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  210. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  211. GHashTableIter iter;
  212. QemuDBusDisplay1AudioOutListener *listener = NULL;
  213. vo->has_volume = true;
  214. vo->volume = *vol;
  215. g_hash_table_iter_init(&iter, da->out_listeners);
  216. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  217. dbus_volume_out_listener(hw, listener);
  218. }
  219. }
  220. static void
  221. dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)
  222. {
  223. qemu_dbus_display1_audio_in_listener_call_init(
  224. listener,
  225. (uintptr_t)hw,
  226. hw->info.bits,
  227. hw->info.is_signed,
  228. hw->info.is_float,
  229. hw->info.freq,
  230. hw->info.nchannels,
  231. hw->info.bytes_per_frame,
  232. hw->info.bytes_per_second,
  233. hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
  234. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  235. }
  236. static int
  237. dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
  238. {
  239. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  240. DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
  241. GHashTableIter iter;
  242. QemuDBusDisplay1AudioInListener *listener = NULL;
  243. audio_pcm_init_info(&hw->info, as);
  244. hw->samples = dbus_audio_get_nsamples(da);
  245. audio_rate_start(&vo->rate);
  246. g_hash_table_iter_init(&iter, da->in_listeners);
  247. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  248. dbus_init_in_listener(listener, hw);
  249. }
  250. return 0;
  251. }
  252. static void
  253. dbus_fini_in(HWVoiceIn *hw)
  254. {
  255. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  256. GHashTableIter iter;
  257. QemuDBusDisplay1AudioInListener *listener = NULL;
  258. g_hash_table_iter_init(&iter, da->in_listeners);
  259. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  260. qemu_dbus_display1_audio_in_listener_call_fini(
  261. listener,
  262. (uintptr_t)hw,
  263. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  264. }
  265. }
  266. static void
  267. dbus_volume_in_listener(HWVoiceIn *hw,
  268. QemuDBusDisplay1AudioInListener *listener)
  269. {
  270. DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
  271. Volume *vol = &vo->volume;
  272. g_autoptr(GBytes) bytes = NULL;
  273. GVariant *v_vol = NULL;
  274. if (!vo->has_volume) {
  275. return;
  276. }
  277. assert(vol->channels < sizeof(vol->vol));
  278. bytes = g_bytes_new(vol->vol, vol->channels);
  279. v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
  280. qemu_dbus_display1_audio_in_listener_call_set_volume(
  281. listener, (uintptr_t)hw, vol->mute, v_vol,
  282. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  283. }
  284. static void
  285. dbus_volume_in(HWVoiceIn *hw, Volume *vol)
  286. {
  287. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  288. DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
  289. GHashTableIter iter;
  290. QemuDBusDisplay1AudioInListener *listener = NULL;
  291. vo->has_volume = true;
  292. vo->volume = *vol;
  293. g_hash_table_iter_init(&iter, da->in_listeners);
  294. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  295. dbus_volume_in_listener(hw, listener);
  296. }
  297. }
  298. static size_t
  299. dbus_read(HWVoiceIn *hw, void *buf, size_t size)
  300. {
  301. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  302. /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
  303. GHashTableIter iter;
  304. QemuDBusDisplay1AudioInListener *listener = NULL;
  305. trace_dbus_audio_read(size);
  306. /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
  307. g_hash_table_iter_init(&iter, da->in_listeners);
  308. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  309. g_autoptr(GVariant) v_data = NULL;
  310. const char *data;
  311. gsize n = 0;
  312. if (qemu_dbus_display1_audio_in_listener_call_read_sync(
  313. listener,
  314. (uintptr_t)hw,
  315. size,
  316. G_DBUS_CALL_FLAGS_NONE, -1,
  317. &v_data, NULL, NULL)) {
  318. data = g_variant_get_fixed_array(v_data, &n, 1);
  319. g_warn_if_fail(n <= size);
  320. size = MIN(n, size);
  321. memcpy(buf, data, size);
  322. break;
  323. }
  324. }
  325. return size;
  326. }
  327. static void
  328. dbus_enable_in(HWVoiceIn *hw, bool enable)
  329. {
  330. DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
  331. DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
  332. GHashTableIter iter;
  333. QemuDBusDisplay1AudioInListener *listener = NULL;
  334. vo->enabled = enable;
  335. if (enable) {
  336. audio_rate_start(&vo->rate);
  337. }
  338. g_hash_table_iter_init(&iter, da->in_listeners);
  339. while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
  340. qemu_dbus_display1_audio_in_listener_call_set_enabled(
  341. listener, (uintptr_t)hw, enable,
  342. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  343. }
  344. }
  345. static void *
  346. dbus_audio_init(Audiodev *dev, Error **errp)
  347. {
  348. DBusAudio *da = g_new0(DBusAudio, 1);
  349. da->dev = dev;
  350. da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
  351. g_free, g_object_unref);
  352. da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
  353. g_free, g_object_unref);
  354. return da;
  355. }
  356. static void
  357. dbus_audio_fini(void *opaque)
  358. {
  359. DBusAudio *da = opaque;
  360. if (da->server) {
  361. g_dbus_object_manager_server_unexport(da->server,
  362. DBUS_DISPLAY1_AUDIO_PATH);
  363. }
  364. g_clear_object(&da->audio);
  365. g_clear_object(&da->iface);
  366. g_clear_pointer(&da->in_listeners, g_hash_table_unref);
  367. g_clear_pointer(&da->out_listeners, g_hash_table_unref);
  368. g_clear_object(&da->server);
  369. g_free(da);
  370. }
  371. static void
  372. listener_out_vanished_cb(GDBusConnection *connection,
  373. gboolean remote_peer_vanished,
  374. GError *error,
  375. DBusAudio *da)
  376. {
  377. char *name = g_object_get_data(G_OBJECT(connection), "name");
  378. g_hash_table_remove(da->out_listeners, name);
  379. }
  380. static void
  381. listener_in_vanished_cb(GDBusConnection *connection,
  382. gboolean remote_peer_vanished,
  383. GError *error,
  384. DBusAudio *da)
  385. {
  386. char *name = g_object_get_data(G_OBJECT(connection), "name");
  387. g_hash_table_remove(da->in_listeners, name);
  388. }
  389. static gboolean
  390. dbus_audio_register_listener(AudioState *s,
  391. GDBusMethodInvocation *invocation,
  392. #ifdef G_OS_UNIX
  393. GUnixFDList *fd_list,
  394. #endif
  395. GVariant *arg_listener,
  396. bool out)
  397. {
  398. DBusAudio *da = s->drv_opaque;
  399. const char *sender =
  400. da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation);
  401. g_autoptr(GDBusConnection) listener_conn = NULL;
  402. g_autoptr(GError) err = NULL;
  403. g_autoptr(GSocket) socket = NULL;
  404. g_autoptr(GSocketConnection) socket_conn = NULL;
  405. g_autofree char *guid = g_dbus_generate_guid();
  406. GHashTable *listeners = out ? da->out_listeners : da->in_listeners;
  407. GObject *listener;
  408. int fd;
  409. trace_dbus_audio_register(sender, out ? "out" : "in");
  410. if (g_hash_table_contains(listeners, sender)) {
  411. g_dbus_method_invocation_return_error(invocation,
  412. DBUS_DISPLAY_ERROR,
  413. DBUS_DISPLAY_ERROR_INVALID,
  414. "`%s` is already registered!",
  415. sender);
  416. return DBUS_METHOD_INVOCATION_HANDLED;
  417. }
  418. #ifdef G_OS_WIN32
  419. if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
  420. return DBUS_METHOD_INVOCATION_HANDLED;
  421. }
  422. #else
  423. fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
  424. if (err) {
  425. g_dbus_method_invocation_return_error(invocation,
  426. DBUS_DISPLAY_ERROR,
  427. DBUS_DISPLAY_ERROR_FAILED,
  428. "Couldn't get peer fd: %s",
  429. err->message);
  430. return DBUS_METHOD_INVOCATION_HANDLED;
  431. }
  432. #endif
  433. socket = g_socket_new_from_fd(fd, &err);
  434. if (err) {
  435. g_dbus_method_invocation_return_error(invocation,
  436. DBUS_DISPLAY_ERROR,
  437. DBUS_DISPLAY_ERROR_FAILED,
  438. "Couldn't make a socket: %s",
  439. err->message);
  440. #ifdef G_OS_WIN32
  441. closesocket(fd);
  442. #else
  443. close(fd);
  444. #endif
  445. return DBUS_METHOD_INVOCATION_HANDLED;
  446. }
  447. socket_conn = g_socket_connection_factory_create_connection(socket);
  448. if (out) {
  449. qemu_dbus_display1_audio_complete_register_out_listener(
  450. da->iface, invocation
  451. #ifdef G_OS_UNIX
  452. , NULL
  453. #endif
  454. );
  455. } else {
  456. qemu_dbus_display1_audio_complete_register_in_listener(
  457. da->iface, invocation
  458. #ifdef G_OS_UNIX
  459. , NULL
  460. #endif
  461. );
  462. }
  463. GDBusConnectionFlags flags =
  464. G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER;
  465. #ifdef WIN32
  466. flags |= G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
  467. #endif
  468. listener_conn =
  469. g_dbus_connection_new_sync(
  470. G_IO_STREAM(socket_conn),
  471. guid,
  472. flags,
  473. NULL, NULL, &err);
  474. if (err) {
  475. error_report("Failed to setup peer connection: %s", err->message);
  476. return DBUS_METHOD_INVOCATION_HANDLED;
  477. }
  478. listener = out ?
  479. G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(
  480. listener_conn,
  481. G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
  482. NULL,
  483. "/org/qemu/Display1/AudioOutListener",
  484. NULL,
  485. &err)) :
  486. G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(
  487. listener_conn,
  488. G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
  489. NULL,
  490. "/org/qemu/Display1/AudioInListener",
  491. NULL,
  492. &err));
  493. if (!listener) {
  494. error_report("Failed to setup proxy: %s", err->message);
  495. return DBUS_METHOD_INVOCATION_HANDLED;
  496. }
  497. if (out) {
  498. HWVoiceOut *hw;
  499. QLIST_FOREACH(hw, &s->hw_head_out, entries) {
  500. DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
  501. QemuDBusDisplay1AudioOutListener *l =
  502. QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);
  503. dbus_init_out_listener(l, hw);
  504. qemu_dbus_display1_audio_out_listener_call_set_enabled(
  505. l, (uintptr_t)hw, vo->enabled,
  506. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  507. }
  508. } else {
  509. HWVoiceIn *hw;
  510. QLIST_FOREACH(hw, &s->hw_head_in, entries) {
  511. DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
  512. QemuDBusDisplay1AudioInListener *l =
  513. QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);
  514. dbus_init_in_listener(
  515. QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
  516. qemu_dbus_display1_audio_in_listener_call_set_enabled(
  517. l, (uintptr_t)hw, vo->enabled,
  518. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  519. }
  520. }
  521. g_object_set_data_full(G_OBJECT(listener_conn), "name",
  522. g_strdup(sender), g_free);
  523. g_hash_table_insert(listeners, g_strdup(sender), listener);
  524. g_object_connect(listener_conn,
  525. "signal::closed",
  526. out ? listener_out_vanished_cb : listener_in_vanished_cb,
  527. da,
  528. NULL);
  529. return DBUS_METHOD_INVOCATION_HANDLED;
  530. }
  531. static gboolean
  532. dbus_audio_register_out_listener(AudioState *s,
  533. GDBusMethodInvocation *invocation,
  534. #ifdef G_OS_UNIX
  535. GUnixFDList *fd_list,
  536. #endif
  537. GVariant *arg_listener)
  538. {
  539. return dbus_audio_register_listener(s, invocation,
  540. #ifdef G_OS_UNIX
  541. fd_list,
  542. #endif
  543. arg_listener, true);
  544. }
  545. static gboolean
  546. dbus_audio_register_in_listener(AudioState *s,
  547. GDBusMethodInvocation *invocation,
  548. #ifdef G_OS_UNIX
  549. GUnixFDList *fd_list,
  550. #endif
  551. GVariant *arg_listener)
  552. {
  553. return dbus_audio_register_listener(s, invocation,
  554. #ifdef G_OS_UNIX
  555. fd_list,
  556. #endif
  557. arg_listener, false);
  558. }
  559. static void
  560. dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
  561. {
  562. DBusAudio *da = s->drv_opaque;
  563. g_assert(da);
  564. g_assert(!da->server);
  565. da->server = g_object_ref(server);
  566. da->p2p = p2p;
  567. da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
  568. da->iface = qemu_dbus_display1_audio_skeleton_new();
  569. g_object_connect(da->iface,
  570. "swapped-signal::handle-register-in-listener",
  571. dbus_audio_register_in_listener, s,
  572. "swapped-signal::handle-register-out-listener",
  573. dbus_audio_register_out_listener, s,
  574. NULL);
  575. qemu_dbus_display1_audio_set_nsamples(da->iface, dbus_audio_get_nsamples(da));
  576. g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
  577. G_DBUS_INTERFACE_SKELETON(da->iface));
  578. g_dbus_object_manager_server_export(da->server, da->audio);
  579. }
  580. static struct audio_pcm_ops dbus_pcm_ops = {
  581. .init_out = dbus_init_out,
  582. .fini_out = dbus_fini_out,
  583. .write = audio_generic_write,
  584. .get_buffer_out = dbus_get_buffer_out,
  585. .put_buffer_out = dbus_put_buffer_out,
  586. .enable_out = dbus_enable_out,
  587. .volume_out = dbus_volume_out,
  588. .init_in = dbus_init_in,
  589. .fini_in = dbus_fini_in,
  590. .read = dbus_read,
  591. .run_buffer_in = audio_generic_run_buffer_in,
  592. .enable_in = dbus_enable_in,
  593. .volume_in = dbus_volume_in,
  594. };
  595. static struct audio_driver dbus_audio_driver = {
  596. .name = "dbus",
  597. .descr = "Timer based audio exposed with DBus interface",
  598. .init = dbus_audio_init,
  599. .fini = dbus_audio_fini,
  600. .set_dbus_server = dbus_audio_set_server,
  601. .pcm_ops = &dbus_pcm_ops,
  602. .max_voices_out = INT_MAX,
  603. .max_voices_in = INT_MAX,
  604. .voice_size_out = sizeof(DBusVoiceOut),
  605. .voice_size_in = sizeof(DBusVoiceIn)
  606. };
  607. static void register_audio_dbus(void)
  608. {
  609. audio_driver_register(&dbus_audio_driver);
  610. }
  611. type_init(register_audio_dbus);
  612. module_dep("ui-dbus")