diff options
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 63 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 13 | ||||
-rw-r--r-- | services/audioflinger/FastMixer.cpp | 13 | ||||
-rw-r--r-- | services/audioflinger/FastMixer.h | 37 |
4 files changed, 97 insertions, 29 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 4e770af..51a8367 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2760,14 +2760,20 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // Determine whether the track is currently in underrun condition, // and whether it had a recent underrun. - uint32_t underruns = mFastMixerDumpState.mTracks[j].mUnderruns; - uint32_t recentUnderruns = (underruns - (track->mObservedUnderruns & ~1)) >> 1; + FastTrackUnderruns underruns = mFastMixerDumpState.mTracks[j].mUnderruns; + uint32_t recentFull = (underruns.mBitFields.mFull - + track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK; + uint32_t recentPartial = (underruns.mBitFields.mPartial - + track->mObservedUnderruns.mBitFields.mPartial) & UNDERRUN_MASK; + uint32_t recentEmpty = (underruns.mBitFields.mEmpty - + track->mObservedUnderruns.mBitFields.mEmpty) & UNDERRUN_MASK; + uint32_t recentUnderruns = recentPartial + recentEmpty; + track->mObservedUnderruns = underruns; // don't count underruns that occur while stopping or pausing // or stopped which can occur when flush() is called while active if (!(track->isStopping() || track->isPausing() || track->isStopped())) { track->mUnderrunCount += recentUnderruns; } - track->mObservedUnderruns = underruns; // This is similar to the state machine for normal tracks, // with a few modifications for fast tracks. @@ -2788,10 +2794,30 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac track->mState = TrackBase::ACTIVE; break; case TrackBase::ACTIVE: - // no minimum frame count for fast tracks; continual underrun is allowed, - // but later could implement automatic pause after several consecutive underruns, - // or auto-mute yet still consider the track active and continue to service it - if (track->sharedBuffer() == 0 || recentUnderruns == 0) { + if (recentFull > 0 || recentPartial > 0) { + // track has provided at least some frames recently: reset retry count + track->mRetryCount = kMaxTrackRetries; + } + if (recentUnderruns == 0) { + // no recent underruns: stay active + break; + } + // there has recently been an underrun of some kind + if (track->sharedBuffer() == 0) { + // were any of the recent underruns "empty" (no frames available)? + if (recentEmpty == 0) { + // no, then ignore the partial underruns as they are allowed indefinitely + break; + } + // there has recently been an "empty" underrun: decrement the retry counter + if (--(track->mRetryCount) > 0) { + break; + } + // indicate to client process that the track was disabled because of underrun; + // it will then automatically call start() when data is available + android_atomic_or(CBLK_DISABLED_ON, &track->mCblk->flags); + // remove from active list, but state remains ACTIVE [confusing but true] + isActive = false; break; } // fall through @@ -2862,7 +2888,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } tracksToRemove->add(track); // Avoids a misleading display in dumpsys - track->mObservedUnderruns &= ~1; + track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL; } continue; } @@ -4107,7 +4133,6 @@ AudioFlinger::PlaybackThread::Track::Track( mPresentationCompleteFrames(0), mFlags(flags), mFastIndex(-1), - mObservedUnderruns(0), mUnderrunCount(0), mCachedVolume(1.0) { @@ -4126,7 +4151,7 @@ AudioFlinger::PlaybackThread::Track::Track( // being created. It would be better to allocate the index dynamically. mFastIndex = i; // Read the initial underruns because this field is never cleared by the fast mixer - mObservedUnderruns = thread->getFastTrackUnderruns(i) & ~1; + mObservedUnderruns = thread->getFastTrackUnderruns(i); thread->mFastTrackAvailMask &= ~(1 << i); } // to avoid leaking a track name, do not allocate one unless there is an mCblk @@ -4231,7 +4256,21 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) stateChar = '?'; break; } - bool nowInUnderrun = mObservedUnderruns & 1; + char nowInUnderrun; + switch (mObservedUnderruns.mBitFields.mMostRecent) { + case UNDERRUN_FULL: + nowInUnderrun = ' '; + break; + case UNDERRUN_PARTIAL: + nowInUnderrun = '<'; + break; + case UNDERRUN_EMPTY: + nowInUnderrun = '*'; + break; + default: + nowInUnderrun = '?'; + break; + } snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %6u %1c %1d %1d %5u %5.2g %5.2g " "0x%08x 0x%08x 0x%08x 0x%08x %#5x %9u%c\n", (mClient == 0) ? getpid_cached : mClient->pid(), @@ -4253,7 +4292,7 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) (int)mAuxBuffer, mCblk->flags, mUnderrunCount, - nowInUnderrun ? '*' : ' '); + nowInUnderrun); } // AudioBufferProvider interface diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index bcf29e9..88056b8 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -793,7 +793,7 @@ private: // index 0 is reserved for normal mixer's submix; // index is allocated statically at track creation time // but the slot is only used if track is active - uint32_t mObservedUnderruns; // Most recently observed value of + FastTrackUnderruns mObservedUnderruns; // Most recently observed value of // mFastMixerDumpState.mTracks[mFastIndex].mUnderruns uint32_t mUnderrunCount; // Counter of total number of underruns, never reset volatile float mCachedVolume; // combined master volume and stream type volume; @@ -1112,7 +1112,8 @@ public: sp<NBAIO_Sink> mNormalSink; public: virtual bool hasFastMixer() const = 0; - virtual uint32_t getFastTrackUnderruns(size_t fastIndex) const { return 0; } + virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const + { FastTrackUnderruns dummy; return dummy; } protected: // accessed by both binder threads and within threadLoop(), lock on mutex needed @@ -1167,10 +1168,10 @@ public: public: virtual bool hasFastMixer() const { return mFastMixer != NULL; } - virtual uint32_t getFastTrackUnderruns(size_t fastIndex) const { - ALOG_ASSERT(0 < fastIndex && - fastIndex < FastMixerState::kMaxFastTracks); - return mFastMixerDumpState.mTracks[fastIndex].mUnderruns; + virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const { + ALOG_ASSERT(0 < fastIndex && + fastIndex < FastMixerState::kMaxFastTracks); + return mFastMixerDumpState.mTracks[fastIndex].mUnderruns; } }; diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 04d0f65..cd55396 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -360,20 +360,25 @@ bool FastMixer::threadLoop() // in the overall fast mix cycle being delayed. Should use a non-blocking FIFO. size_t framesReady = fastTrack->mBufferProvider->framesReady(); FastTrackDump *ftDump = &dumpState->mTracks[i]; - uint32_t underruns = ftDump->mUnderruns; + FastTrackUnderruns underruns = ftDump->mUnderruns; if (framesReady < frameCount) { ATRACE_INT("underrun", i); - ftDump->mUnderruns = (underruns + 2) | 1; if (framesReady == 0) { + underruns.mBitFields.mEmpty++; + underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; mixer->disable(name); } else { // allow mixing partial buffer + underruns.mBitFields.mPartial++; + underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; mixer->enable(name); } - } else if (underruns & 1) { - ftDump->mUnderruns = underruns & ~1; + } else { + underruns.mBitFields.mFull++; + underruns.mBitFields.mMostRecent = UNDERRUN_FULL; mixer->enable(name); } + ftDump->mUnderruns = underruns; } // process() is CPU-bound mixer->process(AudioBufferProvider::kInvalidPTS); diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index e2ed553..e95abf6 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_AUDIO_FAST_MIXER_H #define ANDROID_AUDIO_FAST_MIXER_H +#include <utils/Debug.h> #include <utils/Thread.h> extern "C" { #include "../private/bionic_futex.h" @@ -42,16 +43,38 @@ private: }; // class FastMixer +// Describes the underrun status for a single "pull" attempt +enum FastTrackUnderrunStatus { + UNDERRUN_FULL, // framesReady() is full frame count, no underrun + UNDERRUN_PARTIAL, // framesReady() is non-zero but < full frame count, partial underrun + UNDERRUN_EMPTY, // framesReady() is zero, total underrun +}; + +// Underrun counters are not reset to zero for new tracks or if track generation changes. +// This packed representation is used to keep the information atomic. +union FastTrackUnderruns { + FastTrackUnderruns() { mAtomic = 0; + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(FastTrackUnderruns) == sizeof(uint32_t)); } + FastTrackUnderruns(const FastTrackUnderruns& copyFrom) : mAtomic(copyFrom.mAtomic) { } + FastTrackUnderruns& operator=(const FastTrackUnderruns& rhs) + { if (this != &rhs) mAtomic = rhs.mAtomic; return *this; } + struct { +#define UNDERRUN_BITS 10 +#define UNDERRUN_MASK ((1 << UNDERRUN_BITS) - 1) + uint32_t mFull : UNDERRUN_BITS; // framesReady() is full frame count + uint32_t mPartial : UNDERRUN_BITS; // framesReady() is non-zero but < full frame count + uint32_t mEmpty : UNDERRUN_BITS; // framesReady() is zero + FastTrackUnderrunStatus mMostRecent : 2; // status of most recent framesReady() + } mBitFields; +private: + uint32_t mAtomic; +}; + // Represents the dump state of a fast track struct FastTrackDump { - FastTrackDump() : mUnderruns(0) { } + FastTrackDump() { } /*virtual*/ ~FastTrackDump() { } - uint32_t mUnderruns; // Underrun status, represented as follows: - // bit 0 == 0 means not currently in underrun - // bit 0 == 1 means currently in underrun - // bits 1 to 31 == total number of underruns - // Not reset to zero for new tracks or if track generation changes. - // This representation is used to keep the information atomic. + FastTrackUnderruns mUnderruns; }; // The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys. |