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