media/libcubeb/src/cubeb_opensl.c

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /*
michael@0 2 * Copyright © 2012 Mozilla Foundation
michael@0 3 *
michael@0 4 * This program is made available under an ISC-style license. See the
michael@0 5 * accompanying file LICENSE for details.
michael@0 6 */
michael@0 7 #undef NDEBUG
michael@0 8 #include <assert.h>
michael@0 9 #include <dlfcn.h>
michael@0 10 #include <stdlib.h>
michael@0 11 #include <SLES/OpenSLES.h>
michael@0 12 #if defined(__ANDROID__)
michael@0 13 #include "android/sles_definitions.h"
michael@0 14 #include <SLES/OpenSLES_Android.h>
michael@0 15 #include <android/log.h>
michael@0 16 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args)
michael@0 17 #endif
michael@0 18 #include "cubeb/cubeb.h"
michael@0 19 #include "cubeb-internal.h"
michael@0 20
michael@0 21 static struct cubeb_ops const opensl_ops;
michael@0 22
michael@0 23 struct cubeb {
michael@0 24 struct cubeb_ops const * ops;
michael@0 25 void * lib;
michael@0 26 void * libmedia;
michael@0 27 int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
michael@0 28 SLInterfaceID SL_IID_BUFFERQUEUE;
michael@0 29 SLInterfaceID SL_IID_PLAY;
michael@0 30 #if defined(__ANDROID__)
michael@0 31 SLInterfaceID SL_IID_ANDROIDCONFIGURATION;
michael@0 32 #endif
michael@0 33 SLObjectItf engObj;
michael@0 34 SLEngineItf eng;
michael@0 35 SLObjectItf outmixObj;
michael@0 36 };
michael@0 37
michael@0 38 #define NELEMS(A) (sizeof(A) / sizeof A[0])
michael@0 39 #define NBUFS 4
michael@0 40 #define AUDIO_STREAM_TYPE_MUSIC 3
michael@0 41
michael@0 42 struct cubeb_stream {
michael@0 43 cubeb * context;
michael@0 44 SLObjectItf playerObj;
michael@0 45 SLPlayItf play;
michael@0 46 SLBufferQueueItf bufq;
michael@0 47 void *queuebuf[NBUFS];
michael@0 48 int queuebuf_idx;
michael@0 49 long queuebuf_len;
michael@0 50 long bytespersec;
michael@0 51 long framesize;
michael@0 52 int draining;
michael@0 53 cubeb_stream_type stream_type;
michael@0 54
michael@0 55 cubeb_data_callback data_callback;
michael@0 56 cubeb_state_callback state_callback;
michael@0 57 void * user_ptr;
michael@0 58 };
michael@0 59
michael@0 60 static void
michael@0 61 bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
michael@0 62 {
michael@0 63 cubeb_stream * stm = user_ptr;
michael@0 64 SLBufferQueueState state;
michael@0 65 (*stm->bufq)->GetState(stm->bufq, &state);
michael@0 66
michael@0 67 if (stm->draining) {
michael@0 68 if (!state.count) {
michael@0 69 stm->draining = 0;
michael@0 70 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
michael@0 71 }
michael@0 72 return;
michael@0 73 }
michael@0 74
michael@0 75 if (state.count > 1)
michael@0 76 return;
michael@0 77
michael@0 78 SLuint32 i;
michael@0 79 for (i = state.count; i < NBUFS; i++) {
michael@0 80 void *buf = stm->queuebuf[stm->queuebuf_idx];
michael@0 81 long written = stm->data_callback(stm, stm->user_ptr,
michael@0 82 buf, stm->queuebuf_len / stm->framesize);
michael@0 83 if (written == CUBEB_ERROR) {
michael@0 84 (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_STOPPED);
michael@0 85 return;
michael@0 86 }
michael@0 87
michael@0 88 if (written) {
michael@0 89 (*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
michael@0 90 stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
michael@0 91 } else if (!i) {
michael@0 92 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
michael@0 93 return;
michael@0 94 }
michael@0 95
michael@0 96 if ((written * stm->framesize) < stm->queuebuf_len) {
michael@0 97 stm->draining = 1;
michael@0 98 return;
michael@0 99 }
michael@0 100 }
michael@0 101 }
michael@0 102
michael@0 103 #if defined(__ANDROID__)
michael@0 104 static SLuint32
michael@0 105 convert_stream_type_to_sl_stream(cubeb_stream_type stream_type)
michael@0 106 {
michael@0 107 switch(stream_type) {
michael@0 108 case CUBEB_STREAM_TYPE_SYSTEM:
michael@0 109 return SL_ANDROID_STREAM_SYSTEM;
michael@0 110 case CUBEB_STREAM_TYPE_MUSIC:
michael@0 111 return SL_ANDROID_STREAM_MEDIA;
michael@0 112 case CUBEB_STREAM_TYPE_NOTIFICATION:
michael@0 113 return SL_ANDROID_STREAM_NOTIFICATION;
michael@0 114 case CUBEB_STREAM_TYPE_ALARM:
michael@0 115 return SL_ANDROID_STREAM_ALARM;
michael@0 116 case CUBEB_STREAM_TYPE_VOICE_CALL:
michael@0 117 return SL_ANDROID_STREAM_VOICE;
michael@0 118 case CUBEB_STREAM_TYPE_RING:
michael@0 119 return SL_ANDROID_STREAM_RING;
michael@0 120 case CUBEB_STREAM_TYPE_ENFORCED_AUDIBLE:
michael@0 121 default:
michael@0 122 return 0xFFFFFFFF;
michael@0 123 }
michael@0 124 }
michael@0 125 #endif
michael@0 126
michael@0 127 static void opensl_destroy(cubeb * ctx);
michael@0 128
michael@0 129 /*static*/ int
michael@0 130 opensl_init(cubeb ** context, char const * context_name)
michael@0 131 {
michael@0 132 cubeb * ctx;
michael@0 133
michael@0 134 *context = NULL;
michael@0 135
michael@0 136 ctx = calloc(1, sizeof(*ctx));
michael@0 137 assert(ctx);
michael@0 138
michael@0 139 ctx->ops = &opensl_ops;
michael@0 140
michael@0 141 ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY);
michael@0 142 ctx->libmedia = dlopen("libmedia.so", RTLD_LAZY);
michael@0 143 if (!ctx->lib || !ctx->libmedia) {
michael@0 144 free(ctx);
michael@0 145 return CUBEB_ERROR;
michael@0 146 }
michael@0 147
michael@0 148 /* Get the latency, in ms, from AudioFlinger */
michael@0 149 /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
michael@0 150 * audio_stream_type_t streamType) */
michael@0 151 /* First, try the most recent signature. */
michael@0 152 ctx->get_output_latency =
michael@0 153 dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
michael@0 154 if (!ctx->get_output_latency) {
michael@0 155 /* in case of failure, try the legacy version. */
michael@0 156 /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
michael@0 157 * int streamType) */
michael@0 158 ctx->get_output_latency =
michael@0 159 dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
michael@0 160 if (!ctx->get_output_latency) {
michael@0 161 opensl_destroy(ctx);
michael@0 162 return CUBEB_ERROR;
michael@0 163 }
michael@0 164 }
michael@0 165
michael@0 166 typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
michael@0 167 SLuint32,
michael@0 168 const SLEngineOption *,
michael@0 169 SLuint32,
michael@0 170 const SLInterfaceID *,
michael@0 171 const SLboolean *);
michael@0 172 slCreateEngine_t f_slCreateEngine =
michael@0 173 (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
michael@0 174 SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
michael@0 175 SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
michael@0 176 ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
michael@0 177 #if defined(__ANDROID__)
michael@0 178 ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
michael@0 179 #endif
michael@0 180 ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
michael@0 181 if (!f_slCreateEngine ||
michael@0 182 !SL_IID_ENGINE ||
michael@0 183 !SL_IID_OUTPUTMIX ||
michael@0 184 !ctx->SL_IID_BUFFERQUEUE ||
michael@0 185 #if defined(__ANDROID__)
michael@0 186 !ctx->SL_IID_ANDROIDCONFIGURATION ||
michael@0 187 #endif
michael@0 188 !ctx->SL_IID_PLAY) {
michael@0 189 opensl_destroy(ctx);
michael@0 190 return CUBEB_ERROR;
michael@0 191 }
michael@0 192
michael@0 193 const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}};
michael@0 194
michael@0 195 SLresult res;
michael@0 196 res = f_slCreateEngine(&ctx->engObj, 1, opt, 0, NULL, NULL);
michael@0 197 if (res != SL_RESULT_SUCCESS) {
michael@0 198 opensl_destroy(ctx);
michael@0 199 return CUBEB_ERROR;
michael@0 200 }
michael@0 201
michael@0 202 res = (*ctx->engObj)->Realize(ctx->engObj, SL_BOOLEAN_FALSE);
michael@0 203 if (res != SL_RESULT_SUCCESS) {
michael@0 204 opensl_destroy(ctx);
michael@0 205 return CUBEB_ERROR;
michael@0 206 }
michael@0 207
michael@0 208 res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng);
michael@0 209 if (res != SL_RESULT_SUCCESS) {
michael@0 210 opensl_destroy(ctx);
michael@0 211 return CUBEB_ERROR;
michael@0 212 }
michael@0 213
michael@0 214 const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
michael@0 215 const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
michael@0 216 res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
michael@0 217 if (res != SL_RESULT_SUCCESS) {
michael@0 218 opensl_destroy(ctx);
michael@0 219 return CUBEB_ERROR;
michael@0 220 }
michael@0 221
michael@0 222 res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE);
michael@0 223 if (res != SL_RESULT_SUCCESS) {
michael@0 224 opensl_destroy(ctx);
michael@0 225 return CUBEB_ERROR;
michael@0 226 }
michael@0 227
michael@0 228 *context = ctx;
michael@0 229
michael@0 230 return CUBEB_OK;
michael@0 231 }
michael@0 232
michael@0 233 static char const *
michael@0 234 opensl_get_backend_id(cubeb * ctx)
michael@0 235 {
michael@0 236 return "opensl";
michael@0 237 }
michael@0 238
michael@0 239 static int
michael@0 240 opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
michael@0 241 {
michael@0 242 assert(ctx && max_channels);
michael@0 243 /* The android mixer handles up to two channels, see
michael@0 244 http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
michael@0 245 *max_channels = 2;
michael@0 246
michael@0 247 return CUBEB_OK;
michael@0 248 }
michael@0 249
michael@0 250 static int
michael@0 251 opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
michael@0 252 {
michael@0 253 /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html
michael@0 254 * We don't want to deal with JNI here (and we don't have Java on b2g anyways),
michael@0 255 * so we just dlopen the library and get the two symbols we need. */
michael@0 256 int rv;
michael@0 257 void * libmedia;
michael@0 258 uint32_t (*get_primary_output_samplingrate)();
michael@0 259 uint32_t (*get_output_samplingrate)(int * samplingRate, int streamType);
michael@0 260 uint32_t primary_sampling_rate;
michael@0 261
michael@0 262 libmedia = dlopen("libmedia.so", RTLD_LAZY);
michael@0 263 if (!libmedia) {
michael@0 264 return CUBEB_ERROR;
michael@0 265 }
michael@0 266
michael@0 267 /* uint32_t AudioSystem::getPrimaryOutputSamplingRate(void) */
michael@0 268 get_primary_output_samplingrate =
michael@0 269 dlsym(libmedia, "_ZN7android11AudioSystem28getPrimaryOutputSamplingRateEv");
michael@0 270 if (!get_primary_output_samplingrate) {
michael@0 271 /* fallback to
michael@0 272 * status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType)
michael@0 273 * if we cannot find getPrimaryOutputSamplingRate. */
michael@0 274 get_output_samplingrate =
michael@0 275 dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPj19audio_stream_type_t");
michael@0 276 if (!get_output_samplingrate) {
michael@0 277 /* Another signature exists, with a int instead of an audio_stream_type_t */
michael@0 278 get_output_samplingrate =
michael@0 279 dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPii");
michael@0 280 if (!get_output_samplingrate) {
michael@0 281 dlclose(libmedia);
michael@0 282 return CUBEB_ERROR;
michael@0 283 }
michael@0 284 }
michael@0 285 }
michael@0 286
michael@0 287 if (get_primary_output_samplingrate) {
michael@0 288 *rate = get_primary_output_samplingrate();
michael@0 289 } else {
michael@0 290 /* We don't really know about the type, here, so we just pass music. */
michael@0 291 rv = get_output_samplingrate((int *) rate, AUDIO_STREAM_TYPE_MUSIC);
michael@0 292 if (rv) {
michael@0 293 dlclose(libmedia);
michael@0 294 return CUBEB_ERROR;
michael@0 295 }
michael@0 296 }
michael@0 297
michael@0 298 dlclose(libmedia);
michael@0 299
michael@0 300 /* Depending on which method we called above, we can get a zero back, yet have
michael@0 301 * a non-error return value, especially if the audio system is not
michael@0 302 * ready/shutting down (i.e. when we can't get our hand on the AudioFlinger
michael@0 303 * thread). */
michael@0 304 if (*rate == 0) {
michael@0 305 return CUBEB_ERROR;
michael@0 306 }
michael@0 307
michael@0 308 return CUBEB_OK;
michael@0 309 }
michael@0 310
michael@0 311 static int
michael@0 312 opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
michael@0 313 {
michael@0 314 /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html
michael@0 315 * We don't want to deal with JNI here (and we don't have Java on b2g anyways),
michael@0 316 * so we just dlopen the library and get the two symbols we need. */
michael@0 317
michael@0 318 int rv;
michael@0 319 void * libmedia;
michael@0 320 size_t (*get_primary_output_frame_count)(void);
michael@0 321 int (*get_output_frame_count)(size_t * frameCount, int streamType);
michael@0 322 uint32_t primary_sampling_rate;
michael@0 323 size_t primary_buffer_size;
michael@0 324
michael@0 325 rv = opensl_get_preferred_sample_rate(ctx, &primary_sampling_rate);
michael@0 326
michael@0 327 if (rv) {
michael@0 328 return CUBEB_ERROR;
michael@0 329 }
michael@0 330
michael@0 331 libmedia = dlopen("libmedia.so", RTLD_LAZY);
michael@0 332 if (!libmedia) {
michael@0 333 return CUBEB_ERROR;
michael@0 334 }
michael@0 335
michael@0 336 /* JB variant */
michael@0 337 /* size_t AudioSystem::getPrimaryOutputFrameCount(void) */
michael@0 338 get_primary_output_frame_count =
michael@0 339 dlsym(libmedia, "_ZN7android11AudioSystem26getPrimaryOutputFrameCountEv");
michael@0 340 if (!get_primary_output_frame_count) {
michael@0 341 /* ICS variant */
michael@0 342 /* status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) */
michael@0 343 get_output_frame_count =
michael@0 344 dlsym(libmedia, "_ZN7android11AudioSystem19getOutputFrameCountEPii");
michael@0 345 if (!get_output_frame_count) {
michael@0 346 dlclose(libmedia);
michael@0 347 return CUBEB_ERROR;
michael@0 348 }
michael@0 349 }
michael@0 350
michael@0 351 if (get_primary_output_frame_count) {
michael@0 352 primary_buffer_size = get_primary_output_frame_count();
michael@0 353 } else {
michael@0 354 if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) {
michael@0 355 return CUBEB_ERROR;
michael@0 356 }
michael@0 357 }
michael@0 358
michael@0 359 /* To get a fast track in Android's mixer, we need to be at the native
michael@0 360 * samplerate, which is device dependant. Some devices might be able to
michael@0 361 * resample when playing a fast track, but it's pretty rare. */
michael@0 362 *latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000);
michael@0 363
michael@0 364 dlclose(libmedia);
michael@0 365
michael@0 366 return CUBEB_OK;
michael@0 367 }
michael@0 368
michael@0 369 static void
michael@0 370 opensl_destroy(cubeb * ctx)
michael@0 371 {
michael@0 372 if (ctx->outmixObj)
michael@0 373 (*ctx->outmixObj)->Destroy(ctx->outmixObj);
michael@0 374 if (ctx->engObj)
michael@0 375 (*ctx->engObj)->Destroy(ctx->engObj);
michael@0 376 dlclose(ctx->lib);
michael@0 377 dlclose(ctx->libmedia);
michael@0 378 free(ctx);
michael@0 379 }
michael@0 380
michael@0 381 static void opensl_stream_destroy(cubeb_stream * stm);
michael@0 382
michael@0 383 static int
michael@0 384 opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
michael@0 385 cubeb_stream_params stream_params, unsigned int latency,
michael@0 386 cubeb_data_callback data_callback, cubeb_state_callback state_callback,
michael@0 387 void * user_ptr)
michael@0 388 {
michael@0 389 cubeb_stream * stm;
michael@0 390
michael@0 391 assert(ctx);
michael@0 392
michael@0 393 *stream = NULL;
michael@0 394
michael@0 395 if (stream_params.rate < 8000 || stream_params.rate > 88200 ||
michael@0 396 stream_params.channels < 1 || stream_params.channels > 32 ||
michael@0 397 latency < 1 || latency > 2000) {
michael@0 398 return CUBEB_ERROR_INVALID_FORMAT;
michael@0 399 }
michael@0 400
michael@0 401 SLDataFormat_PCM format;
michael@0 402
michael@0 403 format.formatType = SL_DATAFORMAT_PCM;
michael@0 404 format.numChannels = stream_params.channels;
michael@0 405 // samplesPerSec is in milliHertz
michael@0 406 format.samplesPerSec = stream_params.rate * 1000;
michael@0 407 format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
michael@0 408 format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
michael@0 409 format.channelMask = stream_params.channels == 1 ?
michael@0 410 SL_SPEAKER_FRONT_CENTER :
michael@0 411 SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
michael@0 412
michael@0 413 switch (stream_params.format) {
michael@0 414 case CUBEB_SAMPLE_S16LE:
michael@0 415 format.endianness = SL_BYTEORDER_LITTLEENDIAN;
michael@0 416 break;
michael@0 417 case CUBEB_SAMPLE_S16BE:
michael@0 418 format.endianness = SL_BYTEORDER_BIGENDIAN;
michael@0 419 break;
michael@0 420 default:
michael@0 421 return CUBEB_ERROR_INVALID_FORMAT;
michael@0 422 }
michael@0 423
michael@0 424 stm = calloc(1, sizeof(*stm));
michael@0 425 assert(stm);
michael@0 426
michael@0 427 stm->context = ctx;
michael@0 428 stm->data_callback = data_callback;
michael@0 429 stm->state_callback = state_callback;
michael@0 430 stm->user_ptr = user_ptr;
michael@0 431
michael@0 432 stm->stream_type = stream_params.stream_type;
michael@0 433 stm->framesize = stream_params.channels * sizeof(int16_t);
michael@0 434 stm->bytespersec = stream_params.rate * stm->framesize;
michael@0 435 stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
michael@0 436 // round up to the next multiple of stm->framesize, if needed.
michael@0 437 if (stm->queuebuf_len % stm->framesize) {
michael@0 438 stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
michael@0 439 }
michael@0 440 int i;
michael@0 441 for (i = 0; i < NBUFS; i++) {
michael@0 442 stm->queuebuf[i] = malloc(stm->queuebuf_len);
michael@0 443 assert(stm->queuebuf[i]);
michael@0 444 }
michael@0 445
michael@0 446 SLDataLocator_BufferQueue loc_bufq;
michael@0 447 loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
michael@0 448 loc_bufq.numBuffers = NBUFS;
michael@0 449 SLDataSource source;
michael@0 450 source.pLocator = &loc_bufq;
michael@0 451 source.pFormat = &format;
michael@0 452
michael@0 453 SLDataLocator_OutputMix loc_outmix;
michael@0 454 loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
michael@0 455 loc_outmix.outputMix = ctx->outmixObj;
michael@0 456 SLDataSink sink;
michael@0 457 sink.pLocator = &loc_outmix;
michael@0 458 sink.pFormat = NULL;
michael@0 459
michael@0 460 #if defined(__ANDROID__)
michael@0 461 const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_ANDROIDCONFIGURATION};
michael@0 462 const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
michael@0 463 #else
michael@0 464 const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE};
michael@0 465 const SLboolean req[] = {SL_BOOLEAN_TRUE};
michael@0 466 #endif
michael@0 467 assert(NELEMS(ids) == NELEMS(req));
michael@0 468 SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
michael@0 469 &source, &sink, NELEMS(ids), ids, req);
michael@0 470 if (res != SL_RESULT_SUCCESS) {
michael@0 471 opensl_stream_destroy(stm);
michael@0 472 return CUBEB_ERROR;
michael@0 473 }
michael@0 474
michael@0 475 #if defined(__ANDROID__)
michael@0 476 SLuint32 stream_type = convert_stream_type_to_sl_stream(stream_params.stream_type);
michael@0 477 if (stream_type != 0xFFFFFFFF) {
michael@0 478 SLAndroidConfigurationItf playerConfig;
michael@0 479 res = (*stm->playerObj)->GetInterface(stm->playerObj,
michael@0 480 ctx->SL_IID_ANDROIDCONFIGURATION, &playerConfig);
michael@0 481 res = (*playerConfig)->SetConfiguration(playerConfig,
michael@0 482 SL_ANDROID_KEY_STREAM_TYPE, &stream_type, sizeof(SLint32));
michael@0 483 if (res != SL_RESULT_SUCCESS) {
michael@0 484 opensl_stream_destroy(stm);
michael@0 485 return CUBEB_ERROR;
michael@0 486 }
michael@0 487 }
michael@0 488 #endif
michael@0 489
michael@0 490 res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
michael@0 491 if (res != SL_RESULT_SUCCESS) {
michael@0 492 opensl_stream_destroy(stm);
michael@0 493 return CUBEB_ERROR;
michael@0 494 }
michael@0 495
michael@0 496 res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play);
michael@0 497 if (res != SL_RESULT_SUCCESS) {
michael@0 498 opensl_stream_destroy(stm);
michael@0 499 return CUBEB_ERROR;
michael@0 500 }
michael@0 501
michael@0 502 res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE,
michael@0 503 &stm->bufq);
michael@0 504 if (res != SL_RESULT_SUCCESS) {
michael@0 505 opensl_stream_destroy(stm);
michael@0 506 return CUBEB_ERROR;
michael@0 507 }
michael@0 508
michael@0 509 res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
michael@0 510 if (res != SL_RESULT_SUCCESS) {
michael@0 511 opensl_stream_destroy(stm);
michael@0 512 return CUBEB_ERROR;
michael@0 513 }
michael@0 514
michael@0 515 *stream = stm;
michael@0 516 return CUBEB_OK;
michael@0 517 }
michael@0 518
michael@0 519 static void
michael@0 520 opensl_stream_destroy(cubeb_stream * stm)
michael@0 521 {
michael@0 522 if (stm->playerObj)
michael@0 523 (*stm->playerObj)->Destroy(stm->playerObj);
michael@0 524 int i;
michael@0 525 for (i = 0; i < NBUFS; i++) {
michael@0 526 free(stm->queuebuf[i]);
michael@0 527 }
michael@0 528
michael@0 529 free(stm);
michael@0 530 }
michael@0 531
michael@0 532 static int
michael@0 533 opensl_stream_start(cubeb_stream * stm)
michael@0 534 {
michael@0 535 /* To refill the queues before starting playback in order to avoid racing
michael@0 536 * with refills started by SetPlayState on OpenSLES ndk threads. */
michael@0 537 bufferqueue_callback(NULL, stm);
michael@0 538 SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING);
michael@0 539 if (res != SL_RESULT_SUCCESS)
michael@0 540 return CUBEB_ERROR;
michael@0 541 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
michael@0 542 return CUBEB_OK;
michael@0 543 }
michael@0 544
michael@0 545 static int
michael@0 546 opensl_stream_stop(cubeb_stream * stm)
michael@0 547 {
michael@0 548 SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
michael@0 549 if (res != SL_RESULT_SUCCESS)
michael@0 550 return CUBEB_ERROR;
michael@0 551 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
michael@0 552 return CUBEB_OK;
michael@0 553 }
michael@0 554
michael@0 555 static int
michael@0 556 opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
michael@0 557 {
michael@0 558 SLmillisecond msec;
michael@0 559 uint64_t samplerate;
michael@0 560 SLresult res;
michael@0 561 int rv;
michael@0 562 uint32_t mixer_latency;
michael@0 563
michael@0 564 res = (*stm->play)->GetPosition(stm->play, &msec);
michael@0 565 if (res != SL_RESULT_SUCCESS)
michael@0 566 return CUBEB_ERROR;
michael@0 567
michael@0 568 samplerate = stm->bytespersec / stm->framesize;
michael@0 569
michael@0 570 rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
michael@0 571 if (rv) {
michael@0 572 return CUBEB_ERROR;
michael@0 573 }
michael@0 574
michael@0 575 if (msec > mixer_latency) {
michael@0 576 *position = samplerate * (msec - mixer_latency) / 1000;
michael@0 577 } else {
michael@0 578 *position = 0;
michael@0 579 }
michael@0 580 return CUBEB_OK;
michael@0 581 }
michael@0 582
michael@0 583 int
michael@0 584 opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
michael@0 585 {
michael@0 586 int rv;
michael@0 587 uint32_t mixer_latency;
michael@0 588 uint32_t samplerate;
michael@0 589
michael@0 590 /* The latency returned by AudioFlinger is in ms, so we have to get
michael@0 591 * AudioFlinger's samplerate to convert it to frames. */
michael@0 592 rv = opensl_get_preferred_sample_rate(stm->context, &samplerate);
michael@0 593 if (rv) {
michael@0 594 return CUBEB_ERROR;
michael@0 595 }
michael@0 596
michael@0 597 /* audio_stream_type_t is an int, so this is okay. */
michael@0 598 rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
michael@0 599 if (rv) {
michael@0 600 return CUBEB_ERROR;
michael@0 601 }
michael@0 602
michael@0 603 *latency = NBUFS * stm->queuebuf_len / stm->framesize + // OpenSL latency
michael@0 604 mixer_latency * samplerate / 1000; // AudioFlinger latency
michael@0 605
michael@0 606 return CUBEB_OK;
michael@0 607 }
michael@0 608
michael@0 609 static struct cubeb_ops const opensl_ops = {
michael@0 610 .init = opensl_init,
michael@0 611 .get_backend_id = opensl_get_backend_id,
michael@0 612 .get_max_channel_count = opensl_get_max_channel_count,
michael@0 613 .get_min_latency = opensl_get_min_latency,
michael@0 614 .get_preferred_sample_rate = opensl_get_preferred_sample_rate,
michael@0 615 .destroy = opensl_destroy,
michael@0 616 .stream_init = opensl_stream_init,
michael@0 617 .stream_destroy = opensl_stream_destroy,
michael@0 618 .stream_start = opensl_stream_start,
michael@0 619 .stream_stop = opensl_stream_stop,
michael@0 620 .stream_get_position = opensl_stream_get_position,
michael@0 621 .stream_get_latency = opensl_stream_get_latency
michael@0 622 };

mercurial