diff options
author | Glenn Kasten <gkasten@google.com> | 2012-05-01 18:14:02 -0700 |
---|---|---|
committer | Glenn Kasten <gkasten@google.com> | 2012-05-14 08:25:39 -0700 |
commit | d08f48c2ad2941d62b313007955c7145075d562c (patch) | |
tree | 3cce1d96d82032c977f1f185b8dca8a829b581f6 /services/audioflinger | |
parent | 94f60e297b9e45fac387cf10b6ce1acc067cb4a1 (diff) | |
download | frameworks_av-d08f48c2ad2941d62b313007955c7145075d562c.zip frameworks_av-d08f48c2ad2941d62b313007955c7145075d562c.tar.gz frameworks_av-d08f48c2ad2941d62b313007955c7145075d562c.tar.bz2 |
Fix stopping process for fast tracks
Previously, the state of a fast track "wiggled" back and forth at the end.
Now it goes through these transitions:
active -> stopping_1 -> stopping_2 -> stopped
This CL is only for fast tracks, and does not change how
normal tracks work.
Change-Id: Icc414f2b48c46dda63cfa6373ca22d033dd21cd4
Diffstat (limited to 'services/audioflinger')
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 107 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 14 |
2 files changed, 87 insertions, 34 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 77b2bc8..4e770af 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -2763,32 +2763,69 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac uint32_t underruns = mFastMixerDumpState.mTracks[j].mUnderruns; uint32_t recentUnderruns = (underruns - (track->mObservedUnderruns & ~1)) >> 1; // don't count underruns that occur while stopping or pausing - if (!(track->isStopped() || track->isPausing())) { + // 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 formula for normal tracks, + // This is similar to the state machine for normal tracks, // with a few modifications for fast tracks. - bool isActive; - if (track->isStopped()) { - // track stays active after stop() until first underrun - isActive = recentUnderruns == 0; - } else if (track->isPaused() || track->isTerminated()) { - isActive = false; - } else if (track->isPausing()) { + bool isActive = true; + switch (track->mState) { + case TrackBase::STOPPING_1: + // track stays active in STOPPING_1 state until first underrun + if (recentUnderruns > 0) { + track->mState = TrackBase::STOPPING_2; + } + break; + case TrackBase::PAUSING: // ramp down is not yet implemented - isActive = true; track->setPaused(); - } else if (track->isResuming()) { + break; + case TrackBase::RESUMING: // ramp up is not yet implemented - isActive = true; track->mState = TrackBase::ACTIVE; - } else { + 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 - isActive = true; + if (track->sharedBuffer() == 0 || recentUnderruns == 0) { + break; + } + // fall through + case TrackBase::STOPPING_2: + case TrackBase::PAUSED: + case TrackBase::TERMINATED: + case TrackBase::STOPPED: // flush() while active + // Check for presentation complete if track is inactive + // We have consumed all the buffers of this track. + // This would be incomplete if we auto-paused on underrun + { + size_t audioHALFrames = + (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; + size_t framesWritten = + mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); + if (!track->presentationComplete(framesWritten, audioHALFrames)) { + // track stays in active list until presentation is complete + break; + } + } + if (track->isStopping_2()) { + track->mState = TrackBase::STOPPED; + } + if (track->isStopped()) { + // Can't reset directly, as fast mixer is still polling this track + // track->reset(); + // So instead mark this track as needing to be reset after push with ack + resetMask |= 1 << i; + } + isActive = false; + break; + case TrackBase::IDLE: + default: + LOG_FATAL("unexpected track state %d", track->mState); } if (isActive) { @@ -2820,22 +2857,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // If any fast tracks were removed, we must wait for acknowledgement // because we're about to decrement the last sp<> on those tracks. block = FastMixerStateQueue::BLOCK_UNTIL_ACKED; + } else { + LOG_FATAL("fast track %d should have been active", j); } - // Remainder of this block is copied from similar code for normal tracks - if (track->isStopped()) { - // Can't reset directly, as fast mixer is still polling this track - // track->reset(); - // So instead mark this track as needing to be reset after push with ack - resetMask |= 1 << i; - } - // This would be incomplete if we auto-paused on underrun - size_t audioHALFrames = - (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; - size_t framesWritten = - mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); - if (track->presentationComplete(framesWritten, audioHALFrames)) { - tracksToRemove->add(track); - } + tracksToRemove->add(track); // Avoids a misleading display in dumpsys track->mObservedUnderruns &= ~1; } @@ -4181,6 +4206,12 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) case TERMINATED: stateChar = 'T'; break; + case STOPPING_1: + stateChar = 's'; + break; + case STOPPING_2: + stateChar = '5'; + break; case STOPPED: stateChar = 'S'; break; @@ -4353,14 +4384,20 @@ void AudioFlinger::PlaybackThread::Track::stop() if (thread != 0) { Mutex::Autolock _l(thread->mLock); track_state state = mState; - if (mState > STOPPED) { - mState = STOPPED; + if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) { // If the track is not active (PAUSED and buffers full), flush buffers PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); if (playbackThread->mActiveTracks.indexOf(this) < 0) { reset(); + mState = STOPPED; + } else if (!isFastTrack()) { + mState = STOPPED; + } else { + // prepareTracks_l() will set state to STOPPING_2 after next underrun, + // and then to STOPPED and reset() when presentation is complete + mState = STOPPING_1; } - ALOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread); + ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName, playbackThread); } if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) { thread->mLock.unlock(); @@ -4404,15 +4441,17 @@ void AudioFlinger::PlaybackThread::Track::flush() sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { Mutex::Autolock _l(thread->mLock); - if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { + if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED && + mState != PAUSING) { return; } // No point remaining in PAUSED state after a flush => go to // STOPPED state mState = STOPPED; - // do not reset the track if it is still in the process of being stopped or paused. // this will be done by prepareTracks_l() when the track is stopped. + // prepareTracks_l() will see mState == STOPPED, then + // remove from active track list, reset(), and trigger presentation complete PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); if (playbackThread->mActiveTracks.indexOf(this) < 0) { reset(); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index f10295f..bcf29e9 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -365,6 +365,9 @@ private: // These are order-sensitive; do not change order without reviewing the impact. // In particular there are assumptions about > STOPPED. STOPPED, + // next 2 states are currently used for fast tracks only + STOPPING_1, // waiting for first underrun + STOPPING_2, // waiting for presentation complete RESUMING, ACTIVE, PAUSING, @@ -417,6 +420,17 @@ private: return mState == STOPPED; } + // for fast tracks only + bool isStopping() const { + return mState == STOPPING_1 || mState == STOPPING_2; + } + bool isStopping_1() const { + return mState == STOPPING_1; + } + bool isStopping_2() const { + return mState == STOPPING_2; + } + bool isTerminated() const { return mState == TERMINATED; } |