2 * Copyright © 2012 Mozilla Foundation
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
11 #include <SLES/OpenSLES.h>
12 #if defined(__ANDROID__)
13 #include "android/sles_definitions.h"
14 #include <SLES/OpenSLES_Android.h>
16 #include "cubeb/cubeb.h"
17 #include "cubeb-internal.h"
19 static struct cubeb_ops
const opensl_ops
;
22 struct cubeb_ops
const * ops
;
24 SLInterfaceID SL_IID_BUFFERQUEUE
;
25 SLInterfaceID SL_IID_PLAY
;
26 #if defined(__ANDROID__)
27 SLInterfaceID SL_IID_ANDROIDCONFIGURATION
;
31 SLObjectItf outmixObj
;
34 #define NELEMS(A) (sizeof(A) / sizeof A[0])
39 SLObjectItf playerObj
;
41 SLBufferQueueItf bufq
;
42 void *queuebuf
[NBUFS
];
49 cubeb_data_callback data_callback
;
50 cubeb_state_callback state_callback
;
55 bufferqueue_callback(SLBufferQueueItf caller
, void * user_ptr
)
57 cubeb_stream
* stm
= user_ptr
;
58 SLBufferQueueState state
;
59 (*stm
->bufq
)->GetState(stm
->bufq
, &state
);
64 stm
->state_callback(stm
, stm
->user_ptr
, CUBEB_STATE_DRAINED
);
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
);
83 (*stm
->bufq
)->Enqueue(stm
->bufq
, buf
, written
* stm
->framesize
);
84 stm
->queuebuf_idx
= (stm
->queuebuf_idx
+ 1) % NBUFS
;
86 stm
->state_callback(stm
, stm
->user_ptr
, CUBEB_STATE_DRAINED
);
90 if ((written
* stm
->framesize
) < stm
->queuebuf_len
) {
97 #if defined(__ANDROID__)
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
:
121 static void opensl_destroy(cubeb
* ctx
);
124 opensl_init(cubeb
** context
, char const * context_name
)
130 ctx
= calloc(1, sizeof(*ctx
));
133 ctx
->ops
= &opensl_ops
;
135 ctx
->lib
= dlopen("libOpenSLES.so", RTLD_LAZY
);
141 typedef SLresult (*slCreateEngine_t
)(SLObjectItf
*,
143 const SLEngineOption
*,
145 const SLInterfaceID
*,
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");
155 ctx
->SL_IID_PLAY
= *(SLInterfaceID
*)dlsym(ctx
->lib
, "SL_IID_PLAY");
156 if (!f_slCreateEngine
||
159 !ctx
->SL_IID_BUFFERQUEUE
||
160 #if defined(__ANDROID__)
161 !ctx
->SL_IID_ANDROIDCONFIGURATION
||
169 const SLEngineOption opt
[] = {{SL_ENGINEOPTION_THREADSAFE
, SL_BOOLEAN_TRUE
}};
172 res
= f_slCreateEngine(&ctx
->engObj
, 1, opt
, 0, NULL
, NULL
);
173 if (res
!= SL_RESULT_SUCCESS
) {
178 res
= (*ctx
->engObj
)->Realize(ctx
->engObj
, SL_BOOLEAN_FALSE
);
179 if (res
!= SL_RESULT_SUCCESS
) {
184 res
= (*ctx
->engObj
)->GetInterface(ctx
->engObj
, SL_IID_ENGINE
, &ctx
->eng
);
185 if (res
!= SL_RESULT_SUCCESS
) {
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
) {
198 res
= (*ctx
->outmixObj
)->Realize(ctx
->outmixObj
, SL_BOOLEAN_FALSE
);
199 if (res
!= SL_RESULT_SUCCESS
) {
210 opensl_get_backend_id(cubeb
* ctx
)
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 */
227 opensl_destroy(cubeb
* ctx
)
230 (*ctx
->outmixObj
)->Destroy(ctx
->outmixObj
);
232 (*ctx
->engObj
)->Destroy(ctx
->engObj
);
237 static void opensl_stream_destroy(cubeb_stream
* stm
);
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
,
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
;
273 case CUBEB_SAMPLE_S16BE
:
274 format
.endianness
= SL_BYTEORDER_BIGENDIAN
;
277 return CUBEB_ERROR_INVALID_FORMAT
;
280 stm
= calloc(1, sizeof(*stm
));
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
);
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
;
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
;
309 sink
.pLocator
= &loc_outmix
;
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
};
316 const SLInterfaceID ids
[] = {ctx
->SL_IID_BUFFERQUEUE
};
317 const SLboolean req
[] = {SL_BOOLEAN_TRUE
};
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
);
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
);
342 res
= (*stm
->playerObj
)->Realize(stm
->playerObj
, SL_BOOLEAN_FALSE
);
343 if (res
!= SL_RESULT_SUCCESS
) {
344 opensl_stream_destroy(stm
);
348 res
= (*stm
->playerObj
)->GetInterface(stm
->playerObj
, ctx
->SL_IID_PLAY
, &stm
->play
);
349 if (res
!= SL_RESULT_SUCCESS
) {
350 opensl_stream_destroy(stm
);
354 res
= (*stm
->playerObj
)->GetInterface(stm
->playerObj
, ctx
->SL_IID_BUFFERQUEUE
,
356 if (res
!= SL_RESULT_SUCCESS
) {
357 opensl_stream_destroy(stm
);
361 res
= (*stm
->bufq
)->RegisterCallback(stm
->bufq
, bufferqueue_callback
, stm
);
362 if (res
!= SL_RESULT_SUCCESS
) {
363 opensl_stream_destroy(stm
);
373 opensl_stream_destroy(cubeb_stream
* stm
)
376 (*stm
->playerObj
)->Destroy(stm
->playerObj
);
378 for (i
= 0; i
< NBUFS
; i
++) {
379 free(stm
->queuebuf
[i
]);
385 opensl_stream_start(cubeb_stream
* stm
)
387 SLresult res
= (*stm
->play
)->SetPlayState(stm
->play
, SL_PLAYSTATE_PLAYING
);
388 if (res
!= SL_RESULT_SUCCESS
)
390 stm
->state_callback(stm
, stm
->user_ptr
, CUBEB_STATE_STARTED
);
391 bufferqueue_callback(NULL
, stm
);
396 opensl_stream_stop(cubeb_stream
* stm
)
398 SLresult res
= (*stm
->play
)->SetPlayState(stm
->play
, SL_PLAYSTATE_PAUSED
);
399 if (res
!= SL_RESULT_SUCCESS
)
401 stm
->state_callback(stm
, stm
->user_ptr
, CUBEB_STATE_STOPPED
);
406 opensl_stream_get_position(cubeb_stream
* stm
, uint64_t * position
)
409 SLresult res
= (*stm
->play
)->GetPosition(stm
->play
, &msec
);
410 if (res
!= SL_RESULT_SUCCESS
)
412 *position
= (stm
->bytespersec
/ (1000 * stm
->framesize
)) * msec
;
416 static struct cubeb_ops
const opensl_ops
= {
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