void IThreadSync_deinit(void *self) { IThreadSync *this = (IThreadSync *) self; if (this->mInCriticalSection) { SL_LOGW("ThreadSync::EnterCriticalSection was active at Engine::Destroy"); } }
void object_lock_exclusive_(IObject *thiz, const char *file, int line) { int ok; ok = pthread_mutex_trylock(&thiz->mMutex); if (0 != ok) { // not android_atomic_acquire_load because we don't care about relative load/load ordering int32_t oldGeneration = thiz->mGeneration; // wait up to a total of 250 ms static const long nanoBackoffs[] = { 10 * 1000000, 20 * 1000000, 30 * 1000000, 40 * 1000000, 50 * 1000000, 100 * 1000000}; unsigned i = 0; timespec ts; memset(&ts, 0, sizeof(timespec)); for (;;) { init_time_spec(&ts, nanoBackoffs[i]); ok = pthread_mutex_timedlock(&thiz->mMutex, &ts); if (0 == ok) { break; } if (EBUSY == ok) { // this is the expected return value for timeout, and will be handled below } else if (EDEADLK == ok) { // we don't use the kind of mutex that can return this error, but just in case SL_LOGE("%s:%d: recursive lock detected", file, line); } else { // some other return value SL_LOGE("%s:%d: pthread_mutex_lock_timeout_np returned %d", file, line, ok); } // is anyone else making forward progress? int32_t newGeneration = thiz->mGeneration; if (newGeneration != oldGeneration) { // if we ever see forward progress then lock without timeout (more efficient) goto forward_progress; } // no, then continue trying to lock but with increasing timeouts if (++i >= (sizeof(nanoBackoffs) / sizeof(nanoBackoffs[0]))) { // the extra block avoids a C++ compiler error about goto past initialization { pthread_t me = pthread_self(); pthread_t owner = thiz->mOwner; // unlikely, but this could result in a memory fault if owner is corrupt pid_t ownerTid = LIKELY_VALID(owner) ? __pthread_gettid(owner) : -1; SL_LOGW("%s:%d: pthread %p (tid %d) sees object %p was locked by pthread %p" " (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(), thiz, *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine); } forward_progress: // attempt one more time without timeout; maybe this time we will be successful ok = pthread_mutex_lock(&thiz->mMutex); assert(0 == ok); break; } } } // here if mutex was successfully locked pthread_t zero; memset(&zero, 0, sizeof(pthread_t)); if (0 != memcmp(&zero, &thiz->mOwner, sizeof(pthread_t))) { pthread_t me = pthread_self(); pthread_t owner = thiz->mOwner; pid_t ownerTid = LIKELY_VALID(owner) ? __pthread_gettid(owner) : -1; if (pthread_equal(pthread_self(), owner)) { SL_LOGE("%s:%d: pthread %p (tid %d) sees object %p was recursively locked by pthread" " %p (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(), thiz, *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine); } else { SL_LOGE("%s:%d: pthread %p (tid %d) sees object %p was left unlocked in unexpected" " state by pthread %p (tid %d) at %s:%d\n", file, line, *(void **)&me, gettid(), thiz, *(void **)&owner, ownerTid, thiz->mFile, thiz->mLine); } assert(false); } thiz->mOwner = pthread_self(); thiz->mFile = file; thiz->mLine = line; // not android_atomic_inc because we are already holding a mutex ++thiz->mGeneration; }
//-------------------------------------------------- // 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 }
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 SLresult checkDataFormat(const char *name, void *pFormat, DataFormat *pDataFormat, SLuint32 allowedDataFormatMask, SLboolean isOutputFormat) { assert(NULL != name && NULL != pDataFormat); SLresult result = SL_RESULT_SUCCESS; const SLuint32 *df_representation = NULL; // pointer to representation field, if it exists SLuint32 formatType; if (NULL == pFormat) { pDataFormat->mFormatType = formatType = SL_DATAFORMAT_NULL; } else { formatType = *(SLuint32 *)pFormat; switch (formatType) { case SL_ANDROID_DATAFORMAT_PCM_EX: pDataFormat->mPCMEx.representation = ((SLAndroidDataFormat_PCM_EX *)pFormat)->representation; switch (pDataFormat->mPCMEx.representation) { case SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT: case SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT: case SL_ANDROID_PCM_REPRESENTATION_FLOAT: df_representation = &pDataFormat->mPCMEx.representation; break; default: SL_LOGE("%s: unsupported representation: %d", name, pDataFormat->mPCMEx.representation); result = SL_RESULT_PARAMETER_INVALID; break; } // SL_ANDROID_DATAFORMAT_PCM_EX - fall through to next test. case SL_DATAFORMAT_PCM: pDataFormat->mPCM = *(SLDataFormat_PCM *)pFormat; do { if (pDataFormat->mPCM.numChannels == 0) { result = SL_RESULT_PARAMETER_INVALID; } else if (pDataFormat->mPCM.numChannels > SL_ANDROID_SPEAKER_COUNT_MAX) { result = SL_RESULT_CONTENT_UNSUPPORTED; } if (SL_RESULT_SUCCESS != result) { SL_LOGE("%s: numChannels=%u", name, (unsigned) pDataFormat->mPCM.numChannels); break; } // check the sampling rate if (pDataFormat->mPCM.samplesPerSec == 0) { result = SL_RESULT_PARAMETER_INVALID; } else if (pDataFormat->mPCM.samplesPerSec < SL_SAMPLINGRATE_8 || pDataFormat->mPCM.samplesPerSec > SL_SAMPLINGRATE_192) { result = SL_RESULT_CONTENT_UNSUPPORTED; } if (SL_RESULT_SUCCESS != result) { SL_LOGE("%s: samplesPerSec=%u", name, pDataFormat->mPCM.samplesPerSec); break; } // check the container bit depth and representation switch (pDataFormat->mPCM.containerSize) { case 8: if (df_representation != NULL && *df_representation != SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT) { result = SL_RESULT_PARAMETER_INVALID; } break; case 16: case 24: if (df_representation != NULL && *df_representation != SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT) { result = SL_RESULT_PARAMETER_INVALID; } break; case 32: if (df_representation != NULL && *df_representation != SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT && *df_representation != SL_ANDROID_PCM_REPRESENTATION_FLOAT) { result = SL_RESULT_PARAMETER_INVALID; } break; default: result = SL_RESULT_PARAMETER_INVALID; break; } if (SL_RESULT_SUCCESS != result) { SL_LOGE("%s: containerSize=%u", name, pDataFormat->mPCM.containerSize); break; } // sample size cannot be zero, and container size cannot be less than sample size if (pDataFormat->mPCM.bitsPerSample == 0 || pDataFormat->mPCM.containerSize < pDataFormat->mPCM.bitsPerSample) { result = SL_RESULT_PARAMETER_INVALID; } if (SL_RESULT_SUCCESS != result) { SL_LOGE("%s: containerSize=%u, bitsPerSample=%u", name, (unsigned) pDataFormat->mPCM.containerSize, (unsigned) pDataFormat->mPCM.bitsPerSample); break; } // check the channel mask SL_LOGV("%s: Requested channel mask of 0x%x for %d channel audio", name, pDataFormat->mPCM.channelMask, pDataFormat->mPCM.numChannels); if (pDataFormat->mPCM.channelMask == 0) { // We can derive the channel mask from the channel count, // but issue a warning--the automatic mask generation // makes a lot of assumptions that may or may not be what // the app was expecting. SLuint32 mask = isOutputFormat ? sles_channel_out_mask_from_count(pDataFormat->mPCM.numChannels) : sles_channel_in_mask_from_count(pDataFormat->mPCM.numChannels); if (mask == SL_ANDROID_UNKNOWN_CHANNELMASK) { SL_LOGE("No channel mask specified and no default mapping for" "requested speaker count of %u", pDataFormat->mPCM.numChannels); result = SL_RESULT_PARAMETER_INVALID; } else { pDataFormat->mPCM.channelMask = mask; SL_LOGW("No channel mask specified; Using mask %#x based on requested" "speaker count of %u", pDataFormat->mPCM.channelMask, pDataFormat->mPCM.numChannels); } } SLuint32 mask = pDataFormat->mPCM.channelMask; SLuint32 count = sles_channel_count_from_mask(mask); if (count != pDataFormat->mPCM.numChannels) { SL_LOGE("%s: requested %d channels but mask (0x%x) has %d channel bits set", name, pDataFormat->mPCM.numChannels, mask, count); result = SL_RESULT_PARAMETER_INVALID; break; } SL_LOGV("%s: final channel mask is 0x%x", name, pDataFormat->mPCM.channelMask); // check the endianness / byte order switch (pDataFormat->mPCM.endianness) { case SL_BYTEORDER_LITTLEENDIAN: case SL_BYTEORDER_BIGENDIAN: break; // native is proposed but not yet in spec default: result = SL_RESULT_PARAMETER_INVALID; break; } if (SL_RESULT_SUCCESS != result) { SL_LOGE("%s: endianness=%u", name, (unsigned) pDataFormat->mPCM.endianness); break; } // here if all checks passed successfully } while(0); break; case SL_DATAFORMAT_MIME: pDataFormat->mMIME = *(SLDataFormat_MIME *)pFormat; if (NULL != pDataFormat->mMIME.mimeType) { // NTH check address for validity size_t len = strlen((const char *) pDataFormat->mMIME.mimeType); SLchar *myMIME = (SLchar *) malloc(len + 1); if (NULL == myMIME) { result = SL_RESULT_MEMORY_FAILURE; } else { memcpy(myMIME, pDataFormat->mMIME.mimeType, len + 1); // make sure MIME string was not modified asynchronously if ('\0' != myMIME[len]) { free(myMIME); myMIME = NULL; result = SL_RESULT_PRECONDITIONS_VIOLATED; } } pDataFormat->mMIME.mimeType = myMIME; } break; case XA_DATAFORMAT_RAWIMAGE: pDataFormat->mRawImage = *(XADataFormat_RawImage *)pFormat; switch (pDataFormat->mRawImage.colorFormat) { case XA_COLORFORMAT_MONOCHROME: case XA_COLORFORMAT_8BITRGB332: case XA_COLORFORMAT_12BITRGB444: case XA_COLORFORMAT_16BITARGB4444: case XA_COLORFORMAT_16BITARGB1555: case XA_COLORFORMAT_16BITRGB565: case XA_COLORFORMAT_16BITBGR565: case XA_COLORFORMAT_18BITRGB666: case XA_COLORFORMAT_18BITARGB1665: case XA_COLORFORMAT_19BITARGB1666: case XA_COLORFORMAT_24BITRGB888: case XA_COLORFORMAT_24BITBGR888: case XA_COLORFORMAT_24BITARGB1887: case XA_COLORFORMAT_25BITARGB1888: case XA_COLORFORMAT_32BITBGRA8888: case XA_COLORFORMAT_32BITARGB8888: case XA_COLORFORMAT_YUV411PLANAR: case XA_COLORFORMAT_YUV420PLANAR: case XA_COLORFORMAT_YUV420SEMIPLANAR: case XA_COLORFORMAT_YUV422PLANAR: case XA_COLORFORMAT_YUV422SEMIPLANAR: case XA_COLORFORMAT_YCBYCR: case XA_COLORFORMAT_YCRYCB: case XA_COLORFORMAT_CBYCRY: case XA_COLORFORMAT_CRYCBY: case XA_COLORFORMAT_YUV444INTERLEAVED: case XA_COLORFORMAT_RAWBAYER8BIT: case XA_COLORFORMAT_RAWBAYER10BIT: case XA_COLORFORMAT_RAWBAYER8BITCOMPRESSED: case XA_COLORFORMAT_L2: case XA_COLORFORMAT_L4: case XA_COLORFORMAT_L8: case XA_COLORFORMAT_L16: case XA_COLORFORMAT_L24: case XA_COLORFORMAT_L32: case XA_COLORFORMAT_18BITBGR666: case XA_COLORFORMAT_24BITARGB6666: case XA_COLORFORMAT_24BITABGR6666: break; case XA_COLORFORMAT_UNUSED: default: result = XA_RESULT_PARAMETER_INVALID; SL_LOGE("%s: unsupported color format %d", name, pDataFormat->mRawImage.colorFormat); break; } // no checks for height, width, or stride break; default: result = SL_RESULT_PARAMETER_INVALID; SL_LOGE("%s: formatType=%u", name, (unsigned) formatType); break; } // make sure format type was not modified asynchronously if ((SL_RESULT_SUCCESS == result) && (formatType != pDataFormat->mFormatType)) { SL_LOGE("%s: formatType changed from %u to %u", name, formatType, pDataFormat->mFormatType); result = SL_RESULT_PRECONDITIONS_VIOLATED; } } // Verify that the data format type is allowed in this context if (SL_RESULT_SUCCESS == result) { SLuint32 actualMask; switch (formatType) { case SL_DATAFORMAT_NULL: case SL_DATAFORMAT_MIME: case SL_DATAFORMAT_PCM: case SL_ANDROID_DATAFORMAT_PCM_EX: case XA_DATAFORMAT_RAWIMAGE: actualMask = 1L << formatType; break; default: assert(false); actualMask = 0L; break; } if (!(allowedDataFormatMask & actualMask)) { SL_LOGE("%s: data format %d not allowed", name, formatType); result = SL_RESULT_CONTENT_UNSUPPORTED; } } return result; }
SLresult checkInterfaces(const ClassTable *clazz, SLuint32 numInterfaces, const SLInterfaceID *pInterfaceIds, const SLboolean *pInterfaceRequired, unsigned *pExposedMask, unsigned *pRequiredMask) { assert(NULL != clazz && NULL != pExposedMask); // Initially no interfaces are exposed unsigned exposedMask = 0; unsigned requiredMask = 0; const struct iid_vtable *interfaces = clazz->mInterfaces; SLuint32 interfaceCount = clazz->mInterfaceCount; SLuint32 i; // Expose all implicit interfaces for (i = 0; i < interfaceCount; ++i) { switch (interfaces[i].mInterface) { case INTERFACE_IMPLICIT: case INTERFACE_IMPLICIT_PREREALIZE: // there must be an initialization hook present if (NULL != MPH_init_table[interfaces[i].mMPH].mInit) { exposedMask |= 1 << i; } break; case INTERFACE_EXPLICIT: case INTERFACE_DYNAMIC: case INTERFACE_UNAVAILABLE: case INTERFACE_EXPLICIT_PREREALIZE: break; default: assert(false); break; } } if (0 < numInterfaces) { if (NULL == pInterfaceIds || NULL == pInterfaceRequired) { return SL_RESULT_PARAMETER_INVALID; } bool anyRequiredButUnsupported = false; // Loop for each requested interface for (i = 0; i < numInterfaces; ++i) { SLInterfaceID iid = pInterfaceIds[i]; if (NULL == iid) { return SL_RESULT_PARAMETER_INVALID; } SLboolean isRequired = pInterfaceRequired[i]; int MPH, index; if ((0 > (MPH = IID_to_MPH(iid))) || // there must be an initialization hook present (NULL == MPH_init_table[MPH].mInit) || (0 > (index = clazz->mMPH_to_index[MPH])) || (INTERFACE_UNAVAILABLE == interfaces[index].mInterface)) { // Here if interface was not found, or is not available for this object type if (isRequired) { // Application said it required the interface, so give up SL_LOGE("class %s interface %u required but unavailable MPH=%d", clazz->mName, i, MPH); anyRequiredButUnsupported = true; } // Application said it didn't really need the interface, so ignore with warning SL_LOGW("class %s interface %u requested but unavailable MPH=%d", clazz->mName, i, MPH); continue; } if (isRequired) { requiredMask |= (1 << index); } // The requested interface was both found and available, so expose it exposedMask |= (1 << index); // Note that we ignore duplicate requests, including equal and aliased IDs } if (anyRequiredButUnsupported) { return SL_RESULT_FEATURE_UNSUPPORTED; } } *pExposedMask = exposedMask; if (NULL != pRequiredMask) { *pRequiredMask = requiredMask; } return SL_RESULT_SUCCESS; }
// Called from android_audioRecorder_realize for a PCM buffer queue recorder before creating the // AudioRecord to determine which performance modes are allowed based on effect interfaces present static void checkAndSetPerformanceModePre(CAudioRecorder* ar) { SLuint32 allowedModes = ANDROID_PERFORMANCE_MODE_ALL; assert(ar->mAndroidObjType == AUDIORECORDER_FROM_MIC_TO_PCM_BUFFERQUEUE); // no need to check the buffer queue size, application side // double-buffering (and more) is not a requirement for using fast tracks // Check a blacklist of interfaces that are incompatible with fast tracks. // The alternative, to check a whitelist of compatible interfaces, is // more maintainable but is too slow. As a compromise, in a debug build // we use both methods and warn if they produce different results. // In release builds, we only use the blacklist method. // If a blacklisted interface is added after realization using // DynamicInterfaceManagement::AddInterface, // then this won't be detected but the interface will be ineffective. static const unsigned blacklist[] = { MPH_ANDROIDACOUSTICECHOCANCELLATION, MPH_ANDROIDAUTOMATICGAINCONTROL, MPH_ANDROIDNOISESUPPRESSION, MPH_ANDROIDEFFECT, // FIXME The problem with a blacklist is remembering to add new interfaces here }; for (unsigned i = 0; i < sizeof(blacklist)/sizeof(blacklist[0]); ++i) { if (IsInterfaceInitialized(&ar->mObject, blacklist[i])) { uint32_t flags = 0; allowedModes &= ~ANDROID_PERFORMANCE_MODE_LATENCY; // if generic effect interface is used we don't know which effect will be used and // disable all low latency performance modes if (blacklist[i] != MPH_ANDROIDEFFECT) { switch (blacklist[i]) { case MPH_ANDROIDACOUSTICECHOCANCELLATION: SL_LOGV("checkAndSetPerformanceModePre found AEC name %s", ar->mAcousticEchoCancellation.mAECDescriptor.name); flags = ar->mAcousticEchoCancellation.mAECDescriptor.flags; break; case MPH_ANDROIDAUTOMATICGAINCONTROL: SL_LOGV("checkAndSetPerformanceModePre found AGC name %s", ar->mAutomaticGainControl.mAGCDescriptor.name); flags = ar->mAutomaticGainControl.mAGCDescriptor.flags; break; case MPH_ANDROIDNOISESUPPRESSION: SL_LOGV("checkAndSetPerformanceModePre found NS name %s", ar->mNoiseSuppression.mNSDescriptor.name); flags = ar->mNoiseSuppression.mNSDescriptor.flags; break; default: break; } } if ((flags & EFFECT_FLAG_HW_ACC_TUNNEL) == 0) { allowedModes &= ~ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS; break; } } } #if LOG_NDEBUG == 0 bool blacklistResult = ( (allowedModes & (ANDROID_PERFORMANCE_MODE_LATENCY|ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS)) != 0); bool whitelistResult = true; static const unsigned whitelist[] = { MPH_BUFFERQUEUE, MPH_DYNAMICINTERFACEMANAGEMENT, MPH_OBJECT, MPH_RECORD, MPH_ANDROIDCONFIGURATION, MPH_ANDROIDSIMPLEBUFFERQUEUE, }; for (unsigned mph = MPH_MIN; mph < MPH_MAX; ++mph) { for (unsigned i = 0; i < sizeof(whitelist)/sizeof(whitelist[0]); ++i) { if (mph == whitelist[i]) { goto compatible; } } if (IsInterfaceInitialized(&ar->mObject, mph)) { whitelistResult = false; break; } compatible: ; } if (whitelistResult != blacklistResult) { SL_LOGW("whitelistResult != blacklistResult"); } #endif if (ar->mPerformanceMode == ANDROID_PERFORMANCE_MODE_LATENCY) { if ((allowedModes & ANDROID_PERFORMANCE_MODE_LATENCY) == 0) { ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS; } } if (ar->mPerformanceMode == ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS) { if ((allowedModes & ANDROID_PERFORMANCE_MODE_LATENCY_EFFECTS) == 0) { ar->mPerformanceMode = ANDROID_PERFORMANCE_MODE_NONE; } } }