audioflinger: various offload playback fixes
Revert underrun sleep time optimization added for
offload by commit 51716185 which can cause music to pause for a few
seconds in some corner cases.
Allow underruns in STOPPING_1 state to avoid dropping last buffer
received by the AudioTrack callback after stop() is called by the
client.
Allow interruption of thread loop sleep if a command is pending to
speed up track start sequence.
Do not wait for a full AudioTrack buffer before writing to audio HAL
when resuming playback or transitioning to next track.
Also moved log level for underruns in AudioSink from I to D to reduce
spam on user builds.
Bug: 28545177
Bug: 27682362
Bug: 28347796
Change-Id: I05b651b7878a2d2eedcac43cd669e32add171d40
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 35f439b..cd91e72 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -2150,7 +2150,7 @@
//
// The underrun event is sent once per track underrun; the condition is reset
// when more data is sent to the AudioTrack.
- ALOGI("callbackwrapper: EVENT_UNDERRUN (discarded)");
+ ALOGD("callbackwrapper: EVENT_UNDERRUN (discarded)");
break;
default:
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1d575b3..2b0d4c8 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1843,8 +1843,7 @@
PlaybackThread *thread;
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
- thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady,
- config->offload_info.bit_rate);
+ thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);
ALOGV("openOutput_l() created offload output: ID %d thread %p", *output, thread);
} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
|| !isValidPcmSinkFormat(config->format)
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index bee0447..d296ee3 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -110,12 +110,7 @@
// direct outputs can be a scarce resource in audio hardware and should
// be released as quickly as possible.
static const int8_t kMaxTrackRetriesDirect = 2;
-// retry count before removing active track in case of underrun on offloaded thread:
-// we need to make sure that AudioTrack client has enough time to send large buffers
-//FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
-// for offloaded tracks
-static const int8_t kMaxTrackRetriesOffload = 10;
-static const int8_t kMaxTrackStartupRetriesOffload = 100;
+
// don't warn about blocked writes or record buffer overflows more often than this
@@ -148,10 +143,6 @@
// Direct output thread minimum sleep time in idle or active(underrun) state
static const nsecs_t kDirectMinSleepTimeUs = 10000;
-// Offloaded output bit rate in bits per second when unknown.
-// Used for sleep time calculation, so use a high default bitrate to be conservative on sleep time.
-static const uint32_t kOffloadDefaultBitRateBps = 1500000;
-
// Whether to use fast mixer
static const enum {
@@ -1567,8 +1558,7 @@
audio_io_handle_t id,
audio_devices_t device,
type_t type,
- bool systemReady,
- uint32_t bitRate)
+ bool systemReady)
: ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type, systemReady),
mNormalFrameCount(0), mSinkBuffer(NULL),
mMixerBufferEnabled(AudioFlinger::kEnableExtendedPrecision),
@@ -1631,13 +1621,6 @@
mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
}
-
- if (audio_has_proportional_frames(mFormat)) {
- mBufferDurationUs = (uint32_t)((mNormalFrameCount * 1000000LL) / mSampleRate);
- } else {
- bitRate = bitRate != 0 ? bitRate : kOffloadDefaultBitRateBps;
- mBufferDurationUs = (uint32_t)((mBufferSize * 8 * 1000000LL) / bitRate);
- }
}
AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -2049,12 +2032,18 @@
// set retry count for buffer fill
if (track->isOffloaded()) {
- track->mRetryCount = kMaxTrackStartupRetriesOffload;
+ if (track->isStopping_1()) {
+ track->mRetryCount = kMaxTrackStopRetriesOffload;
+ } else {
+ track->mRetryCount = kMaxTrackStartupRetriesOffload;
+ }
+ track->mFillingUpStatus = mStandby ? Track::FS_FILLING : Track::FS_FILLED;
} else {
track->mRetryCount = kMaxTrackStartupRetries;
+ track->mFillingUpStatus =
+ track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
}
- track->mFillingUpStatus = track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
@@ -3181,32 +3170,9 @@
} else {
ATRACE_BEGIN("sleep");
- if ((mType == OFFLOAD) && !audio_has_proportional_frames(mFormat)) {
- Mutex::Autolock _l(mLock);
- if (!mSignalPending && !exitPending()) {
- // If more than one buffer has been written to the audio HAL since exiting
- // standby or last flush, do not sleep more than one buffer duration
- // since last write and not less than kDirectMinSleepTimeUs.
- // Wake up if a command is received
- uint32_t timeoutUs = mSleepTimeUs;
- if (mBytesWritten >= (int64_t) mBufferSize) {
- nsecs_t now = systemTime();
- uint32_t deltaUs = (uint32_t)((now - mLastWriteTime) / 1000);
- if (timeoutUs + deltaUs > mBufferDurationUs) {
- if (mBufferDurationUs > deltaUs) {
- timeoutUs = mBufferDurationUs - deltaUs;
- if (timeoutUs < kDirectMinSleepTimeUs) {
- timeoutUs = kDirectMinSleepTimeUs;
- }
- } else {
- timeoutUs = kDirectMinSleepTimeUs;
- }
- }
- }
- mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)timeoutUs));
- }
- } else {
- usleep(mSleepTimeUs);
+ Mutex::Autolock _l(mLock);
+ if (!mSignalPending && mConfigEvents.isEmpty() && !exitPending()) {
+ mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)mSleepTimeUs));
}
ATRACE_END();
}
@@ -4592,17 +4558,16 @@
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady,
- uint32_t bitRate)
- : PlaybackThread(audioFlinger, output, id, device, DIRECT, systemReady, bitRate)
+ AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady)
+ : PlaybackThread(audioFlinger, output, id, device, DIRECT, systemReady)
// mLeftVolFloat, mRightVolFloat
{
}
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output, audio_io_handle_t id, uint32_t device,
- ThreadBase::type_t type, bool systemReady, uint32_t bitRate)
- : PlaybackThread(audioFlinger, output, id, device, type, systemReady, bitRate)
+ ThreadBase::type_t type, bool systemReady)
+ : PlaybackThread(audioFlinger, output, id, device, type, systemReady)
// mLeftVolFloat, mRightVolFloat
{
}
@@ -4908,14 +4873,7 @@
}
if (mSleepTimeUs == 0) {
if (mMixerStatus == MIXER_TRACKS_ENABLED) {
- // For compressed offload, use faster sleep time when underruning until more than an
- // entire buffer was written to the audio HAL
- if (!audio_has_proportional_frames(mFormat) &&
- (mType == OFFLOAD) && (mBytesWritten < (int64_t) mBufferSize)) {
- mSleepTimeUs = kDirectMinSleepTimeUs;
- } else {
- mSleepTimeUs = mActiveSleepTimeUs;
- }
+ mSleepTimeUs = mActiveSleepTimeUs;
} else {
mSleepTimeUs = mIdleSleepTimeUs;
}
@@ -5187,9 +5145,8 @@
// ----------------------------------------------------------------------------
AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output, audio_io_handle_t id, uint32_t device, bool systemReady,
- uint32_t bitRate)
- : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD, systemReady, bitRate),
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device, bool systemReady)
+ : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD, systemReady),
mPausedWriteLength(0), mPausedBytesRemaining(0), mKeepWakeLock(true)
{
//FIXME: mStandby should be set to true by ThreadBase constructor
@@ -5273,7 +5230,11 @@
}
tracksToRemove->add(track);
} else if (track->isFlushPending()) {
- track->mRetryCount = kMaxTrackRetriesOffload;
+ if (track->isStopping_1()) {
+ track->mRetryCount = kMaxTrackStopRetriesOffload;
+ } else {
+ track->mRetryCount = kMaxTrackRetriesOffload;
+ }
track->flushAck();
if (last) {
mFlushPending = true;
@@ -5334,38 +5295,47 @@
}
mPreviousTrack = track;
// reset retry count
- track->mRetryCount = kMaxTrackRetriesOffload;
+ if (track->isStopping_1()) {
+ track->mRetryCount = kMaxTrackStopRetriesOffload;
+ } else {
+ track->mRetryCount = kMaxTrackRetriesOffload;
+ }
mActiveTrack = t;
mixerStatus = MIXER_TRACKS_READY;
}
} else {
ALOGVV("OffloadThread: track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
if (track->isStopping_1()) {
- // Hardware buffer can hold a large amount of audio so we must
- // wait for all current track's data to drain before we say
- // that the track is stopped.
- if (mBytesRemaining == 0) {
- // Only start draining when all data in mixbuffer
- // has been written
- ALOGV("OffloadThread: underrun and STOPPING_1 -> draining, STOPPING_2");
- track->mState = TrackBase::STOPPING_2; // so presentation completes after drain
- // do not drain if no data was ever sent to HAL (mStandby == true)
- if (last && !mStandby) {
- // do not modify drain sequence if we are already draining. This happens
- // when resuming from pause after drain.
- if ((mDrainSequence & 1) == 0) {
- mSleepTimeUs = 0;
- mStandbyTimeNs = systemTime() + mStandbyDelayNs;
- mixerStatus = MIXER_DRAIN_TRACK;
- mDrainSequence += 2;
- }
- if (mHwPaused) {
- // It is possible to move from PAUSED to STOPPING_1 without
- // a resume so we must ensure hardware is running
- doHwResume = true;
- mHwPaused = false;
+ if (--(track->mRetryCount) <= 0) {
+ // Hardware buffer can hold a large amount of audio so we must
+ // wait for all current track's data to drain before we say
+ // that the track is stopped.
+ if (mBytesRemaining == 0) {
+ // Only start draining when all data in mixbuffer
+ // has been written
+ ALOGV("OffloadThread: underrun and STOPPING_1 -> draining, STOPPING_2");
+ track->mState = TrackBase::STOPPING_2; // so presentation completes after
+ // drain do not drain if no data was ever sent to HAL (mStandby == true)
+ if (last && !mStandby) {
+ // do not modify drain sequence if we are already draining. This happens
+ // when resuming from pause after drain.
+ if ((mDrainSequence & 1) == 0) {
+ mSleepTimeUs = 0;
+ mStandbyTimeNs = systemTime() + mStandbyDelayNs;
+ mixerStatus = MIXER_DRAIN_TRACK;
+ mDrainSequence += 2;
+ }
+ if (mHwPaused) {
+ // It is possible to move from PAUSED to STOPPING_1 without
+ // a resume so we must ensure hardware is running
+ doHwResume = true;
+ mHwPaused = false;
+ }
}
}
+ } else if (last) {
+ ALOGV("stopping1 underrun retries left %d", track->mRetryCount);
+ mixerStatus = MIXER_TRACKS_ENABLED;
}
} else if (track->isStopping_2()) {
// Drain has completed or we are in standby, signal presentation complete
@@ -5456,20 +5426,6 @@
}
}
-uint32_t AudioFlinger::OffloadThread::activeSleepTimeUs() const
-{
- uint32_t time;
- if (audio_has_proportional_frames(mFormat)) {
- time = PlaybackThread::activeSleepTimeUs();
- } else {
- // sleep time is half the duration of an audio HAL buffer.
- // Note: This can be problematic in case of underrun with variable bit rate and
- // current rate is much less than initial rate.
- time = (uint32_t)max(kDirectMinSleepTimeUs, mBufferDurationUs / 2);
- }
- return time;
-}
-
void AudioFlinger::OffloadThread::invalidateTracks(audio_stream_type_t streamType)
{
Mutex::Autolock _l(mLock);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 0ddd279..1cceb6d 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -486,9 +486,16 @@
// suspend by audio policy manager is orthogonal to mixer state
};
+ // retry count before removing active track in case of underrun on offloaded thread:
+ // we need to make sure that AudioTrack client has enough time to send large buffers
+ //FIXME may be more appropriate if expressed in time units. Need to revise how underrun is
+ // handled for offloaded tracks
+ static const int8_t kMaxTrackRetriesOffload = 20;
+ static const int8_t kMaxTrackStartupRetriesOffload = 100;
+ static const int8_t kMaxTrackStopRetriesOffload = 2;
+
PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady,
- uint32_t bitRate = 0);
+ audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
virtual ~PlaybackThread();
void dump(int fd, const Vector<String16>& args);
@@ -843,8 +850,6 @@
bool mHwSupportsPause;
bool mHwPaused;
bool mFlushPending;
- uint32_t mBufferDurationUs; // estimated duration of an audio HAL buffer
- // based on initial bit rate (offload only)
};
class MixerThread : public PlaybackThread {
@@ -935,8 +940,7 @@
public:
DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device, bool systemReady,
- uint32_t bitRate = 0);
+ audio_io_handle_t id, audio_devices_t device, bool systemReady);
virtual ~DirectOutputThread();
// Thread virtuals
@@ -969,7 +973,7 @@
DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, uint32_t device, ThreadBase::type_t type,
- bool systemReady, uint32_t bitRate = 0);
+ bool systemReady);
void processVolume_l(Track *track, bool lastTrack);
// prepareTracks_l() tells threadLoop_mix() the name of the single active track
@@ -985,8 +989,7 @@
public:
OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, uint32_t device,
- bool systemReady, uint32_t bitRate);
+ audio_io_handle_t id, uint32_t device, bool systemReady);
virtual ~OffloadThread() {};
virtual void flushHw_l();
@@ -995,8 +998,6 @@
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
virtual void threadLoop_exit();
- virtual uint32_t activeSleepTimeUs() const;
-
virtual bool waitingAsyncCallback();
virtual bool waitingAsyncCallback_l();
virtual void invalidateTracks(audio_stream_type_t streamType);
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 41cb030..364e339 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -373,7 +373,7 @@
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
- ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(),
+ ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %zu", sharedBuffer->pointer(),
sharedBuffer->size());
if (mCblk == NULL) {
@@ -728,6 +728,9 @@
// For an offloaded track this starts a drain and state will
// move to STOPPING_2 when drain completes and then STOPPED
mState = STOPPING_1;
+ if (isOffloaded()) {
+ mRetryCount = PlaybackThread::kMaxTrackStopRetriesOffload;
+ }
}
playbackThread->broadcast_l();
ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName,