static SLresult IAndroidBufferQueue_Clear(SLAndroidBufferQueueItf self) { SL_ENTER_INTERFACE result = SL_RESULT_SUCCESS; IAndroidBufferQueue *thiz = (IAndroidBufferQueue *) self; interface_lock_exclusive(thiz); // reset the queue pointers thiz->mFront = &thiz->mBufferArray[0]; thiz->mRear = &thiz->mBufferArray[0]; // reset the queue state thiz->mState.count = 0; thiz->mState.index = 0; // object-specific behavior for a clear switch (InterfaceToObjectID(thiz)) { case SL_OBJECTID_AUDIOPLAYER: android_audioPlayer_androidBufferQueue_clear_l((CAudioPlayer*) thiz->mThis); break; case XA_OBJECTID_MEDIAPLAYER: android_Player_androidBufferQueue_clear_l((CMediaPlayer*) thiz->mThis); break; default: result = SL_RESULT_PARAMETER_INVALID; } interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE }
static SLresult IAndroidBufferQueue_RegisterCallback(SLAndroidBufferQueueItf self, slAndroidBufferQueueCallback callback, void *pContext) { SL_ENTER_INTERFACE IAndroidBufferQueue *thiz = (IAndroidBufferQueue *) self; interface_lock_exclusive(thiz); // verify pre-condition that media object is in the SL_PLAYSTATE_STOPPED state if (SL_PLAYSTATE_STOPPED == getAssociatedState(thiz)) { thiz->mCallback = callback; thiz->mContext = pContext; // FIXME investigate why these two cases are not handled symmetrically any more switch (InterfaceToObjectID(thiz)) { case SL_OBJECTID_AUDIOPLAYER: result = android_audioPlayer_androidBufferQueue_registerCallback_l( (CAudioPlayer*) thiz->mThis); break; case XA_OBJECTID_MEDIAPLAYER: result = SL_RESULT_SUCCESS; break; default: result = SL_RESULT_PARAMETER_INVALID; } } else { result = SL_RESULT_PRECONDITIONS_VIOLATED; } interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE }
static SLresult IThreadSync_EnterCriticalSection(SLThreadSyncItf self) { SL_ENTER_INTERFACE IThreadSync *this = (IThreadSync *) self; interface_lock_exclusive(this); for (;;) { if (this->mInCriticalSection) { if (!pthread_equal(this->mOwner, pthread_self())) { ++this->mWaiting; interface_cond_wait(this); continue; } // nested locks are not allowed result = SL_RESULT_PRECONDITIONS_VIOLATED; break; } this->mInCriticalSection = SL_BOOLEAN_TRUE; this->mOwner = pthread_self(); result = SL_RESULT_SUCCESS; break; } interface_unlock_exclusive(this); SL_LEAVE_INTERFACE }
bool COutputMix_PreDestroy(void *self) { // Ignore destroy requests if there are any players attached to this output mix COutputMix *outputMix = (COutputMix *) self; // See design document for explanation if (0 == outputMix->mObject.mStrongRefCount) { #ifdef USE_OUTPUTMIXEXT // We only support a single active output mix per engine, so check if this is the active mix IEngine *thisEngine = outputMix->mObject.mEngine; interface_lock_exclusive(thisEngine); bool thisIsTheActiveOutputMix = false; if (outputMix == thisEngine->mOutputMix) { thisIsTheActiveOutputMix = true; } interface_unlock_exclusive(thisEngine); if (thisIsTheActiveOutputMix) { // Tell the asynchronous mixer callback that we want to destroy the output mix outputMix->mOutputMixExt.mDestroyRequested = true; while (outputMix->mOutputMixExt.mDestroyRequested) { object_cond_wait(&outputMix->mObject); } #ifdef USE_SDL // Mixer callback has acknowledged our request and unlinked output mix from engine. // Disable SDL_callback from being called periodically by SDL's internal thread. SDL_PauseAudio(1); #endif } #endif return true; } SL_LOGE("Object::Destroy(%p) for OutputMix ignored; %u players attached", outputMix, outputMix->mObject.mStrongRefCount); return false; }
static SLresult IAndroidBufferQueue_Clear(SLAndroidBufferQueueItf self) { SL_ENTER_INTERFACE result = SL_RESULT_SUCCESS; IAndroidBufferQueue *thiz = (IAndroidBufferQueue *) self; interface_lock_exclusive(thiz); // reset the queue pointers thiz->mFront = &thiz->mBufferArray[0]; thiz->mRear = &thiz->mBufferArray[0]; // reset the queue state thiz->mState.count = 0; thiz->mState.index = 0; // reset the individual buffers for (XAuint16 i=0 ; i<(thiz->mNumBuffers + 1) ; i++) { thiz->mBufferArray[i].mDataBuffer = NULL; thiz->mBufferArray[i].mDataSize = 0; thiz->mBufferArray[i].mDataSizeConsumed = 0; thiz->mBufferArray[i].mBufferContext = NULL; thiz->mBufferArray[i].mBufferState = SL_ANDROIDBUFFERQUEUEEVENT_NONE; switch (thiz->mBufferType) { case kAndroidBufferTypeMpeg2Ts: thiz->mBufferArray[i].mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE; thiz->mBufferArray[i].mItems.mTsCmdData.mPts = 0; break; case kAndroidBufferTypeAacadts: thiz->mBufferArray[i].mItems.mAdtsCmdData.mAdtsCmdCode = ANDROID_ADTSEVENT_NONE; break; default: result = SL_RESULT_CONTENT_UNSUPPORTED; } } if (SL_RESULT_SUCCESS == result) { // object-specific behavior for a clear switch (InterfaceToObjectID(thiz)) { case SL_OBJECTID_AUDIOPLAYER: result = SL_RESULT_SUCCESS; android_audioPlayer_androidBufferQueue_clear_l((CAudioPlayer*) thiz->mThis); break; case XA_OBJECTID_MEDIAPLAYER: result = SL_RESULT_SUCCESS; android_Player_androidBufferQueue_clear_l((CMediaPlayer*) thiz->mThis); break; default: result = SL_RESULT_PARAMETER_INVALID; } } interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE }
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 }
static SLresult IRecord_SetDurationLimit(SLRecordItf self, SLmillisecond msec) { SL_ENTER_INTERFACE IRecord *this = (IRecord *) self; interface_lock_exclusive(this); if (this->mDurationLimit != msec) { this->mDurationLimit = msec; interface_unlock_exclusive_attributes(this, ATTR_TRANSPORT); } else { interface_unlock_exclusive(this); } result = SL_RESULT_SUCCESS; SL_LEAVE_INTERFACE }
SLresult IAndroidAutomaticGainControl_SetEnabled(SLAndroidAutomaticGainControlItf self, SLboolean enabled) { SL_ENTER_INTERFACE IAndroidAutomaticGainControl *thiz = (IAndroidAutomaticGainControl *) self; interface_lock_exclusive(thiz); thiz->mEnabled = (SLboolean) enabled; if (NO_AUTOGAIN(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = thiz->mAGCEffect->setEnabled((bool) thiz->mEnabled); result = android_fx_statusToResult(status); } interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE }
static SLresult IAndroidAcousticEchoCancellation_SetEnabled(SLAndroidAcousticEchoCancellationItf self, SLboolean enabled) { SL_ENTER_INTERFACE IAndroidAcousticEchoCancellation *thiz = (IAndroidAcousticEchoCancellation *) self; interface_lock_exclusive(thiz); thiz->mEnabled = (SLboolean) enabled; if (NO_ECHOCANCEL(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = thiz->mAECEffect->setEnabled((bool) thiz->mEnabled); result = android_fx_statusToResult(status); } interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE }
static SLresult IBassBoost_SetEnabled(SLBassBoostItf self, SLboolean enabled) { SL_ENTER_INTERFACE IBassBoost *thiz = (IBassBoost *) self; interface_lock_exclusive(thiz); thiz->mEnabled = (SLboolean) enabled; #if !defined(ANDROID) result = SL_RESULT_SUCCESS; #else if (NO_BASSBOOST(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = thiz->mBassBoostEffect->setEnabled((bool) thiz->mEnabled); result = android_fx_statusToResult(status); } #endif interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE }
static SLresult IThreadSync_ExitCriticalSection(SLThreadSyncItf self) { SL_ENTER_INTERFACE IThreadSync *this = (IThreadSync *) self; interface_lock_exclusive(this); if (!this->mInCriticalSection || !pthread_equal(this->mOwner, pthread_self())) { result = SL_RESULT_PRECONDITIONS_VIOLATED; } else { this->mInCriticalSection = SL_BOOLEAN_FALSE; memset(&this->mOwner, 0, sizeof(pthread_t)); if (this->mWaiting) { --this->mWaiting; interface_cond_signal(this); } result = SL_RESULT_SUCCESS; } interface_unlock_exclusive(this); SL_LEAVE_INTERFACE }
static SLresult IVolume_SetVolumeLevel(SLVolumeItf self, SLmillibel level_) { SL_ENTER_INTERFACE int level = level_; if (!((SL_MILLIBEL_MIN <= level) && (level <= PLATFORM_MILLIBEL_MAX_VOLUME))) { result = SL_RESULT_PARAMETER_INVALID; } else { IVolume *thiz = (IVolume *) self; interface_lock_exclusive(thiz); SLmillibel oldLevel = thiz->mLevel; if (oldLevel != level) { thiz->mLevel = level; interface_unlock_exclusive_attributes(thiz, ATTR_GAIN); } else { interface_unlock_exclusive(thiz); } result = SL_RESULT_SUCCESS; } SL_LEAVE_INTERFACE }
static SLresult IAndroidBufferQueue_RegisterCallback(SLAndroidBufferQueueItf self, slAndroidBufferQueueCallback callback, void *pContext) { SL_ENTER_INTERFACE IAndroidBufferQueue *thiz = (IAndroidBufferQueue *) self; interface_lock_exclusive(thiz); // verify pre-condition that media object is in the SL_PLAYSTATE_STOPPED state if (SL_PLAYSTATE_STOPPED == getAssociatedState(thiz)) { thiz->mCallback = callback; thiz->mContext = pContext; result = SL_RESULT_SUCCESS; } else { result = SL_RESULT_PRECONDITIONS_VIOLATED; } interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE }
//----------------------------------------------------------------------------- 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(); }
IObject *construct(const ClassTable *clazz, unsigned exposedMask, SLEngineItf engine) { IObject *thiz; // Do not change this to malloc; we depend on the object being memset to zero thiz = (IObject *) calloc(1, clazz->mSize); if (NULL != thiz) { SL_LOGV("construct %s at %p", clazz->mName, thiz); unsigned lossOfControlMask = 0; // a NULL engine means we are constructing the engine IEngine *thisEngine = (IEngine *) engine; if (NULL == thisEngine) { // thisEngine = &((CEngine *) thiz)->mEngine; thiz->mEngine = (CEngine *) thiz; } else { thiz->mEngine = (CEngine *) thisEngine->mThis; interface_lock_exclusive(thisEngine); if (MAX_INSTANCE <= thisEngine->mInstanceCount) { SL_LOGE("Too many objects"); interface_unlock_exclusive(thisEngine); free(thiz); return NULL; } // pre-allocate a pending slot, but don't assign bit from mInstanceMask yet ++thisEngine->mInstanceCount; assert(((unsigned) ~0) != thisEngine->mInstanceMask); interface_unlock_exclusive(thisEngine); // const, no lock needed if (thisEngine->mLossOfControlGlobal) { lossOfControlMask = ~0; } } thiz->mLossOfControlMask = lossOfControlMask; thiz->mClass = clazz; const struct iid_vtable *x = clazz->mInterfaces; SLuint8 *interfaceStateP = thiz->mInterfaceStates; SLuint32 index; for (index = 0; index < clazz->mInterfaceCount; ++index, ++x, exposedMask >>= 1) { SLuint8 state; // initialize all interfaces with init hooks, even if not exposed const struct MPH_init *mi = &MPH_init_table[x->mMPH]; VoidHook init = mi->mInit; if (NULL != init) { void *self = (char *) thiz + x->mOffset; // IObject does not have an mThis, so [1] is not always defined if (index) { ((IObject **) self)[1] = thiz; } // call the initialization hook (*init)(self); // IObject does not require a call to GetInterface if (index) { // This trickery invalidates the v-table until GetInterface ((size_t *) self)[0] ^= ~0; } // if interface is exposed, also call the optional expose hook BoolHook expose; state = (exposedMask & 1) && ((NULL == (expose = mi->mExpose)) || (*expose)(self)) ? INTERFACE_EXPOSED : INTERFACE_INITIALIZED; // FIXME log or report to application if an expose hook on a // required explicit interface fails at creation time } else { state = INTERFACE_UNINITIALIZED; } *interfaceStateP++ = state; } // note that the new object is not yet published; creator must call IObject_Publish }
//-------------------------------------------------- // consumption from ABQ: pull from the ABQ, and push to shared memory (media server) void StreamSourceAppProxy::pullFromBuffQueue() { if (android::CallbackProtector::enterCbIfOk(mCallbackProtector)) { size_t bufferId; void* bufferLoc; size_t buffSize; slAndroidBufferQueueCallback callback = NULL; void* pBufferContext, *pBufferData, *callbackPContext = NULL; AdvancedBufferHeader *oldFront = NULL; uint32_t dataSize /* , dataUsed */; // retrieve data from the buffer queue interface_lock_exclusive(mAndroidBufferQueue); // can this read operation cause us to call the buffer queue callback // (either because there was a command with no data, or all the data has been consumed) bool queueCallbackCandidate = false; if (mAndroidBufferQueue->mState.count != 0) { // SL_LOGD("nbBuffers in ABQ = %u, buffSize=%u",abq->mState.count, buffSize); assert(mAndroidBufferQueue->mFront != mAndroidBufferQueue->mRear); oldFront = mAndroidBufferQueue->mFront; AdvancedBufferHeader *newFront = &oldFront[1]; // consume events when starting to read data from a buffer for the first time if (oldFront->mDataSizeConsumed == 0) { // note this code assumes at most one event per buffer; see IAndroidBufferQueue_Enqueue if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_EOS) { receivedCmd_l(IStreamListener::EOS); // EOS has no associated data queueCallbackCandidate = true; } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_DISCONTINUITY) { receivedCmd_l(IStreamListener::DISCONTINUITY); } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_DISCON_NEWPTS) { sp<AMessage> msg = new AMessage(); msg->setInt64(IStreamListener::kKeyResumeAtPTS, (int64_t)oldFront->mItems.mTsCmdData.mPts); receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/); } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_FORMAT_CHANGE_FULL) { sp<AMessage> msg = new AMessage(); msg->setInt32( IStreamListener::kKeyDiscontinuityMask, ATSParser::DISCONTINUITY_FORMATCHANGE); receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/); } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_FORMAT_CHANGE_VIDEO) { sp<AMessage> msg = new AMessage(); msg->setInt32( IStreamListener::kKeyDiscontinuityMask, ATSParser::DISCONTINUITY_VIDEO_FORMAT); receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/); } // note that here we are intentionally only supporting // ANDROID_MP2TSEVENT_FORMAT_CHANGE_VIDEO, see IAndroidBufferQueue.c // some commands may introduce a time discontinuity, reevaluate position if needed if (oldFront->mItems.mTsCmdData.mTsCmdCode & (ANDROID_MP2TSEVENT_DISCONTINUITY | ANDROID_MP2TSEVENT_DISCON_NEWPTS | ANDROID_MP2TSEVENT_FORMAT_CHANGE_FULL)) { const sp<StreamPlayer> player(mPlayer.promote()); if (player != NULL) { // FIXME see note at onSeek player->seek(ANDROID_UNKNOWN_TIME); } } oldFront->mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE; } { // we're going to change the shared mem buffer queue, so lock it Mutex::Autolock _l(mLock); if (!mAvailableBuffers.empty()) { bufferId = *mAvailableBuffers.begin(); CHECK_LT(bufferId, mBuffers.size()); sp<IMemory> mem = mBuffers.itemAt(bufferId); bufferLoc = mem->pointer(); buffSize = mem->size(); char *pSrc = ((char*)oldFront->mDataBuffer) + oldFront->mDataSizeConsumed; if (oldFront->mDataSizeConsumed + buffSize < oldFront->mDataSize) { // more available than requested, copy as much as requested // consume data: 1/ copy to given destination memcpy(bufferLoc, pSrc, buffSize); // 2/ keep track of how much has been consumed oldFront->mDataSizeConsumed += buffSize; // 3/ notify shared mem listener that new data is available receivedBuffer_l(bufferId, buffSize); mAvailableBuffers.erase(mAvailableBuffers.begin()); } else { // requested as much available or more: consume the whole of the current // buffer and move to the next size_t consumed = oldFront->mDataSize - oldFront->mDataSizeConsumed; //SL_LOGD("consuming rest of buffer: enqueueing=%u", consumed); oldFront->mDataSizeConsumed = oldFront->mDataSize; // move queue to next if (newFront == &mAndroidBufferQueue-> mBufferArray[mAndroidBufferQueue->mNumBuffers + 1]) { // reached the end, circle back newFront = mAndroidBufferQueue->mBufferArray; } mAndroidBufferQueue->mFront = newFront; mAndroidBufferQueue->mState.count--; mAndroidBufferQueue->mState.index++; if (consumed > 0) { // consume data: 1/ copy to given destination memcpy(bufferLoc, pSrc, consumed); // 2/ keep track of how much has been consumed // here nothing to do because we are done with this buffer // 3/ notify StreamPlayer that new data is available receivedBuffer_l(bufferId, consumed); mAvailableBuffers.erase(mAvailableBuffers.begin()); } // data has been consumed, and the buffer queue state has been updated // we will notify the client if applicable queueCallbackCandidate = true; } } if (queueCallbackCandidate) { if (mAndroidBufferQueue->mCallbackEventsMask & SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED) { callback = mAndroidBufferQueue->mCallback; // save callback data while under lock callbackPContext = mAndroidBufferQueue->mContext; pBufferContext = (void *)oldFront->mBufferContext; pBufferData = (void *)oldFront->mDataBuffer; dataSize = oldFront->mDataSize; // here a buffer is only dequeued when fully consumed //dataUsed = oldFront->mDataSizeConsumed; } } //SL_LOGD("%d buffers available after reading from queue", mAvailableBuffers.size()); if (!mAvailableBuffers.empty()) { // there is still room in the shared memory, recheck later if we can pull // data from the buffer queue and write it to shared memory const sp<StreamPlayer> player(mPlayer.promote()); if (player != NULL) { player->queueRefilled(); } } } } else { // empty queue SL_LOGD("ABQ empty, starving!"); } interface_unlock_exclusive(mAndroidBufferQueue); // notify client of buffer processed if (NULL != callback) { SLresult result = (*callback)(&mAndroidBufferQueue->mItf, callbackPContext, pBufferContext, pBufferData, dataSize, dataSize, /* dataUsed */ // no messages during playback other than marking the buffer as processed (const SLAndroidBufferItem*)(&kItemProcessed) /* pItems */, NB_BUFFEREVENT_ITEM_FIELDS *sizeof(SLuint32) /* itemsLength */ ); if (SL_RESULT_SUCCESS != result) { // Reserved for future use SL_LOGW("Unsuccessful result %d returned from AndroidBufferQueueCallback", result); } } mCallbackProtector->exitCb(); } // enterCbIfOk }
void object_unlock_exclusive_attributes(IObject *thiz, unsigned attributes) #endif { #ifdef USE_DEBUG assert(pthread_equal(pthread_self(), thiz->mOwner)); assert(NULL != thiz->mFile); assert(0 != thiz->mLine); #endif int ok; // make SL object IDs be contiguous with XA object IDs SLuint32 objectID = IObjectToObjectID(thiz); SLuint32 index = objectID; if ((XA_OBJECTID_ENGINE <= index) && (index <= XA_OBJECTID_CAMERADEVICE)) { ; } else if ((SL_OBJECTID_ENGINE <= index) && (index <= SL_OBJECTID_METADATAEXTRACTOR)) { index -= SL_OBJECTID_ENGINE - XA_OBJECTID_CAMERADEVICE - 1; } else { assert(false); index = 0; } // first synchronously handle updates to attributes here, while object is still locked. // This appears to be a loop, but actually typically runs through the loop only once. unsigned asynchronous = attributes; while (attributes) { // this sequence is carefully crafted to be O(1); tread carefully when making changes unsigned bit = ctz(attributes); // ATTR_INDEX_MAX == next bit position after the last attribute assert(ATTR_INDEX_MAX > bit); // compute the entry in the handler table using object ID and bit number AttributeHandler handler = handlerTable[index][bit]; if (NULL != handler) { asynchronous &= ~(*handler)(thiz); } attributes &= ~(1 << bit); } // any remaining attributes are handled asynchronously in the sync thread if (asynchronous) { unsigned oldAttributesMask = thiz->mAttributesMask; thiz->mAttributesMask = oldAttributesMask | asynchronous; if (oldAttributesMask) { asynchronous = ATTR_NONE; } } #ifdef ANDROID // FIXME hack to safely handle a post-unlock PrefetchStatus callback and/or AudioTrack::start() slPrefetchCallback prefetchCallback = NULL; void *prefetchContext = NULL; SLuint32 prefetchEvents = SL_PREFETCHEVENT_NONE; android::sp<android::AudioTrack> audioTrack; if (SL_OBJECTID_AUDIOPLAYER == objectID) { CAudioPlayer *ap = (CAudioPlayer *) thiz; prefetchCallback = ap->mPrefetchStatus.mDeferredPrefetchCallback; prefetchContext = ap->mPrefetchStatus.mDeferredPrefetchContext; prefetchEvents = ap->mPrefetchStatus.mDeferredPrefetchEvents; ap->mPrefetchStatus.mDeferredPrefetchCallback = NULL; // clearing these next two fields is not required, but avoids stale data during debugging ap->mPrefetchStatus.mDeferredPrefetchContext = NULL; ap->mPrefetchStatus.mDeferredPrefetchEvents = SL_PREFETCHEVENT_NONE; if (ap->mDeferredStart) { audioTrack = ap->mAudioTrack; ap->mDeferredStart = false; } } #endif #ifdef USE_DEBUG memset(&thiz->mOwner, 0, sizeof(pthread_t)); thiz->mFile = file; thiz->mLine = line; #endif ok = pthread_mutex_unlock(&thiz->mMutex); assert(0 == ok); #ifdef ANDROID // FIXME call the prefetch status callback while not holding the mutex on AudioPlayer if (NULL != prefetchCallback) { // note these are synchronous by the application's thread as it is about to return from API assert(prefetchEvents != SL_PREFETCHEVENT_NONE); CAudioPlayer *ap = (CAudioPlayer *) thiz; // spec requires separate callbacks for each event if (SL_PREFETCHEVENT_STATUSCHANGE & prefetchEvents) { (*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext, SL_PREFETCHEVENT_STATUSCHANGE); } if (SL_PREFETCHEVENT_FILLLEVELCHANGE & prefetchEvents) { (*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext, SL_PREFETCHEVENT_FILLLEVELCHANGE); } } // call AudioTrack::start() while not holding the mutex on AudioPlayer if (audioTrack != 0) { audioTrack->start(); audioTrack.clear(); } #endif // first update to this interface since previous sync if (ATTR_NONE != asynchronous) { unsigned id = thiz->mInstanceID; if (0 != id) { --id; assert(MAX_INSTANCE > id); IEngine *thisEngine = &thiz->mEngine->mEngine; // FIXME atomic or here interface_lock_exclusive(thisEngine); thisEngine->mChangedMask |= 1 << id; interface_unlock_exclusive(thisEngine); } } }
ssize_t BufferQueueSource::readAt(off64_t offset, void *data, size_t size) { SL_LOGD("BufferQueueSource::readAt(offset=%lld, data=%p, size=%d)", offset, data, size); if (mEosReached) { // once EOS has been received from the buffer queue, you can't read anymore return 0; } ssize_t readSize; slAndroidBufferQueueCallback callback = NULL; void* pBufferContext, *pBufferData, *callbackPContext; uint32_t dataSize, dataUsed; interface_lock_exclusive(mAndroidBufferQueueSource); if (mAndroidBufferQueueSource->mState.count == 0) { readSize = 0; } else { assert(mAndroidBufferQueueSource->mFront != mAndroidBufferQueueSource->mRear); AdvancedBufferHeader *oldFront = mAndroidBufferQueueSource->mFront; AdvancedBufferHeader *newFront = &oldFront[1]; // where to read from char *pSrc = NULL; // can this read operation cause us to call the buffer queue callback // (either because there was a command with no data, or all the data has been consumed) bool queueCallbackCandidate = false; // consume events when starting to read data from a buffer for the first time if (oldFront->mDataSizeConsumed == 0) { if (oldFront->mItems.mAdtsCmdData.mAdtsCmdCode & ANDROID_ADTSEVENT_EOS) { mEosReached = true; // EOS has no associated data queueCallbackCandidate = true; } oldFront->mItems.mAdtsCmdData.mAdtsCmdCode = ANDROID_ADTSEVENT_NONE; } //assert(mStreamToBqOffset <= offset); CHECK_LE(mStreamToBqOffset, offset); if (offset + (off64_t) size <= mStreamToBqOffset + oldFront->mDataSize) { pSrc = ((char*)oldFront->mDataBuffer) + (offset - mStreamToBqOffset); if (offset - mStreamToBqOffset + size == oldFront->mDataSize) { // consumed buffer entirely oldFront->mDataSizeConsumed = oldFront->mDataSize; mStreamToBqOffset += oldFront->mDataSize; queueCallbackCandidate = true; // move queue to next buffer if (newFront == &mAndroidBufferQueueSource-> mBufferArray[mAndroidBufferQueueSource->mNumBuffers + 1]) { // reached the end, circle back newFront = mAndroidBufferQueueSource->mBufferArray; } mAndroidBufferQueueSource->mFront = newFront; // update the queue state mAndroidBufferQueueSource->mState.count--; mAndroidBufferQueueSource->mState.index++; SL_LOGV("BufferQueueSource moving to next buffer"); } } // consume data: copy to given destination if (NULL != pSrc) { memcpy(data, pSrc, size); readSize = size; } else { readSize = 0; } if (queueCallbackCandidate) { // data has been consumed, and the buffer queue state has been updated // we will notify the client if applicable if (mAndroidBufferQueueSource->mCallbackEventsMask & SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED) { callback = mAndroidBufferQueueSource->mCallback; // save callback data while under lock callbackPContext = mAndroidBufferQueueSource->mContext; pBufferContext = (void *)oldFront->mBufferContext; pBufferData = (void *)oldFront->mDataBuffer; dataSize = oldFront->mDataSize; dataUsed = oldFront->mDataSizeConsumed; } } } interface_unlock_exclusive(mAndroidBufferQueueSource); // notify client if (NULL != callback) { SLresult result = (*callback)(&mAndroidBufferQueueSource->mItf, callbackPContext, pBufferContext, pBufferData, dataSize, dataUsed, // no messages during playback other than marking the buffer as processed (const SLAndroidBufferItem*)(&kItemProcessed) /* pItems */, NB_BUFFEREVENT_ITEM_FIELDS * sizeof(SLuint32) /* itemsLength */ ); if (SL_RESULT_SUCCESS != result) { // Reserved for future use SL_LOGW("Unsuccessful result %d returned from AndroidBufferQueueCallback", result); } } return readSize; }
//----------------------------------------------------------------------------- static void audioRecorder_callback(int event, void* user, void *info) { //SL_LOGV("audioRecorder_callback(%d, %p, %p) entering", event, user, info); CAudioRecorder *ar = (CAudioRecorder *)user; if (!android::CallbackProtector::enterCbIfOk(ar->mCallbackProtector)) { // it is not safe to enter the callback (the track is about to go away) return; } void * callbackPContext = NULL; switch(event) { case android::AudioRecord::EVENT_MORE_DATA: { slBufferQueueCallback callback = NULL; android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info; // push data to the buffer queue interface_lock_exclusive(&ar->mBufferQueue); if (ar->mBufferQueue.mState.count != 0) { assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear); BufferHeader *oldFront = ar->mBufferQueue.mFront; BufferHeader *newFront = &oldFront[1]; // FIXME handle 8bit based on buffer format short *pDest = (short*)((char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed); if (ar->mBufferQueue.mSizeConsumed + pBuff->size < oldFront->mSize) { // can't consume the whole or rest of the buffer in one shot ar->mBufferQueue.mSizeConsumed += pBuff->size; // leave pBuff->size untouched // consume data // FIXME can we avoid holding the lock during the copy? memcpy (pDest, pBuff->i16, pBuff->size); #ifdef MONITOR_RECORDING if (NULL != gMonitorFp) { fwrite(pBuff->i16, pBuff->size, 1, gMonitorFp); } #endif } else { // finish pushing the buffer or push the buffer in one shot pBuff->size = oldFront->mSize - ar->mBufferQueue.mSizeConsumed; ar->mBufferQueue.mSizeConsumed = 0; if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) { newFront = ar->mBufferQueue.mArray; } ar->mBufferQueue.mFront = newFront; ar->mBufferQueue.mState.count--; ar->mBufferQueue.mState.playIndex++; // consume data // FIXME can we avoid holding the lock during the copy? memcpy (pDest, pBuff->i16, pBuff->size); #ifdef MONITOR_RECORDING if (NULL != gMonitorFp) { fwrite(pBuff->i16, pBuff->size, 1, gMonitorFp); } #endif // data has been copied to the buffer, and the buffer queue state has been updated // we will notify the client if applicable callback = ar->mBufferQueue.mCallback; // save callback data callbackPContext = ar->mBufferQueue.mContext; } } else { // no destination to push the data pBuff->size = 0; } interface_unlock_exclusive(&ar->mBufferQueue); // notify client if (NULL != callback) { (*callback)(&ar->mBufferQueue.mItf, callbackPContext); } } break; case android::AudioRecord::EVENT_OVERRUN: audioRecorder_handleOverrun_lockRecord(ar); break; case android::AudioRecord::EVENT_MARKER: audioRecorder_handleMarker_lockRecord(ar); break; case android::AudioRecord::EVENT_NEW_POS: audioRecorder_handleNewPos_lockRecord(ar); break; } ar->mCallbackProtector->exitCb(); }
//----------------------------------------------------------------------------- static void audioRecorder_callback(int event, void* user, void *info) { //SL_LOGV("audioRecorder_callback(%d, %p, %p) entering", event, user, info); CAudioRecorder *ar = (CAudioRecorder *)user; if (!android::CallbackProtector::enterCbIfOk(ar->mCallbackProtector)) { // it is not safe to enter the callback (the track is about to go away) return; } void * callbackPContext = NULL; switch (event) { case android::AudioRecord::EVENT_MORE_DATA: { slBufferQueueCallback callback = NULL; android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info; // push data to the buffer queue interface_lock_exclusive(&ar->mBufferQueue); if (ar->mBufferQueue.mState.count != 0) { assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear); BufferHeader *oldFront = ar->mBufferQueue.mFront; BufferHeader *newFront = &oldFront[1]; size_t availSink = oldFront->mSize - ar->mBufferQueue.mSizeConsumed; size_t availSource = pBuff->size; size_t bytesToCopy = availSink < availSource ? availSink : availSource; void *pDest = (char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed; memcpy(pDest, pBuff->raw, bytesToCopy); if (bytesToCopy < availSink) { // can't consume the whole or rest of the buffer in one shot ar->mBufferQueue.mSizeConsumed += availSource; // pBuff->size is already equal to bytesToCopy in this case } else { // finish pushing the buffer or push the buffer in one shot pBuff->size = bytesToCopy; ar->mBufferQueue.mSizeConsumed = 0; if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) { newFront = ar->mBufferQueue.mArray; } ar->mBufferQueue.mFront = newFront; ar->mBufferQueue.mState.count--; ar->mBufferQueue.mState.playIndex++; // data has been copied to the buffer, and the buffer queue state has been updated // we will notify the client if applicable callback = ar->mBufferQueue.mCallback; // save callback data callbackPContext = ar->mBufferQueue.mContext; } } else { // empty queue // no destination to push the data pBuff->size = 0; } interface_unlock_exclusive(&ar->mBufferQueue); // notify client if (NULL != callback) { (*callback)(&ar->mBufferQueue.mItf, callbackPContext); } } break; case android::AudioRecord::EVENT_OVERRUN: audioRecorder_handleOverrun_lockRecord(ar); break; case android::AudioRecord::EVENT_MARKER: audioRecorder_handleMarker_lockRecord(ar); break; case android::AudioRecord::EVENT_NEW_POS: audioRecorder_handleNewPos_lockRecord(ar); break; case android::AudioRecord::EVENT_NEW_IAUDIORECORD: // ignore for now break; default: SL_LOGE("Encountered unknown AudioRecord event %d for CAudioRecord %p", event, ar); break; } ar->mCallbackProtector->exitCb(); }