Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
1 /*
2 * Copyright © 2011 Mozilla Foundation
3 *
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
6 */
7 #undef NDEBUG
8 #define _BSD_SOURCE
9 #define _XOPEN_SOURCE 500
10 #include <pthread.h>
11 #include <sys/time.h>
12 #include <assert.h>
13 #include <limits.h>
14 #include <poll.h>
15 #include <unistd.h>
16 #include <alsa/asoundlib.h>
17 #include "cubeb/cubeb.h"
18 #include "cubeb-internal.h"
20 #define CUBEB_STREAM_MAX 16
21 #define CUBEB_WATCHDOG_MS 10000
23 #define CUBEB_ALSA_PCM_NAME "default"
25 #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
27 /* ALSA is not thread-safe. snd_pcm_t instances are individually protected
28 by the owning cubeb_stream's mutex. snd_pcm_t creation and destruction
29 is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1),
30 so those calls must be wrapped in the following mutex. */
31 static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
32 static int cubeb_alsa_error_handler_set = 0;
34 static struct cubeb_ops const alsa_ops;
36 struct cubeb {
37 struct cubeb_ops const * ops;
39 pthread_t thread;
41 /* Mutex for streams array, must not be held while blocked in poll(2). */
42 pthread_mutex_t mutex;
44 /* Sparse array of streams managed by this context. */
45 cubeb_stream * streams[CUBEB_STREAM_MAX];
47 /* fds and nfds are only updated by alsa_run when rebuild is set. */
48 struct pollfd * fds;
49 nfds_t nfds;
50 int rebuild;
52 int shutdown;
54 /* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */
55 int control_fd_read;
56 int control_fd_write;
58 /* Track number of active streams. This is limited to CUBEB_STREAM_MAX
59 due to resource contraints. */
60 unsigned int active_streams;
62 /* Local configuration with handle_underrun workaround set for PulseAudio
63 ALSA plugin. Will be NULL if the PA ALSA plugin is not in use or the
64 workaround is not required. */
65 snd_config_t * local_config;
66 int is_pa;
67 };
69 enum stream_state {
70 INACTIVE,
71 RUNNING,
72 DRAINING,
73 PROCESSING,
74 ERROR
75 };
77 struct cubeb_stream {
78 cubeb * context;
79 pthread_mutex_t mutex;
80 snd_pcm_t * pcm;
81 cubeb_data_callback data_callback;
82 cubeb_state_callback state_callback;
83 void * user_ptr;
84 snd_pcm_uframes_t write_position;
85 snd_pcm_uframes_t last_position;
86 snd_pcm_uframes_t buffer_size;
87 snd_pcm_uframes_t period_size;
88 cubeb_stream_params params;
90 /* Every member after this comment is protected by the owning context's
91 mutex rather than the stream's mutex, or is only used on the context's
92 run thread. */
93 pthread_cond_t cond; /* Signaled when the stream's state is changed. */
95 enum stream_state state;
97 struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
98 struct pollfd * fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
99 nfds_t nfds;
101 struct timeval drain_timeout;
103 /* XXX: Horrible hack -- if an active stream has been idle for
104 CUBEB_WATCHDOG_MS it will be disabled and the error callback will be
105 called. This works around a bug seen with older versions of ALSA and
106 PulseAudio where streams would stop requesting new data despite still
107 being logically active and playing. */
108 struct timeval last_activity;
109 };
111 static int
112 any_revents(struct pollfd * fds, nfds_t nfds)
113 {
114 nfds_t i;
116 for (i = 0; i < nfds; ++i) {
117 if (fds[i].revents) {
118 return 1;
119 }
120 }
122 return 0;
123 }
125 static int
126 cmp_timeval(struct timeval * a, struct timeval * b)
127 {
128 if (a->tv_sec == b->tv_sec) {
129 if (a->tv_usec == b->tv_usec) {
130 return 0;
131 }
132 return a->tv_usec > b->tv_usec ? 1 : -1;
133 }
134 return a->tv_sec > b->tv_sec ? 1 : -1;
135 }
137 static int
138 timeval_to_relative_ms(struct timeval * tv)
139 {
140 struct timeval now;
141 struct timeval dt;
142 long long t;
143 int r;
145 gettimeofday(&now, NULL);
146 r = cmp_timeval(tv, &now);
147 if (r >= 0) {
148 timersub(tv, &now, &dt);
149 } else {
150 timersub(&now, tv, &dt);
151 }
152 t = dt.tv_sec;
153 t *= 1000;
154 t += (dt.tv_usec + 500) / 1000;
156 if (t > INT_MAX) {
157 t = INT_MAX;
158 } else if (t < INT_MIN) {
159 t = INT_MIN;
160 }
162 return r >= 0 ? t : -t;
163 }
165 static int
166 ms_until(struct timeval * tv)
167 {
168 return timeval_to_relative_ms(tv);
169 }
171 static int
172 ms_since(struct timeval * tv)
173 {
174 return -timeval_to_relative_ms(tv);
175 }
177 static void
178 rebuild(cubeb * ctx)
179 {
180 nfds_t nfds;
181 int i;
182 nfds_t j;
183 cubeb_stream * stm;
185 assert(ctx->rebuild);
187 /* Always count context's control pipe fd. */
188 nfds = 1;
189 for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
190 stm = ctx->streams[i];
191 if (stm) {
192 stm->fds = NULL;
193 if (stm->state == RUNNING) {
194 nfds += stm->nfds;
195 }
196 }
197 }
199 free(ctx->fds);
200 ctx->fds = calloc(nfds, sizeof(struct pollfd));
201 assert(ctx->fds);
202 ctx->nfds = nfds;
204 /* Include context's control pipe fd. */
205 ctx->fds[0].fd = ctx->control_fd_read;
206 ctx->fds[0].events = POLLIN | POLLERR;
208 for (i = 0, j = 1; i < CUBEB_STREAM_MAX; ++i) {
209 stm = ctx->streams[i];
210 if (stm && stm->state == RUNNING) {
211 memcpy(&ctx->fds[j], stm->saved_fds, stm->nfds * sizeof(struct pollfd));
212 stm->fds = &ctx->fds[j];
213 j += stm->nfds;
214 }
215 }
217 ctx->rebuild = 0;
218 }
220 static void
221 poll_wake(cubeb * ctx)
222 {
223 write(ctx->control_fd_write, "x", 1);
224 }
226 static void
227 set_timeout(struct timeval * timeout, unsigned int ms)
228 {
229 gettimeofday(timeout, NULL);
230 timeout->tv_sec += ms / 1000;
231 timeout->tv_usec += (ms % 1000) * 1000;
232 }
234 static void
235 alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
236 {
237 cubeb * ctx;
238 int r;
240 ctx = stm->context;
241 stm->state = state;
242 r = pthread_cond_broadcast(&stm->cond);
243 assert(r == 0);
244 ctx->rebuild = 1;
245 poll_wake(ctx);
246 }
248 static enum stream_state
249 alsa_refill_stream(cubeb_stream * stm)
250 {
251 int r;
252 unsigned short revents;
253 snd_pcm_sframes_t avail;
254 long got;
255 void * p;
256 int draining;
258 draining = 0;
260 pthread_mutex_lock(&stm->mutex);
262 r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents);
263 if (r < 0 || revents != POLLOUT) {
264 /* This should be a stream error; it makes no sense for poll(2) to wake
265 for this stream and then have the stream report that it's not ready.
266 Unfortunately, this does happen, so just bail out and try again. */
267 pthread_mutex_unlock(&stm->mutex);
268 return RUNNING;
269 }
271 avail = snd_pcm_avail_update(stm->pcm);
272 if (avail == -EPIPE) {
273 snd_pcm_recover(stm->pcm, avail, 1);
274 avail = snd_pcm_avail_update(stm->pcm);
275 }
277 /* Failed to recover from an xrun, this stream must be broken. */
278 if (avail < 0) {
279 pthread_mutex_unlock(&stm->mutex);
280 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
281 return ERROR;
282 }
284 /* This should never happen. */
285 if ((unsigned int) avail > stm->buffer_size) {
286 avail = stm->buffer_size;
287 }
289 /* poll(2) claims this stream is active, so there should be some space
290 available to write. If avail is still zero here, the stream must be in
291 a funky state, so recover and try again. */
292 if (avail == 0) {
293 snd_pcm_recover(stm->pcm, -EPIPE, 1);
294 avail = snd_pcm_avail_update(stm->pcm);
295 if (avail <= 0) {
296 pthread_mutex_unlock(&stm->mutex);
297 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
298 return ERROR;
299 }
300 }
302 p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
303 assert(p);
305 pthread_mutex_unlock(&stm->mutex);
306 got = stm->data_callback(stm, stm->user_ptr, p, avail);
307 pthread_mutex_lock(&stm->mutex);
308 if (got < 0) {
309 pthread_mutex_unlock(&stm->mutex);
310 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
311 return ERROR;
312 }
313 if (got > 0) {
314 snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
315 if (wrote == -EPIPE) {
316 snd_pcm_recover(stm->pcm, wrote, 1);
317 wrote = snd_pcm_writei(stm->pcm, p, got);
318 }
319 assert(wrote >= 0 && wrote == got);
320 stm->write_position += wrote;
321 gettimeofday(&stm->last_activity, NULL);
322 }
323 if (got != avail) {
324 long buffer_fill = stm->buffer_size - (avail - got);
325 double buffer_time = (double) buffer_fill / stm->params.rate;
327 /* Fill the remaining buffer with silence to guarantee one full period
328 has been written. */
329 snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
331 set_timeout(&stm->drain_timeout, buffer_time * 1000);
333 draining = 1;
334 }
336 free(p);
337 pthread_mutex_unlock(&stm->mutex);
338 return draining ? DRAINING : RUNNING;
339 }
341 static int
342 alsa_run(cubeb * ctx)
343 {
344 int r;
345 int timeout;
346 int i;
347 char dummy;
348 cubeb_stream * stm;
349 enum stream_state state;
351 pthread_mutex_lock(&ctx->mutex);
353 if (ctx->rebuild) {
354 rebuild(ctx);
355 }
357 /* Wake up at least once per second for the watchdog. */
358 timeout = 1000;
359 for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
360 stm = ctx->streams[i];
361 if (stm && stm->state == DRAINING) {
362 r = ms_until(&stm->drain_timeout);
363 if (r >= 0 && timeout > r) {
364 timeout = r;
365 }
366 }
367 }
369 pthread_mutex_unlock(&ctx->mutex);
370 r = poll(ctx->fds, ctx->nfds, timeout);
371 pthread_mutex_lock(&ctx->mutex);
373 if (r > 0) {
374 if (ctx->fds[0].revents & POLLIN) {
375 read(ctx->control_fd_read, &dummy, 1);
377 if (ctx->shutdown) {
378 pthread_mutex_unlock(&ctx->mutex);
379 return -1;
380 }
381 }
383 for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
384 stm = ctx->streams[i];
385 if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) {
386 alsa_set_stream_state(stm, PROCESSING);
387 pthread_mutex_unlock(&ctx->mutex);
388 state = alsa_refill_stream(stm);
389 pthread_mutex_lock(&ctx->mutex);
390 alsa_set_stream_state(stm, state);
391 }
392 }
393 } else if (r == 0) {
394 for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
395 stm = ctx->streams[i];
396 if (stm) {
397 if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
398 alsa_set_stream_state(stm, INACTIVE);
399 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
400 } else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
401 alsa_set_stream_state(stm, ERROR);
402 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
403 }
404 }
405 }
406 }
408 pthread_mutex_unlock(&ctx->mutex);
410 return 0;
411 }
413 static void *
414 alsa_run_thread(void * context)
415 {
416 cubeb * ctx = context;
417 int r;
419 do {
420 r = alsa_run(ctx);
421 } while (r >= 0);
423 return NULL;
424 }
426 static snd_config_t *
427 get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
428 {
429 int r;
430 snd_config_t * slave_pcm;
431 snd_config_t * slave_def;
432 snd_config_t * pcm;
433 char const * string;
434 char node_name[64];
436 slave_def = NULL;
438 r = snd_config_search(root_pcm, "slave", &slave_pcm);
439 if (r < 0) {
440 return NULL;
441 }
443 r = snd_config_get_string(slave_pcm, &string);
444 if (r >= 0) {
445 r = snd_config_search_definition(lconf, "pcm_slave", string, &slave_def);
446 if (r < 0) {
447 return NULL;
448 }
449 }
451 do {
452 r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
453 if (r < 0) {
454 break;
455 }
457 r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string);
458 if (r < 0) {
459 break;
460 }
462 r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
463 if (r < 0 || r > (int) sizeof(node_name)) {
464 break;
465 }
466 r = snd_config_search(lconf, node_name, &pcm);
467 if (r < 0) {
468 break;
469 }
471 return pcm;
472 } while (0);
474 if (slave_def) {
475 snd_config_delete(slave_def);
476 }
478 return NULL;
479 }
481 /* Work around PulseAudio ALSA plugin bug where the PA server forces a
482 higher than requested latency, but the plugin does not update its (and
483 ALSA's) internal state to reflect that, leading to an immediate underrun
484 situation. Inspired by WINE's make_handle_underrun_config.
485 Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 */
486 static snd_config_t *
487 init_local_config_with_workaround(char const * pcm_name)
488 {
489 int r;
490 snd_config_t * lconf;
491 snd_config_t * pcm_node;
492 snd_config_t * node;
493 char const * string;
494 char node_name[64];
496 lconf = NULL;
498 if (snd_config == NULL) {
499 return NULL;
500 }
502 r = snd_config_copy(&lconf, snd_config);
503 if (r < 0) {
504 return NULL;
505 }
507 do {
508 r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node);
509 if (r < 0) {
510 break;
511 }
513 r = snd_config_get_id(pcm_node, &string);
514 if (r < 0) {
515 break;
516 }
518 r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
519 if (r < 0 || r > (int) sizeof(node_name)) {
520 break;
521 }
522 r = snd_config_search(lconf, node_name, &pcm_node);
523 if (r < 0) {
524 break;
525 }
527 /* If this PCM has a slave, walk the slave configurations until we reach the bottom. */
528 while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
529 pcm_node = node;
530 }
532 /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
533 r = snd_config_search(pcm_node, "type", &node);
534 if (r < 0) {
535 break;
536 }
538 r = snd_config_get_string(node, &string);
539 if (r < 0) {
540 break;
541 }
543 if (strcmp(string, "pulse") != 0) {
544 break;
545 }
547 /* Don't clobber an explicit existing handle_underrun value, set it only
548 if it doesn't already exist. */
549 r = snd_config_search(pcm_node, "handle_underrun", &node);
550 if (r != -ENOENT) {
551 break;
552 }
554 /* Disable pcm_pulse's asynchronous underrun handling. */
555 r = snd_config_imake_integer(&node, "handle_underrun", 0);
556 if (r < 0) {
557 break;
558 }
560 r = snd_config_add(pcm_node, node);
561 if (r < 0) {
562 break;
563 }
565 return lconf;
566 } while (0);
568 snd_config_delete(lconf);
570 return NULL;
571 }
573 static int
574 alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * local_config)
575 {
576 int r;
578 pthread_mutex_lock(&cubeb_alsa_mutex);
579 if (local_config) {
580 r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config);
581 } else {
582 r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK);
583 }
584 pthread_mutex_unlock(&cubeb_alsa_mutex);
586 return r;
587 }
589 static int
590 alsa_locked_pcm_close(snd_pcm_t * pcm)
591 {
592 int r;
594 pthread_mutex_lock(&cubeb_alsa_mutex);
595 r = snd_pcm_close(pcm);
596 pthread_mutex_unlock(&cubeb_alsa_mutex);
598 return r;
599 }
601 static int
602 alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
603 {
604 int i;
606 pthread_mutex_lock(&ctx->mutex);
607 for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
608 if (!ctx->streams[i]) {
609 ctx->streams[i] = stm;
610 break;
611 }
612 }
613 pthread_mutex_unlock(&ctx->mutex);
615 return i == CUBEB_STREAM_MAX;
616 }
618 static void
619 alsa_unregister_stream(cubeb_stream * stm)
620 {
621 cubeb * ctx;
622 int i;
624 ctx = stm->context;
626 pthread_mutex_lock(&ctx->mutex);
627 for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
628 if (ctx->streams[i] == stm) {
629 ctx->streams[i] = NULL;
630 break;
631 }
632 }
633 pthread_mutex_unlock(&ctx->mutex);
634 }
636 static void
637 silent_error_handler(char const * file, int line, char const * function,
638 int err, char const * fmt, ...)
639 {
640 }
642 /*static*/ int
643 alsa_init(cubeb ** context, char const * context_name)
644 {
645 cubeb * ctx;
646 int r;
647 int i;
648 int fd[2];
649 pthread_attr_t attr;
650 snd_pcm_t * dummy;
652 assert(context);
653 *context = NULL;
655 pthread_mutex_lock(&cubeb_alsa_mutex);
656 if (!cubeb_alsa_error_handler_set) {
657 snd_lib_error_set_handler(silent_error_handler);
658 cubeb_alsa_error_handler_set = 1;
659 }
660 pthread_mutex_unlock(&cubeb_alsa_mutex);
662 ctx = calloc(1, sizeof(*ctx));
663 assert(ctx);
665 ctx->ops = &alsa_ops;
667 r = pthread_mutex_init(&ctx->mutex, NULL);
668 assert(r == 0);
670 r = pipe(fd);
671 assert(r == 0);
673 for (i = 0; i < 2; ++i) {
674 fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC);
675 fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK);
676 }
678 ctx->control_fd_read = fd[0];
679 ctx->control_fd_write = fd[1];
681 /* Force an early rebuild when alsa_run is first called to ensure fds and
682 nfds have been initialized. */
683 ctx->rebuild = 1;
685 r = pthread_attr_init(&attr);
686 assert(r == 0);
688 r = pthread_attr_setstacksize(&attr, 256 * 1024);
689 assert(r == 0);
691 r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
692 assert(r == 0);
694 r = pthread_attr_destroy(&attr);
695 assert(r == 0);
697 /* Open a dummy PCM to force the configuration space to be evaluated so that
698 init_local_config_with_workaround can find and modify the default node. */
699 r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, NULL);
700 if (r >= 0) {
701 alsa_locked_pcm_close(dummy);
702 }
703 ctx->is_pa = 0;
704 pthread_mutex_lock(&cubeb_alsa_mutex);
705 ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME);
706 pthread_mutex_unlock(&cubeb_alsa_mutex);
707 if (ctx->local_config) {
708 ctx->is_pa = 1;
709 r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
710 /* If we got a local_config, we found a PA PCM. If opening a PCM with that
711 config fails with EINVAL, the PA PCM is too old for this workaround. */
712 if (r == -EINVAL) {
713 pthread_mutex_lock(&cubeb_alsa_mutex);
714 snd_config_delete(ctx->local_config);
715 pthread_mutex_unlock(&cubeb_alsa_mutex);
716 ctx->local_config = NULL;
717 } else if (r >= 0) {
718 alsa_locked_pcm_close(dummy);
719 }
720 }
722 *context = ctx;
724 return CUBEB_OK;
725 }
727 static char const *
728 alsa_get_backend_id(cubeb * ctx)
729 {
730 return "alsa";
731 }
733 static void
734 alsa_destroy(cubeb * ctx)
735 {
736 int r;
738 assert(ctx);
740 pthread_mutex_lock(&ctx->mutex);
741 ctx->shutdown = 1;
742 poll_wake(ctx);
743 pthread_mutex_unlock(&ctx->mutex);
745 r = pthread_join(ctx->thread, NULL);
746 assert(r == 0);
748 close(ctx->control_fd_read);
749 close(ctx->control_fd_write);
750 pthread_mutex_destroy(&ctx->mutex);
751 free(ctx->fds);
753 if (ctx->local_config) {
754 pthread_mutex_lock(&cubeb_alsa_mutex);
755 snd_config_delete(ctx->local_config);
756 pthread_mutex_unlock(&cubeb_alsa_mutex);
757 }
759 free(ctx);
760 }
762 static void alsa_stream_destroy(cubeb_stream * stm);
764 static int
765 alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
766 cubeb_stream_params stream_params, unsigned int latency,
767 cubeb_data_callback data_callback, cubeb_state_callback state_callback,
768 void * user_ptr)
769 {
770 cubeb_stream * stm;
771 int r;
772 snd_pcm_format_t format;
774 assert(ctx && stream);
776 *stream = NULL;
778 switch (stream_params.format) {
779 case CUBEB_SAMPLE_S16LE:
780 format = SND_PCM_FORMAT_S16_LE;
781 break;
782 case CUBEB_SAMPLE_S16BE:
783 format = SND_PCM_FORMAT_S16_BE;
784 break;
785 case CUBEB_SAMPLE_FLOAT32LE:
786 format = SND_PCM_FORMAT_FLOAT_LE;
787 break;
788 case CUBEB_SAMPLE_FLOAT32BE:
789 format = SND_PCM_FORMAT_FLOAT_BE;
790 break;
791 default:
792 return CUBEB_ERROR_INVALID_FORMAT;
793 }
795 pthread_mutex_lock(&ctx->mutex);
796 if (ctx->active_streams >= CUBEB_STREAM_MAX) {
797 pthread_mutex_unlock(&ctx->mutex);
798 return CUBEB_ERROR;
799 }
800 ctx->active_streams += 1;
801 pthread_mutex_unlock(&ctx->mutex);
803 stm = calloc(1, sizeof(*stm));
804 assert(stm);
806 stm->context = ctx;
807 stm->data_callback = data_callback;
808 stm->state_callback = state_callback;
809 stm->user_ptr = user_ptr;
810 stm->params = stream_params;
811 stm->state = INACTIVE;
813 r = pthread_mutex_init(&stm->mutex, NULL);
814 assert(r == 0);
816 r = alsa_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
817 if (r < 0) {
818 alsa_stream_destroy(stm);
819 return CUBEB_ERROR;
820 }
822 r = snd_pcm_nonblock(stm->pcm, 1);
823 assert(r == 0);
825 /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
826 possibly work. See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
827 Only resort to this hack if the handle_underrun workaround failed. */
828 if (!ctx->local_config && ctx->is_pa) {
829 latency = latency < 500 ? 500 : latency;
830 }
832 r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
833 stm->params.channels, stm->params.rate, 1,
834 latency * 1000);
835 if (r < 0) {
836 alsa_stream_destroy(stm);
837 return CUBEB_ERROR_INVALID_FORMAT;
838 }
840 r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
841 assert(r == 0);
843 stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
844 assert(stm->nfds > 0);
846 stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
847 assert(stm->saved_fds);
848 r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
849 assert((nfds_t) r == stm->nfds);
851 r = pthread_cond_init(&stm->cond, NULL);
852 assert(r == 0);
854 if (alsa_register_stream(ctx, stm) != 0) {
855 alsa_stream_destroy(stm);
856 return CUBEB_ERROR;
857 }
859 *stream = stm;
861 return CUBEB_OK;
862 }
864 static void
865 alsa_stream_destroy(cubeb_stream * stm)
866 {
867 int r;
868 cubeb * ctx;
870 assert(stm && (stm->state == INACTIVE || stm->state == ERROR));
872 ctx = stm->context;
874 pthread_mutex_lock(&stm->mutex);
875 if (stm->pcm) {
876 alsa_locked_pcm_close(stm->pcm);
877 stm->pcm = NULL;
878 }
879 free(stm->saved_fds);
880 pthread_mutex_unlock(&stm->mutex);
881 pthread_mutex_destroy(&stm->mutex);
883 r = pthread_cond_destroy(&stm->cond);
884 assert(r == 0);
886 alsa_unregister_stream(stm);
888 pthread_mutex_lock(&ctx->mutex);
889 assert(ctx->active_streams >= 1);
890 ctx->active_streams -= 1;
891 pthread_mutex_unlock(&ctx->mutex);
893 free(stm);
894 }
896 static int
897 alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
898 {
899 int rv;
900 cubeb_stream * stm;
901 snd_pcm_hw_params_t* hw_params;
902 cubeb_stream_params params;
903 params.rate = 44100;
904 params.format = CUBEB_SAMPLE_FLOAT32NE;
905 params.channels = 2;
907 snd_pcm_hw_params_alloca(&hw_params);
909 assert(ctx);
911 rv = alsa_stream_init(ctx, &stm, "", params, 100, NULL, NULL, NULL);
912 if (rv != CUBEB_OK) {
913 return CUBEB_ERROR;
914 }
916 rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
917 if (rv < 0) {
918 return CUBEB_ERROR;
919 }
921 rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
922 if (rv < 0) {
923 return CUBEB_ERROR;
924 }
926 alsa_stream_destroy(stm);
928 return CUBEB_OK;
929 }
931 static int
932 alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
933 int rv, dir;
934 snd_pcm_t * pcm;
935 snd_pcm_hw_params_t * hw_params;
937 snd_pcm_hw_params_alloca(&hw_params);
939 /* get a pcm, disabling resampling, so we get a rate the
940 * hardware/dmix/pulse/etc. supports. */
941 rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
942 if (rv < 0) {
943 return CUBEB_ERROR;
944 }
946 rv = snd_pcm_hw_params_any(pcm, hw_params);
947 if (rv < 0) {
948 snd_pcm_close(pcm);
949 return CUBEB_ERROR;
950 }
952 rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir);
953 if (rv >= 0) {
954 /* There is a default rate: use it. */
955 snd_pcm_close(pcm);
956 return CUBEB_OK;
957 }
959 /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
960 *rate = 44100;
962 rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL);
963 if (rv < 0) {
964 snd_pcm_close(pcm);
965 return CUBEB_ERROR;
966 }
968 snd_pcm_close(pcm);
970 return CUBEB_OK;
971 }
973 static int
974 alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
975 {
976 /* This is found to be an acceptable minimum, even on a super low-end
977 * machine. */
978 *latency_ms = 40;
980 return CUBEB_OK;
981 }
983 static int
984 alsa_stream_start(cubeb_stream * stm)
985 {
986 cubeb * ctx;
988 assert(stm);
989 ctx = stm->context;
991 pthread_mutex_lock(&stm->mutex);
992 snd_pcm_pause(stm->pcm, 0);
993 gettimeofday(&stm->last_activity, NULL);
994 pthread_mutex_unlock(&stm->mutex);
996 pthread_mutex_lock(&ctx->mutex);
997 if (stm->state != INACTIVE) {
998 pthread_mutex_unlock(&ctx->mutex);
999 return CUBEB_ERROR;
1000 }
1001 alsa_set_stream_state(stm, RUNNING);
1002 pthread_mutex_unlock(&ctx->mutex);
1004 return CUBEB_OK;
1005 }
1007 static int
1008 alsa_stream_stop(cubeb_stream * stm)
1009 {
1010 cubeb * ctx;
1011 int r;
1013 assert(stm);
1014 ctx = stm->context;
1016 pthread_mutex_lock(&ctx->mutex);
1017 while (stm->state == PROCESSING) {
1018 r = pthread_cond_wait(&stm->cond, &ctx->mutex);
1019 assert(r == 0);
1020 }
1022 alsa_set_stream_state(stm, INACTIVE);
1023 pthread_mutex_unlock(&ctx->mutex);
1025 pthread_mutex_lock(&stm->mutex);
1026 snd_pcm_pause(stm->pcm, 1);
1027 pthread_mutex_unlock(&stm->mutex);
1029 return CUBEB_OK;
1030 }
1032 static int
1033 alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
1034 {
1035 snd_pcm_sframes_t delay;
1037 assert(stm && position);
1039 pthread_mutex_lock(&stm->mutex);
1041 delay = -1;
1042 if (snd_pcm_state(stm->pcm) != SND_PCM_STATE_RUNNING ||
1043 snd_pcm_delay(stm->pcm, &delay) != 0) {
1044 *position = stm->last_position;
1045 pthread_mutex_unlock(&stm->mutex);
1046 return CUBEB_OK;
1047 }
1049 assert(delay >= 0);
1051 *position = 0;
1052 if (stm->write_position >= (snd_pcm_uframes_t) delay) {
1053 *position = stm->write_position - delay;
1054 }
1056 stm->last_position = *position;
1058 pthread_mutex_unlock(&stm->mutex);
1059 return CUBEB_OK;
1060 }
1062 int
1063 alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
1064 {
1065 snd_pcm_sframes_t delay;
1066 /* This function returns the delay in frames until a frame written using
1067 snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
1068 if (snd_pcm_delay(stm->pcm, &delay)) {
1069 return CUBEB_ERROR;
1070 }
1072 *latency = delay;
1074 return CUBEB_OK;
1075 }
1077 static struct cubeb_ops const alsa_ops = {
1078 .init = alsa_init,
1079 .get_backend_id = alsa_get_backend_id,
1080 .get_max_channel_count = alsa_get_max_channel_count,
1081 .get_min_latency = alsa_get_min_latency,
1082 .get_preferred_sample_rate = alsa_get_preferred_sample_rate,
1083 .destroy = alsa_destroy,
1084 .stream_init = alsa_stream_init,
1085 .stream_destroy = alsa_stream_destroy,
1086 .stream_start = alsa_stream_start,
1087 .stream_stop = alsa_stream_stop,
1088 .stream_get_position = alsa_stream_get_position,
1089 .stream_get_latency = alsa_stream_get_latency
1090 };