From 2f73bdcf21e8316e0dc5b38bb6ec534cf4c1d792 Mon Sep 17 00:00:00 2001 From: Marc Salem Date: Wed, 9 Jan 2013 17:19:25 -0800 Subject: [PATCH] initial Android/OpenSL multiple context code --- jni/OpenAL/Alc/opensles.c | 285 +++++++++++++++++++++++--------------- 1 file changed, 176 insertions(+), 109 deletions(-) diff --git a/jni/OpenAL/Alc/opensles.c b/jni/OpenAL/Alc/opensles.c index 98cb259..c6a5dca 100644 --- a/jni/OpenAL/Alc/opensles.c +++ b/jni/OpenAL/Alc/opensles.c @@ -66,11 +66,6 @@ static SLEngineItf engineEngine; // output mix interfaces static SLObjectItf outputMixObject = NULL; -// buffer queue player interfaces -static SLObjectItf bqPlayerObject = NULL; -static SLPlayItf bqPlayerPlay; -static ALCdevice *openSLESDevice = NULL; -static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; // JNI stuff so we can get the runtime OS version number static JavaVM* javaVM = NULL; @@ -103,7 +98,6 @@ typedef struct outputBuffer_s { } outputBuffer_t; // Will dynamically create the number of buffers (array elements) based on OS version. -static outputBuffer_t* outputBuffers = NULL; typedef struct { @@ -113,7 +107,54 @@ typedef struct { char lastBufferEnqueued; char lastBufferMixed; + outputBuffer_t *outputBuffers; + + // buffer queue player interfaces + SLObjectItf bqPlayerObject; + SLPlayItf bqPlayerPlay; + SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; } opesles_data_t; +#define MAX_DEVICES 3 +static ALCdevice *deviceList[MAX_DEVICES] = {NULL}; +static pthread_mutex_t deviceListMutex = PTHREAD_MUTEX_INITIALIZER; + +typedef void (*deviceListFn)(ALCdevice *); + +static void devlist_add(ALCdevice *pDevice) { + pthread_mutex_lock(&(deviceListMutex)); + for (int i = 0; i < MAX_DEVICES; i++) { + if (deviceList[i] == pDevice) { + break; + } else if (deviceList[i] == NULL) { + deviceList[i] = pDevice; + break; + } + } + pthread_mutex_unlock(&(deviceListMutex)); +} + +static void devlist_remove(ALCdevice *pDevice) { + pthread_mutex_lock(&(deviceListMutex)); + for (int i = 0; i < MAX_DEVICES; i++) { + if (deviceList[i] == pDevice) { + deviceList[i] = NULL; + } + } + pthread_mutex_unlock(&(deviceListMutex)); +} + +static void devlist_process(deviceListFn mapFunction) { + pthread_mutex_lock(&(deviceListMutex)); + for (int i = 0; i < MAX_DEVICES; i++) { + if (deviceList[i]) { + pthread_mutex_unlock(&(deviceListMutex)); + mapFunction(deviceList[i]); + pthread_mutex_lock(&(deviceListMutex)); + } + } + pthread_mutex_unlock(&(deviceListMutex)); +} + static void *playback_function(void * context) { LOGV("playback_function started"); @@ -137,7 +178,7 @@ static void *playback_function(void * context) { } bufferIndex = (++bufferIndex) % bufferCount; - buffer = &(outputBuffers[bufferIndex]); + buffer = &(devState->outputBuffers[bufferIndex]); pthread_mutex_lock(&(buffer->mutex)); @@ -152,9 +193,9 @@ static void *playback_function(void * context) { // This is a little hacky, but here we avoid using a buffer too soon after it is enqueued if (buffer->state == OUTPUT_BUFFER_STATE_ENQUEUED) { - outputBuffer_t *buffer1 = &(outputBuffers[(bufferIndex + 1) % bufferCount]); - outputBuffer_t *buffer2 = &(outputBuffers[(bufferIndex + 2) % bufferCount]); - outputBuffer_t *buffer3 = &(outputBuffers[(bufferIndex + 3) % bufferCount]); + outputBuffer_t *buffer1 = &(devState->outputBuffers[(bufferIndex + 1) % bufferCount]); + outputBuffer_t *buffer2 = &(devState->outputBuffers[(bufferIndex + 2) % bufferCount]); + outputBuffer_t *buffer3 = &(devState->outputBuffers[(bufferIndex + 3) % bufferCount]); if (buffer1->state == OUTPUT_BUFFER_STATE_ENQUEUED && buffer2->state == OUTPUT_BUFFER_STATE_ENQUEUED && buffer3->state == OUTPUT_BUFFER_STATE_ENQUEUED) { @@ -180,32 +221,50 @@ static void *playback_function(void * context) { } } +SLresult alc_opensles_init_extradata(ALCdevice *pDevice) +{ + opesles_data_t *devState = NULL; + int i; + devState = malloc(sizeof(opesles_data_t)); + if (!devState) { + return SL_RESULT_MEMORY_FAILURE; + } + bzero(devState, sizeof(opesles_data_t)); + devState->outputBuffers = (outputBuffer_t*) malloc(sizeof(outputBuffer_t)*bufferCount); + if (!devState->outputBuffers) { + free(devState); + return SL_RESULT_MEMORY_FAILURE; + } + pDevice->ExtraData = devState; + bzero(devState->outputBuffers, sizeof(outputBuffer_t)*bufferCount); + devState->lastBufferEnqueued = -1; + devState->lastBufferMixed = -1; + for (i = 0; i < bufferCount; i++) { + if (pthread_mutex_init(&(devState->outputBuffers[i].mutex), (pthread_mutexattr_t*) NULL) != 0) { + LOGV("Error on init of mutex"); + free(devState->outputBuffers); + free(devState); + return SL_RESULT_UNKNOWN_ERROR; + } + if (pthread_cond_init(&(devState->outputBuffers[i].cond), (pthread_condattr_t*) NULL) != 0) { + LOGV("Error on init of cond"); + free(devState->outputBuffers); + free(devState); + return SL_RESULT_UNKNOWN_ERROR; + } + devState->outputBuffers[i].state = OUTPUT_BUFFER_STATE_FREE; + } + // For the Android suspend/resume functionaly, keep track of all device contexts + devlist_add(pDevice); + return SL_RESULT_SUCCESS; +} + static void start_playback(ALCdevice *pDevice) { opesles_data_t *devState = NULL; int i; if (pDevice->ExtraData == NULL) { - devState = malloc(sizeof(opesles_data_t)); - bzero(devState, sizeof(opesles_data_t)); - pDevice->ExtraData = devState; - - devState->lastBufferEnqueued = -1; - devState->lastBufferMixed = -1; - - for (i = 0; i < bufferCount; i++) { - bzero(&outputBuffers[i], sizeof(outputBuffer_t)); - - if (pthread_mutex_init(&(outputBuffers[i].mutex), (pthread_mutexattr_t*) NULL) != 0) { - LOGV("Error on init of mutex"); - } - if (pthread_cond_init(&(outputBuffers[i].cond), (pthread_condattr_t*) NULL) != 0) { - LOGV("Error on init of cond"); - } - outputBuffers[i].state = OUTPUT_BUFFER_STATE_FREE; - } - - // For the suspend/resume functionaly, we only support one device for now - openSLESDevice = pDevice; + alc_opensles_init_extradata(pDevice); } else { devState = (opesles_data_t *) pDevice->ExtraData; } @@ -250,7 +309,7 @@ static void opensles_callback(SLAndroidSimpleBufferQueueItf bq, void *context) outputBuffer_t *buffer = NULL; bufferIndex = (++bufferIndex) % bufferCount; - buffer = &(outputBuffers[bufferIndex]); + buffer = &(devState->outputBuffers[bufferIndex]); pthread_mutex_lock(&(buffer->mutex)); while (buffer->state != OUTPUT_BUFFER_STATE_MIXED) { @@ -266,7 +325,7 @@ static void opensles_callback(SLAndroidSimpleBufferQueueItf bq, void *context) } } - result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer->buffer, bufferSize); + result = (*devState->bqPlayerBufferQueue)->Enqueue(devState->bqPlayerBufferQueue, buffer->buffer, bufferSize); if (SL_RESULT_SUCCESS == result) { buffer->state = OUTPUT_BUFFER_STATE_ENQUEUED; devState->lastBufferEnqueued = bufferIndex; @@ -315,7 +374,14 @@ SLresult alc_opensles_create_native_audio_engine() static ALCboolean opensles_open_playback(ALCdevice *pDevice, const ALCchar *deviceName) { LOGV("opensles_open_playback pDevice=%p, deviceName=%s", pDevice, deviceName); + opesles_data_t *devState; + if (pDevice->ExtraData == NULL) { + alc_opensles_init_extradata(pDevice); + } else { + devState = (opesles_data_t *) pDevice->ExtraData; + } + // Check if probe has linked the opensl symbols if (pslCreateEngine == NULL) { alc_opensles_probe(DEVICE_PROBE); if (pslCreateEngine == NULL) { @@ -332,39 +398,27 @@ static ALCboolean opensles_open_playback(ALCdevice *pDevice, const ALCchar *devi static void opensles_close_playback(ALCdevice *pDevice) { LOGV("opensles_close_playback pDevice=%p", pDevice); + opesles_data_t *devState = (opesles_data_t *) pDevice->ExtraData; // shut down the native audio system // destroy buffer queue audio player object, and invalidate all associated interfaces - if (bqPlayerObject != NULL) { - (*bqPlayerObject)->Destroy(bqPlayerObject); - bqPlayerObject = NULL; - bqPlayerPlay = NULL; - bqPlayerBufferQueue = NULL; + if (devState->bqPlayerObject != NULL) { + (*devState->bqPlayerObject)->Destroy(devState->bqPlayerObject); + devState->bqPlayerObject = NULL; + devState->bqPlayerPlay = NULL; + devState->bqPlayerBufferQueue = NULL; } - // destroy output mix object, and invalidate all associated interfaces - if (outputMixObject != NULL) { - (*outputMixObject)->Destroy(outputMixObject); - outputMixObject = NULL; - } - // destroy engine object, and invalidate all associated interfaces - if (engineObject != NULL) { - (*engineObject)->Destroy(engineObject); - engineObject = NULL; - engineEngine = NULL; - } - if (openSLESDevice == pDevice) - { - openSLESDevice = NULL; - } + devlist_remove(pDevice); } static ALCboolean opensles_reset_playback(ALCdevice *pDevice) { LOGV("opensles_reset_playback pDevice=%p", pDevice); + opesles_data_t *devState; unsigned bits = BytesFromDevFmt(pDevice->FmtType) * 8; unsigned channels = ChannelsFromDevFmt(pDevice->FmtChans); unsigned samples = pDevice->UpdateSize; @@ -373,6 +427,8 @@ static ALCboolean opensles_reset_playback(ALCdevice *pDevice) SLresult result; LOGV("bits=%u, channels=%u, samples=%u, size=%u, freq=%u", bits, channels, samples, size, pDevice->Frequency); + devState = (opesles_data_t *) pDevice->ExtraData; + // create buffer queue audio player // configure audio source @@ -391,40 +447,41 @@ static ALCboolean opensles_reset_playback(ALCdevice *pDevice) LOGV("create audio player"); const SLInterfaceID ids[1] = {*pSL_IID_ANDROIDSIMPLEBUFFERQUEUE}; const SLboolean req[1] = {SL_BOOLEAN_TRUE}; - result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, + result = (*engineEngine)->CreateAudioPlayer(engineEngine, &devState->bqPlayerObject, &audioSrc, &audioSnk, 1, ids, req); assert(SL_RESULT_SUCCESS == result); // realize the player - result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); + result = (*devState->bqPlayerObject)->Realize(devState->bqPlayerObject, SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS == result); // get the play interface - result = (*bqPlayerObject)->GetInterface(bqPlayerObject, *pSL_IID_PLAY, &bqPlayerPlay); + result = (*devState->bqPlayerObject)->GetInterface(devState->bqPlayerObject, *pSL_IID_PLAY, &devState->bqPlayerPlay); assert(SL_RESULT_SUCCESS == result); // get the buffer queue interface - result = (*bqPlayerObject)->GetInterface(bqPlayerObject, *pSL_IID_BUFFERQUEUE, - &bqPlayerBufferQueue); + result = (*devState->bqPlayerObject)->GetInterface(devState->bqPlayerObject, *pSL_IID_BUFFERQUEUE, + &devState->bqPlayerBufferQueue); assert(SL_RESULT_SUCCESS == result); // register callback on the buffer queue - result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, opensles_callback, (void *) pDevice); + result = (*devState->bqPlayerBufferQueue)->RegisterCallback(devState->bqPlayerBufferQueue, opensles_callback, (void *) pDevice); assert(SL_RESULT_SUCCESS == result); // playback_lock = createThreadLock(); start_playback(pDevice); // set the player's state to playing - result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); + result = (*devState->bqPlayerPlay)->SetPlayState(devState->bqPlayerPlay, SL_PLAYSTATE_PLAYING); assert(SL_RESULT_SUCCESS == result); // enqueue the first buffer to kick off the callbacks - result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, "\0", 1); + result = (*devState->bqPlayerBufferQueue)->Enqueue(devState->bqPlayerBufferQueue, "\0", 1); assert(SL_RESULT_SUCCESS == result); - SetDefaultWFXChannelOrder(pDevice); + SetDefaultWFXChannelOrder(pDevice); + devlist_add(pDevice); return ALC_TRUE; } @@ -485,38 +542,44 @@ BackendFuncs opensles_funcs = { // global entry points called from XYZZY - -void alc_opensles_suspend() -{ + +static void suspend_device(ALCdevice *pDevice) { SLresult result; - - if (bqPlayerPlay) { - result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED); - assert(SL_RESULT_SUCCESS == result); - result = (*bqPlayerBufferQueue)->Clear(bqPlayerBufferQueue); - assert(SL_RESULT_SUCCESS == result); - } - - if (openSLESDevice) { - stop_playback(openSLESDevice); + if (pDevice) { + opesles_data_t *devState = (opesles_data_t *) pDevice->ExtraData; + if (devState->bqPlayerPlay) { + result = (*devState->bqPlayerPlay)->SetPlayState(devState->bqPlayerPlay, SL_PLAYSTATE_PAUSED); + assert(SL_RESULT_SUCCESS == result); + result = (*devState->bqPlayerBufferQueue)->Clear(devState->bqPlayerBufferQueue); + assert(SL_RESULT_SUCCESS == result); + } + stop_playback(pDevice); } } +static void resume_device(ALCdevice *pDevice) { + SLresult result; + if (pDevice) { + opesles_data_t *devState = (opesles_data_t *) pDevice->ExtraData; + if (devState->bqPlayerPlay) { + result = (*devState->bqPlayerPlay)->SetPlayState(devState->bqPlayerPlay, SL_PLAYSTATE_PLAYING); + assert(SL_RESULT_SUCCESS == result); + // Pump some blank data into the buffer to stimulate the callback + result = (*devState->bqPlayerBufferQueue)->Enqueue(devState->bqPlayerBufferQueue, "\0", 1); + assert(SL_RESULT_SUCCESS == result); + } + start_playback(pDevice); + } + } + +void alc_opensles_suspend() +{ + devlist_process(&suspend_device); +} + void alc_opensles_resume() { - SLresult result; - - if (bqPlayerPlay) { - result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); - assert(SL_RESULT_SUCCESS == result); - // Pump some blank data into the buffer to stimulate the callback - result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, "\0", 1); - assert(SL_RESULT_SUCCESS == result); - } - - if (openSLESDevice) { - start_playback(openSLESDevice); - } + devlist_process(&resume_device); } static int alc_opensles_get_android_api() @@ -542,29 +605,20 @@ static int alc_opensles_get_android_api() static void alc_opensles_set_java_vm(JavaVM *vm) { + // Called once and only once from JNI_OnLoad javaVM = vm; - if(NULL == javaVM) + if(NULL != javaVM) { - free(outputBuffers); - outputBuffers = NULL; - } - else - { - if(NULL == outputBuffers) + int android_os_version = alc_opensles_get_android_api(); + // If running on 4.1 (Jellybean) or later, use 8 buffers to avoid breakup/stuttering. + if(android_os_version >= 16) { - int android_os_version = alc_opensles_get_android_api(); - // If running on 4.1 (Jellybean) or later, use 8 buffers to avoid breakup/stuttering. - if(android_os_version >= 16) - { - bufferCount = 8; - } - // Else, use 4 buffers to reduce latency - else - { - bufferCount = 4; - } - - outputBuffers = (outputBuffer_t*)malloc(sizeof(outputBuffer_t)*bufferCount); + bufferCount = 8; + } + // Else, use 4 buffers to reduce latency + else + { + bufferCount = 4; } } } @@ -582,13 +636,26 @@ void alc_opensles_init(BackendFuncs *func_list) // We need the JavaVM for JNI so we can detect the OS version number at runtime. // This is because we need to use different bufferCount values for Android 4.1 vs. pre-4.1. - // This must be set before JNI_OnLoad is invoked. + // This must be set at constructor time before JNI_OnLoad is invoked. apportableOpenALFuncs.alc_android_set_java_vm = alc_opensles_set_java_vm; } void alc_opensles_deinit(void) { LOGV("alc_opensles_deinit"); + + // destroy output mix object, and invalidate all associated interfaces + if (outputMixObject != NULL) { + (*outputMixObject)->Destroy(outputMixObject); + outputMixObject = NULL; + } + + // destroy engine object, and invalidate all associated interfaces + if (engineObject != NULL) { + (*engineObject)->Destroy(engineObject); + engineObject = NULL; + engineEngine = NULL; + } } void alc_opensles_probe(int type)