Fix issue 2323920: Notification & A2DP audio stutter.
Modified AudioFlinger duplicating output thread so that audio tracks are not mixed until both outputs (A2DP and hardware) have exited standby mode. This avoids to have one output far ahead of the other and audio frames dropped because the compensation mechanism cannot keep up.
Also calculate the maximum wait time in OutputTrack::write() based the on smallest frame count of all output threads instead of the frame count of the thread the OutputTrack is connected to. This avoids starving the thread with the smallest frame count by waiting too long on the other thread.
Since the frame count was reduced on hardware output to reduce latency the difference between A2DP and hardware outputs frame counts had become problematic.
Also increased the number of overflow buffers to cope with bigger timing differences among outputs.
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 2024cc0..ecfe1e0 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -932,6 +932,8 @@
result.append(buffer);
snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite);
result.append(buffer);
+ snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended);
+ result.append(buffer);
write(fd, result.string(), result.size());
dumpBase(fd, args);
@@ -1344,7 +1346,7 @@
if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
!track->isPaused())
{
- //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+ //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this);
// compute volume for this track
int16_t left, right;
@@ -1400,7 +1402,7 @@
track->mRetryCount = kMaxTrackRetries;
mixerStatus = MIXER_TRACKS_READY;
} else {
- //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+ //LOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", track->name(), cblk->user, cblk->server, this);
if (track->isStopped()) {
track->reset();
}
@@ -1914,7 +1916,7 @@
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
- : MixerThread(audioFlinger, mainThread->getOutput(), id)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX)
{
mType = PlaybackThread::DUPLICATING;
addOutputTrack(mainThread);
@@ -1952,6 +1954,7 @@
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount*mFrameSize;
+ updateWaitTime();
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
}
@@ -2003,7 +2006,11 @@
if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
- mAudioMixer->process(curBuf);
+ if (outputsReady(outputTracks)) {
+ mAudioMixer->process(curBuf);
+ } else {
+ memset(curBuf, 0, mixBufferSize);
+ }
sleepTime = 0;
writeFrames = mFrameCount;
} else {
@@ -2054,6 +2061,7 @@
{
int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate();
OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread,
+ this,
mSampleRate,
mFormat,
mChannelCount,
@@ -2062,6 +2070,7 @@
thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f);
mOutputTracks.add(outputTrack);
LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
+ updateWaitTime();
}
}
@@ -2072,12 +2081,50 @@
if (mOutputTracks[i]->thread() == (ThreadBase *)thread) {
mOutputTracks[i]->destroy();
mOutputTracks.removeAt(i);
+ updateWaitTime();
return;
}
}
LOGV("removeOutputTrack(): unkonwn thread: %p", thread);
}
+void AudioFlinger::DuplicatingThread::updateWaitTime()
+{
+ mWaitTimeMs = UINT_MAX;
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ sp<ThreadBase> strong = mOutputTracks[i]->thread().promote();
+ if (strong != NULL) {
+ uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate();
+ if (waitTimeMs < mWaitTimeMs) {
+ mWaitTimeMs = waitTimeMs;
+ }
+ }
+ }
+}
+
+
+bool AudioFlinger::DuplicatingThread::outputsReady(SortedVector< sp<OutputTrack> > &outputTracks)
+{
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ sp <ThreadBase> thread = outputTracks[i]->thread().promote();
+ if (thread == 0) {
+ LOGW("DuplicatingThread::outputsReady() could not promote thread on output track %p", outputTracks[i].get());
+ return false;
+ }
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (playbackThread->standby() && !playbackThread->isSuspended()) {
+ LOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get());
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs()
+{
+ return (mWaitTimeMs * 1000) / 2;
+}
+
// ----------------------------------------------------------------------------
// TrackBase constructor must be called with AudioFlinger::mLock held
@@ -2616,12 +2663,13 @@
AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
const wp<ThreadBase>& thread,
+ DuplicatingThread *sourceThread,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount)
: Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL),
- mActive(false)
+ mActive(false), mSourceThread(sourceThread)
{
PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get();
@@ -2630,10 +2678,9 @@
mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
mCblk->volume[0] = mCblk->volume[1] = 0x1000;
mOutBuffer.frameCount = 0;
- mWaitTimeMs = (playbackThread->frameCount() * 2 * 1000) / playbackThread->sampleRate();
playbackThread->mTracks.add(this);
- LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p mWaitTimeMs %d",
- mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd, mWaitTimeMs);
+ LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p",
+ mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd);
} else {
LOGW("Error creating output track on thread %p", playbackThread);
}
@@ -2673,7 +2720,7 @@
inBuffer.frameCount = frames;
inBuffer.i16 = data;
- uint32_t waitTimeLeftMs = mWaitTimeMs;
+ uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
if (!mActive && frames != 0) {
start();
@@ -2712,12 +2759,11 @@
mOutBuffer.frameCount = pInBuffer->frameCount;
nsecs_t startTime = systemTime();
if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) {
- LOGV ("OutputTrack::write() %p no more output buffers", this);
+ LOGV ("OutputTrack::write() %p thread %p no more output buffers", this, mThread.unsafe_get());
outputBufferFull = true;
break;
}
uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime);
- LOGV("OutputTrack::write() to thread %p waitTimeMs %d waitTimeLeftMs %d", mThread.unsafe_get(), waitTimeMs, waitTimeLeftMs);
if (waitTimeLeftMs >= waitTimeMs) {
waitTimeLeftMs -= waitTimeMs;
} else {
@@ -2738,7 +2784,7 @@
mBufferQueue.removeAt(0);
delete [] pInBuffer->mBuffer;
delete pInBuffer;
- LOGV("OutputTrack::write() %p released overflow buffer %d", this, mBufferQueue.size());
+ LOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size());
} else {
break;
}
@@ -2747,16 +2793,19 @@
// If we could not write all frames, allocate a buffer and queue it for next time.
if (inBuffer.frameCount) {
- if (mBufferQueue.size() < kMaxOverFlowBuffers) {
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels];
- pInBuffer->frameCount = inBuffer.frameCount;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
- LOGV("OutputTrack::write() %p adding overflow buffer %d", this, mBufferQueue.size());
- } else {
- LOGW("OutputTrack::write() %p no more overflow buffers", this);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0 && !thread->standby()) {
+ if (mBufferQueue.size() < kMaxOverFlowBuffers) {
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels];
+ pInBuffer->frameCount = inBuffer.frameCount;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ LOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size());
+ } else {
+ LOGW("OutputTrack::write() %p thread %p no more overflow buffers", mThread.unsafe_get(), this);
+ }
}
}
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 8c29da8..12c90eb 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <limits.h>
#include <media/IAudioFlinger.h>
#include <media/IAudioFlingerClient.h>
@@ -208,6 +209,7 @@
class PlaybackThread;
class MixerThread;
class DirectOutputThread;
+ class DuplicatingThread;
class Track;
class RecordTrack;
@@ -324,6 +326,7 @@
void sendConfigEvent_l(int event, int param = 0);
void processConfigEvents();
int id() const { return mId;}
+ bool standby() { return mStandby; }
mutable Mutex mLock;
@@ -452,6 +455,7 @@
};
OutputTrack( const wp<ThreadBase>& thread,
+ DuplicatingThread *sourceThread,
uint32_t sampleRate,
int format,
int channelCount,
@@ -471,13 +475,12 @@
void clearBufferQueue();
// Maximum number of pending buffers allocated by OutputTrack::write()
- static const uint8_t kMaxOverFlowBuffers = 3;
+ static const uint8_t kMaxOverFlowBuffers = 10;
Vector < Buffer* > mBufferQueue;
AudioBufferProvider::Buffer mOutBuffer;
- uint32_t mWaitTimeMs;
bool mActive;
-
+ DuplicatingThread* mSourceThread;
}; // end of OutputTrack
PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
@@ -520,6 +523,7 @@
virtual int type() const { return mType; }
void suspend() { mSuspended++; }
void restore() { if (mSuspended) mSuspended--; }
+ bool isSuspended() { return (mSuspended != 0); }
virtual String8 getParameters(const String8& keys);
virtual void audioConfigChanged(int event, int param = 0);
@@ -635,9 +639,16 @@
virtual bool threadLoop();
void addOutputTrack(MixerThread* thread);
void removeOutputTrack(MixerThread* thread);
+ uint32_t waitTimeMs() { return mWaitTimeMs; }
+ protected:
+ virtual uint32_t activeSleepTimeUs();
private:
+ bool outputsReady(SortedVector< sp<OutputTrack> > &outputTracks);
+ void updateWaitTime();
+
SortedVector < sp<OutputTrack> > mOutputTracks;
+ uint32_t mWaitTimeMs;
};
PlaybackThread *checkPlaybackThread_l(int output) const;