From 6667ac33f4d7084fa240baf719282051f1e9ff25 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 21 Mar 2011 11:49:00 -0700 Subject: [PATCH] Fix issue 3483718: audio streaming and A2DP. The problem is that when switching from A2DP to device speakers or headset, The AudioTrack binder interface to AudioFlinger must be destroyed and restored to accomodate new buffer size requirements. Current AudioTrack implementation did not restore properly the PCM buffer write index which caused a mismatch between the written frame count in the mediaplayer renderer and the AudioTrack. The renderer could then believe the AudioTrack buffer was full and stop writing data preventing the AudioTrack to reach a bufffer full condition and resume playback. The rendered was also modified to refresh the AudioTrack frame count (buffer size) inside the write loop in NuPlayer::Renderer::onDrainAudioQueue() as this count can change from one write to the next. Also modified AudioTrack::obtainBuffer() to check for track invalidated status before querying for available space in the buffer. This avoids writing to the old track's buffer until full before detecting the invalidated condition and create a new track. Change-Id: I16a857e464e466880847f52f640820aa271539ad --- media/libmedia/AudioTrack.cpp | 24 ++++++++++++++++--- .../nuplayer/NuPlayerRenderer.cpp | 21 +++++++++------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 02e15702981fb..fb2ee0f813f36 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -546,12 +546,13 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou } if (loopStart >= loopEnd || - loopEnd - loopStart > cblk->frameCount) { + loopEnd - loopStart > cblk->frameCount || + cblk->server > loopStart) { LOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, cblk->frameCount, cblk->user); return BAD_VALUE; } - if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) { + if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) { LOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d", loopStart, loopEnd, cblk->frameCount); return BAD_VALUE; @@ -825,6 +826,12 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) uint32_t framesAvail = cblk->framesAvailable(); + cblk->lock.lock(); + if (cblk->flags & CBLK_INVALID_MSK) { + goto create_new_track; + } + cblk->lock.unlock(); + if (framesAvail == 0) { cblk->lock.lock(); goto start_loop_here; @@ -1148,6 +1155,7 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) fromStart ? "start()" : "obtainBuffer()"); cblk->flags |= CBLK_RESTORING_ON; + // signal old cblk condition so that other threads waiting for available buffers stop // waiting now cblk->cv.broadcast(); @@ -1167,10 +1175,20 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) false); if (result == NO_ERROR) { + // restore write index and set other indexes to reflect empty buffer status + mCblk->user = cblk->user; + mCblk->server = cblk->user; + mCblk->userBase = cblk->user; + mCblk->serverBase = cblk->user; + // restore loop: this is not guaranteed to succeed if new frame count is not + // compatible with loop length + setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount); if (!fromStart) { mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; } - result = mAudioTrack->start(); + if (mActive) { + result = mAudioTrack->start(); + } if (fromStart && result == NO_ERROR) { mNewPosition = mCblk->server + mUpdatePeriod; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 369a3a8149fa1..828e00863ea81 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -198,18 +198,21 @@ void NuPlayer::Renderer::signalAudioSinkChanged() { } void NuPlayer::Renderer::onDrainAudioQueue() { - uint32_t numFramesPlayed; - CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); - ssize_t numFramesAvailableToWrite = - mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed); + for (;;) { + uint32_t numFramesPlayed; + CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK); - CHECK_GE(numFramesAvailableToWrite, 0); + ssize_t numFramesAvailableToWrite = + mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed); - size_t numBytesAvailableToWrite = - numFramesAvailableToWrite * mAudioSink->frameSize(); + size_t numBytesAvailableToWrite = + numFramesAvailableToWrite * mAudioSink->frameSize(); + + if (numBytesAvailableToWrite == 0) { + break; + } - while (numBytesAvailableToWrite > 0) { if (mAudioQueue.empty()) { break; } @@ -264,10 +267,10 @@ void NuPlayer::Renderer::onDrainAudioQueue() { if (entry->mOffset == entry->mBuffer->size()) { entry->mNotifyConsumed->post(); mAudioQueue.erase(mAudioQueue.begin()); + entry = NULL; } - numBytesAvailableToWrite -= copy; mNumFramesWritten += copy / mAudioSink->frameSize(); }