Merge "Switch audio tracks at runtime" into jb-dev
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 7ac0f23..3d7ea92 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -499,13 +499,6 @@
return err;
}
- err = native_window_set_scaling_mode(mNativeWindow.get(),
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-
- if (err != OK) {
- return err;
- }
-
err = native_window_set_buffers_geometry(
mNativeWindow.get(),
def.format.video.nFrameWidth,
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 77b2bc8..51a8367 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2760,35 +2760,98 @@
// 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
- 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 {
- // 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;
+ break;
+ case TrackBase::ACTIVE:
+ 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
+ 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,24 +2883,12 @@
// 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;
+ track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;
}
continue;
}
@@ -4082,7 +4133,6 @@
mPresentationCompleteFrames(0),
mFlags(flags),
mFastIndex(-1),
- mObservedUnderruns(0),
mUnderrunCount(0),
mCachedVolume(1.0)
{
@@ -4101,7 +4151,7 @@
// 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
@@ -4181,6 +4231,12 @@
case TERMINATED:
stateChar = 'T';
break;
+ case STOPPING_1:
+ stateChar = 's';
+ break;
+ case STOPPING_2:
+ stateChar = '5';
+ break;
case STOPPED:
stateChar = 'S';
break;
@@ -4200,7 +4256,21 @@
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(),
@@ -4222,7 +4292,7 @@
(int)mAuxBuffer,
mCblk->flags,
mUnderrunCount,
- nowInUnderrun ? '*' : ' ');
+ nowInUnderrun);
}
// AudioBufferProvider interface
@@ -4353,14 +4423,20 @@
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 +4480,17 @@
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..88056b8 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -365,6 +365,9 @@
// 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 @@
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;
}
@@ -779,7 +793,7 @@
// 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;
@@ -1098,7 +1112,8 @@
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
@@ -1153,10 +1168,10 @@
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 @@
// 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 @@
}; // 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.