//----------------------------------------------------------------------------- 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(); }
// 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; } } }