/** * Called with a lock on AudioRecorder, and blocks until safe to destroy */ void android_audioRecorder_preDestroy(CAudioRecorder* ar) { object_unlock_exclusive(&ar->mObject); if (ar->mCallbackProtector != 0) { ar->mCallbackProtector->requestCbExitAndWait(); } object_lock_exclusive(&ar->mObject); }
static SLresult I3DGrouping_Set3DGroup(SL3DGroupingItf self, SLObjectItf group) { SL_ENTER_INTERFACE // validate input parameters C3DGroup *newGroup = (C3DGroup *) group; result = SL_RESULT_SUCCESS; if (NULL != newGroup) { // check that new group has the correct object ID and is realized, and acquire a strong // reference to it. FYI note that a deadlock will occur if application incorrectly // specifies group as this audio player result = AcquireStrongRef(&newGroup->mObject, SL_OBJECTID_3DGROUP); // the new group is left unlocked, but it will be locked again below } if (SL_RESULT_SUCCESS == result) { I3DGrouping *thiz = (I3DGrouping *) self; IObject *thisObject = InterfaceToIObject(thiz); unsigned id = thisObject->mInstanceID; assert(0 != id); // player object must be published by this point --id; assert(MAX_INSTANCE > id); unsigned mask = 1 << id; interface_lock_exclusive(thiz); C3DGroup *oldGroup = thiz->mGroup; if (newGroup != oldGroup) { // remove this object from the old group's set of objects if (NULL != oldGroup) { IObject *oldGroupObject = &oldGroup->mObject; // note that we already have a strong reference to the old group object_lock_exclusive(oldGroupObject); assert(oldGroup->mMemberMask & mask); oldGroup->mMemberMask &= ~mask; ReleaseStrongRefAndUnlockExclusive(oldGroupObject); } // add this object to the new group's set of objects if (NULL != newGroup) { IObject *newGroupObject = &newGroup->mObject; // we already have a strong reference to the new group, but we need to re-lock it // so that we always lock objects in the same nesting order to prevent a deadlock object_lock_exclusive(newGroupObject); assert(!(newGroup->mMemberMask & mask)); newGroup->mMemberMask |= mask; object_unlock_exclusive(newGroupObject); } thiz->mGroup = newGroup; } interface_unlock_exclusive(thiz); } SL_LEAVE_INTERFACE }
void ReleaseStrongRefAndUnlockExclusive(IObject *object) { #ifdef USE_DEBUG assert(pthread_equal(pthread_self(), object->mOwner)); #endif assert(0 < object->mStrongRefCount); if ((0 == --object->mStrongRefCount) && (SL_OBJECT_STATE_DESTROYING == object->mState)) { // FIXME do the destroy here - merge with IDestroy // but can't do this until we move Destroy to the sync thread // as Destroy is now a blocking operation, and to avoid a race } else { object_unlock_exclusive(object); } }
//----------------------------------------------------------------------------- void android_eq_init(audio_session_t sessionId, IEqualizer* ieq) { SL_LOGV("android_eq_init on session %d", sessionId); if (!android_fx_initEffectObj(sessionId, ieq->mEqEffect, &ieq->mEqDescriptor.type)) { SL_LOGE("Equalizer effect initialization failed"); return; } // initialize number of bands, band level range, and number of presets uint16_t num = 0; if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect, EQ_PARAM_NUM_BANDS, 0, &num)) { ieq->mNumBands = num; } int16_t range[2] = {0, 0}; if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect, EQ_PARAM_LEVEL_RANGE, 0, range)) { ieq->mBandLevelRangeMin = range[0]; ieq->mBandLevelRangeMax = range[1]; } SL_LOGV(" EQ init: num bands = %u, band range=[%d %d]mB", num, range[0], range[1]); // FIXME don't store presets names, they can be queried each time they're needed // initialize preset number and names, store in IEngine uint16_t numPresets = 0; if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect, EQ_PARAM_GET_NUM_OF_PRESETS, 0, &numPresets)) { ieq->mThis->mEngine->mEqNumPresets = numPresets; ieq->mNumPresets = numPresets; } object_lock_exclusive(&ieq->mThis->mEngine->mObject); char name[EFFECT_STRING_LEN_MAX]; if ((0 < numPresets) && (NULL == ieq->mThis->mEngine->mEqPresetNames)) { ieq->mThis->mEngine->mEqPresetNames = (char **)new char *[numPresets]; for(uint32_t i = 0 ; i < numPresets ; i++) { if (android::NO_ERROR == android_eq_getParam(ieq->mEqEffect, EQ_PARAM_GET_PRESET_NAME, i, name)) { ieq->mThis->mEngine->mEqPresetNames[i] = new char[strlen(name) + 1]; strcpy(ieq->mThis->mEngine->mEqPresetNames[i], name); SL_LOGV(" EQ init: presets = %u is %s", i, ieq->mThis->mEngine->mEqPresetNames[i]); } } } object_unlock_exclusive(&ieq->mThis->mEngine->mObject); }
// Called with a lock on MediaPlayer, and blocks until safe to destroy XAresult android_Player_preDestroy(CMediaPlayer *mp) { SL_LOGV("android_Player_preDestroy(%p)", mp); // Not yet clear why this order is important, but it reduces detected deadlocks object_unlock_exclusive(&mp->mObject); if (mp->mCallbackProtector != 0) { mp->mCallbackProtector->requestCbExitAndWait(); } object_lock_exclusive(&mp->mObject); if (mp->mAVPlayer != 0) { mp->mAVPlayer->preDestroy(); } SL_LOGV("android_Player_preDestroy(%p) after mAVPlayer->preDestroy()", mp); return XA_RESULT_SUCCESS; }
SLresult AcquireStrongRef(IObject *object, SLuint32 expectedObjectID) { if (NULL == object) { return SL_RESULT_PARAMETER_INVALID; } // NTH additional validity checks on address here SLresult result; object_lock_exclusive(object); SLuint32 actualObjectID = IObjectToObjectID(object); if (expectedObjectID != actualObjectID) { SL_LOGE("object %p has object ID %u but expected %u", object, actualObjectID, expectedObjectID); result = SL_RESULT_PARAMETER_INVALID; } else if (SL_OBJECT_STATE_REALIZED != object->mState) { SL_LOGE("object %p with object ID %u is not realized", object, actualObjectID); result = SL_RESULT_PRECONDITIONS_VIOLATED; } else { ++object->mStrongRefCount; result = SL_RESULT_SUCCESS; } object_unlock_exclusive(object); return result; }
//----------------------------------------------------------------------------- static void player_handleMediaPlayerEventNotifications(int event, int data1, int data2, void* user) { // FIXME This code is derived from similar code in sfplayer_handlePrefetchEvent. The two // versions are quite similar, but still different enough that they need to be separate. // At some point they should be re-factored and merged if feasible. // As with other OpenMAX AL implementation code, this copy mostly uses SL_ symbols // rather than XA_ unless the difference is significant. if (NULL == user) { return; } CMediaPlayer* mp = (CMediaPlayer*) user; if (!android::CallbackProtector::enterCbIfOk(mp->mCallbackProtector)) { // it is not safe to enter the callback (the media player is about to go away) return; } union { char c[sizeof(int)]; int i; } u; u.i = event; SL_LOGV("player_handleMediaPlayerEventNotifications(event='%c%c%c%c' (%d), data1=%d, data2=%d, " "user=%p) from AVPlayer", u.c[3], u.c[2], u.c[1], u.c[0], event, data1, data2, user); switch(event) { case android::GenericPlayer::kEventPrepared: { SL_LOGV("Received GenericPlayer::kEventPrepared for CMediaPlayer %p", mp); // assume no callback slPrefetchCallback callback = NULL; void* callbackPContext; XAuint32 events; object_lock_exclusive(&mp->mObject); // mark object as prepared; same state is used for successful or unsuccessful prepare assert(mp->mAndroidObjState == ANDROID_PREPARING); mp->mAndroidObjState = ANDROID_READY; if (PLAYER_SUCCESS == data1) { // Most of successful prepare completion for mp->mAVPlayer // is handled by GenericPlayer and its subclasses. } else { // AVPlayer prepare() failed prefetching, there is no event in XAPrefetchStatus to // indicate a prefetch error, so we signal it by sending simultaneously two events: // - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0 // - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW SL_LOGE(ERROR_PLAYER_PREFETCH_d, data1); if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { mp->mPrefetchStatus.mLevel = 0; mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW; if (!(~mp->mPrefetchStatus.mCallbackEventsMask & (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) { callback = mp->mPrefetchStatus.mCallback; callbackPContext = mp->mPrefetchStatus.mContext; events = SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE; } } } object_unlock_exclusive(&mp->mObject); // callback with no lock held if (NULL != callback) { (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, events); } break; } case android::GenericPlayer::kEventHasVideoSize: { SL_LOGV("Received AVPlayer::kEventHasVideoSize (%d,%d) for CMediaPlayer %p", data1, data2, mp); object_lock_exclusive(&mp->mObject); // remove an existing video info entry (here we only have one video stream) for(size_t i=0 ; i < mp->mStreamInfo.mStreamInfoTable.size() ; i++) { if (XA_DOMAINTYPE_VIDEO == mp->mStreamInfo.mStreamInfoTable.itemAt(i).domain) { mp->mStreamInfo.mStreamInfoTable.removeAt(i); break; } } // update the stream information with a new video info entry StreamInfo streamInfo; streamInfo.domain = XA_DOMAINTYPE_VIDEO; streamInfo.videoInfo.codecId = 0;// unknown, we don't have that info FIXME streamInfo.videoInfo.width = (XAuint32)data1; streamInfo.videoInfo.height = (XAuint32)data2; streamInfo.videoInfo.bitRate = 0;// unknown, we don't have that info FIXME streamInfo.videoInfo.frameRate = 0; streamInfo.videoInfo.duration = XA_TIME_UNKNOWN; StreamInfo &contInfo = mp->mStreamInfo.mStreamInfoTable.editItemAt(0); contInfo.containerInfo.numStreams = 1; ssize_t index = mp->mStreamInfo.mStreamInfoTable.add(streamInfo); // callback is unconditional; there is no bitmask of enabled events xaStreamEventChangeCallback callback = mp->mStreamInfo.mCallback; void* callbackPContext = mp->mStreamInfo.mContext; object_unlock_exclusive(&mp->mObject); // enqueue notification (outside of lock) that the stream information has been updated if ((NULL != callback) && (index >= 0)) { #ifndef USE_ASYNCHRONOUS_STREAMCBEVENT_PROPERTYCHANGE_CALLBACK (*callback)(&mp->mStreamInfo.mItf, XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/, 1 /*streamIndex, only one stream supported here, 0 is reserved*/, NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/, callbackPContext /*pContext*/); #else SLresult res = EnqueueAsyncCallback_piipp(mp, callback, /*p1*/ &mp->mStreamInfo.mItf, /*i1*/ XA_STREAMCBEVENT_PROPERTYCHANGE /*eventId*/, /*i2*/ 1 /*streamIndex, only one stream supported here, 0 is reserved*/, /*p2*/ NULL /*pEventData, always NULL in OpenMAX AL 1.0.1*/, /*p3*/ callbackPContext /*pContext*/); ALOGW_IF(SL_RESULT_SUCCESS != res, "Callback %p(%p, XA_STREAMCBEVENT_PROPERTYCHANGE, 1, NULL, %p) dropped", callback, &mp->mStreamInfo.mItf, callbackPContext); #endif } break; } case android::GenericPlayer::kEventEndOfStream: { SL_LOGV("Received AVPlayer::kEventEndOfStream for CMediaPlayer %p", mp); object_lock_exclusive(&mp->mObject); // should be xaPlayCallback but we're sharing the itf between SL and AL slPlayCallback playCallback = NULL; void * playContext = NULL; // XAPlayItf callback or no callback? if (mp->mPlay.mEventFlags & XA_PLAYEVENT_HEADATEND) { playCallback = mp->mPlay.mCallback; playContext = mp->mPlay.mContext; } mp->mPlay.mState = XA_PLAYSTATE_PAUSED; object_unlock_exclusive(&mp->mObject); // enqueue callback with no lock held if (NULL != playCallback) { #ifndef USE_ASYNCHRONOUS_PLAY_CALLBACK (*playCallback)(&mp->mPlay.mItf, playContext, XA_PLAYEVENT_HEADATEND); #else SLresult res = EnqueueAsyncCallback_ppi(mp, playCallback, &mp->mPlay.mItf, playContext, XA_PLAYEVENT_HEADATEND); ALOGW_IF(SL_RESULT_SUCCESS != res, "Callback %p(%p, %p, SL_PLAYEVENT_HEADATEND) dropped", playCallback, &mp->mPlay.mItf, playContext); #endif } break; } case android::GenericPlayer::kEventChannelCount: { SL_LOGV("kEventChannelCount channels = %d", data1); object_lock_exclusive(&mp->mObject); if (UNKNOWN_NUMCHANNELS == mp->mNumChannels && UNKNOWN_NUMCHANNELS != data1) { mp->mNumChannels = data1; android_Player_volumeUpdate(mp); } object_unlock_exclusive(&mp->mObject); } break; case android::GenericPlayer::kEventPrefetchFillLevelUpdate: { SL_LOGV("kEventPrefetchFillLevelUpdate"); if (!IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { break; } slPrefetchCallback callback = NULL; void* callbackPContext = NULL; // SLPrefetchStatusItf callback or no callback? interface_lock_exclusive(&mp->mPrefetchStatus); if (mp->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_FILLLEVELCHANGE) { callback = mp->mPrefetchStatus.mCallback; callbackPContext = mp->mPrefetchStatus.mContext; } mp->mPrefetchStatus.mLevel = (SLpermille)data1; interface_unlock_exclusive(&mp->mPrefetchStatus); // callback with no lock held if (NULL != callback) { (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, SL_PREFETCHEVENT_FILLLEVELCHANGE); } } break; case android::GenericPlayer::kEventPrefetchStatusChange: { SL_LOGV("kEventPrefetchStatusChange"); if (!IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { break; } slPrefetchCallback callback = NULL; void* callbackPContext = NULL; // SLPrefetchStatusItf callback or no callback? object_lock_exclusive(&mp->mObject); if (mp->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_STATUSCHANGE) { callback = mp->mPrefetchStatus.mCallback; callbackPContext = mp->mPrefetchStatus.mContext; } if (data1 >= android::kStatusIntermediate) { mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_SUFFICIENTDATA; } else if (data1 < android::kStatusIntermediate) { mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW; } object_unlock_exclusive(&mp->mObject); // callback with no lock held if (NULL != callback) { (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, SL_PREFETCHEVENT_STATUSCHANGE); } } break; case android::GenericPlayer::kEventPlay: { SL_LOGV("kEventPlay"); interface_lock_shared(&mp->mPlay); slPlayCallback callback = mp->mPlay.mCallback; void* callbackPContext = mp->mPlay.mContext; interface_unlock_shared(&mp->mPlay); if (NULL != callback) { (*callback)(&mp->mPlay.mItf, callbackPContext, (SLuint32) data1); // SL_PLAYEVENT_HEAD* } } break; case android::GenericPlayer::kEventErrorAfterPrepare: { SL_LOGV("kEventErrorAfterPrepare"); // assume no callback slPrefetchCallback callback = NULL; void* callbackPContext = NULL; object_lock_exclusive(&mp->mObject); if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) { mp->mPrefetchStatus.mLevel = 0; mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW; if (!(~mp->mPrefetchStatus.mCallbackEventsMask & (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) { callback = mp->mPrefetchStatus.mCallback; callbackPContext = mp->mPrefetchStatus.mContext; } } object_unlock_exclusive(&mp->mObject); // FIXME there's interesting information in data1, but no API to convey it to client SL_LOGE("Error after prepare: %d", data1); // callback with no lock held if (NULL != callback) { (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE); } } break; default: { SL_LOGE("Received unknown event %d, data %d from AVPlayer", event, data1); } } mp->mCallbackProtector->exitCb(); }
static void HandleAdd(void *self, int MPH) { // validate input parameters IDynamicInterfaceManagement *this = (IDynamicInterfaceManagement *) self; assert(NULL != this); IObject *thisObject = InterfaceToIObject(this); assert(NULL != thisObject); assert(0 <= MPH && MPH < MPH_MAX); const ClassTable *class__ = thisObject->mClass; assert(NULL != class__); int index = class__->mMPH_to_index[MPH]; assert(0 <= index && index < (int) class__->mInterfaceCount); SLuint8 *interfaceStateP = &thisObject->mInterfaceStates[index]; SLresult result; // check interface state object_lock_exclusive(thisObject); SLuint8 state = *interfaceStateP; switch (state) { case INTERFACE_ADDING_1: // normal case { // change state to indicate we are now adding the interface *interfaceStateP = INTERFACE_ADDING_2; object_unlock_exclusive(thisObject); // this section runs with mutex unlocked const struct iid_vtable *x = &class__->mInterfaces[index]; size_t offset = x->mOffset; void *thisItf = (char *) thisObject + offset; BoolHook expose = MPH_init_table[MPH].mExpose; // call the optional expose hook if ((NULL == expose) || (*expose)(thisItf)) { result = SL_RESULT_SUCCESS; } else { result = SL_RESULT_FEATURE_UNSUPPORTED; } // re-lock mutex to update state object_lock_exclusive(thisObject); assert(INTERFACE_ADDING_2 == *interfaceStateP); if (SL_RESULT_SUCCESS == result) { ((size_t *) thisItf)[0] ^= ~0; state = INTERFACE_ADDED; } else { state = INTERFACE_INITIALIZED; } } break; case INTERFACE_ADDING_1A: // operation was aborted while on work queue result = SL_RESULT_OPERATION_ABORTED; state = INTERFACE_INITIALIZED; break; default: // impossible assert(SL_BOOLEAN_FALSE); result = SL_RESULT_INTERNAL_ERROR; break; } // mutex is locked, update state *interfaceStateP = state; // Make a copy of these, so we can call the callback with mutex unlocked slDynamicInterfaceManagementCallback callback = this->mCallback; void *context = this->mContext; object_unlock_exclusive(thisObject); // Note that the mutex is unlocked during the callback if (NULL != callback) { const SLInterfaceID iid = &SL_IID_array[MPH]; // equal but not == to the original IID (*callback)(&this->mItf, context, SL_DYNAMIC_ITF_EVENT_ASYNC_TERMINATION, result, iid); } }