/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** \file CEngine.c Engine class */ #include "sles_allinclusive.h" #ifdef ANDROID #include #include #include #include #endif /* This implementation supports at most one engine, identical for both OpenSL ES and OpenMAX AL */ CEngine *theOneTrueEngine = NULL; pthread_mutex_t theOneTrueMutex = PTHREAD_MUTEX_INITIALIZER; unsigned theOneTrueRefCount = 0; // incremented by slCreateEngine or xaCreateEngine, decremented by Object::Destroy on engine /** \brief Called by dlopen when .so is loaded */ __attribute__((constructor)) static void onDlOpen(void) { } /** \brief Called by dlclose when .so is unloaded */ __attribute__((destructor)) static void onDlClose(void) { // a memory barrier would be sufficient, but the mutex is easier (void) pthread_mutex_lock(&theOneTrueMutex); if ((NULL != theOneTrueEngine) || (0 < theOneTrueRefCount)) { SL_LOGE("Object::Destroy omitted for engine %p", theOneTrueEngine); } (void) pthread_mutex_unlock(&theOneTrueMutex); } /** \brief Hook called by Object::Realize when an engine is realized */ SLresult CEngine_Realize(void *self, SLboolean async) { CEngine *thiz = (CEngine *) self; SLresult result; #ifndef ANDROID // create the sync thread int err = pthread_create(&thiz->mSyncThread, (const pthread_attr_t *) NULL, sync_start, thiz); result = err_to_result(err); if (SL_RESULT_SUCCESS != result) return result; #endif // initialize the thread pool for asynchronous operations result = ThreadPool_init(&thiz->mThreadPool, 0, 0); if (SL_RESULT_SUCCESS != result) { thiz->mEngine.mShutdown = SL_BOOLEAN_TRUE; (void) pthread_join(thiz->mSyncThread, (void **) NULL); return result; } #ifdef ANDROID // use checkService() to avoid blocking if audio service is not up yet android::sp binder = android::defaultServiceManager()->checkService(android::String16("audio")); if (binder == 0) { SL_LOGE("CEngine_Realize: binding to audio service failed, service up?"); } else { thiz->mAudioManager = android::interface_cast(binder); } #endif #ifdef USE_SDL SDL_open(&thiz->mEngine); #endif return SL_RESULT_SUCCESS; } /** \brief Hook called by Object::Resume when an engine is resumed */ SLresult CEngine_Resume(void *self, SLboolean async) { return SL_RESULT_SUCCESS; } /** \brief Hook called by Object::Destroy when an engine is destroyed */ void CEngine_Destroy(void *self) { CEngine *thiz = (CEngine *) self; // Verify that there are no extant objects unsigned instanceCount = thiz->mEngine.mInstanceCount; unsigned instanceMask = thiz->mEngine.mInstanceMask; if ((0 < instanceCount) || (0 != instanceMask)) { SL_LOGE("Object::Destroy(%p) for engine ignored; %u total active objects", thiz, instanceCount); while (0 != instanceMask) { unsigned i = ctz(instanceMask); assert(MAX_INSTANCE > i); SL_LOGE("Object::Destroy(%p) for engine ignored; active object ID %u at %p", thiz, i + 1, thiz->mEngine.mInstances[i]); instanceMask &= ~(1 << i); } } // If engine was created but not realized, there will be no sync thread yet pthread_t zero; memset(&zero, 0, sizeof(pthread_t)); if (0 != memcmp(&zero, &thiz->mSyncThread, sizeof(pthread_t))) { // Announce to the sync thread that engine is shutting down; it polls so should see it soon thiz->mEngine.mShutdown = SL_BOOLEAN_TRUE; // Wait for the sync thread to acknowledge the shutdown while (!thiz->mEngine.mShutdownAck) { object_cond_wait(&thiz->mObject); } // The sync thread should have exited by now, so collect it by joining (void) pthread_join(thiz->mSyncThread, (void **) NULL); } // Shutdown the thread pool used for asynchronous operations (there should not be any) ThreadPool_deinit(&thiz->mThreadPool); #if defined(ANDROID) if (thiz->mAudioManager != 0) { thiz->mAudioManager.clear(); } // free equalizer preset names if (NULL != thiz->mEqPresetNames) { for (unsigned i = 0; i < thiz->mEqNumPresets; ++i) { if (NULL != thiz->mEqPresetNames[i]) { delete[] thiz->mEqPresetNames[i]; thiz->mEqPresetNames[i] = NULL; } } delete[] thiz->mEqPresetNames; thiz->mEqPresetNames = NULL; } thiz->mEqNumPresets = 0; #endif #ifdef USE_SDL SDL_close(); #endif } /** \brief Hook called by Object::Destroy before an engine is about to be destroyed */ predestroy_t CEngine_PreDestroy(void *self) { predestroy_t ret; (void) pthread_mutex_lock(&theOneTrueMutex); assert(self == theOneTrueEngine); switch (theOneTrueRefCount) { case 0: assert(false); ret = predestroy_error; break; case 1: ret = predestroy_ok; break; default: --theOneTrueRefCount; ret = predestroy_again; break; } (void) pthread_mutex_unlock(&theOneTrueMutex); return ret; } /** \brief Called by IObject::Destroy after engine is destroyed. The parameter refers to the * previous engine, which is now undefined memory. */ void CEngine_Destroyed(CEngine *self) { int ok; ok = pthread_mutex_lock(&theOneTrueMutex); assert(0 == ok); assert(self == theOneTrueEngine); theOneTrueEngine = NULL; assert(1 == theOneTrueRefCount); theOneTrueRefCount = 0; ok = pthread_mutex_unlock(&theOneTrueMutex); assert(0 == ok); }