1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/libcubeb/src/cubeb_opensl.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,622 @@ 1.4 +/* 1.5 + * Copyright © 2012 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 <SLES/OpenSLES.h> 1.15 +#if defined(__ANDROID__) 1.16 +#include "android/sles_definitions.h" 1.17 +#include <SLES/OpenSLES_Android.h> 1.18 +#include <android/log.h> 1.19 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args) 1.20 +#endif 1.21 +#include "cubeb/cubeb.h" 1.22 +#include "cubeb-internal.h" 1.23 + 1.24 +static struct cubeb_ops const opensl_ops; 1.25 + 1.26 +struct cubeb { 1.27 + struct cubeb_ops const * ops; 1.28 + void * lib; 1.29 + void * libmedia; 1.30 + int32_t (* get_output_latency)(uint32_t * latency, int stream_type); 1.31 + SLInterfaceID SL_IID_BUFFERQUEUE; 1.32 + SLInterfaceID SL_IID_PLAY; 1.33 +#if defined(__ANDROID__) 1.34 + SLInterfaceID SL_IID_ANDROIDCONFIGURATION; 1.35 +#endif 1.36 + SLObjectItf engObj; 1.37 + SLEngineItf eng; 1.38 + SLObjectItf outmixObj; 1.39 +}; 1.40 + 1.41 +#define NELEMS(A) (sizeof(A) / sizeof A[0]) 1.42 +#define NBUFS 4 1.43 +#define AUDIO_STREAM_TYPE_MUSIC 3 1.44 + 1.45 +struct cubeb_stream { 1.46 + cubeb * context; 1.47 + SLObjectItf playerObj; 1.48 + SLPlayItf play; 1.49 + SLBufferQueueItf bufq; 1.50 + void *queuebuf[NBUFS]; 1.51 + int queuebuf_idx; 1.52 + long queuebuf_len; 1.53 + long bytespersec; 1.54 + long framesize; 1.55 + int draining; 1.56 + cubeb_stream_type stream_type; 1.57 + 1.58 + cubeb_data_callback data_callback; 1.59 + cubeb_state_callback state_callback; 1.60 + void * user_ptr; 1.61 +}; 1.62 + 1.63 +static void 1.64 +bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr) 1.65 +{ 1.66 + cubeb_stream * stm = user_ptr; 1.67 + SLBufferQueueState state; 1.68 + (*stm->bufq)->GetState(stm->bufq, &state); 1.69 + 1.70 + if (stm->draining) { 1.71 + if (!state.count) { 1.72 + stm->draining = 0; 1.73 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); 1.74 + } 1.75 + return; 1.76 + } 1.77 + 1.78 + if (state.count > 1) 1.79 + return; 1.80 + 1.81 + SLuint32 i; 1.82 + for (i = state.count; i < NBUFS; i++) { 1.83 + void *buf = stm->queuebuf[stm->queuebuf_idx]; 1.84 + long written = stm->data_callback(stm, stm->user_ptr, 1.85 + buf, stm->queuebuf_len / stm->framesize); 1.86 + if (written == CUBEB_ERROR) { 1.87 + (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_STOPPED); 1.88 + return; 1.89 + } 1.90 + 1.91 + if (written) { 1.92 + (*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize); 1.93 + stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS; 1.94 + } else if (!i) { 1.95 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); 1.96 + return; 1.97 + } 1.98 + 1.99 + if ((written * stm->framesize) < stm->queuebuf_len) { 1.100 + stm->draining = 1; 1.101 + return; 1.102 + } 1.103 + } 1.104 +} 1.105 + 1.106 +#if defined(__ANDROID__) 1.107 +static SLuint32 1.108 +convert_stream_type_to_sl_stream(cubeb_stream_type stream_type) 1.109 +{ 1.110 + switch(stream_type) { 1.111 + case CUBEB_STREAM_TYPE_SYSTEM: 1.112 + return SL_ANDROID_STREAM_SYSTEM; 1.113 + case CUBEB_STREAM_TYPE_MUSIC: 1.114 + return SL_ANDROID_STREAM_MEDIA; 1.115 + case CUBEB_STREAM_TYPE_NOTIFICATION: 1.116 + return SL_ANDROID_STREAM_NOTIFICATION; 1.117 + case CUBEB_STREAM_TYPE_ALARM: 1.118 + return SL_ANDROID_STREAM_ALARM; 1.119 + case CUBEB_STREAM_TYPE_VOICE_CALL: 1.120 + return SL_ANDROID_STREAM_VOICE; 1.121 + case CUBEB_STREAM_TYPE_RING: 1.122 + return SL_ANDROID_STREAM_RING; 1.123 + case CUBEB_STREAM_TYPE_ENFORCED_AUDIBLE: 1.124 + default: 1.125 + return 0xFFFFFFFF; 1.126 + } 1.127 +} 1.128 +#endif 1.129 + 1.130 +static void opensl_destroy(cubeb * ctx); 1.131 + 1.132 +/*static*/ int 1.133 +opensl_init(cubeb ** context, char const * context_name) 1.134 +{ 1.135 + cubeb * ctx; 1.136 + 1.137 + *context = NULL; 1.138 + 1.139 + ctx = calloc(1, sizeof(*ctx)); 1.140 + assert(ctx); 1.141 + 1.142 + ctx->ops = &opensl_ops; 1.143 + 1.144 + ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY); 1.145 + ctx->libmedia = dlopen("libmedia.so", RTLD_LAZY); 1.146 + if (!ctx->lib || !ctx->libmedia) { 1.147 + free(ctx); 1.148 + return CUBEB_ERROR; 1.149 + } 1.150 + 1.151 + /* Get the latency, in ms, from AudioFlinger */ 1.152 + /* status_t AudioSystem::getOutputLatency(uint32_t* latency, 1.153 + * audio_stream_type_t streamType) */ 1.154 + /* First, try the most recent signature. */ 1.155 + ctx->get_output_latency = 1.156 + dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"); 1.157 + if (!ctx->get_output_latency) { 1.158 + /* in case of failure, try the legacy version. */ 1.159 + /* status_t AudioSystem::getOutputLatency(uint32_t* latency, 1.160 + * int streamType) */ 1.161 + ctx->get_output_latency = 1.162 + dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji"); 1.163 + if (!ctx->get_output_latency) { 1.164 + opensl_destroy(ctx); 1.165 + return CUBEB_ERROR; 1.166 + } 1.167 + } 1.168 + 1.169 + typedef SLresult (*slCreateEngine_t)(SLObjectItf *, 1.170 + SLuint32, 1.171 + const SLEngineOption *, 1.172 + SLuint32, 1.173 + const SLInterfaceID *, 1.174 + const SLboolean *); 1.175 + slCreateEngine_t f_slCreateEngine = 1.176 + (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine"); 1.177 + SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE"); 1.178 + SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX"); 1.179 + ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE"); 1.180 +#if defined(__ANDROID__) 1.181 + ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION"); 1.182 +#endif 1.183 + ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY"); 1.184 + if (!f_slCreateEngine || 1.185 + !SL_IID_ENGINE || 1.186 + !SL_IID_OUTPUTMIX || 1.187 + !ctx->SL_IID_BUFFERQUEUE || 1.188 +#if defined(__ANDROID__) 1.189 + !ctx->SL_IID_ANDROIDCONFIGURATION || 1.190 +#endif 1.191 + !ctx->SL_IID_PLAY) { 1.192 + opensl_destroy(ctx); 1.193 + return CUBEB_ERROR; 1.194 + } 1.195 + 1.196 + const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}}; 1.197 + 1.198 + SLresult res; 1.199 + res = f_slCreateEngine(&ctx->engObj, 1, opt, 0, NULL, NULL); 1.200 + if (res != SL_RESULT_SUCCESS) { 1.201 + opensl_destroy(ctx); 1.202 + return CUBEB_ERROR; 1.203 + } 1.204 + 1.205 + res = (*ctx->engObj)->Realize(ctx->engObj, SL_BOOLEAN_FALSE); 1.206 + if (res != SL_RESULT_SUCCESS) { 1.207 + opensl_destroy(ctx); 1.208 + return CUBEB_ERROR; 1.209 + } 1.210 + 1.211 + res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng); 1.212 + if (res != SL_RESULT_SUCCESS) { 1.213 + opensl_destroy(ctx); 1.214 + return CUBEB_ERROR; 1.215 + } 1.216 + 1.217 + const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX}; 1.218 + const SLboolean reqom[] = {SL_BOOLEAN_TRUE}; 1.219 + res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom); 1.220 + if (res != SL_RESULT_SUCCESS) { 1.221 + opensl_destroy(ctx); 1.222 + return CUBEB_ERROR; 1.223 + } 1.224 + 1.225 + res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE); 1.226 + if (res != SL_RESULT_SUCCESS) { 1.227 + opensl_destroy(ctx); 1.228 + return CUBEB_ERROR; 1.229 + } 1.230 + 1.231 + *context = ctx; 1.232 + 1.233 + return CUBEB_OK; 1.234 +} 1.235 + 1.236 +static char const * 1.237 +opensl_get_backend_id(cubeb * ctx) 1.238 +{ 1.239 + return "opensl"; 1.240 +} 1.241 + 1.242 +static int 1.243 +opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) 1.244 +{ 1.245 + assert(ctx && max_channels); 1.246 + /* The android mixer handles up to two channels, see 1.247 + http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */ 1.248 + *max_channels = 2; 1.249 + 1.250 + return CUBEB_OK; 1.251 +} 1.252 + 1.253 +static int 1.254 +opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) 1.255 +{ 1.256 + /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html 1.257 + * We don't want to deal with JNI here (and we don't have Java on b2g anyways), 1.258 + * so we just dlopen the library and get the two symbols we need. */ 1.259 + int rv; 1.260 + void * libmedia; 1.261 + uint32_t (*get_primary_output_samplingrate)(); 1.262 + uint32_t (*get_output_samplingrate)(int * samplingRate, int streamType); 1.263 + uint32_t primary_sampling_rate; 1.264 + 1.265 + libmedia = dlopen("libmedia.so", RTLD_LAZY); 1.266 + if (!libmedia) { 1.267 + return CUBEB_ERROR; 1.268 + } 1.269 + 1.270 + /* uint32_t AudioSystem::getPrimaryOutputSamplingRate(void) */ 1.271 + get_primary_output_samplingrate = 1.272 + dlsym(libmedia, "_ZN7android11AudioSystem28getPrimaryOutputSamplingRateEv"); 1.273 + if (!get_primary_output_samplingrate) { 1.274 + /* fallback to 1.275 + * status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType) 1.276 + * if we cannot find getPrimaryOutputSamplingRate. */ 1.277 + get_output_samplingrate = 1.278 + dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPj19audio_stream_type_t"); 1.279 + if (!get_output_samplingrate) { 1.280 + /* Another signature exists, with a int instead of an audio_stream_type_t */ 1.281 + get_output_samplingrate = 1.282 + dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"); 1.283 + if (!get_output_samplingrate) { 1.284 + dlclose(libmedia); 1.285 + return CUBEB_ERROR; 1.286 + } 1.287 + } 1.288 + } 1.289 + 1.290 + if (get_primary_output_samplingrate) { 1.291 + *rate = get_primary_output_samplingrate(); 1.292 + } else { 1.293 + /* We don't really know about the type, here, so we just pass music. */ 1.294 + rv = get_output_samplingrate((int *) rate, AUDIO_STREAM_TYPE_MUSIC); 1.295 + if (rv) { 1.296 + dlclose(libmedia); 1.297 + return CUBEB_ERROR; 1.298 + } 1.299 + } 1.300 + 1.301 + dlclose(libmedia); 1.302 + 1.303 + /* Depending on which method we called above, we can get a zero back, yet have 1.304 + * a non-error return value, especially if the audio system is not 1.305 + * ready/shutting down (i.e. when we can't get our hand on the AudioFlinger 1.306 + * thread). */ 1.307 + if (*rate == 0) { 1.308 + return CUBEB_ERROR; 1.309 + } 1.310 + 1.311 + return CUBEB_OK; 1.312 +} 1.313 + 1.314 +static int 1.315 +opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) 1.316 +{ 1.317 + /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html 1.318 + * We don't want to deal with JNI here (and we don't have Java on b2g anyways), 1.319 + * so we just dlopen the library and get the two symbols we need. */ 1.320 + 1.321 + int rv; 1.322 + void * libmedia; 1.323 + size_t (*get_primary_output_frame_count)(void); 1.324 + int (*get_output_frame_count)(size_t * frameCount, int streamType); 1.325 + uint32_t primary_sampling_rate; 1.326 + size_t primary_buffer_size; 1.327 + 1.328 + rv = opensl_get_preferred_sample_rate(ctx, &primary_sampling_rate); 1.329 + 1.330 + if (rv) { 1.331 + return CUBEB_ERROR; 1.332 + } 1.333 + 1.334 + libmedia = dlopen("libmedia.so", RTLD_LAZY); 1.335 + if (!libmedia) { 1.336 + return CUBEB_ERROR; 1.337 + } 1.338 + 1.339 + /* JB variant */ 1.340 + /* size_t AudioSystem::getPrimaryOutputFrameCount(void) */ 1.341 + get_primary_output_frame_count = 1.342 + dlsym(libmedia, "_ZN7android11AudioSystem26getPrimaryOutputFrameCountEv"); 1.343 + if (!get_primary_output_frame_count) { 1.344 + /* ICS variant */ 1.345 + /* status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) */ 1.346 + get_output_frame_count = 1.347 + dlsym(libmedia, "_ZN7android11AudioSystem19getOutputFrameCountEPii"); 1.348 + if (!get_output_frame_count) { 1.349 + dlclose(libmedia); 1.350 + return CUBEB_ERROR; 1.351 + } 1.352 + } 1.353 + 1.354 + if (get_primary_output_frame_count) { 1.355 + primary_buffer_size = get_primary_output_frame_count(); 1.356 + } else { 1.357 + if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) { 1.358 + return CUBEB_ERROR; 1.359 + } 1.360 + } 1.361 + 1.362 + /* To get a fast track in Android's mixer, we need to be at the native 1.363 + * samplerate, which is device dependant. Some devices might be able to 1.364 + * resample when playing a fast track, but it's pretty rare. */ 1.365 + *latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000); 1.366 + 1.367 + dlclose(libmedia); 1.368 + 1.369 + return CUBEB_OK; 1.370 +} 1.371 + 1.372 +static void 1.373 +opensl_destroy(cubeb * ctx) 1.374 +{ 1.375 + if (ctx->outmixObj) 1.376 + (*ctx->outmixObj)->Destroy(ctx->outmixObj); 1.377 + if (ctx->engObj) 1.378 + (*ctx->engObj)->Destroy(ctx->engObj); 1.379 + dlclose(ctx->lib); 1.380 + dlclose(ctx->libmedia); 1.381 + free(ctx); 1.382 +} 1.383 + 1.384 +static void opensl_stream_destroy(cubeb_stream * stm); 1.385 + 1.386 +static int 1.387 +opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, 1.388 + cubeb_stream_params stream_params, unsigned int latency, 1.389 + cubeb_data_callback data_callback, cubeb_state_callback state_callback, 1.390 + void * user_ptr) 1.391 +{ 1.392 + cubeb_stream * stm; 1.393 + 1.394 + assert(ctx); 1.395 + 1.396 + *stream = NULL; 1.397 + 1.398 + if (stream_params.rate < 8000 || stream_params.rate > 88200 || 1.399 + stream_params.channels < 1 || stream_params.channels > 32 || 1.400 + latency < 1 || latency > 2000) { 1.401 + return CUBEB_ERROR_INVALID_FORMAT; 1.402 + } 1.403 + 1.404 + SLDataFormat_PCM format; 1.405 + 1.406 + format.formatType = SL_DATAFORMAT_PCM; 1.407 + format.numChannels = stream_params.channels; 1.408 + // samplesPerSec is in milliHertz 1.409 + format.samplesPerSec = stream_params.rate * 1000; 1.410 + format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; 1.411 + format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; 1.412 + format.channelMask = stream_params.channels == 1 ? 1.413 + SL_SPEAKER_FRONT_CENTER : 1.414 + SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; 1.415 + 1.416 + switch (stream_params.format) { 1.417 + case CUBEB_SAMPLE_S16LE: 1.418 + format.endianness = SL_BYTEORDER_LITTLEENDIAN; 1.419 + break; 1.420 + case CUBEB_SAMPLE_S16BE: 1.421 + format.endianness = SL_BYTEORDER_BIGENDIAN; 1.422 + break; 1.423 + default: 1.424 + return CUBEB_ERROR_INVALID_FORMAT; 1.425 + } 1.426 + 1.427 + stm = calloc(1, sizeof(*stm)); 1.428 + assert(stm); 1.429 + 1.430 + stm->context = ctx; 1.431 + stm->data_callback = data_callback; 1.432 + stm->state_callback = state_callback; 1.433 + stm->user_ptr = user_ptr; 1.434 + 1.435 + stm->stream_type = stream_params.stream_type; 1.436 + stm->framesize = stream_params.channels * sizeof(int16_t); 1.437 + stm->bytespersec = stream_params.rate * stm->framesize; 1.438 + stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS); 1.439 + // round up to the next multiple of stm->framesize, if needed. 1.440 + if (stm->queuebuf_len % stm->framesize) { 1.441 + stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize); 1.442 + } 1.443 + int i; 1.444 + for (i = 0; i < NBUFS; i++) { 1.445 + stm->queuebuf[i] = malloc(stm->queuebuf_len); 1.446 + assert(stm->queuebuf[i]); 1.447 + } 1.448 + 1.449 + SLDataLocator_BufferQueue loc_bufq; 1.450 + loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE; 1.451 + loc_bufq.numBuffers = NBUFS; 1.452 + SLDataSource source; 1.453 + source.pLocator = &loc_bufq; 1.454 + source.pFormat = &format; 1.455 + 1.456 + SLDataLocator_OutputMix loc_outmix; 1.457 + loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 1.458 + loc_outmix.outputMix = ctx->outmixObj; 1.459 + SLDataSink sink; 1.460 + sink.pLocator = &loc_outmix; 1.461 + sink.pFormat = NULL; 1.462 + 1.463 +#if defined(__ANDROID__) 1.464 + const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_ANDROIDCONFIGURATION}; 1.465 + const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; 1.466 +#else 1.467 + const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE}; 1.468 + const SLboolean req[] = {SL_BOOLEAN_TRUE}; 1.469 +#endif 1.470 + assert(NELEMS(ids) == NELEMS(req)); 1.471 + SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj, 1.472 + &source, &sink, NELEMS(ids), ids, req); 1.473 + if (res != SL_RESULT_SUCCESS) { 1.474 + opensl_stream_destroy(stm); 1.475 + return CUBEB_ERROR; 1.476 + } 1.477 + 1.478 +#if defined(__ANDROID__) 1.479 + SLuint32 stream_type = convert_stream_type_to_sl_stream(stream_params.stream_type); 1.480 + if (stream_type != 0xFFFFFFFF) { 1.481 + SLAndroidConfigurationItf playerConfig; 1.482 + res = (*stm->playerObj)->GetInterface(stm->playerObj, 1.483 + ctx->SL_IID_ANDROIDCONFIGURATION, &playerConfig); 1.484 + res = (*playerConfig)->SetConfiguration(playerConfig, 1.485 + SL_ANDROID_KEY_STREAM_TYPE, &stream_type, sizeof(SLint32)); 1.486 + if (res != SL_RESULT_SUCCESS) { 1.487 + opensl_stream_destroy(stm); 1.488 + return CUBEB_ERROR; 1.489 + } 1.490 + } 1.491 +#endif 1.492 + 1.493 + res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE); 1.494 + if (res != SL_RESULT_SUCCESS) { 1.495 + opensl_stream_destroy(stm); 1.496 + return CUBEB_ERROR; 1.497 + } 1.498 + 1.499 + res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play); 1.500 + if (res != SL_RESULT_SUCCESS) { 1.501 + opensl_stream_destroy(stm); 1.502 + return CUBEB_ERROR; 1.503 + } 1.504 + 1.505 + res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE, 1.506 + &stm->bufq); 1.507 + if (res != SL_RESULT_SUCCESS) { 1.508 + opensl_stream_destroy(stm); 1.509 + return CUBEB_ERROR; 1.510 + } 1.511 + 1.512 + res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm); 1.513 + if (res != SL_RESULT_SUCCESS) { 1.514 + opensl_stream_destroy(stm); 1.515 + return CUBEB_ERROR; 1.516 + } 1.517 + 1.518 + *stream = stm; 1.519 + return CUBEB_OK; 1.520 +} 1.521 + 1.522 +static void 1.523 +opensl_stream_destroy(cubeb_stream * stm) 1.524 +{ 1.525 + if (stm->playerObj) 1.526 + (*stm->playerObj)->Destroy(stm->playerObj); 1.527 + int i; 1.528 + for (i = 0; i < NBUFS; i++) { 1.529 + free(stm->queuebuf[i]); 1.530 + } 1.531 + 1.532 + free(stm); 1.533 +} 1.534 + 1.535 +static int 1.536 +opensl_stream_start(cubeb_stream * stm) 1.537 +{ 1.538 + /* To refill the queues before starting playback in order to avoid racing 1.539 + * with refills started by SetPlayState on OpenSLES ndk threads. */ 1.540 + bufferqueue_callback(NULL, stm); 1.541 + SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING); 1.542 + if (res != SL_RESULT_SUCCESS) 1.543 + return CUBEB_ERROR; 1.544 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); 1.545 + return CUBEB_OK; 1.546 +} 1.547 + 1.548 +static int 1.549 +opensl_stream_stop(cubeb_stream * stm) 1.550 +{ 1.551 + SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED); 1.552 + if (res != SL_RESULT_SUCCESS) 1.553 + return CUBEB_ERROR; 1.554 + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); 1.555 + return CUBEB_OK; 1.556 +} 1.557 + 1.558 +static int 1.559 +opensl_stream_get_position(cubeb_stream * stm, uint64_t * position) 1.560 +{ 1.561 + SLmillisecond msec; 1.562 + uint64_t samplerate; 1.563 + SLresult res; 1.564 + int rv; 1.565 + uint32_t mixer_latency; 1.566 + 1.567 + res = (*stm->play)->GetPosition(stm->play, &msec); 1.568 + if (res != SL_RESULT_SUCCESS) 1.569 + return CUBEB_ERROR; 1.570 + 1.571 + samplerate = stm->bytespersec / stm->framesize; 1.572 + 1.573 + rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type); 1.574 + if (rv) { 1.575 + return CUBEB_ERROR; 1.576 + } 1.577 + 1.578 + if (msec > mixer_latency) { 1.579 + *position = samplerate * (msec - mixer_latency) / 1000; 1.580 + } else { 1.581 + *position = 0; 1.582 + } 1.583 + return CUBEB_OK; 1.584 +} 1.585 + 1.586 +int 1.587 +opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency) 1.588 +{ 1.589 + int rv; 1.590 + uint32_t mixer_latency; 1.591 + uint32_t samplerate; 1.592 + 1.593 + /* The latency returned by AudioFlinger is in ms, so we have to get 1.594 + * AudioFlinger's samplerate to convert it to frames. */ 1.595 + rv = opensl_get_preferred_sample_rate(stm->context, &samplerate); 1.596 + if (rv) { 1.597 + return CUBEB_ERROR; 1.598 + } 1.599 + 1.600 + /* audio_stream_type_t is an int, so this is okay. */ 1.601 + rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type); 1.602 + if (rv) { 1.603 + return CUBEB_ERROR; 1.604 + } 1.605 + 1.606 + *latency = NBUFS * stm->queuebuf_len / stm->framesize + // OpenSL latency 1.607 + mixer_latency * samplerate / 1000; // AudioFlinger latency 1.608 + 1.609 + return CUBEB_OK; 1.610 +} 1.611 + 1.612 +static struct cubeb_ops const opensl_ops = { 1.613 + .init = opensl_init, 1.614 + .get_backend_id = opensl_get_backend_id, 1.615 + .get_max_channel_count = opensl_get_max_channel_count, 1.616 + .get_min_latency = opensl_get_min_latency, 1.617 + .get_preferred_sample_rate = opensl_get_preferred_sample_rate, 1.618 + .destroy = opensl_destroy, 1.619 + .stream_init = opensl_stream_init, 1.620 + .stream_destroy = opensl_stream_destroy, 1.621 + .stream_start = opensl_stream_start, 1.622 + .stream_stop = opensl_stream_stop, 1.623 + .stream_get_position = opensl_stream_get_position, 1.624 + .stream_get_latency = opensl_stream_get_latency 1.625 +};