1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/libcubeb/src/cubeb_alsa.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1090 @@ 1.4 +/* 1.5 + * Copyright © 2011 Mozilla Foundation 1.6 + * 1.7 + * This program is made available under an ISC-style license. See the 1.8 + * accompanying file LICENSE for details. 1.9 + */ 1.10 +#undef NDEBUG 1.11 +#define _BSD_SOURCE 1.12 +#define _XOPEN_SOURCE 500 1.13 +#include <pthread.h> 1.14 +#include <sys/time.h> 1.15 +#include <assert.h> 1.16 +#include <limits.h> 1.17 +#include <poll.h> 1.18 +#include <unistd.h> 1.19 +#include <alsa/asoundlib.h> 1.20 +#include "cubeb/cubeb.h" 1.21 +#include "cubeb-internal.h" 1.22 + 1.23 +#define CUBEB_STREAM_MAX 16 1.24 +#define CUBEB_WATCHDOG_MS 10000 1.25 + 1.26 +#define CUBEB_ALSA_PCM_NAME "default" 1.27 + 1.28 +#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin" 1.29 + 1.30 +/* ALSA is not thread-safe. snd_pcm_t instances are individually protected 1.31 + by the owning cubeb_stream's mutex. snd_pcm_t creation and destruction 1.32 + is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1), 1.33 + so those calls must be wrapped in the following mutex. */ 1.34 +static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER; 1.35 +static int cubeb_alsa_error_handler_set = 0; 1.36 + 1.37 +static struct cubeb_ops const alsa_ops; 1.38 + 1.39 +struct cubeb { 1.40 + struct cubeb_ops const * ops; 1.41 + 1.42 + pthread_t thread; 1.43 + 1.44 + /* Mutex for streams array, must not be held while blocked in poll(2). */ 1.45 + pthread_mutex_t mutex; 1.46 + 1.47 + /* Sparse array of streams managed by this context. */ 1.48 + cubeb_stream * streams[CUBEB_STREAM_MAX]; 1.49 + 1.50 + /* fds and nfds are only updated by alsa_run when rebuild is set. */ 1.51 + struct pollfd * fds; 1.52 + nfds_t nfds; 1.53 + int rebuild; 1.54 + 1.55 + int shutdown; 1.56 + 1.57 + /* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */ 1.58 + int control_fd_read; 1.59 + int control_fd_write; 1.60 + 1.61 + /* Track number of active streams. This is limited to CUBEB_STREAM_MAX 1.62 + due to resource contraints. */ 1.63 + unsigned int active_streams; 1.64 + 1.65 + /* Local configuration with handle_underrun workaround set for PulseAudio 1.66 + ALSA plugin. Will be NULL if the PA ALSA plugin is not in use or the 1.67 + workaround is not required. */ 1.68 + snd_config_t * local_config; 1.69 + int is_pa; 1.70 +}; 1.71 + 1.72 +enum stream_state { 1.73 + INACTIVE, 1.74 + RUNNING, 1.75 + DRAINING, 1.76 + PROCESSING, 1.77 + ERROR 1.78 +}; 1.79 + 1.80 +struct cubeb_stream { 1.81 + cubeb * context; 1.82 + pthread_mutex_t mutex; 1.83 + snd_pcm_t * pcm; 1.84 + cubeb_data_callback data_callback; 1.85 + cubeb_state_callback state_callback; 1.86 + void * user_ptr; 1.87 + snd_pcm_uframes_t write_position; 1.88 + snd_pcm_uframes_t last_position; 1.89 + snd_pcm_uframes_t buffer_size; 1.90 + snd_pcm_uframes_t period_size; 1.91 + cubeb_stream_params params; 1.92 + 1.93 + /* Every member after this comment is protected by the owning context's 1.94 + mutex rather than the stream's mutex, or is only used on the context's 1.95 + run thread. */ 1.96 + pthread_cond_t cond; /* Signaled when the stream's state is changed. */ 1.97 + 1.98 + enum stream_state state; 1.99 + 1.100 + struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */ 1.101 + struct pollfd * fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */ 1.102 + nfds_t nfds; 1.103 + 1.104 + struct timeval drain_timeout; 1.105 + 1.106 + /* XXX: Horrible hack -- if an active stream has been idle for 1.107 + CUBEB_WATCHDOG_MS it will be disabled and the error callback will be 1.108 + called. This works around a bug seen with older versions of ALSA and 1.109 + PulseAudio where streams would stop requesting new data despite still 1.110 + being logically active and playing. */ 1.111 + struct timeval last_activity; 1.112 +}; 1.113 + 1.114 +static int 1.115 +any_revents(struct pollfd * fds, nfds_t nfds) 1.116 +{ 1.117 + nfds_t i; 1.118 + 1.119 + for (i = 0; i < nfds; ++i) { 1.120 + if (fds[i].revents) { 1.121 + return 1; 1.122 + } 1.123 + } 1.124 + 1.125 + return 0; 1.126 +} 1.127 + 1.128 +static int 1.129 +cmp_timeval(struct timeval * a, struct timeval * b) 1.130 +{ 1.131 + if (a->tv_sec == b->tv_sec) { 1.132 + if (a->tv_usec == b->tv_usec) { 1.133 + return 0; 1.134 + } 1.135 + return a->tv_usec > b->tv_usec ? 1 : -1; 1.136 + } 1.137 + return a->tv_sec > b->tv_sec ? 1 : -1; 1.138 +} 1.139 + 1.140 +static int 1.141 +timeval_to_relative_ms(struct timeval * tv) 1.142 +{ 1.143 + struct timeval now; 1.144 + struct timeval dt; 1.145 + long long t; 1.146 + int r; 1.147 + 1.148 + gettimeofday(&now, NULL); 1.149 + r = cmp_timeval(tv, &now); 1.150 + if (r >= 0) { 1.151 + timersub(tv, &now, &dt); 1.152 + } else { 1.153 + timersub(&now, tv, &dt); 1.154 + } 1.155 + t = dt.tv_sec; 1.156 + t *= 1000; 1.157 + t += (dt.tv_usec + 500) / 1000; 1.158 + 1.159 + if (t > INT_MAX) { 1.160 + t = INT_MAX; 1.161 + } else if (t < INT_MIN) { 1.162 + t = INT_MIN; 1.163 + } 1.164 + 1.165 + return r >= 0 ? t : -t; 1.166 +} 1.167 + 1.168 +static int 1.169 +ms_until(struct timeval * tv) 1.170 +{ 1.171 + return timeval_to_relative_ms(tv); 1.172 +} 1.173 + 1.174 +static int 1.175 +ms_since(struct timeval * tv) 1.176 +{ 1.177 + return -timeval_to_relative_ms(tv); 1.178 +} 1.179 + 1.180 +static void 1.181 +rebuild(cubeb * ctx) 1.182 +{ 1.183 + nfds_t nfds; 1.184 + int i; 1.185 + nfds_t j; 1.186 + cubeb_stream * stm; 1.187 + 1.188 + assert(ctx->rebuild); 1.189 + 1.190 + /* Always count context's control pipe fd. */ 1.191 + nfds = 1; 1.192 + for (i = 0; i < CUBEB_STREAM_MAX; ++i) { 1.193 + stm = ctx->streams[i]; 1.194 + if (stm) { 1.195 + stm->fds = NULL; 1.196 + if (stm->state == RUNNING) { 1.197 + nfds += stm->nfds; 1.198 + } 1.199 + } 1.200 + } 1.201 + 1.202 + free(ctx->fds); 1.203 + ctx->fds = calloc(nfds, sizeof(struct pollfd)); 1.204 + assert(ctx->fds); 1.205 + ctx->nfds = nfds; 1.206 + 1.207 + /* Include context's control pipe fd. */ 1.208 + ctx->fds[0].fd = ctx->control_fd_read; 1.209 + ctx->fds[0].events = POLLIN | POLLERR; 1.210 + 1.211 + for (i = 0, j = 1; i < CUBEB_STREAM_MAX; ++i) { 1.212 + stm = ctx->streams[i]; 1.213 + if (stm && stm->state == RUNNING) { 1.214 + memcpy(&ctx->fds[j], stm->saved_fds, stm->nfds * sizeof(struct pollfd)); 1.215 + stm->fds = &ctx->fds[j]; 1.216 + j += stm->nfds; 1.217 + } 1.218 + } 1.219 + 1.220 + ctx->rebuild = 0; 1.221 +} 1.222 + 1.223 +static void 1.224 +poll_wake(cubeb * ctx) 1.225 +{ 1.226 + write(ctx->control_fd_write, "x", 1); 1.227 +} 1.228 + 1.229 +static void 1.230 +set_timeout(struct timeval * timeout, unsigned int ms) 1.231 +{ 1.232 + gettimeofday(timeout, NULL); 1.233 + timeout->tv_sec += ms / 1000; 1.234 + timeout->tv_usec += (ms % 1000) * 1000; 1.235 +} 1.236 + 1.237 +static void 1.238 +alsa_set_stream_state(cubeb_stream * stm, enum stream_state state) 1.239 +{ 1.240 + cubeb * ctx; 1.241 + int r; 1.242 + 1.243 + ctx = stm->context; 1.244 + stm->state = state; 1.245 + r = pthread_cond_broadcast(&stm->cond); 1.246 + assert(r == 0); 1.247 + ctx->rebuild = 1; 1.248 + poll_wake(ctx); 1.249 +} 1.250 + 1.251 +static enum stream_state 1.252 +alsa_refill_stream(cubeb_stream * stm) 1.253 +{ 1.254 + int r; 1.255 + unsigned short revents; 1.256 + snd_pcm_sframes_t avail; 1.257 + long got; 1.258 + void * p; 1.259 + int draining; 1.260 + 1.261 + draining = 0; 1.262 + 1.263 + pthread_mutex_lock(&stm->mutex); 1.264 + 1.265 + r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents); 1.266 + if (r < 0 || revents != POLLOUT) { 1.267 + /* This should be a stream error; it makes no sense for poll(2) to wake 1.268 + for this stream and then have the stream report that it's not ready. 1.269 + Unfortunately, this does happen, so just bail out and try again. */ 1.270 + pthread_mutex_unlock(&stm->mutex); 1.271 + return RUNNING; 1.272 + } 1.273 + 1.274 + avail = snd_pcm_avail_update(stm->pcm); 1.275 + if (avail == -EPIPE) { 1.276 + snd_pcm_recover(stm->pcm, avail, 1); 1.277 + avail = snd_pcm_avail_update(stm->pcm); 1.278 + } 1.279 + 1.280 + /* Failed to recover from an xrun, this stream must be broken. */ 1.281 + if (avail < 0) { 1.282 + pthread_mutex_unlock(&stm->mutex); 1.283 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 1.284 + return ERROR; 1.285 + } 1.286 + 1.287 + /* This should never happen. */ 1.288 + if ((unsigned int) avail > stm->buffer_size) { 1.289 + avail = stm->buffer_size; 1.290 + } 1.291 + 1.292 + /* poll(2) claims this stream is active, so there should be some space 1.293 + available to write. If avail is still zero here, the stream must be in 1.294 + a funky state, so recover and try again. */ 1.295 + if (avail == 0) { 1.296 + snd_pcm_recover(stm->pcm, -EPIPE, 1); 1.297 + avail = snd_pcm_avail_update(stm->pcm); 1.298 + if (avail <= 0) { 1.299 + pthread_mutex_unlock(&stm->mutex); 1.300 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 1.301 + return ERROR; 1.302 + } 1.303 + } 1.304 + 1.305 + p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail)); 1.306 + assert(p); 1.307 + 1.308 + pthread_mutex_unlock(&stm->mutex); 1.309 + got = stm->data_callback(stm, stm->user_ptr, p, avail); 1.310 + pthread_mutex_lock(&stm->mutex); 1.311 + if (got < 0) { 1.312 + pthread_mutex_unlock(&stm->mutex); 1.313 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 1.314 + return ERROR; 1.315 + } 1.316 + if (got > 0) { 1.317 + snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got); 1.318 + if (wrote == -EPIPE) { 1.319 + snd_pcm_recover(stm->pcm, wrote, 1); 1.320 + wrote = snd_pcm_writei(stm->pcm, p, got); 1.321 + } 1.322 + assert(wrote >= 0 && wrote == got); 1.323 + stm->write_position += wrote; 1.324 + gettimeofday(&stm->last_activity, NULL); 1.325 + } 1.326 + if (got != avail) { 1.327 + long buffer_fill = stm->buffer_size - (avail - got); 1.328 + double buffer_time = (double) buffer_fill / stm->params.rate; 1.329 + 1.330 + /* Fill the remaining buffer with silence to guarantee one full period 1.331 + has been written. */ 1.332 + snd_pcm_writei(stm->pcm, (char *) p + got, avail - got); 1.333 + 1.334 + set_timeout(&stm->drain_timeout, buffer_time * 1000); 1.335 + 1.336 + draining = 1; 1.337 + } 1.338 + 1.339 + free(p); 1.340 + pthread_mutex_unlock(&stm->mutex); 1.341 + return draining ? DRAINING : RUNNING; 1.342 +} 1.343 + 1.344 +static int 1.345 +alsa_run(cubeb * ctx) 1.346 +{ 1.347 + int r; 1.348 + int timeout; 1.349 + int i; 1.350 + char dummy; 1.351 + cubeb_stream * stm; 1.352 + enum stream_state state; 1.353 + 1.354 + pthread_mutex_lock(&ctx->mutex); 1.355 + 1.356 + if (ctx->rebuild) { 1.357 + rebuild(ctx); 1.358 + } 1.359 + 1.360 + /* Wake up at least once per second for the watchdog. */ 1.361 + timeout = 1000; 1.362 + for (i = 0; i < CUBEB_STREAM_MAX; ++i) { 1.363 + stm = ctx->streams[i]; 1.364 + if (stm && stm->state == DRAINING) { 1.365 + r = ms_until(&stm->drain_timeout); 1.366 + if (r >= 0 && timeout > r) { 1.367 + timeout = r; 1.368 + } 1.369 + } 1.370 + } 1.371 + 1.372 + pthread_mutex_unlock(&ctx->mutex); 1.373 + r = poll(ctx->fds, ctx->nfds, timeout); 1.374 + pthread_mutex_lock(&ctx->mutex); 1.375 + 1.376 + if (r > 0) { 1.377 + if (ctx->fds[0].revents & POLLIN) { 1.378 + read(ctx->control_fd_read, &dummy, 1); 1.379 + 1.380 + if (ctx->shutdown) { 1.381 + pthread_mutex_unlock(&ctx->mutex); 1.382 + return -1; 1.383 + } 1.384 + } 1.385 + 1.386 + for (i = 0; i < CUBEB_STREAM_MAX; ++i) { 1.387 + stm = ctx->streams[i]; 1.388 + if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) { 1.389 + alsa_set_stream_state(stm, PROCESSING); 1.390 + pthread_mutex_unlock(&ctx->mutex); 1.391 + state = alsa_refill_stream(stm); 1.392 + pthread_mutex_lock(&ctx->mutex); 1.393 + alsa_set_stream_state(stm, state); 1.394 + } 1.395 + } 1.396 + } else if (r == 0) { 1.397 + for (i = 0; i < CUBEB_STREAM_MAX; ++i) { 1.398 + stm = ctx->streams[i]; 1.399 + if (stm) { 1.400 + if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) { 1.401 + alsa_set_stream_state(stm, INACTIVE); 1.402 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); 1.403 + } else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) { 1.404 + alsa_set_stream_state(stm, ERROR); 1.405 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 1.406 + } 1.407 + } 1.408 + } 1.409 + } 1.410 + 1.411 + pthread_mutex_unlock(&ctx->mutex); 1.412 + 1.413 + return 0; 1.414 +} 1.415 + 1.416 +static void * 1.417 +alsa_run_thread(void * context) 1.418 +{ 1.419 + cubeb * ctx = context; 1.420 + int r; 1.421 + 1.422 + do { 1.423 + r = alsa_run(ctx); 1.424 + } while (r >= 0); 1.425 + 1.426 + return NULL; 1.427 +} 1.428 + 1.429 +static snd_config_t * 1.430 +get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) 1.431 +{ 1.432 + int r; 1.433 + snd_config_t * slave_pcm; 1.434 + snd_config_t * slave_def; 1.435 + snd_config_t * pcm; 1.436 + char const * string; 1.437 + char node_name[64]; 1.438 + 1.439 + slave_def = NULL; 1.440 + 1.441 + r = snd_config_search(root_pcm, "slave", &slave_pcm); 1.442 + if (r < 0) { 1.443 + return NULL; 1.444 + } 1.445 + 1.446 + r = snd_config_get_string(slave_pcm, &string); 1.447 + if (r >= 0) { 1.448 + r = snd_config_search_definition(lconf, "pcm_slave", string, &slave_def); 1.449 + if (r < 0) { 1.450 + return NULL; 1.451 + } 1.452 + } 1.453 + 1.454 + do { 1.455 + r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm); 1.456 + if (r < 0) { 1.457 + break; 1.458 + } 1.459 + 1.460 + r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string); 1.461 + if (r < 0) { 1.462 + break; 1.463 + } 1.464 + 1.465 + r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); 1.466 + if (r < 0 || r > (int) sizeof(node_name)) { 1.467 + break; 1.468 + } 1.469 + r = snd_config_search(lconf, node_name, &pcm); 1.470 + if (r < 0) { 1.471 + break; 1.472 + } 1.473 + 1.474 + return pcm; 1.475 + } while (0); 1.476 + 1.477 + if (slave_def) { 1.478 + snd_config_delete(slave_def); 1.479 + } 1.480 + 1.481 + return NULL; 1.482 +} 1.483 + 1.484 +/* Work around PulseAudio ALSA plugin bug where the PA server forces a 1.485 + higher than requested latency, but the plugin does not update its (and 1.486 + ALSA's) internal state to reflect that, leading to an immediate underrun 1.487 + situation. Inspired by WINE's make_handle_underrun_config. 1.488 + Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 */ 1.489 +static snd_config_t * 1.490 +init_local_config_with_workaround(char const * pcm_name) 1.491 +{ 1.492 + int r; 1.493 + snd_config_t * lconf; 1.494 + snd_config_t * pcm_node; 1.495 + snd_config_t * node; 1.496 + char const * string; 1.497 + char node_name[64]; 1.498 + 1.499 + lconf = NULL; 1.500 + 1.501 + if (snd_config == NULL) { 1.502 + return NULL; 1.503 + } 1.504 + 1.505 + r = snd_config_copy(&lconf, snd_config); 1.506 + if (r < 0) { 1.507 + return NULL; 1.508 + } 1.509 + 1.510 + do { 1.511 + r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node); 1.512 + if (r < 0) { 1.513 + break; 1.514 + } 1.515 + 1.516 + r = snd_config_get_id(pcm_node, &string); 1.517 + if (r < 0) { 1.518 + break; 1.519 + } 1.520 + 1.521 + r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); 1.522 + if (r < 0 || r > (int) sizeof(node_name)) { 1.523 + break; 1.524 + } 1.525 + r = snd_config_search(lconf, node_name, &pcm_node); 1.526 + if (r < 0) { 1.527 + break; 1.528 + } 1.529 + 1.530 + /* If this PCM has a slave, walk the slave configurations until we reach the bottom. */ 1.531 + while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) { 1.532 + pcm_node = node; 1.533 + } 1.534 + 1.535 + /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */ 1.536 + r = snd_config_search(pcm_node, "type", &node); 1.537 + if (r < 0) { 1.538 + break; 1.539 + } 1.540 + 1.541 + r = snd_config_get_string(node, &string); 1.542 + if (r < 0) { 1.543 + break; 1.544 + } 1.545 + 1.546 + if (strcmp(string, "pulse") != 0) { 1.547 + break; 1.548 + } 1.549 + 1.550 + /* Don't clobber an explicit existing handle_underrun value, set it only 1.551 + if it doesn't already exist. */ 1.552 + r = snd_config_search(pcm_node, "handle_underrun", &node); 1.553 + if (r != -ENOENT) { 1.554 + break; 1.555 + } 1.556 + 1.557 + /* Disable pcm_pulse's asynchronous underrun handling. */ 1.558 + r = snd_config_imake_integer(&node, "handle_underrun", 0); 1.559 + if (r < 0) { 1.560 + break; 1.561 + } 1.562 + 1.563 + r = snd_config_add(pcm_node, node); 1.564 + if (r < 0) { 1.565 + break; 1.566 + } 1.567 + 1.568 + return lconf; 1.569 + } while (0); 1.570 + 1.571 + snd_config_delete(lconf); 1.572 + 1.573 + return NULL; 1.574 +} 1.575 + 1.576 +static int 1.577 +alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * local_config) 1.578 +{ 1.579 + int r; 1.580 + 1.581 + pthread_mutex_lock(&cubeb_alsa_mutex); 1.582 + if (local_config) { 1.583 + r = snd_pcm_open_lconf(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK, local_config); 1.584 + } else { 1.585 + r = snd_pcm_open(pcm, CUBEB_ALSA_PCM_NAME, stream, SND_PCM_NONBLOCK); 1.586 + } 1.587 + pthread_mutex_unlock(&cubeb_alsa_mutex); 1.588 + 1.589 + return r; 1.590 +} 1.591 + 1.592 +static int 1.593 +alsa_locked_pcm_close(snd_pcm_t * pcm) 1.594 +{ 1.595 + int r; 1.596 + 1.597 + pthread_mutex_lock(&cubeb_alsa_mutex); 1.598 + r = snd_pcm_close(pcm); 1.599 + pthread_mutex_unlock(&cubeb_alsa_mutex); 1.600 + 1.601 + return r; 1.602 +} 1.603 + 1.604 +static int 1.605 +alsa_register_stream(cubeb * ctx, cubeb_stream * stm) 1.606 +{ 1.607 + int i; 1.608 + 1.609 + pthread_mutex_lock(&ctx->mutex); 1.610 + for (i = 0; i < CUBEB_STREAM_MAX; ++i) { 1.611 + if (!ctx->streams[i]) { 1.612 + ctx->streams[i] = stm; 1.613 + break; 1.614 + } 1.615 + } 1.616 + pthread_mutex_unlock(&ctx->mutex); 1.617 + 1.618 + return i == CUBEB_STREAM_MAX; 1.619 +} 1.620 + 1.621 +static void 1.622 +alsa_unregister_stream(cubeb_stream * stm) 1.623 +{ 1.624 + cubeb * ctx; 1.625 + int i; 1.626 + 1.627 + ctx = stm->context; 1.628 + 1.629 + pthread_mutex_lock(&ctx->mutex); 1.630 + for (i = 0; i < CUBEB_STREAM_MAX; ++i) { 1.631 + if (ctx->streams[i] == stm) { 1.632 + ctx->streams[i] = NULL; 1.633 + break; 1.634 + } 1.635 + } 1.636 + pthread_mutex_unlock(&ctx->mutex); 1.637 +} 1.638 + 1.639 +static void 1.640 +silent_error_handler(char const * file, int line, char const * function, 1.641 + int err, char const * fmt, ...) 1.642 +{ 1.643 +} 1.644 + 1.645 +/*static*/ int 1.646 +alsa_init(cubeb ** context, char const * context_name) 1.647 +{ 1.648 + cubeb * ctx; 1.649 + int r; 1.650 + int i; 1.651 + int fd[2]; 1.652 + pthread_attr_t attr; 1.653 + snd_pcm_t * dummy; 1.654 + 1.655 + assert(context); 1.656 + *context = NULL; 1.657 + 1.658 + pthread_mutex_lock(&cubeb_alsa_mutex); 1.659 + if (!cubeb_alsa_error_handler_set) { 1.660 + snd_lib_error_set_handler(silent_error_handler); 1.661 + cubeb_alsa_error_handler_set = 1; 1.662 + } 1.663 + pthread_mutex_unlock(&cubeb_alsa_mutex); 1.664 + 1.665 + ctx = calloc(1, sizeof(*ctx)); 1.666 + assert(ctx); 1.667 + 1.668 + ctx->ops = &alsa_ops; 1.669 + 1.670 + r = pthread_mutex_init(&ctx->mutex, NULL); 1.671 + assert(r == 0); 1.672 + 1.673 + r = pipe(fd); 1.674 + assert(r == 0); 1.675 + 1.676 + for (i = 0; i < 2; ++i) { 1.677 + fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC); 1.678 + fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK); 1.679 + } 1.680 + 1.681 + ctx->control_fd_read = fd[0]; 1.682 + ctx->control_fd_write = fd[1]; 1.683 + 1.684 + /* Force an early rebuild when alsa_run is first called to ensure fds and 1.685 + nfds have been initialized. */ 1.686 + ctx->rebuild = 1; 1.687 + 1.688 + r = pthread_attr_init(&attr); 1.689 + assert(r == 0); 1.690 + 1.691 + r = pthread_attr_setstacksize(&attr, 256 * 1024); 1.692 + assert(r == 0); 1.693 + 1.694 + r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx); 1.695 + assert(r == 0); 1.696 + 1.697 + r = pthread_attr_destroy(&attr); 1.698 + assert(r == 0); 1.699 + 1.700 + /* Open a dummy PCM to force the configuration space to be evaluated so that 1.701 + init_local_config_with_workaround can find and modify the default node. */ 1.702 + r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, NULL); 1.703 + if (r >= 0) { 1.704 + alsa_locked_pcm_close(dummy); 1.705 + } 1.706 + ctx->is_pa = 0; 1.707 + pthread_mutex_lock(&cubeb_alsa_mutex); 1.708 + ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_NAME); 1.709 + pthread_mutex_unlock(&cubeb_alsa_mutex); 1.710 + if (ctx->local_config) { 1.711 + ctx->is_pa = 1; 1.712 + r = alsa_locked_pcm_open(&dummy, SND_PCM_STREAM_PLAYBACK, ctx->local_config); 1.713 + /* If we got a local_config, we found a PA PCM. If opening a PCM with that 1.714 + config fails with EINVAL, the PA PCM is too old for this workaround. */ 1.715 + if (r == -EINVAL) { 1.716 + pthread_mutex_lock(&cubeb_alsa_mutex); 1.717 + snd_config_delete(ctx->local_config); 1.718 + pthread_mutex_unlock(&cubeb_alsa_mutex); 1.719 + ctx->local_config = NULL; 1.720 + } else if (r >= 0) { 1.721 + alsa_locked_pcm_close(dummy); 1.722 + } 1.723 + } 1.724 + 1.725 + *context = ctx; 1.726 + 1.727 + return CUBEB_OK; 1.728 +} 1.729 + 1.730 +static char const * 1.731 +alsa_get_backend_id(cubeb * ctx) 1.732 +{ 1.733 + return "alsa"; 1.734 +} 1.735 + 1.736 +static void 1.737 +alsa_destroy(cubeb * ctx) 1.738 +{ 1.739 + int r; 1.740 + 1.741 + assert(ctx); 1.742 + 1.743 + pthread_mutex_lock(&ctx->mutex); 1.744 + ctx->shutdown = 1; 1.745 + poll_wake(ctx); 1.746 + pthread_mutex_unlock(&ctx->mutex); 1.747 + 1.748 + r = pthread_join(ctx->thread, NULL); 1.749 + assert(r == 0); 1.750 + 1.751 + close(ctx->control_fd_read); 1.752 + close(ctx->control_fd_write); 1.753 + pthread_mutex_destroy(&ctx->mutex); 1.754 + free(ctx->fds); 1.755 + 1.756 + if (ctx->local_config) { 1.757 + pthread_mutex_lock(&cubeb_alsa_mutex); 1.758 + snd_config_delete(ctx->local_config); 1.759 + pthread_mutex_unlock(&cubeb_alsa_mutex); 1.760 + } 1.761 + 1.762 + free(ctx); 1.763 +} 1.764 + 1.765 +static void alsa_stream_destroy(cubeb_stream * stm); 1.766 + 1.767 +static int 1.768 +alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, 1.769 + cubeb_stream_params stream_params, unsigned int latency, 1.770 + cubeb_data_callback data_callback, cubeb_state_callback state_callback, 1.771 + void * user_ptr) 1.772 +{ 1.773 + cubeb_stream * stm; 1.774 + int r; 1.775 + snd_pcm_format_t format; 1.776 + 1.777 + assert(ctx && stream); 1.778 + 1.779 + *stream = NULL; 1.780 + 1.781 + switch (stream_params.format) { 1.782 + case CUBEB_SAMPLE_S16LE: 1.783 + format = SND_PCM_FORMAT_S16_LE; 1.784 + break; 1.785 + case CUBEB_SAMPLE_S16BE: 1.786 + format = SND_PCM_FORMAT_S16_BE; 1.787 + break; 1.788 + case CUBEB_SAMPLE_FLOAT32LE: 1.789 + format = SND_PCM_FORMAT_FLOAT_LE; 1.790 + break; 1.791 + case CUBEB_SAMPLE_FLOAT32BE: 1.792 + format = SND_PCM_FORMAT_FLOAT_BE; 1.793 + break; 1.794 + default: 1.795 + return CUBEB_ERROR_INVALID_FORMAT; 1.796 + } 1.797 + 1.798 + pthread_mutex_lock(&ctx->mutex); 1.799 + if (ctx->active_streams >= CUBEB_STREAM_MAX) { 1.800 + pthread_mutex_unlock(&ctx->mutex); 1.801 + return CUBEB_ERROR; 1.802 + } 1.803 + ctx->active_streams += 1; 1.804 + pthread_mutex_unlock(&ctx->mutex); 1.805 + 1.806 + stm = calloc(1, sizeof(*stm)); 1.807 + assert(stm); 1.808 + 1.809 + stm->context = ctx; 1.810 + stm->data_callback = data_callback; 1.811 + stm->state_callback = state_callback; 1.812 + stm->user_ptr = user_ptr; 1.813 + stm->params = stream_params; 1.814 + stm->state = INACTIVE; 1.815 + 1.816 + r = pthread_mutex_init(&stm->mutex, NULL); 1.817 + assert(r == 0); 1.818 + 1.819 + r = alsa_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config); 1.820 + if (r < 0) { 1.821 + alsa_stream_destroy(stm); 1.822 + return CUBEB_ERROR; 1.823 + } 1.824 + 1.825 + r = snd_pcm_nonblock(stm->pcm, 1); 1.826 + assert(r == 0); 1.827 + 1.828 + /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't 1.829 + possibly work. See https://bugzilla.mozilla.org/show_bug.cgi?id=761274. 1.830 + Only resort to this hack if the handle_underrun workaround failed. */ 1.831 + if (!ctx->local_config && ctx->is_pa) { 1.832 + latency = latency < 500 ? 500 : latency; 1.833 + } 1.834 + 1.835 + r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, 1.836 + stm->params.channels, stm->params.rate, 1, 1.837 + latency * 1000); 1.838 + if (r < 0) { 1.839 + alsa_stream_destroy(stm); 1.840 + return CUBEB_ERROR_INVALID_FORMAT; 1.841 + } 1.842 + 1.843 + r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size); 1.844 + assert(r == 0); 1.845 + 1.846 + stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm); 1.847 + assert(stm->nfds > 0); 1.848 + 1.849 + stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd)); 1.850 + assert(stm->saved_fds); 1.851 + r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds); 1.852 + assert((nfds_t) r == stm->nfds); 1.853 + 1.854 + r = pthread_cond_init(&stm->cond, NULL); 1.855 + assert(r == 0); 1.856 + 1.857 + if (alsa_register_stream(ctx, stm) != 0) { 1.858 + alsa_stream_destroy(stm); 1.859 + return CUBEB_ERROR; 1.860 + } 1.861 + 1.862 + *stream = stm; 1.863 + 1.864 + return CUBEB_OK; 1.865 +} 1.866 + 1.867 +static void 1.868 +alsa_stream_destroy(cubeb_stream * stm) 1.869 +{ 1.870 + int r; 1.871 + cubeb * ctx; 1.872 + 1.873 + assert(stm && (stm->state == INACTIVE || stm->state == ERROR)); 1.874 + 1.875 + ctx = stm->context; 1.876 + 1.877 + pthread_mutex_lock(&stm->mutex); 1.878 + if (stm->pcm) { 1.879 + alsa_locked_pcm_close(stm->pcm); 1.880 + stm->pcm = NULL; 1.881 + } 1.882 + free(stm->saved_fds); 1.883 + pthread_mutex_unlock(&stm->mutex); 1.884 + pthread_mutex_destroy(&stm->mutex); 1.885 + 1.886 + r = pthread_cond_destroy(&stm->cond); 1.887 + assert(r == 0); 1.888 + 1.889 + alsa_unregister_stream(stm); 1.890 + 1.891 + pthread_mutex_lock(&ctx->mutex); 1.892 + assert(ctx->active_streams >= 1); 1.893 + ctx->active_streams -= 1; 1.894 + pthread_mutex_unlock(&ctx->mutex); 1.895 + 1.896 + free(stm); 1.897 +} 1.898 + 1.899 +static int 1.900 +alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) 1.901 +{ 1.902 + int rv; 1.903 + cubeb_stream * stm; 1.904 + snd_pcm_hw_params_t* hw_params; 1.905 + cubeb_stream_params params; 1.906 + params.rate = 44100; 1.907 + params.format = CUBEB_SAMPLE_FLOAT32NE; 1.908 + params.channels = 2; 1.909 + 1.910 + snd_pcm_hw_params_alloca(&hw_params); 1.911 + 1.912 + assert(ctx); 1.913 + 1.914 + rv = alsa_stream_init(ctx, &stm, "", params, 100, NULL, NULL, NULL); 1.915 + if (rv != CUBEB_OK) { 1.916 + return CUBEB_ERROR; 1.917 + } 1.918 + 1.919 + rv = snd_pcm_hw_params_any(stm->pcm, hw_params); 1.920 + if (rv < 0) { 1.921 + return CUBEB_ERROR; 1.922 + } 1.923 + 1.924 + rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels); 1.925 + if (rv < 0) { 1.926 + return CUBEB_ERROR; 1.927 + } 1.928 + 1.929 + alsa_stream_destroy(stm); 1.930 + 1.931 + return CUBEB_OK; 1.932 +} 1.933 + 1.934 +static int 1.935 +alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { 1.936 + int rv, dir; 1.937 + snd_pcm_t * pcm; 1.938 + snd_pcm_hw_params_t * hw_params; 1.939 + 1.940 + snd_pcm_hw_params_alloca(&hw_params); 1.941 + 1.942 + /* get a pcm, disabling resampling, so we get a rate the 1.943 + * hardware/dmix/pulse/etc. supports. */ 1.944 + rv = snd_pcm_open(&pcm, "", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0); 1.945 + if (rv < 0) { 1.946 + return CUBEB_ERROR; 1.947 + } 1.948 + 1.949 + rv = snd_pcm_hw_params_any(pcm, hw_params); 1.950 + if (rv < 0) { 1.951 + snd_pcm_close(pcm); 1.952 + return CUBEB_ERROR; 1.953 + } 1.954 + 1.955 + rv = snd_pcm_hw_params_get_rate(hw_params, rate, &dir); 1.956 + if (rv >= 0) { 1.957 + /* There is a default rate: use it. */ 1.958 + snd_pcm_close(pcm); 1.959 + return CUBEB_OK; 1.960 + } 1.961 + 1.962 + /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */ 1.963 + *rate = 44100; 1.964 + 1.965 + rv = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL); 1.966 + if (rv < 0) { 1.967 + snd_pcm_close(pcm); 1.968 + return CUBEB_ERROR; 1.969 + } 1.970 + 1.971 + snd_pcm_close(pcm); 1.972 + 1.973 + return CUBEB_OK; 1.974 +} 1.975 + 1.976 +static int 1.977 +alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) 1.978 +{ 1.979 + /* This is found to be an acceptable minimum, even on a super low-end 1.980 + * machine. */ 1.981 + *latency_ms = 40; 1.982 + 1.983 + return CUBEB_OK; 1.984 +} 1.985 + 1.986 +static int 1.987 +alsa_stream_start(cubeb_stream * stm) 1.988 +{ 1.989 + cubeb * ctx; 1.990 + 1.991 + assert(stm); 1.992 + ctx = stm->context; 1.993 + 1.994 + pthread_mutex_lock(&stm->mutex); 1.995 + snd_pcm_pause(stm->pcm, 0); 1.996 + gettimeofday(&stm->last_activity, NULL); 1.997 + pthread_mutex_unlock(&stm->mutex); 1.998 + 1.999 + pthread_mutex_lock(&ctx->mutex); 1.1000 + if (stm->state != INACTIVE) { 1.1001 + pthread_mutex_unlock(&ctx->mutex); 1.1002 + return CUBEB_ERROR; 1.1003 + } 1.1004 + alsa_set_stream_state(stm, RUNNING); 1.1005 + pthread_mutex_unlock(&ctx->mutex); 1.1006 + 1.1007 + return CUBEB_OK; 1.1008 +} 1.1009 + 1.1010 +static int 1.1011 +alsa_stream_stop(cubeb_stream * stm) 1.1012 +{ 1.1013 + cubeb * ctx; 1.1014 + int r; 1.1015 + 1.1016 + assert(stm); 1.1017 + ctx = stm->context; 1.1018 + 1.1019 + pthread_mutex_lock(&ctx->mutex); 1.1020 + while (stm->state == PROCESSING) { 1.1021 + r = pthread_cond_wait(&stm->cond, &ctx->mutex); 1.1022 + assert(r == 0); 1.1023 + } 1.1024 + 1.1025 + alsa_set_stream_state(stm, INACTIVE); 1.1026 + pthread_mutex_unlock(&ctx->mutex); 1.1027 + 1.1028 + pthread_mutex_lock(&stm->mutex); 1.1029 + snd_pcm_pause(stm->pcm, 1); 1.1030 + pthread_mutex_unlock(&stm->mutex); 1.1031 + 1.1032 + return CUBEB_OK; 1.1033 +} 1.1034 + 1.1035 +static int 1.1036 +alsa_stream_get_position(cubeb_stream * stm, uint64_t * position) 1.1037 +{ 1.1038 + snd_pcm_sframes_t delay; 1.1039 + 1.1040 + assert(stm && position); 1.1041 + 1.1042 + pthread_mutex_lock(&stm->mutex); 1.1043 + 1.1044 + delay = -1; 1.1045 + if (snd_pcm_state(stm->pcm) != SND_PCM_STATE_RUNNING || 1.1046 + snd_pcm_delay(stm->pcm, &delay) != 0) { 1.1047 + *position = stm->last_position; 1.1048 + pthread_mutex_unlock(&stm->mutex); 1.1049 + return CUBEB_OK; 1.1050 + } 1.1051 + 1.1052 + assert(delay >= 0); 1.1053 + 1.1054 + *position = 0; 1.1055 + if (stm->write_position >= (snd_pcm_uframes_t) delay) { 1.1056 + *position = stm->write_position - delay; 1.1057 + } 1.1058 + 1.1059 + stm->last_position = *position; 1.1060 + 1.1061 + pthread_mutex_unlock(&stm->mutex); 1.1062 + return CUBEB_OK; 1.1063 +} 1.1064 + 1.1065 +int 1.1066 +alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency) 1.1067 +{ 1.1068 + snd_pcm_sframes_t delay; 1.1069 + /* This function returns the delay in frames until a frame written using 1.1070 + snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */ 1.1071 + if (snd_pcm_delay(stm->pcm, &delay)) { 1.1072 + return CUBEB_ERROR; 1.1073 + } 1.1074 + 1.1075 + *latency = delay; 1.1076 + 1.1077 + return CUBEB_OK; 1.1078 +} 1.1079 + 1.1080 +static struct cubeb_ops const alsa_ops = { 1.1081 + .init = alsa_init, 1.1082 + .get_backend_id = alsa_get_backend_id, 1.1083 + .get_max_channel_count = alsa_get_max_channel_count, 1.1084 + .get_min_latency = alsa_get_min_latency, 1.1085 + .get_preferred_sample_rate = alsa_get_preferred_sample_rate, 1.1086 + .destroy = alsa_destroy, 1.1087 + .stream_init = alsa_stream_init, 1.1088 + .stream_destroy = alsa_stream_destroy, 1.1089 + .stream_start = alsa_stream_start, 1.1090 + .stream_stop = alsa_stream_stop, 1.1091 + .stream_get_position = alsa_stream_get_position, 1.1092 + .stream_get_latency = alsa_stream_get_latency 1.1093 +};