media/libcubeb/src/cubeb_alsa.c

branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
equal deleted inserted replaced
-1:000000000000 0:bda1bfe7a6a9
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"
19
20 #define CUBEB_STREAM_MAX 16
21 #define CUBEB_WATCHDOG_MS 10000
22
23 #define CUBEB_ALSA_PCM_NAME "default"
24
25 #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"
26
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;
33
34 static struct cubeb_ops const alsa_ops;
35
36 struct cubeb {
37 struct cubeb_ops const * ops;
38
39 pthread_t thread;
40
41 /* Mutex for streams array, must not be held while blocked in poll(2). */
42 pthread_mutex_t mutex;
43
44 /* Sparse array of streams managed by this context. */
45 cubeb_stream * streams[CUBEB_STREAM_MAX];
46
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;
51
52 int shutdown;
53
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;
57
58 /* Track number of active streams. This is limited to CUBEB_STREAM_MAX
59 due to resource contraints. */
60 unsigned int active_streams;
61
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 };
68
69 enum stream_state {
70 INACTIVE,
71 RUNNING,
72 DRAINING,
73 PROCESSING,
74 ERROR
75 };
76
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;
89
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. */
94
95 enum stream_state state;
96
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;
100
101 struct timeval drain_timeout;
102
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 };
110
111 static int
112 any_revents(struct pollfd * fds, nfds_t nfds)
113 {
114 nfds_t i;
115
116 for (i = 0; i < nfds; ++i) {
117 if (fds[i].revents) {
118 return 1;
119 }
120 }
121
122 return 0;
123 }
124
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 }
136
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;
144
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;
155
156 if (t > INT_MAX) {
157 t = INT_MAX;
158 } else if (t < INT_MIN) {
159 t = INT_MIN;
160 }
161
162 return r >= 0 ? t : -t;
163 }
164
165 static int
166 ms_until(struct timeval * tv)
167 {
168 return timeval_to_relative_ms(tv);
169 }
170
171 static int
172 ms_since(struct timeval * tv)
173 {
174 return -timeval_to_relative_ms(tv);
175 }
176
177 static void
178 rebuild(cubeb * ctx)
179 {
180 nfds_t nfds;
181 int i;
182 nfds_t j;
183 cubeb_stream * stm;
184
185 assert(ctx->rebuild);
186
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 }
198
199 free(ctx->fds);
200 ctx->fds = calloc(nfds, sizeof(struct pollfd));
201 assert(ctx->fds);
202 ctx->nfds = nfds;
203
204 /* Include context's control pipe fd. */
205 ctx->fds[0].fd = ctx->control_fd_read;
206 ctx->fds[0].events = POLLIN | POLLERR;
207
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 }
216
217 ctx->rebuild = 0;
218 }
219
220 static void
221 poll_wake(cubeb * ctx)
222 {
223 write(ctx->control_fd_write, "x", 1);
224 }
225
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 }
233
234 static void
235 alsa_set_stream_state(cubeb_stream * stm, enum stream_state state)
236 {
237 cubeb * ctx;
238 int r;
239
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 }
247
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;
257
258 draining = 0;
259
260 pthread_mutex_lock(&stm->mutex);
261
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 }
270
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 }
276
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 }
283
284 /* This should never happen. */
285 if ((unsigned int) avail > stm->buffer_size) {
286 avail = stm->buffer_size;
287 }
288
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 }
301
302 p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
303 assert(p);
304
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;
326
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);
330
331 set_timeout(&stm->drain_timeout, buffer_time * 1000);
332
333 draining = 1;
334 }
335
336 free(p);
337 pthread_mutex_unlock(&stm->mutex);
338 return draining ? DRAINING : RUNNING;
339 }
340
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;
350
351 pthread_mutex_lock(&ctx->mutex);
352
353 if (ctx->rebuild) {
354 rebuild(ctx);
355 }
356
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 }
368
369 pthread_mutex_unlock(&ctx->mutex);
370 r = poll(ctx->fds, ctx->nfds, timeout);
371 pthread_mutex_lock(&ctx->mutex);
372
373 if (r > 0) {
374 if (ctx->fds[0].revents & POLLIN) {
375 read(ctx->control_fd_read, &dummy, 1);
376
377 if (ctx->shutdown) {
378 pthread_mutex_unlock(&ctx->mutex);
379 return -1;
380 }
381 }
382
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 }
407
408 pthread_mutex_unlock(&ctx->mutex);
409
410 return 0;
411 }
412
413 static void *
414 alsa_run_thread(void * context)
415 {
416 cubeb * ctx = context;
417 int r;
418
419 do {
420 r = alsa_run(ctx);
421 } while (r >= 0);
422
423 return NULL;
424 }
425
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];
435
436 slave_def = NULL;
437
438 r = snd_config_search(root_pcm, "slave", &slave_pcm);
439 if (r < 0) {
440 return NULL;
441 }
442
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 }
450
451 do {
452 r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm);
453 if (r < 0) {
454 break;
455 }
456
457 r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string);
458 if (r < 0) {
459 break;
460 }
461
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 }
470
471 return pcm;
472 } while (0);
473
474 if (slave_def) {
475 snd_config_delete(slave_def);
476 }
477
478 return NULL;
479 }
480
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];
495
496 lconf = NULL;
497
498 if (snd_config == NULL) {
499 return NULL;
500 }
501
502 r = snd_config_copy(&lconf, snd_config);
503 if (r < 0) {
504 return NULL;
505 }
506
507 do {
508 r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node);
509 if (r < 0) {
510 break;
511 }
512
513 r = snd_config_get_id(pcm_node, &string);
514 if (r < 0) {
515 break;
516 }
517
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 }
526
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 }
531
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 }
537
538 r = snd_config_get_string(node, &string);
539 if (r < 0) {
540 break;
541 }
542
543 if (strcmp(string, "pulse") != 0) {
544 break;
545 }
546
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 }
553
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 }
559
560 r = snd_config_add(pcm_node, node);
561 if (r < 0) {
562 break;
563 }
564
565 return lconf;
566 } while (0);
567
568 snd_config_delete(lconf);
569
570 return NULL;
571 }
572
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;
577
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);
585
586 return r;
587 }
588
589 static int
590 alsa_locked_pcm_close(snd_pcm_t * pcm)
591 {
592 int r;
593
594 pthread_mutex_lock(&cubeb_alsa_mutex);
595 r = snd_pcm_close(pcm);
596 pthread_mutex_unlock(&cubeb_alsa_mutex);
597
598 return r;
599 }
600
601 static int
602 alsa_register_stream(cubeb * ctx, cubeb_stream * stm)
603 {
604 int i;
605
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);
614
615 return i == CUBEB_STREAM_MAX;
616 }
617
618 static void
619 alsa_unregister_stream(cubeb_stream * stm)
620 {
621 cubeb * ctx;
622 int i;
623
624 ctx = stm->context;
625
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 }
635
636 static void
637 silent_error_handler(char const * file, int line, char const * function,
638 int err, char const * fmt, ...)
639 {
640 }
641
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;
651
652 assert(context);
653 *context = NULL;
654
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);
661
662 ctx = calloc(1, sizeof(*ctx));
663 assert(ctx);
664
665 ctx->ops = &alsa_ops;
666
667 r = pthread_mutex_init(&ctx->mutex, NULL);
668 assert(r == 0);
669
670 r = pipe(fd);
671 assert(r == 0);
672
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 }
677
678 ctx->control_fd_read = fd[0];
679 ctx->control_fd_write = fd[1];
680
681 /* Force an early rebuild when alsa_run is first called to ensure fds and
682 nfds have been initialized. */
683 ctx->rebuild = 1;
684
685 r = pthread_attr_init(&attr);
686 assert(r == 0);
687
688 r = pthread_attr_setstacksize(&attr, 256 * 1024);
689 assert(r == 0);
690
691 r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx);
692 assert(r == 0);
693
694 r = pthread_attr_destroy(&attr);
695 assert(r == 0);
696
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 }
721
722 *context = ctx;
723
724 return CUBEB_OK;
725 }
726
727 static char const *
728 alsa_get_backend_id(cubeb * ctx)
729 {
730 return "alsa";
731 }
732
733 static void
734 alsa_destroy(cubeb * ctx)
735 {
736 int r;
737
738 assert(ctx);
739
740 pthread_mutex_lock(&ctx->mutex);
741 ctx->shutdown = 1;
742 poll_wake(ctx);
743 pthread_mutex_unlock(&ctx->mutex);
744
745 r = pthread_join(ctx->thread, NULL);
746 assert(r == 0);
747
748 close(ctx->control_fd_read);
749 close(ctx->control_fd_write);
750 pthread_mutex_destroy(&ctx->mutex);
751 free(ctx->fds);
752
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 }
758
759 free(ctx);
760 }
761
762 static void alsa_stream_destroy(cubeb_stream * stm);
763
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;
773
774 assert(ctx && stream);
775
776 *stream = NULL;
777
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 }
794
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);
802
803 stm = calloc(1, sizeof(*stm));
804 assert(stm);
805
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;
812
813 r = pthread_mutex_init(&stm->mutex, NULL);
814 assert(r == 0);
815
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 }
821
822 r = snd_pcm_nonblock(stm->pcm, 1);
823 assert(r == 0);
824
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 }
831
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 }
839
840 r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
841 assert(r == 0);
842
843 stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
844 assert(stm->nfds > 0);
845
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);
850
851 r = pthread_cond_init(&stm->cond, NULL);
852 assert(r == 0);
853
854 if (alsa_register_stream(ctx, stm) != 0) {
855 alsa_stream_destroy(stm);
856 return CUBEB_ERROR;
857 }
858
859 *stream = stm;
860
861 return CUBEB_OK;
862 }
863
864 static void
865 alsa_stream_destroy(cubeb_stream * stm)
866 {
867 int r;
868 cubeb * ctx;
869
870 assert(stm && (stm->state == INACTIVE || stm->state == ERROR));
871
872 ctx = stm->context;
873
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);
882
883 r = pthread_cond_destroy(&stm->cond);
884 assert(r == 0);
885
886 alsa_unregister_stream(stm);
887
888 pthread_mutex_lock(&ctx->mutex);
889 assert(ctx->active_streams >= 1);
890 ctx->active_streams -= 1;
891 pthread_mutex_unlock(&ctx->mutex);
892
893 free(stm);
894 }
895
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;
906
907 snd_pcm_hw_params_alloca(&hw_params);
908
909 assert(ctx);
910
911 rv = alsa_stream_init(ctx, &stm, "", params, 100, NULL, NULL, NULL);
912 if (rv != CUBEB_OK) {
913 return CUBEB_ERROR;
914 }
915
916 rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
917 if (rv < 0) {
918 return CUBEB_ERROR;
919 }
920
921 rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
922 if (rv < 0) {
923 return CUBEB_ERROR;
924 }
925
926 alsa_stream_destroy(stm);
927
928 return CUBEB_OK;
929 }
930
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;
936
937 snd_pcm_hw_params_alloca(&hw_params);
938
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 }
945
946 rv = snd_pcm_hw_params_any(pcm, hw_params);
947 if (rv < 0) {
948 snd_pcm_close(pcm);
949 return CUBEB_ERROR;
950 }
951
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 }
958
959 /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
960 *rate = 44100;
961
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 }
967
968 snd_pcm_close(pcm);
969
970 return CUBEB_OK;
971 }
972
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;
979
980 return CUBEB_OK;
981 }
982
983 static int
984 alsa_stream_start(cubeb_stream * stm)
985 {
986 cubeb * ctx;
987
988 assert(stm);
989 ctx = stm->context;
990
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);
995
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);
1003
1004 return CUBEB_OK;
1005 }
1006
1007 static int
1008 alsa_stream_stop(cubeb_stream * stm)
1009 {
1010 cubeb * ctx;
1011 int r;
1012
1013 assert(stm);
1014 ctx = stm->context;
1015
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 }
1021
1022 alsa_set_stream_state(stm, INACTIVE);
1023 pthread_mutex_unlock(&ctx->mutex);
1024
1025 pthread_mutex_lock(&stm->mutex);
1026 snd_pcm_pause(stm->pcm, 1);
1027 pthread_mutex_unlock(&stm->mutex);
1028
1029 return CUBEB_OK;
1030 }
1031
1032 static int
1033 alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
1034 {
1035 snd_pcm_sframes_t delay;
1036
1037 assert(stm && position);
1038
1039 pthread_mutex_lock(&stm->mutex);
1040
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 }
1048
1049 assert(delay >= 0);
1050
1051 *position = 0;
1052 if (stm->write_position >= (snd_pcm_uframes_t) delay) {
1053 *position = stm->write_position - delay;
1054 }
1055
1056 stm->last_position = *position;
1057
1058 pthread_mutex_unlock(&stm->mutex);
1059 return CUBEB_OK;
1060 }
1061
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 }
1071
1072 *latency = delay;
1073
1074 return CUBEB_OK;
1075 }
1076
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 };

mercurial