1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4 This file is part of libcanberra.
5
6 Copyright 2008 Lennart Poettering
7
8 libcanberra is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation, either version 2.1 of the
11 License, or (at your option) any later version.
12
13 libcanberra is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with libcanberra. If not, see
20 <http://www.gnu.org/licenses/>.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 /* The locking order needs to be strictly followed! First take the
28 * mainloop mutex, only then take outstanding_mutex if you need both!
29 * Not the other way round, beacause we might then enter a
30 * deadlock! */
31
32 #include <errno.h>
33 #include <stdlib.h>
34
35 #include <pulse/thread-mainloop.h>
36 #include <pulse/context.h>
37 #include <pulse/scache.h>
38 #include <pulse/subscribe.h>
39 #include <pulse/introspect.h>
40
41 #include "canberra.h"
42 #include "common.h"
43 #include "driver.h"
44 #include "llist.h"
45 #include "read-sound-file.h"
46 #include "sound-theme-spec.h"
47 #include "malloc.h"
48
49 enum outstanding_type {
50 OUTSTANDING_SAMPLE,
51 OUTSTANDING_STREAM,
52 OUTSTANDING_UPLOAD
53 };
54
55 struct outstanding {
56 CA_LLIST_FIELDS(struct outstanding);
57 enum outstanding_type type;
58 ca_context *context;
59 uint32_t id;
60 uint32_t sink_input;
61 pa_stream *stream;
62 pa_operation *drain_operation;
63 ca_finish_callback_t callback;
64 void *userdata;
65 ca_sound_file *file;
66 int error;
67 unsigned clean_up:1; /* Handler needs to clean up the outstanding struct */
68 unsigned finished:1; /* finished playing */
69 };
70
71 struct private {
72 pa_threaded_mainloop *mainloop;
73 pa_context *context;
74 ca_theme_data *theme;
75 ca_bool_t subscribed;
76 ca_bool_t reconnect;
77
78 ca_mutex *outstanding_mutex;
79 CA_LLIST_HEAD(struct outstanding, outstanding);
80 };
81
82 #define PRIVATE(c) ((struct private *) ((c)->private))
83
84 static void context_state_cb(pa_context *pc, void *userdata);
85 static void context_subscribe_cb(pa_context *pc, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
86
outstanding_disconnect(struct outstanding * o)87 static void outstanding_disconnect(struct outstanding *o) {
88 ca_assert(o);
89
90 if (o->stream) {
91 if (o->drain_operation) {
92 pa_operation_cancel(o->drain_operation);
93 pa_operation_unref(o->drain_operation);
94 o->drain_operation = NULL;
95 }
96
97 pa_stream_set_write_callback(o->stream, NULL, NULL);
98 pa_stream_set_state_callback(o->stream, NULL, NULL);
99 pa_stream_disconnect(o->stream);
100 pa_stream_unref(o->stream);
101 o->stream = NULL;
102 }
103 }
104
outstanding_free(struct outstanding * o)105 static void outstanding_free(struct outstanding *o) {
106 ca_assert(o);
107
108 outstanding_disconnect(o);
109
110 if (o->file)
111 ca_sound_file_close(o->file);
112
113 ca_free(o);
114 }
115
convert_proplist(pa_proplist ** _l,ca_proplist * c)116 static int convert_proplist(pa_proplist **_l, ca_proplist *c) {
117 pa_proplist *l;
118 ca_prop *i;
119
120 ca_return_val_if_fail(_l, CA_ERROR_INVALID);
121 ca_return_val_if_fail(c, CA_ERROR_INVALID);
122
123 if (!(l = pa_proplist_new()))
124 return CA_ERROR_OOM;
125
126 ca_mutex_lock(c->mutex);
127
128 for (i = c->first_item; i; i = i->next_item)
129 if (pa_proplist_set(l, i->key, CA_PROP_DATA(i), i->nbytes) < 0) {
130 ca_mutex_unlock(c->mutex);
131 pa_proplist_free(l);
132 return CA_ERROR_INVALID;
133 }
134
135 ca_mutex_unlock(c->mutex);
136
137 *_l = l;
138
139 return CA_SUCCESS;
140 }
141
strip_prefix(pa_proplist * l,const char * prefix)142 static pa_proplist *strip_prefix(pa_proplist *l, const char *prefix) {
143 const char *key;
144 void *state = NULL;
145 ca_assert(l);
146
147 while ((key = pa_proplist_iterate(l, &state)))
148 if (strncmp(key, prefix, strlen(prefix)) == 0)
149 pa_proplist_unset(l, key);
150
151 return l;
152 }
153
add_common(pa_proplist * l)154 static void add_common(pa_proplist *l) {
155 ca_assert(l);
156
157 if (!pa_proplist_contains(l, CA_PROP_MEDIA_ROLE))
158 pa_proplist_sets(l, CA_PROP_MEDIA_ROLE, "event");
159
160 if (!pa_proplist_contains(l, CA_PROP_MEDIA_NAME)) {
161 const char *t;
162
163 if ((t = pa_proplist_gets(l, CA_PROP_EVENT_ID)))
164 pa_proplist_sets(l, CA_PROP_MEDIA_NAME, t);
165 else if ((t = pa_proplist_gets(l, CA_PROP_MEDIA_FILENAME)))
166 pa_proplist_sets(l, CA_PROP_MEDIA_NAME, t);
167 else
168 pa_proplist_sets(l, CA_PROP_MEDIA_NAME, "libcanberra");
169 }
170 }
171
translate_error(int error)172 static int translate_error(int error) {
173 static const int table[PA_ERR_MAX] = {
174 [PA_OK] = CA_SUCCESS,
175 [PA_ERR_ACCESS] = CA_ERROR_ACCESS,
176 [PA_ERR_COMMAND] = CA_ERROR_IO,
177 [PA_ERR_INVALID] = CA_ERROR_INVALID,
178 [PA_ERR_EXIST] = CA_ERROR_IO,
179 [PA_ERR_NOENTITY] = CA_ERROR_NOTFOUND,
180 [PA_ERR_CONNECTIONREFUSED] = CA_ERROR_NOTAVAILABLE,
181 [PA_ERR_PROTOCOL] = CA_ERROR_IO,
182 [PA_ERR_TIMEOUT] = CA_ERROR_IO,
183 [PA_ERR_AUTHKEY] = CA_ERROR_ACCESS,
184 [PA_ERR_INTERNAL] = CA_ERROR_IO,
185 [PA_ERR_CONNECTIONTERMINATED] = CA_ERROR_IO,
186 [PA_ERR_KILLED] = CA_ERROR_DESTROYED,
187 [PA_ERR_INVALIDSERVER] = CA_ERROR_INVALID,
188 [PA_ERR_MODINITFAILED] = CA_ERROR_NODRIVER,
189 [PA_ERR_BADSTATE] = CA_ERROR_STATE,
190 [PA_ERR_NODATA] = CA_ERROR_IO,
191 [PA_ERR_VERSION] = CA_ERROR_NOTSUPPORTED,
192 [PA_ERR_TOOLARGE] = CA_ERROR_TOOBIG,
193 #ifdef PA_ERR_NOTSUPPORTED
194 [PA_ERR_NOTSUPPORTED] = CA_ERROR_NOTSUPPORTED,
195 #endif
196 #ifdef PA_ERR_UNKNOWN
197 [PA_ERR_UNKNOWN] = CA_ERROR_IO,
198 #endif
199 #ifdef PA_ERR_NOEXTENSION
200 [PA_ERR_NOEXTENSION] = CA_ERROR_NOTSUPPORTED,
201 #endif
202 #ifdef PA_ERR_OBSOLETE
203 [PA_ERR_OBSOLETE] = CA_ERROR_NOTSUPPORTED,
204 #endif
205 #ifdef PA_ERR_NOTIMPLEMENTED
206 [PA_ERR_NOTIMPLEMENTED] = CA_ERROR_NOTSUPPORTED
207 #endif
208 };
209
210 ca_assert(error >= 0);
211
212 if (error >= PA_ERR_MAX || !table[error])
213 return CA_ERROR_IO;
214
215 return table[error];
216 }
217
context_connect(ca_context * c,ca_bool_t nofail)218 static int context_connect(ca_context *c, ca_bool_t nofail) {
219 pa_proplist *l;
220 struct private *p;
221 int ret;
222
223 ca_return_val_if_fail(c, CA_ERROR_INVALID);
224 ca_return_val_if_fail(p = c->private, CA_ERROR_STATE);
225 ca_return_val_if_fail(p->mainloop, CA_ERROR_STATE);
226 ca_return_val_if_fail(!p->context, CA_ERROR_STATE);
227
228 /* If this immediate attempt fails, don't try to reconnect. */
229 p->reconnect = FALSE;
230
231 if ((ret = convert_proplist(&l, c->props)) < 0)
232 return ret;
233
234 strip_prefix(l, "canberra.");
235
236 if (!pa_proplist_contains(l, PA_PROP_APPLICATION_NAME)) {
237 pa_proplist_sets(l, PA_PROP_APPLICATION_NAME, "libcanberra");
238 pa_proplist_sets(l, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
239
240 if (!pa_proplist_contains(l, PA_PROP_APPLICATION_ID))
241 pa_proplist_sets(l, PA_PROP_APPLICATION_ID, "org.freedesktop.libcanberra");
242
243 }
244
245 if (!(p->context = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(p->mainloop), NULL, l))) {
246 pa_proplist_free(l);
247 return CA_ERROR_OOM;
248 }
249
250 pa_proplist_free(l);
251
252 pa_context_set_state_callback(p->context, context_state_cb, c);
253 pa_context_set_subscribe_callback(p->context, context_subscribe_cb, c);
254
255 if (pa_context_connect(p->context, NULL, nofail ? PA_CONTEXT_NOFAIL : 0, NULL) < 0) {
256 ret = translate_error(p->context ? pa_context_errno(p->context) : PA_ERR_CONNECTIONREFUSED);
257
258 if (p->context) {
259 pa_context_disconnect(p->context);
260 pa_context_unref(p->context);
261 p->context = NULL;
262 }
263
264 return ret;
265 }
266
267 return CA_SUCCESS;
268 }
269
context_state_cb(pa_context * pc,void * userdata)270 static void context_state_cb(pa_context *pc, void *userdata) {
271 ca_context *c = userdata;
272 pa_context_state_t state;
273 struct private *p;
274
275 ca_assert(pc);
276 ca_assert(c);
277
278 p = PRIVATE(c);
279
280 state = pa_context_get_state(pc);
281 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) {
282 struct outstanding *out;
283 int ret;
284
285 if (state == PA_CONTEXT_TERMINATED)
286 ret = CA_ERROR_DESTROYED;
287 else
288 ret = translate_error(pa_context_errno(pc));
289
290 ca_mutex_lock(p->outstanding_mutex);
291
292 while ((out = p->outstanding)) {
293
294 outstanding_disconnect(out);
295 CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
296
297 ca_mutex_unlock(p->outstanding_mutex);
298
299 if (out->callback)
300 out->callback(c, out->id, ret, out->userdata);
301
302 outstanding_free(out);
303
304 ca_mutex_lock(p->outstanding_mutex);
305 }
306
307 ca_mutex_unlock(p->outstanding_mutex);
308
309 if (state == PA_CONTEXT_FAILED && p->reconnect) {
310
311 if (p->context) {
312 pa_context_disconnect(p->context);
313 pa_context_unref(p->context);
314 p->context = NULL;
315 }
316
317 p->subscribed = FALSE;
318
319 /* If we managed to connect once, then let's try to
320 * reconnect, and pass NOFAIL */
321 context_connect(c, TRUE);
322 }
323
324 } else if (state == PA_CONTEXT_READY)
325 /* OK, the connection suceeded once, if it dies now try to
326 * reconnect */
327 p->reconnect = TRUE;
328
329 pa_threaded_mainloop_signal(p->mainloop, FALSE);
330 }
331
context_subscribe_cb(pa_context * pc,pa_subscription_event_type_t t,uint32_t idx,void * userdata)332 static void context_subscribe_cb(pa_context *pc, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
333 struct outstanding *out, *n;
334 CA_LLIST_HEAD(struct outstanding, l);
335 ca_context *c = userdata;
336 struct private *p;
337
338 ca_assert(pc);
339 ca_assert(c);
340
341 if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE))
342 return;
343
344 p = PRIVATE(c);
345
346 CA_LLIST_HEAD_INIT(struct outstanding, l);
347
348 ca_mutex_lock(p->outstanding_mutex);
349
350 for (out = p->outstanding; out; out = n) {
351 n = out->next;
352
353 if (!out->clean_up || out->type != OUTSTANDING_SAMPLE || out->sink_input != idx)
354 continue;
355
356 outstanding_disconnect(out);
357 CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
358
359 CA_LLIST_PREPEND(struct outstanding, l, out);
360 }
361
362 ca_mutex_unlock(p->outstanding_mutex);
363
364 while (l) {
365 out = l;
366
367 CA_LLIST_REMOVE(struct outstanding, l, out);
368
369 if (out->callback)
370 out->callback(c, out->id, CA_SUCCESS, out->userdata);
371
372 outstanding_free(out);
373 }
374 }
375
driver_open(ca_context * c)376 int driver_open(ca_context *c) {
377 struct private *p;
378 int ret;
379
380 ca_return_val_if_fail(c, CA_ERROR_INVALID);
381 ca_return_val_if_fail(!c->driver || ca_streq(c->driver, "pulse"), CA_ERROR_NODRIVER);
382 ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_STATE);
383
384 if (!(c->private = p = ca_new0(struct private, 1)))
385 return CA_ERROR_OOM;
386
387 if (!(p->outstanding_mutex = ca_mutex_new())) {
388 driver_destroy(c);
389 return CA_ERROR_OOM;
390 }
391
392 if (!(p->mainloop = pa_threaded_mainloop_new())) {
393 driver_destroy(c);
394 return CA_ERROR_OOM;
395 }
396
397 /* The initial connection is without NOFAIL, since we want to have
398 * this call fail cleanly if we cannot connect. */
399 if ((ret = context_connect(c, FALSE)) != CA_SUCCESS) {
400 driver_destroy(c);
401 return ret;
402 }
403
404 pa_threaded_mainloop_lock(p->mainloop);
405
406 if (pa_threaded_mainloop_start(p->mainloop) < 0) {
407 pa_threaded_mainloop_unlock(p->mainloop);
408 driver_destroy(c);
409 return CA_ERROR_OOM;
410 }
411
412 for (;;) {
413 pa_context_state_t state;
414
415 if (!p->context) {
416 ret = translate_error(PA_ERR_CONNECTIONREFUSED);
417 pa_threaded_mainloop_unlock(p->mainloop);
418 driver_destroy(c);
419 return ret;
420 }
421
422 state = pa_context_get_state(p->context);
423
424 if (state == PA_CONTEXT_READY)
425 break;
426
427 if (state == PA_CONTEXT_FAILED) {
428 ret = translate_error(pa_context_errno(p->context));
429 pa_threaded_mainloop_unlock(p->mainloop);
430 driver_destroy(c);
431 return ret;
432 }
433
434 ca_assert(state != PA_CONTEXT_TERMINATED);
435
436 pa_threaded_mainloop_wait(p->mainloop);
437 }
438
439 pa_threaded_mainloop_unlock(p->mainloop);
440
441 return CA_SUCCESS;
442 }
443
driver_destroy(ca_context * c)444 int driver_destroy(ca_context *c) {
445 struct private *p;
446
447 ca_return_val_if_fail(c, CA_ERROR_INVALID);
448 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
449
450 p = PRIVATE(c);
451
452 if (p->mainloop)
453 pa_threaded_mainloop_stop(p->mainloop);
454
455 if (p->context) {
456 pa_context_disconnect(p->context);
457 pa_context_unref(p->context);
458 }
459
460 while (p->outstanding) {
461 struct outstanding *out = p->outstanding;
462 CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
463
464 if (out->callback)
465 out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata);
466
467 outstanding_free(out);
468 }
469
470 if (p->mainloop)
471 pa_threaded_mainloop_free(p->mainloop);
472
473 if (p->theme)
474 ca_theme_data_free(p->theme);
475
476 if (p->outstanding_mutex)
477 ca_mutex_free(p->outstanding_mutex);
478
479 ca_free(p);
480
481 c->private = NULL;
482
483 return CA_SUCCESS;
484 }
485
driver_change_device(ca_context * c,const char * device)486 int driver_change_device(ca_context *c, const char *device) {
487 ca_return_val_if_fail(c, CA_ERROR_INVALID);
488 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
489
490 /* We're happy with any device change. We might however add code
491 * here eventually to move all currently played back event sounds
492 * to the new device. */
493
494 return CA_SUCCESS;
495 }
496
driver_change_props(ca_context * c,ca_proplist * changed,ca_proplist * merged)497 int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) {
498 struct private *p;
499 pa_operation *o;
500 pa_proplist *l;
501 int ret = CA_SUCCESS;
502
503 ca_return_val_if_fail(c, CA_ERROR_INVALID);
504 ca_return_val_if_fail(changed, CA_ERROR_INVALID);
505 ca_return_val_if_fail(merged, CA_ERROR_INVALID);
506 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
507
508 p = PRIVATE(c);
509
510 ca_return_val_if_fail(p->mainloop, CA_ERROR_STATE);
511
512 pa_threaded_mainloop_lock(p->mainloop);
513
514 if (!p->context) {
515 pa_threaded_mainloop_unlock(p->mainloop);
516 return CA_ERROR_STATE; /* can be silently ignored */
517 }
518
519 if ((ret = convert_proplist(&l, changed)) < 0)
520 return ret;
521
522 strip_prefix(l, "canberra.");
523
524 /* We start these asynchronously and don't care about the return
525 * value */
526
527 if (!(o = pa_context_proplist_update(p->context, PA_UPDATE_REPLACE, l, NULL, NULL)))
528 ret = translate_error(pa_context_errno(p->context));
529 else
530 pa_operation_unref(o);
531
532 pa_threaded_mainloop_unlock(p->mainloop);
533
534 pa_proplist_free(l);
535
536 return ret;
537 }
538
subscribe(ca_context * c)539 static int subscribe(ca_context *c) {
540 struct private *p;
541 pa_operation *o;
542 int ret = CA_SUCCESS;
543
544 ca_return_val_if_fail(c, CA_ERROR_INVALID);
545 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
546 p = PRIVATE(c);
547
548 ca_return_val_if_fail(p->mainloop, CA_ERROR_STATE);
549
550 if (p->subscribed)
551 return CA_SUCCESS;
552
553 pa_threaded_mainloop_lock(p->mainloop);
554
555 if (!p->context) {
556 pa_threaded_mainloop_unlock(p->mainloop);
557 return CA_ERROR_STATE;
558 }
559
560 /* We start these asynchronously and don't care about the return
561 * value */
562
563 if (!(o = pa_context_subscribe(p->context, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL)))
564 ret = translate_error(pa_context_errno(p->context));
565 else
566 pa_operation_unref(o);
567
568 pa_threaded_mainloop_unlock(p->mainloop);
569
570 p->subscribed = TRUE;
571
572 return ret;
573 }
574
play_sample_cb(pa_context * c,uint32_t idx,void * userdata)575 static void play_sample_cb(pa_context *c, uint32_t idx, void *userdata) {
576 struct private *p;
577 struct outstanding *out = userdata;
578
579 ca_assert(c);
580 ca_assert(out);
581
582 p = PRIVATE(out->context);
583
584 if (idx != PA_INVALID_INDEX) {
585 out->error = CA_SUCCESS;
586 out->sink_input = idx;
587 } else
588 out->error = translate_error(pa_context_errno(c));
589
590 pa_threaded_mainloop_signal(p->mainloop, FALSE);
591 }
592
stream_state_cb(pa_stream * s,void * userdata)593 static void stream_state_cb(pa_stream *s, void *userdata) {
594 struct private *p;
595 struct outstanding *out = userdata;
596 pa_stream_state_t state;
597
598 ca_assert(s);
599 ca_assert(out);
600
601 p = PRIVATE(out->context);
602
603 state = pa_stream_get_state(s);
604
605 switch (state) {
606 case PA_STREAM_CREATING:
607 case PA_STREAM_UNCONNECTED:
608 break;
609
610 case PA_STREAM_READY:
611 out->sink_input = pa_stream_get_index(out->stream);
612 break;
613
614 case PA_STREAM_FAILED:
615 case PA_STREAM_TERMINATED: {
616 int err;
617
618 err = state == PA_STREAM_FAILED ? translate_error(pa_context_errno(pa_stream_get_context(s))) : CA_ERROR_DESTROYED;
619
620 if (out->clean_up) {
621 ca_mutex_lock(p->outstanding_mutex);
622 outstanding_disconnect(out);
623 CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
624 ca_mutex_unlock(p->outstanding_mutex);
625
626 if (out->callback)
627 out->callback(out->context, out->id, out->error, out->userdata);
628
629 outstanding_free(out);
630 } else {
631 out->finished = TRUE;
632 out->error = err;
633 }
634
635 break;
636 }
637 }
638
639 pa_threaded_mainloop_signal(p->mainloop, FALSE);
640 }
641
stream_drain_cb(pa_stream * s,int success,void * userdata)642 static void stream_drain_cb(pa_stream *s, int success, void *userdata) {
643 struct private *p;
644 struct outstanding *out = userdata;
645 int err;
646
647 ca_assert(s);
648 ca_assert(out);
649 ca_assert(out->type == OUTSTANDING_STREAM);
650
651 p = PRIVATE(out->context);
652 err = success ? CA_SUCCESS : translate_error(pa_context_errno(p->context));
653
654 if (out->clean_up) {
655 ca_mutex_lock(p->outstanding_mutex);
656 outstanding_disconnect(out);
657 CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
658 ca_mutex_unlock(p->outstanding_mutex);
659
660 if (out->callback)
661 out->callback(out->context, out->id, err, out->userdata);
662
663 outstanding_free(out);
664
665 } else {
666 pa_stream_disconnect(s);
667 out->error = err;
668 out->finished = TRUE;
669
670 if (out->drain_operation) {
671 pa_operation_unref(out->drain_operation);
672 out->drain_operation = NULL;
673 }
674 }
675
676 pa_threaded_mainloop_signal(p->mainloop, FALSE);
677 }
678
stream_write_cb(pa_stream * s,size_t bytes,void * userdata)679 static void stream_write_cb(pa_stream *s, size_t bytes, void *userdata) {
680 struct outstanding *out = userdata;
681 struct private *p;
682 void *data;
683 int ret;
684 ca_bool_t eof = FALSE;
685
686 ca_assert(s);
687 ca_assert(bytes > 0);
688 ca_assert(out);
689
690 p = PRIVATE(out->context);
691
692 while (bytes > 0) {
693 size_t rbytes = bytes;
694
695 if (!(data = ca_malloc(rbytes))) {
696 ret = CA_ERROR_OOM;
697 goto finish;
698 }
699
700 if ((ret = ca_sound_file_read_arbitrary(out->file, data, &rbytes)) < 0)
701 goto finish;
702
703 if (rbytes <= 0) {
704 eof = TRUE;
705 break;
706 }
707
708 ca_assert(rbytes <= bytes);
709
710 if ((ret = pa_stream_write(s, data, rbytes, ca_free, 0, PA_SEEK_RELATIVE)) < 0) {
711 ret = translate_error(ret);
712 goto finish;
713 }
714
715 data = NULL;
716
717 bytes -= rbytes;
718 }
719
720 if (eof || ca_sound_file_get_size(out->file) <= 0) {
721
722 /* We reached EOF */
723
724 if (out->type == OUTSTANDING_UPLOAD) {
725
726 if (pa_stream_finish_upload(s) < 0) {
727 ret = translate_error(pa_context_errno(p->context));
728 goto finish;
729 }
730
731 /* Let's just signal driver_cache() which has been waiting for us */
732 pa_threaded_mainloop_signal(p->mainloop, FALSE);
733
734 } else {
735 ca_assert(out->type == OUTSTANDING_STREAM);
736
737 if (out->drain_operation) {
738 pa_operation_cancel(out->drain_operation);
739 pa_operation_unref(out->drain_operation);
740 }
741
742 if (!(out->drain_operation = pa_stream_drain(s, stream_drain_cb, out))) {
743 ret = translate_error(pa_context_errno(p->context));
744 goto finish;
745 }
746 }
747
748 pa_stream_set_write_callback(s, NULL, NULL);
749 }
750
751 ca_free(data);
752
753 return;
754
755 finish:
756
757 ca_free(data);
758
759 if (out->clean_up) {
760 ca_mutex_lock(p->outstanding_mutex);
761 outstanding_disconnect(out);
762 CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
763 ca_mutex_unlock(p->outstanding_mutex);
764
765 if (out->callback)
766 out->callback(out->context, out->id, ret, out->userdata);
767
768 outstanding_free(out);
769
770 } else {
771 pa_stream_disconnect(s);
772 out->error = ret;
773 out->finished = TRUE;
774 }
775
776 pa_threaded_mainloop_signal(p->mainloop, FALSE);
777 }
778
779 static const pa_sample_format_t sample_type_table[] = {
780 [CA_SAMPLE_S16NE] = PA_SAMPLE_S16NE,
781 [CA_SAMPLE_S16RE] = PA_SAMPLE_S16RE,
782 [CA_SAMPLE_U8] = PA_SAMPLE_U8
783 };
784
785 static const pa_channel_position_t channel_table[_CA_CHANNEL_POSITION_MAX] = {
786 [CA_CHANNEL_MONO] = PA_CHANNEL_POSITION_MONO,
787 [CA_CHANNEL_FRONT_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT,
788 [CA_CHANNEL_FRONT_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
789 [CA_CHANNEL_FRONT_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
790 [CA_CHANNEL_REAR_LEFT] = PA_CHANNEL_POSITION_REAR_LEFT,
791 [CA_CHANNEL_REAR_RIGHT] = PA_CHANNEL_POSITION_REAR_RIGHT,
792 [CA_CHANNEL_REAR_CENTER] = PA_CHANNEL_POSITION_REAR_CENTER,
793 [CA_CHANNEL_LFE] = PA_CHANNEL_POSITION_LFE,
794 [CA_CHANNEL_FRONT_LEFT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
795 [CA_CHANNEL_FRONT_RIGHT_OF_CENTER] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
796 [CA_CHANNEL_SIDE_LEFT] = PA_CHANNEL_POSITION_SIDE_LEFT,
797 [CA_CHANNEL_SIDE_RIGHT] = PA_CHANNEL_POSITION_SIDE_RIGHT,
798 [CA_CHANNEL_TOP_CENTER] = PA_CHANNEL_POSITION_TOP_CENTER,
799 [CA_CHANNEL_TOP_FRONT_LEFT] = PA_CHANNEL_POSITION_FRONT_LEFT,
800 [CA_CHANNEL_TOP_FRONT_RIGHT] = PA_CHANNEL_POSITION_FRONT_RIGHT,
801 [CA_CHANNEL_TOP_FRONT_CENTER] = PA_CHANNEL_POSITION_FRONT_CENTER,
802 [CA_CHANNEL_TOP_REAR_LEFT] = PA_CHANNEL_POSITION_REAR_LEFT,
803 [CA_CHANNEL_TOP_REAR_RIGHT] = PA_CHANNEL_POSITION_REAR_RIGHT,
804 [CA_CHANNEL_TOP_REAR_CENTER] = PA_CHANNEL_POSITION_TOP_REAR_CENTER
805 };
806
convert_channel_map(ca_sound_file * f,pa_channel_map * cm)807 static ca_bool_t convert_channel_map(ca_sound_file *f, pa_channel_map *cm) {
808 const ca_channel_position_t *positions;
809 unsigned c;
810
811 ca_assert(f);
812 ca_assert(cm);
813
814 if (!(positions = ca_sound_file_get_channel_map(f)))
815 return FALSE;
816
817 cm->channels = ca_sound_file_get_nchannels(f);
818 for (c = 0; c < cm->channels; c++)
819 cm->map[c] = channel_table[positions[c]];
820
821 return TRUE;
822 }
823
driver_play(ca_context * c,uint32_t id,ca_proplist * proplist,ca_finish_callback_t cb,void * userdata)824 int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) {
825 struct private *p;
826 pa_proplist *l = NULL;
827 const char *n, *vol, *ct, *channel;
828 char *name = NULL;
829 #if defined(PA_MAJOR) && ((PA_MAJOR > 0) || (PA_MAJOR == 0 && PA_MINOR > 9) || (PA_MAJOR == 0 && PA_MINOR == 9 && PA_MICRO >= 15))
830 pa_volume_t v = (pa_volume_t) -1;
831 #else
832 pa_volume_t v = PA_VOLUME_NORM;
833 #endif
834 ca_bool_t volume_set = FALSE;
835 pa_cvolume cvol;
836 pa_sample_spec ss;
837 pa_channel_map cm;
838 pa_channel_position_t position = PA_CHANNEL_POSITION_INVALID;
839 ca_bool_t cm_good;
840 ca_cache_control_t cache_control = CA_CACHE_CONTROL_NEVER;
841 struct outstanding *out = NULL;
842 int try = 3;
843 int ret;
844 pa_operation *o;
845 char *sp;
846 pa_buffer_attr ba;
847
848 ca_return_val_if_fail(c, CA_ERROR_INVALID);
849 ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
850 ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID);
851 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
852
853 p = PRIVATE(c);
854
855 ca_return_val_if_fail(p->mainloop, CA_ERROR_STATE);
856
857 if (!(out = ca_new0(struct outstanding, 1))) {
858 ret = CA_ERROR_OOM;
859 goto finish_unlocked;
860 }
861
862 out->type = OUTSTANDING_SAMPLE;
863 out->context = c;
864 out->sink_input = PA_INVALID_INDEX;
865 out->id = id;
866 out->callback = cb;
867 out->userdata = userdata;
868
869 if ((ret = convert_proplist(&l, proplist)) < 0)
870 goto finish_unlocked;
871
872 if ((n = pa_proplist_gets(l, CA_PROP_EVENT_ID)))
873 if (!(name = ca_strdup(n))) {
874 ret = CA_ERROR_OOM;
875 goto finish_unlocked;
876 }
877
878 if ((vol = pa_proplist_gets(l, CA_PROP_CANBERRA_VOLUME))) {
879 char *e = NULL;
880 double dvol;
881
882 errno = 0;
883 dvol = strtod(vol, &e);
884 if (errno != 0 || !e || *e) {
885 ret = CA_ERROR_INVALID;
886 goto finish_unlocked;
887 }
888
889 v = pa_sw_volume_from_dB(dvol);
890 volume_set = TRUE;
891 }
892
893 if ((ct = pa_proplist_gets(l, CA_PROP_CANBERRA_CACHE_CONTROL)))
894 if ((ret = ca_parse_cache_control(&cache_control, ct)) < 0) {
895 ret = CA_ERROR_INVALID;
896 goto finish_unlocked;
897 }
898
899 if ((channel = pa_proplist_gets(l, CA_PROP_CANBERRA_FORCE_CHANNEL))) {
900 pa_channel_map t;
901
902 if (!pa_channel_map_parse(&t, channel) ||
903 t.channels != 1) {
904 ret = CA_ERROR_INVALID;
905 goto finish_unlocked;
906 }
907
908 position = t.map[0];
909
910 /* We cannot remap cached samples, so let's fail when cacheing
911 * shall be used */
912 if (cache_control != CA_CACHE_CONTROL_NEVER) {
913 ret = CA_ERROR_NOTSUPPORTED;
914 goto finish_unlocked;
915 }
916 }
917
918 strip_prefix(l, "canberra.");
919 add_common(l);
920
921 if ((ret = subscribe(c)) < 0)
922 goto finish_unlocked;
923
924 if (name && cache_control != CA_CACHE_CONTROL_NEVER) {
925
926 /* Ok, this sample has an event id, let's try to play it from the cache */
927
928 for (;;) {
929 ca_bool_t canceled;
930
931 pa_threaded_mainloop_lock(p->mainloop);
932
933 if (!p->context) {
934 ret = CA_ERROR_STATE;
935 goto finish_locked;
936 }
937
938 /* Let's try to play the sample */
939 if (!(o = pa_context_play_sample_with_proplist(p->context, name, c->device, v, l, play_sample_cb, out))) {
940 ret = translate_error(pa_context_errno(p->context));
941 goto finish_locked;
942 }
943
944 for (;;) {
945 pa_operation_state_t state = pa_operation_get_state(o);
946
947 if (state == PA_OPERATION_DONE) {
948 canceled = FALSE;
949 break;
950 } else if (state == PA_OPERATION_CANCELED) {
951 canceled = TRUE;
952 break;
953 }
954
955 pa_threaded_mainloop_wait(p->mainloop);
956 }
957
958 pa_operation_unref(o);
959
960 if (!canceled && p->context && out->error == CA_SUCCESS) {
961 ret = CA_SUCCESS;
962 goto finish_locked;
963 }
964
965 pa_threaded_mainloop_unlock(p->mainloop);
966
967 /* The operation might have been canceled due to connection termination */
968 if (canceled || !p->context) {
969 ret = CA_ERROR_DISCONNECTED;
970 goto finish_unlocked;
971 }
972
973 /* Did some other error occur? */
974 if (out->error != CA_ERROR_NOTFOUND) {
975 ret = out->error;
976 goto finish_unlocked;
977 }
978
979 /* Hmm, we need to play it directly */
980 if (cache_control != CA_CACHE_CONTROL_PERMANENT)
981 break;
982
983 /* Don't loop forever */
984 if (--try <= 0)
985 break;
986
987 /* Let's upload the sample and retry playing */
988 if ((ret = driver_cache(c, proplist)) < 0)
989 goto finish_unlocked;
990 }
991 }
992
993 out->type = OUTSTANDING_STREAM;
994
995 /* Let's stream the sample directly */
996 if ((ret = ca_lookup_sound(&out->file, &sp, &p->theme, c->props, proplist)) < 0)
997 goto finish_unlocked;
998
999 if (sp)
1000 if (!pa_proplist_contains(l, CA_PROP_MEDIA_FILENAME))
1001 pa_proplist_sets(l, CA_PROP_MEDIA_FILENAME, sp);
1002
1003 ca_free(sp);
1004
1005 ss.format = sample_type_table[ca_sound_file_get_sample_type(out->file)];
1006 ss.channels = (uint8_t) ca_sound_file_get_nchannels(out->file);
1007 ss.rate = ca_sound_file_get_rate(out->file);
1008
1009 if (position != PA_CHANNEL_POSITION_INVALID) {
1010 unsigned u;
1011 /* Apply canberra.force_channel */
1012
1013 cm.channels = ss.channels;
1014 for (u = 0; u < cm.channels; u++)
1015 cm.map[u] = position;
1016
1017 cm_good = TRUE;
1018 } else
1019 cm_good = convert_channel_map(out->file, &cm);
1020
1021 pa_threaded_mainloop_lock(p->mainloop);
1022
1023 if (!p->context) {
1024 ret = CA_ERROR_STATE;
1025 goto finish_locked;
1026 }
1027
1028 if (!(out->stream = pa_stream_new_with_proplist(p->context, NULL, &ss, cm_good ? &cm : NULL, l))) {
1029 ret = translate_error(pa_context_errno(p->context));
1030 goto finish_locked;
1031 }
1032
1033 pa_stream_set_state_callback(out->stream, stream_state_cb, out);
1034 pa_stream_set_write_callback(out->stream, stream_write_cb, out);
1035
1036 if (volume_set)
1037 pa_cvolume_set(&cvol, ss.channels, v);
1038
1039 /* Make sure we get the longest latency possible, to minimize CPU
1040 * consumption */
1041 ba.maxlength = (uint32_t) -1;
1042 ba.tlength = (uint32_t) -1;
1043 ba.prebuf = (uint32_t) -1;
1044 ba.minreq = (uint32_t) -1;
1045 ba.fragsize = (uint32_t) -1;
1046
1047 if (pa_stream_connect_playback(out->stream, c->device, &ba,
1048 #ifdef PA_STREAM_FAIL_ON_SUSPEND
1049 PA_STREAM_FAIL_ON_SUSPEND
1050 #else
1051 0
1052 #endif
1053 | (position != PA_CHANNEL_POSITION_INVALID ? PA_STREAM_NO_REMIX_CHANNELS : 0)
1054 , volume_set ? &cvol : NULL, NULL) < 0) {
1055 ret = translate_error(pa_context_errno(p->context));
1056 goto finish_locked;
1057 }
1058
1059 for (;;) {
1060 pa_stream_state_t state;
1061
1062 if (!p->context || !out->stream) {
1063 ret = CA_ERROR_STATE;
1064 goto finish_locked;
1065 }
1066
1067 state = pa_stream_get_state(out->stream);
1068
1069 /* Stream sucessfully created */
1070 if (state == PA_STREAM_READY)
1071 break;
1072
1073 /* Check for failure */
1074 if (state == PA_STREAM_FAILED) {
1075 ret = translate_error(pa_context_errno(p->context));
1076 goto finish_locked;
1077 }
1078
1079 /* Prematurely ended */
1080 if (state == PA_STREAM_TERMINATED) {
1081 ret = out->error;
1082 goto finish_locked;
1083 }
1084
1085 pa_threaded_mainloop_wait(p->mainloop);
1086 }
1087
1088 ret = CA_SUCCESS;
1089
1090 finish_locked:
1091
1092 /* We keep the outstanding struct around to clean up later if the sound din't finish yet*/
1093 if (ret == CA_SUCCESS && !out->finished) {
1094 out->clean_up = TRUE;
1095
1096 ca_mutex_lock(p->outstanding_mutex);
1097 CA_LLIST_PREPEND(struct outstanding, p->outstanding, out);
1098 ca_mutex_unlock(p->outstanding_mutex);
1099 } else
1100 outstanding_free(out);
1101
1102 out = NULL;
1103
1104 pa_threaded_mainloop_unlock(p->mainloop);
1105
1106 finish_unlocked:
1107
1108 if (out)
1109 outstanding_free(out);
1110
1111 if (l)
1112 pa_proplist_free(l);
1113
1114 ca_free(name);
1115
1116 return ret;
1117 }
1118
driver_cancel(ca_context * c,uint32_t id)1119 int driver_cancel(ca_context *c, uint32_t id) {
1120 struct private *p;
1121 pa_operation *o;
1122 int ret = CA_SUCCESS;
1123 struct outstanding *out, *n;
1124
1125 ca_return_val_if_fail(c, CA_ERROR_INVALID);
1126 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
1127
1128 p = PRIVATE(c);
1129
1130 ca_return_val_if_fail(p->mainloop, CA_ERROR_STATE);
1131
1132 pa_threaded_mainloop_lock(p->mainloop);
1133
1134 if (!p->context) {
1135 pa_threaded_mainloop_unlock(p->mainloop);
1136 return CA_ERROR_STATE;
1137 }
1138
1139 ca_mutex_lock(p->outstanding_mutex);
1140
1141 /* We start these asynchronously and don't care about the return
1142 * value */
1143
1144 for (out = p->outstanding; out; out = n) {
1145 int ret2 = CA_SUCCESS;
1146 n = out->next;
1147
1148 if (out->type == OUTSTANDING_UPLOAD ||
1149 out->id != id ||
1150 out->sink_input == PA_INVALID_INDEX)
1151 continue;
1152
1153 if (!(o = pa_context_kill_sink_input(p->context, out->sink_input, NULL, NULL)))
1154 ret2 = translate_error(pa_context_errno(p->context));
1155 else
1156 pa_operation_unref(o);
1157
1158 /* We make sure here to kill all streams identified by the id
1159 * here. However, we will return only the first error we
1160 * encounter */
1161
1162 if (ret2 && ret == CA_SUCCESS)
1163 ret = ret2;
1164
1165 if (out->callback)
1166 out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata);
1167
1168 outstanding_disconnect(out);
1169 CA_LLIST_REMOVE(struct outstanding, p->outstanding, out);
1170 outstanding_free(out);
1171 }
1172
1173 ca_mutex_unlock(p->outstanding_mutex);
1174
1175 pa_threaded_mainloop_unlock(p->mainloop);
1176
1177 return ret;
1178 }
1179
driver_cache(ca_context * c,ca_proplist * proplist)1180 int driver_cache(ca_context *c, ca_proplist *proplist) {
1181 struct private *p;
1182 pa_proplist *l = NULL;
1183 const char *n, *ct;
1184 pa_sample_spec ss;
1185 pa_channel_map cm;
1186 ca_bool_t cm_good;
1187 ca_cache_control_t cache_control = CA_CACHE_CONTROL_PERMANENT;
1188 struct outstanding *out;
1189 int ret;
1190 char *sp;
1191
1192 ca_return_val_if_fail(c, CA_ERROR_INVALID);
1193 ca_return_val_if_fail(proplist, CA_ERROR_INVALID);
1194 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
1195
1196 p = PRIVATE(c);
1197
1198 ca_return_val_if_fail(p->mainloop, CA_ERROR_STATE);
1199
1200 if (!(out = ca_new0(struct outstanding, 1))) {
1201 ret = CA_ERROR_OOM;
1202 goto finish_unlocked;
1203 }
1204
1205 out->type = OUTSTANDING_UPLOAD;
1206 out->context = c;
1207 out->sink_input = PA_INVALID_INDEX;
1208
1209 if ((ret = convert_proplist(&l, proplist)) < 0)
1210 goto finish_unlocked;
1211
1212 if (!(n = pa_proplist_gets(l, CA_PROP_EVENT_ID))) {
1213 ret = CA_ERROR_INVALID;
1214 goto finish_unlocked;
1215 }
1216
1217 if ((ct = pa_proplist_gets(l, CA_PROP_CANBERRA_CACHE_CONTROL)))
1218 if ((ret = ca_parse_cache_control(&cache_control, ct)) < 0) {
1219 ret = CA_ERROR_INVALID;
1220 goto finish_unlocked;
1221 }
1222
1223 if (cache_control != CA_CACHE_CONTROL_PERMANENT) {
1224 ret = CA_ERROR_INVALID;
1225 goto finish_unlocked;
1226 }
1227
1228 if ((ct = pa_proplist_gets(l, CA_PROP_CANBERRA_FORCE_CHANNEL))) {
1229 ret = CA_ERROR_NOTSUPPORTED;
1230 goto finish_unlocked;
1231 }
1232
1233 strip_prefix(l, "canberra.");
1234 strip_prefix(l, "event.mouse.");
1235 strip_prefix(l, "window.");
1236 add_common(l);
1237
1238 /* Let's stream the sample directly */
1239 if ((ret = ca_lookup_sound(&out->file, &sp, &p->theme, c->props, proplist)) < 0)
1240 goto finish_unlocked;
1241
1242 if (sp)
1243 if (!pa_proplist_contains(l, CA_PROP_MEDIA_FILENAME))
1244 pa_proplist_sets(l, CA_PROP_MEDIA_FILENAME, sp);
1245
1246 ca_free(sp);
1247
1248 ss.format = sample_type_table[ca_sound_file_get_sample_type(out->file)];
1249 ss.channels = (uint8_t) ca_sound_file_get_nchannels(out->file);
1250 ss.rate = ca_sound_file_get_rate(out->file);
1251
1252 cm_good = convert_channel_map(out->file, &cm);
1253
1254 pa_threaded_mainloop_lock(p->mainloop);
1255
1256 if (!p->context) {
1257 ret = CA_ERROR_STATE;
1258 goto finish_locked;
1259 }
1260
1261 if (!(out->stream = pa_stream_new_with_proplist(p->context, NULL, &ss, cm_good ? &cm : NULL, l))) {
1262 ret = translate_error(pa_context_errno(p->context));
1263 goto finish_locked;
1264 }
1265
1266 pa_stream_set_state_callback(out->stream, stream_state_cb, out);
1267 pa_stream_set_write_callback(out->stream, stream_write_cb, out);
1268
1269 if (pa_stream_connect_upload(out->stream, (size_t) ca_sound_file_get_size(out->file)) < 0) {
1270 ret = translate_error(pa_context_errno(p->context));
1271 goto finish_locked;
1272 }
1273
1274 for (;;) {
1275 pa_stream_state_t state;
1276
1277 if (!p->context || !out->stream) {
1278 ret = CA_ERROR_STATE;
1279 goto finish_locked;
1280 }
1281
1282 state = pa_stream_get_state(out->stream);
1283
1284 /* Stream sucessfully created and uploaded */
1285 if (state == PA_STREAM_TERMINATED)
1286 break;
1287
1288 /* Check for failure */
1289 if (state == PA_STREAM_FAILED) {
1290 ret = translate_error(pa_context_errno(p->context));
1291 goto finish_locked;
1292 }
1293
1294 pa_threaded_mainloop_wait(p->mainloop);
1295 }
1296
1297 ret = CA_SUCCESS;
1298
1299 finish_locked:
1300 outstanding_free(out);
1301 out = NULL;
1302
1303 pa_threaded_mainloop_unlock(p->mainloop);
1304
1305 finish_unlocked:
1306
1307 if (out)
1308 outstanding_free(out);
1309
1310 if (l)
1311 pa_proplist_free(l);
1312
1313 return ret;
1314 }
1315
driver_playing(ca_context * c,uint32_t id,int * playing)1316 int driver_playing(ca_context *c, uint32_t id, int *playing) {
1317 struct private *p;
1318 struct outstanding *out;
1319
1320 ca_return_val_if_fail(c, CA_ERROR_INVALID);
1321 ca_return_val_if_fail(c->private, CA_ERROR_STATE);
1322 ca_return_val_if_fail(playing, CA_ERROR_INVALID);
1323
1324 p = PRIVATE(c);
1325
1326 *playing = 0;
1327
1328 ca_mutex_lock(p->outstanding_mutex);
1329
1330 for (out = p->outstanding; out; out = out->next) {
1331
1332 if (out->type == OUTSTANDING_UPLOAD ||
1333 out->id != id ||
1334 out->sink_input == PA_INVALID_INDEX)
1335 continue;
1336
1337 *playing = 1;
1338 break;
1339 }
1340
1341 ca_mutex_unlock(p->outstanding_mutex);
1342
1343 return CA_SUCCESS;
1344 }
1345