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