1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/libcubeb/src/cubeb_pulse.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,677 @@ 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 +#include <assert.h> 1.12 +#include <dlfcn.h> 1.13 +#include <stdlib.h> 1.14 +#include <pulse/pulseaudio.h> 1.15 +#include <string.h> 1.16 +#include "cubeb/cubeb.h" 1.17 +#include "cubeb-internal.h" 1.18 + 1.19 +#ifdef DISABLE_LIBPULSE_DLOPEN 1.20 +#define WRAP(x) x 1.21 +#else 1.22 +#define WRAP(x) cubeb_##x 1.23 +#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x 1.24 +MAKE_TYPEDEF(pa_channel_map_init_auto); 1.25 +MAKE_TYPEDEF(pa_context_connect); 1.26 +MAKE_TYPEDEF(pa_context_disconnect); 1.27 +MAKE_TYPEDEF(pa_context_drain); 1.28 +MAKE_TYPEDEF(pa_context_get_state); 1.29 +MAKE_TYPEDEF(pa_context_new); 1.30 +MAKE_TYPEDEF(pa_context_rttime_new); 1.31 +MAKE_TYPEDEF(pa_context_set_state_callback); 1.32 +MAKE_TYPEDEF(pa_context_unref); 1.33 +MAKE_TYPEDEF(pa_context_get_sink_info_by_name); 1.34 +MAKE_TYPEDEF(pa_context_get_server_info); 1.35 +MAKE_TYPEDEF(pa_frame_size); 1.36 +MAKE_TYPEDEF(pa_operation_get_state); 1.37 +MAKE_TYPEDEF(pa_operation_unref); 1.38 +MAKE_TYPEDEF(pa_rtclock_now); 1.39 +MAKE_TYPEDEF(pa_stream_begin_write); 1.40 +MAKE_TYPEDEF(pa_stream_cancel_write); 1.41 +MAKE_TYPEDEF(pa_stream_connect_playback); 1.42 +MAKE_TYPEDEF(pa_stream_cork); 1.43 +MAKE_TYPEDEF(pa_stream_disconnect); 1.44 +MAKE_TYPEDEF(pa_stream_get_latency); 1.45 +MAKE_TYPEDEF(pa_stream_get_state); 1.46 +MAKE_TYPEDEF(pa_stream_get_time); 1.47 +MAKE_TYPEDEF(pa_stream_new); 1.48 +MAKE_TYPEDEF(pa_stream_set_state_callback); 1.49 +MAKE_TYPEDEF(pa_stream_set_write_callback); 1.50 +MAKE_TYPEDEF(pa_stream_unref); 1.51 +MAKE_TYPEDEF(pa_stream_update_timing_info); 1.52 +MAKE_TYPEDEF(pa_stream_write); 1.53 +MAKE_TYPEDEF(pa_threaded_mainloop_free); 1.54 +MAKE_TYPEDEF(pa_threaded_mainloop_get_api); 1.55 +MAKE_TYPEDEF(pa_threaded_mainloop_lock); 1.56 +MAKE_TYPEDEF(pa_threaded_mainloop_in_thread); 1.57 +MAKE_TYPEDEF(pa_threaded_mainloop_new); 1.58 +MAKE_TYPEDEF(pa_threaded_mainloop_signal); 1.59 +MAKE_TYPEDEF(pa_threaded_mainloop_start); 1.60 +MAKE_TYPEDEF(pa_threaded_mainloop_stop); 1.61 +MAKE_TYPEDEF(pa_threaded_mainloop_unlock); 1.62 +MAKE_TYPEDEF(pa_threaded_mainloop_wait); 1.63 +MAKE_TYPEDEF(pa_usec_to_bytes); 1.64 +#undef MAKE_TYPEDEF 1.65 +#endif 1.66 + 1.67 +static struct cubeb_ops const pulse_ops; 1.68 + 1.69 +struct cubeb { 1.70 + struct cubeb_ops const * ops; 1.71 + void * libpulse; 1.72 + pa_threaded_mainloop * mainloop; 1.73 + pa_context * context; 1.74 + pa_sink_info * default_sink_info; 1.75 + char * context_name; 1.76 + int error; 1.77 +}; 1.78 + 1.79 +struct cubeb_stream { 1.80 + cubeb * context; 1.81 + pa_stream * stream; 1.82 + cubeb_data_callback data_callback; 1.83 + cubeb_state_callback state_callback; 1.84 + void * user_ptr; 1.85 + pa_time_event * drain_timer; 1.86 + pa_sample_spec sample_spec; 1.87 + int shutdown; 1.88 +}; 1.89 + 1.90 +enum cork_state { 1.91 + UNCORK = 0, 1.92 + CORK = 1 << 0, 1.93 + NOTIFY = 1 << 1 1.94 +}; 1.95 + 1.96 +static void 1.97 +sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u) 1.98 +{ 1.99 + cubeb * ctx = u; 1.100 + if (!eol) { 1.101 + ctx->default_sink_info = malloc(sizeof(pa_sink_info)); 1.102 + memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info)); 1.103 + } 1.104 + WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); 1.105 +} 1.106 + 1.107 +static void 1.108 +server_info_callback(pa_context * context, const pa_server_info * info, void * u) 1.109 +{ 1.110 + WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u); 1.111 +} 1.112 + 1.113 +static void 1.114 +context_state_callback(pa_context * c, void * u) 1.115 +{ 1.116 + cubeb * ctx = u; 1.117 + if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(c))) { 1.118 + ctx->error = 1; 1.119 + } 1.120 + WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); 1.121 +} 1.122 + 1.123 +static void 1.124 +context_notify_callback(pa_context * c, void * u) 1.125 +{ 1.126 + cubeb * ctx = u; 1.127 + WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); 1.128 +} 1.129 + 1.130 +static void 1.131 +stream_success_callback(pa_stream * s, int success, void * u) 1.132 +{ 1.133 + cubeb_stream * stm = u; 1.134 + WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); 1.135 +} 1.136 + 1.137 +static void 1.138 +stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u) 1.139 +{ 1.140 + cubeb_stream * stm = u; 1.141 + /* there's no pa_rttime_free, so use this instead. */ 1.142 + a->time_free(stm->drain_timer); 1.143 + stm->drain_timer = NULL; 1.144 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); 1.145 +} 1.146 + 1.147 +static void 1.148 +stream_state_callback(pa_stream * s, void * u) 1.149 +{ 1.150 + cubeb_stream * stm = u; 1.151 + if (!PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(s))) { 1.152 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 1.153 + } 1.154 + WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); 1.155 +} 1.156 + 1.157 +static void 1.158 +stream_request_callback(pa_stream * s, size_t nbytes, void * u) 1.159 +{ 1.160 + cubeb_stream * stm; 1.161 + void * buffer; 1.162 + size_t size; 1.163 + int r; 1.164 + long got; 1.165 + size_t towrite; 1.166 + size_t frame_size; 1.167 + 1.168 + stm = u; 1.169 + 1.170 + if (stm->shutdown) 1.171 + return; 1.172 + 1.173 + frame_size = WRAP(pa_frame_size)(&stm->sample_spec); 1.174 + 1.175 + assert(nbytes % frame_size == 0); 1.176 + 1.177 + towrite = nbytes; 1.178 + 1.179 + while (towrite) { 1.180 + size = towrite; 1.181 + r = WRAP(pa_stream_begin_write)(s, &buffer, &size); 1.182 + assert(r == 0); 1.183 + assert(size > 0); 1.184 + assert(size % frame_size == 0); 1.185 + 1.186 + got = stm->data_callback(stm, stm->user_ptr, buffer, size / frame_size); 1.187 + if (got < 0) { 1.188 + WRAP(pa_stream_cancel_write)(s); 1.189 + stm->shutdown = 1; 1.190 + return; 1.191 + } 1.192 + 1.193 + r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE); 1.194 + assert(r == 0); 1.195 + 1.196 + if ((size_t) got < size / frame_size) { 1.197 + pa_usec_t latency = 0; 1.198 + r = WRAP(pa_stream_get_latency)(s, &latency, NULL); 1.199 + if (r == -PA_ERR_NODATA) { 1.200 + /* this needs a better guess. */ 1.201 + latency = 100 * PA_USEC_PER_MSEC; 1.202 + } 1.203 + assert(r == 0 || r == -PA_ERR_NODATA); 1.204 + /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */ 1.205 + /* arbitrary safety margin: double the current latency. */ 1.206 + stm->drain_timer = WRAP(pa_context_rttime_new)(stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, stream_drain_callback, stm); 1.207 + stm->shutdown = 1; 1.208 + return; 1.209 + } 1.210 + 1.211 + towrite -= size; 1.212 + } 1.213 + 1.214 + assert(towrite == 0); 1.215 +} 1.216 + 1.217 +static int 1.218 +wait_until_context_ready(cubeb * ctx) 1.219 +{ 1.220 + for (;;) { 1.221 + pa_context_state_t state = WRAP(pa_context_get_state)(ctx->context); 1.222 + if (!PA_CONTEXT_IS_GOOD(state)) 1.223 + return -1; 1.224 + if (state == PA_CONTEXT_READY) 1.225 + break; 1.226 + WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); 1.227 + } 1.228 + return 0; 1.229 +} 1.230 + 1.231 +static int 1.232 +wait_until_stream_ready(cubeb_stream * stm) 1.233 +{ 1.234 + for (;;) { 1.235 + pa_stream_state_t state = WRAP(pa_stream_get_state)(stm->stream); 1.236 + if (!PA_STREAM_IS_GOOD(state)) 1.237 + return -1; 1.238 + if (state == PA_STREAM_READY) 1.239 + break; 1.240 + WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop); 1.241 + } 1.242 + return 0; 1.243 +} 1.244 + 1.245 +static int 1.246 +operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o) 1.247 +{ 1.248 + while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) { 1.249 + WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); 1.250 + if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context))) 1.251 + return -1; 1.252 + if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream))) 1.253 + return -1; 1.254 + } 1.255 + return 0; 1.256 +} 1.257 + 1.258 +static void 1.259 +stream_cork(cubeb_stream * stm, enum cork_state state) 1.260 +{ 1.261 + pa_operation * o; 1.262 + 1.263 + WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1.264 + o = WRAP(pa_stream_cork)(stm->stream, state & CORK, stream_success_callback, stm); 1.265 + if (o) { 1.266 + operation_wait(stm->context, stm->stream, o); 1.267 + WRAP(pa_operation_unref)(o); 1.268 + } 1.269 + WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1.270 + 1.271 + if (state & NOTIFY) { 1.272 + stm->state_callback(stm, stm->user_ptr, 1.273 + state & CORK ? CUBEB_STATE_STOPPED : CUBEB_STATE_STARTED); 1.274 + } 1.275 +} 1.276 + 1.277 +static void pulse_context_destroy(cubeb * ctx); 1.278 +static void pulse_destroy(cubeb * ctx); 1.279 + 1.280 +static int 1.281 +pulse_context_init(cubeb * ctx) 1.282 +{ 1.283 + if (ctx->context) { 1.284 + assert(ctx->error == 1); 1.285 + pulse_context_destroy(ctx); 1.286 + } 1.287 + 1.288 + ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), 1.289 + ctx->context_name); 1.290 + WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx); 1.291 + 1.292 + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 1.293 + WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL); 1.294 + 1.295 + if (wait_until_context_ready(ctx) != 0) { 1.296 + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 1.297 + pulse_context_destroy(ctx); 1.298 + ctx->context = NULL; 1.299 + return -1; 1.300 + } 1.301 + 1.302 + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 1.303 + 1.304 + ctx->error = 0; 1.305 + 1.306 + return 0; 1.307 +} 1.308 + 1.309 +/*static*/ int 1.310 +pulse_init(cubeb ** context, char const * context_name) 1.311 +{ 1.312 + void * libpulse = NULL; 1.313 + cubeb * ctx; 1.314 + 1.315 + *context = NULL; 1.316 + 1.317 +#ifndef DISABLE_LIBPULSE_DLOPEN 1.318 + libpulse = dlopen("libpulse.so.0", RTLD_LAZY); 1.319 + if (!libpulse) { 1.320 + return CUBEB_ERROR; 1.321 + } 1.322 + 1.323 +#define LOAD(x) do { \ 1.324 + cubeb_##x = dlsym(libpulse, #x); \ 1.325 + if (!cubeb_##x) { \ 1.326 + dlclose(libpulse); \ 1.327 + return CUBEB_ERROR; \ 1.328 + } \ 1.329 + } while(0) 1.330 + LOAD(pa_channel_map_init_auto); 1.331 + LOAD(pa_context_connect); 1.332 + LOAD(pa_context_disconnect); 1.333 + LOAD(pa_context_drain); 1.334 + LOAD(pa_context_get_state); 1.335 + LOAD(pa_context_new); 1.336 + LOAD(pa_context_rttime_new); 1.337 + LOAD(pa_context_set_state_callback); 1.338 + LOAD(pa_context_get_sink_info_by_name); 1.339 + LOAD(pa_context_get_server_info); 1.340 + LOAD(pa_context_unref); 1.341 + LOAD(pa_frame_size); 1.342 + LOAD(pa_operation_get_state); 1.343 + LOAD(pa_operation_unref); 1.344 + LOAD(pa_rtclock_now); 1.345 + LOAD(pa_stream_begin_write); 1.346 + LOAD(pa_stream_cancel_write); 1.347 + LOAD(pa_stream_connect_playback); 1.348 + LOAD(pa_stream_cork); 1.349 + LOAD(pa_stream_disconnect); 1.350 + LOAD(pa_stream_get_latency); 1.351 + LOAD(pa_stream_get_state); 1.352 + LOAD(pa_stream_get_time); 1.353 + LOAD(pa_stream_new); 1.354 + LOAD(pa_stream_set_state_callback); 1.355 + LOAD(pa_stream_set_write_callback); 1.356 + LOAD(pa_stream_unref); 1.357 + LOAD(pa_stream_update_timing_info); 1.358 + LOAD(pa_stream_write); 1.359 + LOAD(pa_threaded_mainloop_free); 1.360 + LOAD(pa_threaded_mainloop_get_api); 1.361 + LOAD(pa_threaded_mainloop_lock); 1.362 + LOAD(pa_threaded_mainloop_in_thread); 1.363 + LOAD(pa_threaded_mainloop_new); 1.364 + LOAD(pa_threaded_mainloop_signal); 1.365 + LOAD(pa_threaded_mainloop_start); 1.366 + LOAD(pa_threaded_mainloop_stop); 1.367 + LOAD(pa_threaded_mainloop_unlock); 1.368 + LOAD(pa_threaded_mainloop_wait); 1.369 + LOAD(pa_usec_to_bytes); 1.370 +#undef LOAD 1.371 +#endif 1.372 + 1.373 + ctx = calloc(1, sizeof(*ctx)); 1.374 + assert(ctx); 1.375 + 1.376 + ctx->ops = &pulse_ops; 1.377 + ctx->libpulse = libpulse; 1.378 + 1.379 + ctx->mainloop = WRAP(pa_threaded_mainloop_new)(); 1.380 + ctx->default_sink_info = NULL; 1.381 + 1.382 + WRAP(pa_threaded_mainloop_start)(ctx->mainloop); 1.383 + 1.384 + ctx->context_name = context_name ? strdup(context_name) : NULL; 1.385 + if (pulse_context_init(ctx) != 0) { 1.386 + pulse_destroy(ctx); 1.387 + return CUBEB_ERROR; 1.388 + } 1.389 + 1.390 + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 1.391 + WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx); 1.392 + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 1.393 + 1.394 + *context = ctx; 1.395 + 1.396 + return CUBEB_OK; 1.397 +} 1.398 + 1.399 +static char const * 1.400 +pulse_get_backend_id(cubeb * ctx) 1.401 +{ 1.402 + return "pulse"; 1.403 +} 1.404 + 1.405 +static int 1.406 +pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) 1.407 +{ 1.408 + assert(ctx && max_channels); 1.409 + 1.410 + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 1.411 + while (!ctx->default_sink_info) { 1.412 + WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); 1.413 + } 1.414 + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 1.415 + 1.416 + *max_channels = ctx->default_sink_info->channel_map.channels; 1.417 + 1.418 + return CUBEB_OK; 1.419 +} 1.420 + 1.421 +static int 1.422 +pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) 1.423 +{ 1.424 + assert(ctx && rate); 1.425 + 1.426 + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 1.427 + while (!ctx->default_sink_info) { 1.428 + WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); 1.429 + } 1.430 + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 1.431 + 1.432 + *rate = ctx->default_sink_info->sample_spec.rate; 1.433 + 1.434 + return CUBEB_OK; 1.435 +} 1.436 + 1.437 +static int 1.438 +pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) 1.439 +{ 1.440 + // According to PulseAudio developers, this is a safe minimum. 1.441 + *latency_ms = 40; 1.442 + 1.443 + return CUBEB_OK; 1.444 +} 1.445 + 1.446 +static void 1.447 +pulse_context_destroy(cubeb * ctx) 1.448 +{ 1.449 + pa_operation * o; 1.450 + 1.451 + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); 1.452 + o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx); 1.453 + if (o) { 1.454 + operation_wait(ctx, NULL, o); 1.455 + WRAP(pa_operation_unref)(o); 1.456 + } 1.457 + WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL); 1.458 + WRAP(pa_context_disconnect)(ctx->context); 1.459 + WRAP(pa_context_unref)(ctx->context); 1.460 + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); 1.461 +} 1.462 + 1.463 +static void 1.464 +pulse_destroy(cubeb * ctx) 1.465 +{ 1.466 + pa_operation * o; 1.467 + 1.468 + if (ctx->context_name) { 1.469 + free(ctx->context_name); 1.470 + } 1.471 + if (ctx->context) { 1.472 + pulse_context_destroy(ctx); 1.473 + } 1.474 + 1.475 + if (ctx->mainloop) { 1.476 + WRAP(pa_threaded_mainloop_stop)(ctx->mainloop); 1.477 + WRAP(pa_threaded_mainloop_free)(ctx->mainloop); 1.478 + } 1.479 + 1.480 + if (ctx->libpulse) { 1.481 + dlclose(ctx->libpulse); 1.482 + } 1.483 + if (ctx->default_sink_info) { 1.484 + free(ctx->default_sink_info); 1.485 + } 1.486 + free(ctx); 1.487 +} 1.488 + 1.489 +static void pulse_stream_destroy(cubeb_stream * stm); 1.490 + 1.491 +static int 1.492 +pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, 1.493 + cubeb_stream_params stream_params, unsigned int latency, 1.494 + cubeb_data_callback data_callback, cubeb_state_callback state_callback, 1.495 + void * user_ptr) 1.496 +{ 1.497 + pa_sample_spec ss; 1.498 + cubeb_stream * stm; 1.499 + pa_operation * o; 1.500 + pa_buffer_attr battr; 1.501 + int r; 1.502 + 1.503 + assert(context); 1.504 + 1.505 + *stream = NULL; 1.506 + 1.507 + switch (stream_params.format) { 1.508 + case CUBEB_SAMPLE_S16LE: 1.509 + ss.format = PA_SAMPLE_S16LE; 1.510 + break; 1.511 + case CUBEB_SAMPLE_S16BE: 1.512 + ss.format = PA_SAMPLE_S16BE; 1.513 + break; 1.514 + case CUBEB_SAMPLE_FLOAT32LE: 1.515 + ss.format = PA_SAMPLE_FLOAT32LE; 1.516 + break; 1.517 + case CUBEB_SAMPLE_FLOAT32BE: 1.518 + ss.format = PA_SAMPLE_FLOAT32BE; 1.519 + break; 1.520 + default: 1.521 + return CUBEB_ERROR_INVALID_FORMAT; 1.522 + } 1.523 + 1.524 + // If the connection failed for some reason, try to reconnect 1.525 + if (context->error == 1 && pulse_context_init(context) != 0) { 1.526 + return CUBEB_ERROR; 1.527 + } 1.528 + 1.529 + ss.rate = stream_params.rate; 1.530 + ss.channels = stream_params.channels; 1.531 + 1.532 + stm = calloc(1, sizeof(*stm)); 1.533 + assert(stm); 1.534 + 1.535 + stm->context = context; 1.536 + 1.537 + stm->data_callback = data_callback; 1.538 + stm->state_callback = state_callback; 1.539 + stm->user_ptr = user_ptr; 1.540 + 1.541 + stm->sample_spec = ss; 1.542 + 1.543 + battr.maxlength = -1; 1.544 + battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, &stm->sample_spec); 1.545 + battr.prebuf = -1; 1.546 + battr.minreq = battr.tlength / 4; 1.547 + battr.fragsize = -1; 1.548 + 1.549 + WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1.550 + stm->stream = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); 1.551 + if (!stm->stream) { 1.552 + pulse_stream_destroy(stm); 1.553 + return CUBEB_ERROR; 1.554 + } 1.555 + WRAP(pa_stream_set_state_callback)(stm->stream, stream_state_callback, stm); 1.556 + WRAP(pa_stream_set_write_callback)(stm->stream, stream_request_callback, stm); 1.557 + WRAP(pa_stream_connect_playback)(stm->stream, NULL, &battr, 1.558 + PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | 1.559 + PA_STREAM_START_CORKED, 1.560 + NULL, NULL); 1.561 + 1.562 + r = wait_until_stream_ready(stm); 1.563 + if (r == 0) { 1.564 + /* force a timing update now, otherwise timing info does not become valid 1.565 + until some point after initialization has completed. */ 1.566 + o = WRAP(pa_stream_update_timing_info)(stm->stream, stream_success_callback, stm); 1.567 + if (o) { 1.568 + r = operation_wait(stm->context, stm->stream, o); 1.569 + WRAP(pa_operation_unref)(o); 1.570 + } 1.571 + } 1.572 + WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1.573 + 1.574 + if (r != 0) { 1.575 + pulse_stream_destroy(stm); 1.576 + return CUBEB_ERROR; 1.577 + } 1.578 + 1.579 + *stream = stm; 1.580 + 1.581 + return CUBEB_OK; 1.582 +} 1.583 + 1.584 +static void 1.585 +pulse_stream_destroy(cubeb_stream * stm) 1.586 +{ 1.587 + if (stm->stream) { 1.588 + stream_cork(stm, CORK); 1.589 + 1.590 + WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1.591 + 1.592 + if (stm->drain_timer) { 1.593 + /* there's no pa_rttime_free, so use this instead. */ 1.594 + WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer); 1.595 + } 1.596 + 1.597 + WRAP(pa_stream_set_state_callback)(stm->stream, NULL, NULL); 1.598 + WRAP(pa_stream_disconnect)(stm->stream); 1.599 + WRAP(pa_stream_unref)(stm->stream); 1.600 + WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1.601 + } 1.602 + 1.603 + free(stm); 1.604 +} 1.605 + 1.606 +static int 1.607 +pulse_stream_start(cubeb_stream * stm) 1.608 +{ 1.609 + stream_cork(stm, UNCORK | NOTIFY); 1.610 + return CUBEB_OK; 1.611 +} 1.612 + 1.613 +static int 1.614 +pulse_stream_stop(cubeb_stream * stm) 1.615 +{ 1.616 + stream_cork(stm, CORK | NOTIFY); 1.617 + return CUBEB_OK; 1.618 +} 1.619 + 1.620 +static int 1.621 +pulse_stream_get_position(cubeb_stream * stm, uint64_t * position) 1.622 +{ 1.623 + int r, in_thread; 1.624 + pa_usec_t r_usec; 1.625 + uint64_t bytes; 1.626 + 1.627 + in_thread = WRAP(pa_threaded_mainloop_in_thread)(stm->context->mainloop); 1.628 + 1.629 + if (!in_thread) { 1.630 + WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); 1.631 + } 1.632 + r = WRAP(pa_stream_get_time)(stm->stream, &r_usec); 1.633 + if (!in_thread) { 1.634 + WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); 1.635 + } 1.636 + 1.637 + if (r != 0) { 1.638 + return CUBEB_ERROR; 1.639 + } 1.640 + 1.641 + bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->sample_spec); 1.642 + *position = bytes / WRAP(pa_frame_size)(&stm->sample_spec); 1.643 + 1.644 + return CUBEB_OK; 1.645 +} 1.646 + 1.647 +int 1.648 +pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency) 1.649 +{ 1.650 + pa_usec_t r_usec; 1.651 + int negative, r; 1.652 + 1.653 + if (!stm) { 1.654 + return CUBEB_ERROR; 1.655 + } 1.656 + 1.657 + r = WRAP(pa_stream_get_latency)(stm->stream, &r_usec, &negative); 1.658 + assert(!negative); 1.659 + if (r) { 1.660 + return CUBEB_ERROR; 1.661 + } 1.662 + 1.663 + *latency = r_usec * stm->sample_spec.rate / PA_USEC_PER_SEC; 1.664 + return CUBEB_OK; 1.665 +} 1.666 + 1.667 +static struct cubeb_ops const pulse_ops = { 1.668 + .init = pulse_init, 1.669 + .get_backend_id = pulse_get_backend_id, 1.670 + .get_max_channel_count = pulse_get_max_channel_count, 1.671 + .get_min_latency = pulse_get_min_latency, 1.672 + .get_preferred_sample_rate = pulse_get_preferred_sample_rate, 1.673 + .destroy = pulse_destroy, 1.674 + .stream_init = pulse_stream_init, 1.675 + .stream_destroy = pulse_stream_destroy, 1.676 + .stream_start = pulse_stream_start, 1.677 + .stream_stop = pulse_stream_stop, 1.678 + .stream_get_position = pulse_stream_get_position, 1.679 + .stream_get_latency = pulse_stream_get_latency 1.680 +};