|
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 }; |