Bug 865244 - Update in-tree cubeb to have cubeb_get_max_channels. r=kinetik
[gecko.git] / media / libcubeb / src / cubeb_opensl.c
blobf1183f4473e4f655830118e22a0ababdde0d6d2b
1 /*
2 * Copyright © 2012 Mozilla Foundation
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
6 */
7 #undef NDEBUG
8 #include <assert.h>
9 #include <dlfcn.h>
10 #include <stdlib.h>
11 #include <SLES/OpenSLES.h>
12 #if defined(__ANDROID__)
13 #include "android/sles_definitions.h"
14 #include <SLES/OpenSLES_Android.h>
15 #endif
16 #include "cubeb/cubeb.h"
17 #include "cubeb-internal.h"
19 static struct cubeb_ops const opensl_ops;
21 struct cubeb {
22 struct cubeb_ops const * ops;
23 void * lib;
24 SLInterfaceID SL_IID_BUFFERQUEUE;
25 SLInterfaceID SL_IID_PLAY;
26 #if defined(__ANDROID__)
27 SLInterfaceID SL_IID_ANDROIDCONFIGURATION;
28 #endif
29 SLObjectItf engObj;
30 SLEngineItf eng;
31 SLObjectItf outmixObj;
34 #define NELEMS(A) (sizeof(A) / sizeof A[0])
35 #define NBUFS 4
37 struct cubeb_stream {
38 cubeb * context;
39 SLObjectItf playerObj;
40 SLPlayItf play;
41 SLBufferQueueItf bufq;
42 void *queuebuf[NBUFS];
43 int queuebuf_idx;
44 long queuebuf_len;
45 long bytespersec;
46 long framesize;
47 int draining;
49 cubeb_data_callback data_callback;
50 cubeb_state_callback state_callback;
51 void * user_ptr;
54 static void
55 bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
57 cubeb_stream * stm = user_ptr;
58 SLBufferQueueState state;
59 (*stm->bufq)->GetState(stm->bufq, &state);
61 if (stm->draining) {
62 if (!state.count) {
63 stm->draining = 0;
64 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
66 return;
69 if (state.count > 1)
70 return;
72 SLuint32 i;
73 for (i = state.count; i < NBUFS; i++) {
74 void *buf = stm->queuebuf[stm->queuebuf_idx];
75 long written = stm->data_callback(stm, stm->user_ptr,
76 buf, stm->queuebuf_len / stm->framesize);
77 if (written == CUBEB_ERROR) {
78 (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_STOPPED);
79 return;
82 if (written) {
83 (*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
84 stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
85 } else if (!i) {
86 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
87 return;
90 if ((written * stm->framesize) < stm->queuebuf_len) {
91 stm->draining = 1;
92 return;
97 #if defined(__ANDROID__)
98 static SLuint32
99 convert_stream_type_to_sl_stream(cubeb_stream_type stream_type)
101 switch(stream_type) {
102 case CUBEB_STREAM_TYPE_SYSTEM:
103 return SL_ANDROID_STREAM_SYSTEM;
104 case CUBEB_STREAM_TYPE_MUSIC:
105 return SL_ANDROID_STREAM_MEDIA;
106 case CUBEB_STREAM_TYPE_NOTIFICATION:
107 return SL_ANDROID_STREAM_NOTIFICATION;
108 case CUBEB_STREAM_TYPE_ALARM:
109 return SL_ANDROID_STREAM_ALARM;
110 case CUBEB_STREAM_TYPE_VOICE_CALL:
111 return SL_ANDROID_STREAM_VOICE;
112 case CUBEB_STREAM_TYPE_RING:
113 return SL_ANDROID_STREAM_RING;
114 case CUBEB_STREAM_TYPE_ENFORCED_AUDIBLE:
115 default:
116 return 0xFFFFFFFF;
119 #endif
121 static void opensl_destroy(cubeb * ctx);
123 /*static*/ int
124 opensl_init(cubeb ** context, char const * context_name)
126 cubeb * ctx;
128 *context = NULL;
130 ctx = calloc(1, sizeof(*ctx));
131 assert(ctx);
133 ctx->ops = &opensl_ops;
135 ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY);
136 if (!ctx->lib) {
137 free(ctx);
138 return CUBEB_ERROR;
141 typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
142 SLuint32,
143 const SLEngineOption *,
144 SLuint32,
145 const SLInterfaceID *,
146 const SLboolean *);
147 slCreateEngine_t f_slCreateEngine =
148 (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
149 SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
150 SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
151 ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
152 #if defined(__ANDROID__)
153 ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
154 #endif
155 ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
156 if (!f_slCreateEngine ||
157 !SL_IID_ENGINE ||
158 !SL_IID_OUTPUTMIX ||
159 !ctx->SL_IID_BUFFERQUEUE ||
160 #if defined(__ANDROID__)
161 !ctx->SL_IID_ANDROIDCONFIGURATION ||
162 #endif
163 !ctx->SL_IID_PLAY) {
164 opensl_destroy(ctx);
165 return CUBEB_ERROR;
169 const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}};
171 SLresult res;
172 res = f_slCreateEngine(&ctx->engObj, 1, opt, 0, NULL, NULL);
173 if (res != SL_RESULT_SUCCESS) {
174 opensl_destroy(ctx);
175 return CUBEB_ERROR;
178 res = (*ctx->engObj)->Realize(ctx->engObj, SL_BOOLEAN_FALSE);
179 if (res != SL_RESULT_SUCCESS) {
180 opensl_destroy(ctx);
181 return CUBEB_ERROR;
184 res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng);
185 if (res != SL_RESULT_SUCCESS) {
186 opensl_destroy(ctx);
187 return CUBEB_ERROR;
190 const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
191 const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
192 res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
193 if (res != SL_RESULT_SUCCESS) {
194 opensl_destroy(ctx);
195 return CUBEB_ERROR;
198 res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE);
199 if (res != SL_RESULT_SUCCESS) {
200 opensl_destroy(ctx);
201 return CUBEB_ERROR;
204 *context = ctx;
206 return CUBEB_OK;
209 static char const *
210 opensl_get_backend_id(cubeb * ctx)
212 return "opensl";
215 static int
216 opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
218 assert(ctx && max_channels);
219 /* The android mixer handles up to two channels, see
220 http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
221 *max_channels = 2;
223 return CUBEB_OK;
226 static void
227 opensl_destroy(cubeb * ctx)
229 if (ctx->outmixObj)
230 (*ctx->outmixObj)->Destroy(ctx->outmixObj);
231 if (ctx->engObj)
232 (*ctx->engObj)->Destroy(ctx->engObj);
233 dlclose(ctx->lib);
234 free(ctx);
237 static void opensl_stream_destroy(cubeb_stream * stm);
239 static int
240 opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
241 cubeb_stream_params stream_params, unsigned int latency,
242 cubeb_data_callback data_callback, cubeb_state_callback state_callback,
243 void * user_ptr)
245 cubeb_stream * stm;
247 assert(ctx);
249 *stream = NULL;
251 if (stream_params.rate < 8000 || stream_params.rate > 48000 ||
252 stream_params.channels < 1 || stream_params.channels > 32 ||
253 latency < 1 || latency > 2000) {
254 return CUBEB_ERROR_INVALID_FORMAT;
257 SLDataFormat_PCM format;
259 format.formatType = SL_DATAFORMAT_PCM;
260 format.numChannels = stream_params.channels;
261 // samplesPerSec is in milliHertz
262 format.samplesPerSec = stream_params.rate * 1000;
263 format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
264 format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
265 format.channelMask = stream_params.channels == 1 ?
266 SL_SPEAKER_FRONT_CENTER :
267 SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
269 switch (stream_params.format) {
270 case CUBEB_SAMPLE_S16LE:
271 format.endianness = SL_BYTEORDER_LITTLEENDIAN;
272 break;
273 case CUBEB_SAMPLE_S16BE:
274 format.endianness = SL_BYTEORDER_BIGENDIAN;
275 break;
276 default:
277 return CUBEB_ERROR_INVALID_FORMAT;
280 stm = calloc(1, sizeof(*stm));
281 assert(stm);
283 stm->context = ctx;
284 stm->data_callback = data_callback;
285 stm->state_callback = state_callback;
286 stm->user_ptr = user_ptr;
288 stm->framesize = stream_params.channels * sizeof(int16_t);
289 stm->bytespersec = stream_params.rate * stm->framesize;
290 stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
291 stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
292 int i;
293 for (i = 0; i < NBUFS; i++) {
294 stm->queuebuf[i] = malloc(stm->queuebuf_len);
295 assert(stm->queuebuf[i]);
298 SLDataLocator_BufferQueue loc_bufq;
299 loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
300 loc_bufq.numBuffers = NBUFS;
301 SLDataSource source;
302 source.pLocator = &loc_bufq;
303 source.pFormat = &format;
305 SLDataLocator_OutputMix loc_outmix;
306 loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
307 loc_outmix.outputMix = ctx->outmixObj;
308 SLDataSink sink;
309 sink.pLocator = &loc_outmix;
310 sink.pFormat = NULL;
312 #if defined(__ANDROID__)
313 const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_ANDROIDCONFIGURATION};
314 const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
315 #else
316 const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE};
317 const SLboolean req[] = {SL_BOOLEAN_TRUE};
318 #endif
319 assert(NELEMS(ids) == NELEMS(req));
320 SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
321 &source, &sink, NELEMS(ids), ids, req);
322 if (res != SL_RESULT_SUCCESS) {
323 opensl_stream_destroy(stm);
324 return CUBEB_ERROR;
327 #if defined(__ANDROID__)
328 SLuint32 stream_type = convert_stream_type_to_sl_stream(stream_params.stream_type);
329 if (stream_type != 0xFFFFFFFF) {
330 SLAndroidConfigurationItf playerConfig;
331 res = (*stm->playerObj)->GetInterface(stm->playerObj,
332 ctx->SL_IID_ANDROIDCONFIGURATION, &playerConfig);
333 res = (*playerConfig)->SetConfiguration(playerConfig,
334 SL_ANDROID_KEY_STREAM_TYPE, &stream_type, sizeof(SLint32));
335 if (res != SL_RESULT_SUCCESS) {
336 opensl_stream_destroy(stm);
337 return CUBEB_ERROR;
340 #endif
342 res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
343 if (res != SL_RESULT_SUCCESS) {
344 opensl_stream_destroy(stm);
345 return CUBEB_ERROR;
348 res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play);
349 if (res != SL_RESULT_SUCCESS) {
350 opensl_stream_destroy(stm);
351 return CUBEB_ERROR;
354 res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE,
355 &stm->bufq);
356 if (res != SL_RESULT_SUCCESS) {
357 opensl_stream_destroy(stm);
358 return CUBEB_ERROR;
361 res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
362 if (res != SL_RESULT_SUCCESS) {
363 opensl_stream_destroy(stm);
364 return CUBEB_ERROR;
367 *stream = stm;
369 return CUBEB_OK;
372 static void
373 opensl_stream_destroy(cubeb_stream * stm)
375 if (stm->playerObj)
376 (*stm->playerObj)->Destroy(stm->playerObj);
377 int i;
378 for (i = 0; i < NBUFS; i++) {
379 free(stm->queuebuf[i]);
381 free(stm);
384 static int
385 opensl_stream_start(cubeb_stream * stm)
387 SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING);
388 if (res != SL_RESULT_SUCCESS)
389 return CUBEB_ERROR;
390 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
391 bufferqueue_callback(NULL, stm);
392 return CUBEB_OK;
395 static int
396 opensl_stream_stop(cubeb_stream * stm)
398 SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
399 if (res != SL_RESULT_SUCCESS)
400 return CUBEB_ERROR;
401 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
402 return CUBEB_OK;
405 static int
406 opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
408 SLmillisecond msec;
409 SLresult res = (*stm->play)->GetPosition(stm->play, &msec);
410 if (res != SL_RESULT_SUCCESS)
411 return CUBEB_ERROR;
412 *position = (stm->bytespersec / (1000 * stm->framesize)) * msec;
413 return CUBEB_OK;
416 static struct cubeb_ops const opensl_ops = {
417 .init = opensl_init,
418 .get_backend_id = opensl_get_backend_id,
419 .get_max_channel_count = opensl_get_max_channel_count,
420 .destroy = opensl_destroy,
421 .stream_init = opensl_stream_init,
422 .stream_destroy = opensl_stream_destroy,
423 .stream_start = opensl_stream_start,
424 .stream_stop = opensl_stream_stop,
425 .stream_get_position = opensl_stream_get_position