Loading services/audioflinger/Threads.cpp +34 −11 Original line number Diff line number Diff line Loading @@ -178,6 +178,11 @@ static const nsecs_t kOffloadStandbyDelayNs = seconds(1); // Direct output thread minimum sleep time in idle or active(underrun) state static const nsecs_t kDirectMinSleepTimeUs = 10000; // Minimum amount of time between checking to see if the timestamp is advancing // for underrun detection. If we check too frequently, we may not detect a // timestamp update and will falsely detect underrun. static const nsecs_t kMinimumTimeBetweenTimestampChecksNs = 150 /* ms */ * 1000; // The universal constant for ubiquitous 20ms value. The value of 20ms seems to provide a good // balance between power consumption and latency, and allows threads to be scheduled reliably // by the CFS scheduler. Loading Loading @@ -2040,7 +2045,8 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge mFastTrackAvailMask(((1 << FastMixerState::sMaxFastTracks) - 1) & ~1), mHwSupportsPause(false), mHwPaused(false), mFlushPending(false), mLeftVolFloat(-1.0), mRightVolFloat(-1.0), mDownStreamPatch{} mDownStreamPatch{}, mIsTimestampAdvancing(kMinimumTimeBetweenTimestampChecksNs) { snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id); mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName); Loading Loading @@ -5902,18 +5908,35 @@ uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid) const return trackCount; } bool AudioFlinger::PlaybackThread::checkRunningTimestamp() bool AudioFlinger::PlaybackThread::IsTimestampAdvancing::check(AudioStreamOut * output) { // Check the timestamp to see if it's advancing once every 150ms. If we check too frequently, we // could falsely detect that the frame position has stalled due to underrun because we haven't // given the Audio HAL enough time to update. const nsecs_t nowNs = systemTime(); if (nowNs - mPreviousNs < mMinimumTimeBetweenChecksNs) { return mLatchedValue; } mPreviousNs = nowNs; mLatchedValue = false; // Determine if the presentation position is still advancing. uint64_t position = 0; struct timespec unused; const status_t ret = mOutput->getPresentationPosition(&position, &unused); const status_t ret = output->getPresentationPosition(&position, &unused); if (ret == NO_ERROR) { if (position != mLastCheckedTimestampPosition) { mLastCheckedTimestampPosition = position; return true; if (position != mPreviousPosition) { mPreviousPosition = position; mLatchedValue = true; } } return false; return mLatchedValue; } void AudioFlinger::PlaybackThread::IsTimestampAdvancing::clear() { mLatchedValue = true; mPreviousPosition = 0; mPreviousNs = 0; } // isTrackAllowed_l() must be called with ThreadBase::mLock held Loading Loading @@ -6348,9 +6371,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. // Only consider last track started for mixer state control bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput); if (--(track->mRetryCount) <= 0) { const bool running = checkRunningTimestamp(); if (running) { // still running, give us more time. if (isTimestampAdvancing) { // HAL is still playing audio, give us more time. track->mRetryCount = kMaxTrackRetriesOffload; } else { ALOGV("BUFFER TIMEOUT: remove track(%d) from active list", trackId); Loading Loading @@ -6931,9 +6954,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr } else { // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput); if (--(track->mRetryCount) <= 0) { const bool running = checkRunningTimestamp(); if (running) { // still running, give us more time. if (isTimestampAdvancing) { // HAL is still playing audio, give us more time. track->mRetryCount = kMaxTrackRetriesOffload; } else { ALOGV("OffloadThread: BUFFER TIMEOUT: remove track(%d) from active list", Loading services/audioflinger/Threads.h +30 −5 Original line number Diff line number Diff line Loading @@ -1403,13 +1403,38 @@ protected: std::atomic_bool mCheckOutputStageEffects{}; // A differential check on the timestamps to see if there is a change in the // timestamp frame position between the last call to checkRunningTimestamp. uint64_t mLastCheckedTimestampPosition = ~0LL; bool checkRunningTimestamp(); // Provides periodic checking for timestamp advancement for underrun detection. class IsTimestampAdvancing { public: // The timestamp will not be checked any faster than the specified time. IsTimestampAdvancing(nsecs_t minimumTimeBetweenChecksNs) : mMinimumTimeBetweenChecksNs(minimumTimeBetweenChecksNs) { clear(); } // Check if the presentation position has advanced in the last periodic time. bool check(AudioStreamOut * output); // Clear the internal state when the playback state changes for the output // stream. void clear(); private: // The minimum time between timestamp checks. const nsecs_t mMinimumTimeBetweenChecksNs; // Add differential check on the timestamps to see if there is a change in the // timestamp frame position between the last call to check. uint64_t mPreviousPosition; // The time at which the last check occurred, to ensure we don't check too // frequently, giving the Audio HAL enough time to update its timestamps. nsecs_t mPreviousNs; // The valued is latched so we don't check timestamps too frequently. bool mLatchedValue; }; IsTimestampAdvancing mIsTimestampAdvancing; virtual void flushHw_l() { mLastCheckedTimestampPosition = ~0LL; } virtual void flushHw_l() { mIsTimestampAdvancing.clear(); } }; class MixerThread : public PlaybackThread { Loading Loading
services/audioflinger/Threads.cpp +34 −11 Original line number Diff line number Diff line Loading @@ -178,6 +178,11 @@ static const nsecs_t kOffloadStandbyDelayNs = seconds(1); // Direct output thread minimum sleep time in idle or active(underrun) state static const nsecs_t kDirectMinSleepTimeUs = 10000; // Minimum amount of time between checking to see if the timestamp is advancing // for underrun detection. If we check too frequently, we may not detect a // timestamp update and will falsely detect underrun. static const nsecs_t kMinimumTimeBetweenTimestampChecksNs = 150 /* ms */ * 1000; // The universal constant for ubiquitous 20ms value. The value of 20ms seems to provide a good // balance between power consumption and latency, and allows threads to be scheduled reliably // by the CFS scheduler. Loading Loading @@ -2040,7 +2045,8 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge mFastTrackAvailMask(((1 << FastMixerState::sMaxFastTracks) - 1) & ~1), mHwSupportsPause(false), mHwPaused(false), mFlushPending(false), mLeftVolFloat(-1.0), mRightVolFloat(-1.0), mDownStreamPatch{} mDownStreamPatch{}, mIsTimestampAdvancing(kMinimumTimeBetweenTimestampChecksNs) { snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id); mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName); Loading Loading @@ -5902,18 +5908,35 @@ uint32_t AudioFlinger::PlaybackThread::trackCountForUid_l(uid_t uid) const return trackCount; } bool AudioFlinger::PlaybackThread::checkRunningTimestamp() bool AudioFlinger::PlaybackThread::IsTimestampAdvancing::check(AudioStreamOut * output) { // Check the timestamp to see if it's advancing once every 150ms. If we check too frequently, we // could falsely detect that the frame position has stalled due to underrun because we haven't // given the Audio HAL enough time to update. const nsecs_t nowNs = systemTime(); if (nowNs - mPreviousNs < mMinimumTimeBetweenChecksNs) { return mLatchedValue; } mPreviousNs = nowNs; mLatchedValue = false; // Determine if the presentation position is still advancing. uint64_t position = 0; struct timespec unused; const status_t ret = mOutput->getPresentationPosition(&position, &unused); const status_t ret = output->getPresentationPosition(&position, &unused); if (ret == NO_ERROR) { if (position != mLastCheckedTimestampPosition) { mLastCheckedTimestampPosition = position; return true; if (position != mPreviousPosition) { mPreviousPosition = position; mLatchedValue = true; } } return false; return mLatchedValue; } void AudioFlinger::PlaybackThread::IsTimestampAdvancing::clear() { mLatchedValue = true; mPreviousPosition = 0; mPreviousNs = 0; } // isTrackAllowed_l() must be called with ThreadBase::mLock held Loading Loading @@ -6348,9 +6371,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. // Only consider last track started for mixer state control bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput); if (--(track->mRetryCount) <= 0) { const bool running = checkRunningTimestamp(); if (running) { // still running, give us more time. if (isTimestampAdvancing) { // HAL is still playing audio, give us more time. track->mRetryCount = kMaxTrackRetriesOffload; } else { ALOGV("BUFFER TIMEOUT: remove track(%d) from active list", trackId); Loading Loading @@ -6931,9 +6954,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr } else { // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput); if (--(track->mRetryCount) <= 0) { const bool running = checkRunningTimestamp(); if (running) { // still running, give us more time. if (isTimestampAdvancing) { // HAL is still playing audio, give us more time. track->mRetryCount = kMaxTrackRetriesOffload; } else { ALOGV("OffloadThread: BUFFER TIMEOUT: remove track(%d) from active list", Loading
services/audioflinger/Threads.h +30 −5 Original line number Diff line number Diff line Loading @@ -1403,13 +1403,38 @@ protected: std::atomic_bool mCheckOutputStageEffects{}; // A differential check on the timestamps to see if there is a change in the // timestamp frame position between the last call to checkRunningTimestamp. uint64_t mLastCheckedTimestampPosition = ~0LL; bool checkRunningTimestamp(); // Provides periodic checking for timestamp advancement for underrun detection. class IsTimestampAdvancing { public: // The timestamp will not be checked any faster than the specified time. IsTimestampAdvancing(nsecs_t minimumTimeBetweenChecksNs) : mMinimumTimeBetweenChecksNs(minimumTimeBetweenChecksNs) { clear(); } // Check if the presentation position has advanced in the last periodic time. bool check(AudioStreamOut * output); // Clear the internal state when the playback state changes for the output // stream. void clear(); private: // The minimum time between timestamp checks. const nsecs_t mMinimumTimeBetweenChecksNs; // Add differential check on the timestamps to see if there is a change in the // timestamp frame position between the last call to check. uint64_t mPreviousPosition; // The time at which the last check occurred, to ensure we don't check too // frequently, giving the Audio HAL enough time to update its timestamps. nsecs_t mPreviousNs; // The valued is latched so we don't check timestamps too frequently. bool mLatchedValue; }; IsTimestampAdvancing mIsTimestampAdvancing; virtual void flushHw_l() { mLastCheckedTimestampPosition = ~0LL; } virtual void flushHw_l() { mIsTimestampAdvancing.clear(); } }; class MixerThread : public PlaybackThread { Loading