summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/Threads.cpp
diff options
context:
space:
mode:
authorAndy Hung <hunga@google.com>2015-05-31 23:22:10 -0700
committerAndy Hung <hunga@google.com>2015-06-03 13:28:27 -0700
commit08fb1743f80437c38b4094070d851ea7f6d485e5 (patch)
tree5c2c3d8a6401247415e5e106140afdea2113661f /services/audioflinger/Threads.cpp
parent0eafa9d8ee4069aa709ff85ceffe94d12995956d (diff)
downloadframeworks_av-08fb1743f80437c38b4094070d851ea7f6d485e5.zip
frameworks_av-08fb1743f80437c38b4094070d851ea7f6d485e5.tar.gz
frameworks_av-08fb1743f80437c38b4094070d851ea7f6d485e5.tar.bz2
Throttle MixerThread data pull to no more than twice expected rate
This helps prevent underruns with NuPlayer and other applications which set up buffers that are close to minimum size or use deep buffers, and rely on a double-buffering sleep strategy to fill. Enabled by default. Disabled by setting af.thread.throttle 0 Bug: 19062223 Bug: 21198655 Change-Id: Ia52b48e0c99588af5db53c43fede2afd775b8899
Diffstat (limited to 'services/audioflinger/Threads.cpp')
-rw-r--r--services/audioflinger/Threads.cpp38
1 files changed, 35 insertions, 3 deletions
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7809eff..dc6710f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2191,6 +2191,10 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l()
ALOGI("HAL output buffer size %u frames, normal sink buffer size %u frames", mFrameCount,
mNormalFrameCount);
+ // Check if we want to throttle the processing to no more than 2x normal rate
+ mThreadThrottle = property_get_bool("af.thread.throttle", true /* default_value */);
+ mHalfBufferMs = mNormalFrameCount * 1000 / (2 * mSampleRate);
+
// mSinkBuffer is the sink buffer. Size is always multiple-of-16 frames.
// Originally this was int16_t[] array, need to remove legacy implications.
free(mSinkBuffer);
@@ -2908,8 +2912,9 @@ bool AudioFlinger::PlaybackThread::threadLoop()
if (!waitingAsyncCallback()) {
// mSleepTimeUs == 0 means we must write to audio hardware
if (mSleepTimeUs == 0) {
+ ssize_t ret = 0;
if (mBytesRemaining) {
- ssize_t ret = threadLoop_write();
+ ret = threadLoop_write();
if (ret < 0) {
mBytesRemaining = 0;
} else {
@@ -2920,11 +2925,11 @@ bool AudioFlinger::PlaybackThread::threadLoop()
(mMixerStatus == MIXER_DRAIN_ALL)) {
threadLoop_drain();
}
- if (mType == MIXER) {
+ if (mType == MIXER && !mStandby) {
// write blocked detection
nsecs_t now = systemTime();
nsecs_t delta = now - mLastWriteTime;
- if (!mStandby && delta > maxPeriod) {
+ if (delta > maxPeriod) {
mNumDelayedWrites++;
if ((now - lastWarning) > kWarningThrottleNs) {
ATRACE_NAME("underrun");
@@ -2933,6 +2938,31 @@ bool AudioFlinger::PlaybackThread::threadLoop()
lastWarning = now;
}
}
+
+ if (mThreadThrottle
+ && mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks)
+ && ret > 0) { // we wrote something
+ // Limit MixerThread data processing to no more than twice the
+ // expected processing rate.
+ //
+ // This helps prevent underruns with NuPlayer and other applications
+ // which may set up buffers that are close to the minimum size, or use
+ // deep buffers, and rely on a double-buffering sleep strategy to fill.
+ //
+ // The throttle smooths out sudden large data drains from the device,
+ // e.g. when it comes out of standby, which often causes problems with
+ // (1) mixer threads without a fast mixer (which has its own warm-up)
+ // (2) minimum buffer sized tracks (even if the track is full,
+ // the app won't fill fast enough to handle the sudden draw).
+
+ const int32_t deltaMs = delta / 1000000;
+ const int32_t throttleMs = mHalfBufferMs - deltaMs;
+ if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
+ usleep(throttleMs * 1000);
+ ALOGD("mixer(%p) throttle: ret(%zd) deltaMs(%d) requires sleep %d ms",
+ this, ret, deltaMs, throttleMs);
+ }
+ }
}
} else {
@@ -4023,6 +4053,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
} else {
if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
+ ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)",
+ track, framesReady, desiredFrames);
track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
}
// clear effect chain input buffer if an active track underruns to avoid sending