AudioFlinger: Make Direct AudioTrack behavior more flexible for underrun
Allow more time for data to arrive ~200ms.
Push remaining track data in underrun.
Document direct track underrun handling.
Test: directed test (see b/186596553)
Bug: 34711969
Bug: 157682459
Bug: 161089206
Bug: 186596553
Merged-In: I9cd7f1f89287c180c30bba35e73d8d3fd6296eeb
Change-Id: I9cd7f1f89287c180c30bba35e73d8d3fd6296eeb
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c0c9cc4..1522746 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -120,12 +120,26 @@
// 50 * ~20msecs = 1 second
static const int8_t kMaxTrackRetries = 50;
static const int8_t kMaxTrackStartupRetries = 50;
+
// allow less retry attempts on direct output thread.
// direct outputs can be a scarce resource in audio hardware and should
// be released as quickly as possible.
-static const int8_t kMaxTrackRetriesDirect = 2;
-
-
+// Notes:
+// 1) The retry duration kMaxTrackRetriesDirectMs may be increased
+// in case the data write is bursty for the AudioTrack. The application
+// should endeavor to write at least once every kMaxTrackRetriesDirectMs
+// to prevent an underrun situation. If the data is bursty, then
+// the application can also throttle the data sent to be even.
+// 2) For compressed audio data, any data present in the AudioTrack buffer
+// will be sent and reset the retry count. This delivers data as
+// it arrives, with approximately kDirectMinSleepTimeUs = 10ms checking interval.
+// 3) For linear PCM or proportional PCM, we wait one period for a period's worth
+// of data to be available, then any remaining data is delivered.
+// This is required to ensure the last bit of data is delivered before underrun.
+//
+// Sleep time per cycle is kDirectMinSleepTimeUs for compressed tracks
+// or the size of the HAL period for proportional / linear PCM tracks.
+static const int32_t kMaxTrackRetriesDirectMs = 200;
// don't warn about blocked writes or record buffer overflows more often than this
static const nsecs_t kWarningThrottleNs = seconds(5);
@@ -5859,11 +5873,19 @@
// Allow draining the buffer in case the client
// app does not call stop() and relies on underrun to stop:
// hence the test on (track->mRetryCount > 1).
- // If retryCount<=1 then track is about to underrun and be removed.
+ // If track->mRetryCount <= 1 then track is about to be disabled, paused, removed,
+ // so we accept any nonzero amount of data delivered by the AudioTrack (which will
+ // reset the retry counter).
// Do not use a high threshold for compressed audio.
+
+ // target retry count that we will use is based on the time we wait for retries.
+ const int32_t targetRetryCount = kMaxTrackRetriesDirectMs * 1000 / mActiveSleepTimeUs;
+ // the retry threshold is when we accept any size for PCM data. This is slightly
+ // smaller than the retry count so we can push small bits of data without a glitch.
+ const int32_t retryThreshold = targetRetryCount > 2 ? targetRetryCount - 1 : 1;
uint32_t minFrames;
if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()
- && (track->mRetryCount > 1) && audio_has_proportional_frames(mFormat)) {
+ && (track->mRetryCount > retryThreshold) && audio_has_proportional_frames(mFormat)) {
minFrames = mNormalFrameCount;
} else {
minFrames = 1;
@@ -5907,7 +5929,7 @@
mPreviousTrack = track;
// reset retry count
- track->mRetryCount = kMaxTrackRetriesDirect;
+ track->mRetryCount = targetRetryCount;
mActiveTrack = t;
mixerStatus = MIXER_TRACKS_READY;
if (mHwPaused) {