diff options
author | Eric Laurent <elaurent@google.com> | 2010-05-14 03:26:45 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2010-05-17 02:23:47 -0700 |
commit | eb8f850d0b7e53956e917fd9645f808c1a09bc88 (patch) | |
tree | 07b5194b348dcd40b46fbf049066e45320099fdb | |
parent | 533844d9688f7c9269372f97db17c9bc4d19c04b (diff) | |
download | frameworks_base-eb8f850d0b7e53956e917fd9645f808c1a09bc88.zip frameworks_base-eb8f850d0b7e53956e917fd9645f808c1a09bc88.tar.gz frameworks_base-eb8f850d0b7e53956e917fd9645f808c1a09bc88.tar.bz2 |
Fix issue 2553359: Pandora does not work well with Passion deskdock / Cardock.
The problem is due to a too big difference between the buffer size used at the hardware interface and at the A2DP interface.
When no resampling occurs we don't notice problems but the timing is very tight. As soon as resampling is activated, the AudioTrack underruns.
This is because the AudioTrack buffers are not resized when moving the AudioTrack from hardware to A2DP output.
The AudioTrack buffers are calculated based on a hardware output buffer size of 3072 bytes. Which is much less than the A2DP output buffer size (10240).
The solution consists in creating new tracks with new buffers in AudioFlinger when the A2DP output is opened
instead of just transfering active tracks from hardware output mixer thread to the new A2DP output mixer thread.
To avoid synchronization issues between mixer threads and client processes, this is done by invalidating tracks
by setting a flag in their control block and having AudioTrack release the handle on this track (IAudioTrack)
and create a new IAudioTrack when this flag is detected next time obtainBuffer() or start() is executed.
AudioFlinger modifications:
- invalidate the tracks when setStreamOutput() is called
- make sure that notifications of output opening/closing and change of stream type to output mapping are sent synchronously to client process.
This is necessary so that AudioSystem has the new stream to output mapping when the AudioTrack detects the invalidate flag in the client process.
Previously their were sent when the corresponding thread loop was executed.
AudioTrack modifications:
- move frame count calculation and verification from set() to createTrack() so that is is updated every time a new IAudioTrack is created.
- detect track invalidate flag in obtainBuffer() and start() and create a new IAudioTrack.
AudioTrackShared modifications
- group all flags (out, flowControlFlag, forceReady...) into a single bit filed to save space.
Change-Id: I9ac26b6192230627d35084e1449640caaf7d56ee
-rw-r--r-- | include/media/AudioTrack.h | 6 | ||||
-rw-r--r-- | include/private/media/AudioTrackShared.h | 35 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.cpp | 109 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.h | 12 | ||||
-rw-r--r-- | libs/audioflinger/AudioPolicyManagerBase.cpp | 21 | ||||
-rw-r--r-- | media/libmedia/AudioRecord.cpp | 8 | ||||
-rw-r--r-- | media/libmedia/AudioTrack.cpp | 176 |
7 files changed, 181 insertions, 186 deletions
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 14b30ae..cc4ab74 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -398,7 +398,8 @@ private: int frameCount, uint32_t flags, const sp<IMemory>& sharedBuffer, - audio_io_handle_t output); + audio_io_handle_t output, + bool enforceFrameCount); sp<IAudioTrack> mAudioTrack; sp<IMemory> mCblkMemory; @@ -420,7 +421,8 @@ private: callback_t mCbf; void* mUserData; - uint32_t mNotificationFrames; + uint32_t mNotificationFramesReq; // requested number of frames between each notification callback + uint32_t mNotificationFramesAct; // actual number of frames between each notification callback sp<IMemory> mSharedBuffer; int mLoopCount; uint32_t mRemainingFrames; diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index ab5ac64..cd47fdf 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -32,6 +32,18 @@ namespace android { #define MAX_RUN_TIMEOUT_MS 1000 #define WAIT_PERIOD_MS 10 +#define CBLK_UNDERRUN_MSK 0x0001 +#define CBLK_UNDERRUN_ON 0x0001 // underrun (out) or overrrun (in) indication +#define CBLK_UNDERRUN_OFF 0x0000 // no underrun +#define CBLK_DIRECTION_MSK 0x0002 +#define CBLK_DIRECTION_OUT 0x0002 // this cblk is for an AudioTrack +#define CBLK_DIRECTION_IN 0x0000 // this cblk is for an AudioRecord +#define CBLK_FORCEREADY_MSK 0x0004 +#define CBLK_FORCEREADY_ON 0x0004 // track is considered ready immediately by AudioFlinger +#define CBLK_FORCEREADY_OFF 0x0000 // track is ready when buffer full +#define CBLK_INVALID_MSK 0x0008 +#define CBLK_INVALID_ON 0x0008 // track buffer is invalidated by AudioFlinger: must be re-created +#define CBLK_INVALID_OFF 0x0000 struct audio_track_cblk_t { @@ -44,12 +56,12 @@ struct audio_track_cblk_t volatile uint32_t server; uint32_t userBase; uint32_t serverBase; - void* buffers; - uint32_t frameCount; - // Cache line boundary - uint32_t loopStart; - uint32_t loopEnd; - int loopCount; + void* buffers; + uint32_t frameCount; + // Cache line boundary + uint32_t loopStart; + uint32_t loopEnd; + int loopCount; volatile union { uint16_t volume[2]; uint32_t volumeLR; @@ -58,15 +70,16 @@ struct audio_track_cblk_t // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for // 8 bit PCM data: in this case, mCblk->frameSize is based on a sample size of // 16 bit because data is converted to 16 bit before being stored in buffer - uint32_t frameSize; + + uint8_t frameSize; uint8_t channelCount; - uint8_t flowControlFlag; // underrun (out) or overrrun (in) indication - uint8_t out; // out equals 1 for AudioTrack and 0 for AudioRecord - uint8_t forceReady; + uint16_t flags; + uint16_t bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger uint16_t waitTimeMs; // Cumulated wait time - // Cache line boundary (32 bytes) + uint32_t reserved; + // Cache line boundary (32 bytes) audio_track_cblk_t(); uint32_t stepUser(uint32_t frameCount); bool stepServer(uint32_t frameCount); diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 58eb590..3b38d83 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -873,11 +873,12 @@ void AudioFlinger::ThreadBase::processConfigEvents() LOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); ConfigEvent *configEvent = mConfigEvents[0]; mConfigEvents.removeAt(0); - // release mLock because audioConfigChanged() will lock AudioFlinger mLock - // before calling Audioflinger::audioConfigChanged_l() thus creating - // potential cross deadlock between AudioFlinger::mLock and mLock + // release mLock before locking AudioFlinger mLock: lock order is always + // AudioFlinger then ThreadBase to avoid cross deadlock mLock.unlock(); - audioConfigChanged(configEvent->mEvent, configEvent->mParam); + mAudioFlinger->mLock.lock(); + audioConfigChanged_l(configEvent->mEvent, configEvent->mParam); + mAudioFlinger->mLock.unlock(); delete configEvent; mLock.lock(); } @@ -953,8 +954,6 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream); mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream); } - // notify client processes that a new input has been opened - sendConfigEvent(AudioSystem::OUTPUT_OPENED); } AudioFlinger::PlaybackThread::~PlaybackThread() @@ -1234,11 +1233,12 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) return mOutput->getParameters(keys); } -void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { +// destroyTrack_l() must be called with AudioFlinger::mLock held +void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { AudioSystem::OutputDescriptor desc; void *param2 = 0; - LOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param); + LOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param); switch (event) { case AudioSystem::OUTPUT_OPENED: @@ -1257,7 +1257,6 @@ void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { default: break; } - Mutex::Autolock _l(mAudioFlinger->mLock); mAudioFlinger->audioConfigChanged_l(event, mId, param2); } @@ -1614,66 +1613,22 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track return mixerStatus; } -void AudioFlinger::MixerThread::getTracks( - SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks, - int streamType) +void AudioFlinger::MixerThread::invalidateTracks(int streamType) { - LOGV ("MixerThread::getTracks() mixer %p, mTracks.size %d, mActiveTracks.size %d", this, mTracks.size(), mActiveTracks.size()); + LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d", this, streamType, mTracks.size()); Mutex::Autolock _l(mLock); size_t size = mTracks.size(); for (size_t i = 0; i < size; i++) { sp<Track> t = mTracks[i]; if (t->type() == streamType) { - tracks.add(t); - int j = mActiveTracks.indexOf(t); - if (j >= 0) { - t = mActiveTracks[j].promote(); - if (t != NULL) { - activeTracks.add(t); + t->mCblk->lock.lock(); + t->mCblk->flags |= CBLK_INVALID_ON; + t->mCblk->cv.signal(); + t->mCblk->lock.unlock(); } } } - } - - size = activeTracks.size(); - for (size_t i = 0; i < size; i++) { - mActiveTracks.remove(activeTracks[i]); - } - - size = tracks.size(); - for (size_t i = 0; i < size; i++) { - sp<Track> t = tracks[i]; - mTracks.remove(t); - deleteTrackName_l(t->name()); - } -} - -void AudioFlinger::MixerThread::putTracks( - SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks) -{ - LOGV ("MixerThread::putTracks() mixer %p, tracks.size %d, activeTracks.size %d", this, tracks.size(), activeTracks.size()); - Mutex::Autolock _l(mLock); - size_t size = tracks.size(); - for (size_t i = 0; i < size ; i++) { - sp<Track> t = tracks[i]; - int name = getTrackName_l(); - - if (name < 0) return; - t->mName = name; - t->mThread = this; - mTracks.add(t); - - int j = activeTracks.indexOf(t); - if (j >= 0) { - mActiveTracks.add(t); - // force buffer refilling and no ramp volume when the track is mixed for the first time - t->mFillingUpStatus = Track::FS_FILLING; - } - } -} // getTrackName_l() must be called with ThreadBase::mLock held int AudioFlinger::MixerThread::getTrackName_l() @@ -2348,7 +2303,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); // Force underrun condition to avoid false underrun callback until first data is // written to buffer - mCblk->flowControlFlag = 1; + mCblk->flags = CBLK_UNDERRUN_ON; } else { mBuffer = sharedBuffer->pointer(); } @@ -2371,7 +2326,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); // Force underrun condition to avoid false underrun callback until first data is // written to buffer - mCblk->flowControlFlag = 1; + mCblk->flags = CBLK_UNDERRUN_ON; mBufferEnd = (uint8_t *)mBuffer + bufferSize; } } @@ -2589,9 +2544,9 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { if (mFillingUpStatus != FS_FILLING) return true; if (mCblk->framesReady() >= mCblk->frameCount || - mCblk->forceReady) { + (mCblk->flags & CBLK_FORCEREADY_MSK)) { mFillingUpStatus = FS_FILLED; - mCblk->forceReady = 0; + mCblk->flags &= ~CBLK_FORCEREADY_MSK; return true; } return false; @@ -2706,8 +2661,8 @@ void AudioFlinger::PlaybackThread::Track::reset() TrackBase::reset(); // Force underrun condition to avoid false underrun callback until first data is // written to buffer - mCblk->flowControlFlag = 1; - mCblk->forceReady = 0; + mCblk->flags |= CBLK_UNDERRUN_ON; + mCblk->flags &= ~CBLK_FORCEREADY_MSK; mFillingUpStatus = FS_FILLING; mResetDone = true; } @@ -2818,7 +2773,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop() TrackBase::reset(); // Force overerrun condition to avoid false overrun callback until first data is // read from buffer - mCblk->flowControlFlag = 1; + mCblk->flags |= CBLK_UNDERRUN_ON; } } @@ -2851,7 +2806,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get(); if (mCblk != NULL) { - mCblk->out = 1; + mCblk->flags |= CBLK_DIRECTION_OUT; mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); mCblk->volume[0] = mCblk->volume[1] = 0x1000; mOutBuffer.frameCount = 0; @@ -3274,7 +3229,6 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, A mReqChannelCount = AudioSystem::popCount(channels); mReqSampleRate = sampleRate; readInputParameters(); - sendConfigEvent(AudioSystem::INPUT_OPENED); } @@ -3689,7 +3643,7 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys) return mInput->getParameters(keys); } -void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) { +void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) { AudioSystem::OutputDescriptor desc; void *param2 = 0; @@ -3708,7 +3662,6 @@ void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) { default: break; } - Mutex::Autolock _l(mAudioFlinger->mLock); mAudioFlinger->audioConfigChanged_l(event, mId, param2); } @@ -3828,6 +3781,8 @@ int AudioFlinger::openOutput(uint32_t *pDevices, if (pChannels) *pChannels = channels; if (pLatencyMs) *pLatencyMs = thread->latency(); + // notify client processes of the new output creation + thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); return mNextThreadId; } @@ -3849,6 +3804,8 @@ int AudioFlinger::openDuplicateOutput(int output1, int output2) DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId); thread->addOutputTrack(thread2); mPlaybackThreads.add(mNextThreadId, thread); + // notify client processes of the new output creation + thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); return mNextThreadId; } @@ -3978,6 +3935,8 @@ int AudioFlinger::openInput(uint32_t *pDevices, input->standby(); + // notify client processes of the new input creation + thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED); return mNextThreadId; } @@ -4018,22 +3977,16 @@ status_t AudioFlinger::setStreamOutput(uint32_t stream, int output) } LOGV("setStreamOutput() stream %d to output %d", stream, output); + audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream); for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); if (thread != dstThread && thread->type() != PlaybackThread::DIRECT) { MixerThread *srcThread = (MixerThread *)thread; - SortedVector < sp<MixerThread::Track> > tracks; - SortedVector < wp<MixerThread::Track> > activeTracks; - srcThread->getTracks(tracks, activeTracks, stream); - if (tracks.size()) { - dstThread->putTracks(tracks, activeTracks); + srcThread->invalidateTracks(stream); } } - } - - dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream); return NO_ERROR; } diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index c4a5305..f35f38b 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -342,7 +342,7 @@ private: virtual bool checkForNewParameters_l() = 0; virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys) = 0; - virtual void audioConfigChanged(int event, int param = 0) = 0; + virtual void audioConfigChanged_l(int event, int param = 0) = 0; void sendConfigEvent(int event, int param = 0); void sendConfigEvent_l(int event, int param = 0); void processConfigEvents(); @@ -547,7 +547,7 @@ private: void restore() { if (mSuspended) mSuspended--; } bool isSuspended() { return (mSuspended != 0); } virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged(int event, int param = 0); + virtual void audioConfigChanged_l(int event, int param = 0); virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); struct stream_type_t { @@ -613,11 +613,7 @@ private: // Thread virtuals virtual bool threadLoop(); - void getTracks(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks, - int streamType); - void putTracks(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks); + void invalidateTracks(int streamType); virtual bool checkForNewParameters_l(); virtual status_t dumpInternals(int fd, const Vector<String16>& args); @@ -764,7 +760,7 @@ private: virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); virtual bool checkForNewParameters_l(); virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged(int event, int param = 0); + virtual void audioConfigChanged_l(int event, int param = 0); void readInputParameters(); virtual unsigned int getInputFramesLost(); diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp index c8b3f48..381a958 100644 --- a/libs/audioflinger/AudioPolicyManagerBase.cpp +++ b/libs/audioflinger/AudioPolicyManagerBase.cpp @@ -1249,6 +1249,17 @@ void AudioPolicyManagerBase::closeA2dpOutputs() LOGV("setDeviceConnectionState() closing A2DP and duplicated output!"); if (mDuplicatedOutput != 0) { + AudioOutputDescriptor *dupOutputDesc = mOutputs.valueFor(mDuplicatedOutput); + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + // As all active tracks on duplicated output will be deleted, + // and as they were also referenced on hardware output, the reference + // count for their stream type must be adjusted accordingly on + // hardware output. + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + int refCount = dupOutputDesc->mRefCount[i]; + hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); + } + mpClientInterface->closeOutput(mDuplicatedOutput); delete mOutputs.valueFor(mDuplicatedOutput); mOutputs.removeItem(mDuplicatedOutput); @@ -1288,11 +1299,6 @@ void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, u for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { if (getStrategy((AudioSystem::stream_type)i) == strategy) { mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput); - int refCount = a2dpOutputDesc->mRefCount[i]; - // in the case of duplicated output, the ref count is first incremented - // and then decremented on hardware output tus keeping its value - hwOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); - a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); } } // do not change newDevice if it was already set before this call by a previous call to @@ -1318,11 +1324,6 @@ void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, u for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { if (getStrategy((AudioSystem::stream_type)i) == strategy) { mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, a2dpOutput); - int refCount = hwOutputDesc->mRefCount[i]; - // in the case of duplicated output, the ref count is first incremented - // and then decremented on hardware output tus keeping its value - a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); - hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); } } } diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index ad037d6..fd2b1ce 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -430,7 +430,7 @@ status_t AudioRecord::openRecord( mCblkMemory = cblk; mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); - mCblk->out = 0; + mCblk->flags &= ~CBLK_DIRECTION_MSK; mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; mCblk->waitTimeMs = 0; return NO_ERROR; @@ -644,10 +644,10 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) // Manage overrun callback if (mActive && (mCblk->framesAvailable_l() == 0)) { - LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); - if (mCblk->flowControlFlag == 0) { + LOGV("Overrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags); + if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { mCbf(EVENT_OVERRUN, mUserData, 0); - mCblk->flowControlFlag = 1; + mCblk->flags |= CBLK_UNDERRUN_ON; } } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index cd7bcd5..c350532 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -124,10 +124,6 @@ status_t AudioTrack::set( if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { return NO_INIT; } - int afFrameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { - return NO_INIT; - } uint32_t afLatency; if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { return NO_INIT; @@ -173,48 +169,13 @@ status_t AudioTrack::set( return BAD_VALUE; } - if (!AudioSystem::isLinearPCM(format)) { - if (sharedBuffer != 0) { - frameCount = sharedBuffer->size(); - } - } else { - // Ensure that buffer depth covers at least audio hardware latency - uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); - if (minBufCount < 2) minBufCount = 2; - - int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; - - if (sharedBuffer == 0) { - if (frameCount == 0) { - frameCount = minFrameCount; - } - if (notificationFrames == 0) { - notificationFrames = frameCount/2; - } - // Make sure that application is notified with sufficient margin - // before underrun - if (notificationFrames > frameCount/2) { - notificationFrames = frameCount/2; - } - if (frameCount < minFrameCount) { - LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount); - return BAD_VALUE; - } - } else { - // Ensure that buffer alignment matches channelcount - if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) { - LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount); - return BAD_VALUE; - } - frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t); - } - } - mVolume[LEFT] = 1.0f; mVolume[RIGHT] = 1.0f; + mFrameCount = frameCount; + mNotificationFramesReq = notificationFrames; // create the IAudioTrack status_t status = createTrack(streamType, sampleRate, format, channelCount, - frameCount, flags, sharedBuffer, output); + frameCount, flags, sharedBuffer, output, true); if (status != NO_ERROR) { return status; @@ -238,10 +199,7 @@ status_t AudioTrack::set( mMuted = false; mActive = 0; mCbf = cbf; - mNotificationFrames = notificationFrames; - mRemainingFrames = notificationFrames; mUserData = user; - mLatency = afLatency + (1000*mFrameCount) / sampleRate; mLoopCount = 0; mMarkerPosition = 0; mMarkerReached = false; @@ -281,7 +239,7 @@ int AudioTrack::channelCount() const uint32_t AudioTrack::frameCount() const { - return mFrameCount; + return mCblk->frameCount; } int AudioTrack::frameSize() const @@ -303,6 +261,7 @@ sp<IMemory>& AudioTrack::sharedBuffer() void AudioTrack::start() { sp<AudioTrackThread> t = mAudioTrackThread; + status_t status; LOGV("start %p", this); if (t != 0) { @@ -325,11 +284,18 @@ void AudioTrack::start() setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); } - status_t status = mAudioTrack->start(); + if (mCblk->flags & CBLK_INVALID_MSK) { + LOGW("start() track %p invalidated, creating a new one", this); + // no need to clear the invalid flag as this cblk will not be used anymore + // force new track creation + status = DEAD_OBJECT; + } else { + status = mAudioTrack->start(); + } if (status == DEAD_OBJECT) { LOGV("start() dead IAudioTrack: creating a new one"); status = createTrack(mStreamType, mCblk->sampleRate, mFormat, mChannelCount, - mFrameCount, mFlags, mSharedBuffer, getOutput()); + mFrameCount, mFlags, mSharedBuffer, getOutput(), false); if (status == NO_ERROR) { status = mAudioTrack->start(); if (status == NO_ERROR) { @@ -479,14 +445,14 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount } if (loopStart >= loopEnd || - loopEnd - loopStart > mFrameCount) { - LOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user); + loopEnd - loopStart > cblk->frameCount) { + 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 > mFrameCount)) { + if ((mSharedBuffer != 0) && (loopEnd > cblk->frameCount)) { LOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d", - loopStart, loopEnd, mFrameCount); + loopStart, loopEnd, cblk->frameCount); return BAD_VALUE; } @@ -566,7 +532,7 @@ status_t AudioTrack::setPosition(uint32_t position) if (position > mCblk->user) return BAD_VALUE; mCblk->server = position; - mCblk->forceReady = 1; + mCblk->flags |= CBLK_FORCEREADY_ON; return NO_ERROR; } @@ -586,7 +552,7 @@ status_t AudioTrack::reload() flush(); - mCblk->stepUser(mFrameCount); + mCblk->stepUser(mCblk->frameCount); return NO_ERROR; } @@ -607,7 +573,8 @@ status_t AudioTrack::createTrack( int frameCount, uint32_t flags, const sp<IMemory>& sharedBuffer, - audio_io_handle_t output) + audio_io_handle_t output, + bool enforceFrameCount) { status_t status; const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); @@ -616,6 +583,61 @@ status_t AudioTrack::createTrack( return NO_INIT; } + int afSampleRate; + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { + return NO_INIT; + } + int afFrameCount; + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { + return NO_INIT; + } + uint32_t afLatency; + if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { + return NO_INIT; + } + + mNotificationFramesAct = mNotificationFramesReq; + if (!AudioSystem::isLinearPCM(format)) { + if (sharedBuffer != 0) { + frameCount = sharedBuffer->size(); + } + } else { + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); + if (minBufCount < 2) minBufCount = 2; + + int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate; + + if (sharedBuffer == 0) { + if (frameCount == 0) { + frameCount = minFrameCount; + } + if (mNotificationFramesAct == 0) { + mNotificationFramesAct = frameCount/2; + } + // Make sure that application is notified with sufficient margin + // before underrun + if (mNotificationFramesAct > (uint32_t)frameCount/2) { + mNotificationFramesAct = frameCount/2; + } + if (frameCount < minFrameCount) { + if (enforceFrameCount) { + LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount); + return BAD_VALUE; + } else { + frameCount = minFrameCount; + } + } + } else { + // Ensure that buffer alignment matches channelcount + if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) { + LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount); + return BAD_VALUE; + } + frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t); + } + } + sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), streamType, sampleRate, @@ -641,20 +663,20 @@ status_t AudioTrack::createTrack( mCblkMemory.clear(); mCblkMemory = cblk; mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer()); - mCblk->out = 1; - // Update buffer size in case it has been limited by AudioFlinger during track creation - mFrameCount = mCblk->frameCount; + mCblk->flags |= CBLK_DIRECTION_OUT; if (sharedBuffer == 0) { mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); } else { mCblk->buffers = sharedBuffer->pointer(); // Force buffer full condition as data is already present in shared memory - mCblk->stepUser(mFrameCount); + mCblk->stepUser(mCblk->frameCount); } mCblk->volumeLR = (int32_t(int16_t(mVolume[LEFT] * 0x1000)) << 16) | int16_t(mVolume[RIGHT] * 0x1000); mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; mCblk->waitTimeMs = 0; + mRemainingFrames = mNotificationFramesAct; + mLatency = afLatency + (1000*mCblk->frameCount) / sampleRate; return NO_ERROR; } @@ -685,8 +707,15 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) cblk->lock.unlock(); return WOULD_BLOCK; } - - result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); + if (!(cblk->flags & CBLK_INVALID_MSK)) { + result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); + } + if (cblk->flags & CBLK_INVALID_MSK) { + LOGW("obtainBuffer() track %p invalidated, creating a new one", this); + // no need to clear the invalid flag as this cblk will not be used anymore + cblk->lock.unlock(); + goto create_new_track; + } if (__builtin_expect(result!=NO_ERROR, false)) { cblk->waitTimeMs += waitTimeMs; if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { @@ -700,8 +729,9 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) result = mAudioTrack->start(); if (result == DEAD_OBJECT) { LOGW("obtainBuffer() dead IAudioTrack: creating a new one"); +create_new_track: result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount, - mFrameCount, mFlags, mSharedBuffer, getOutput()); + mFrameCount, mFlags, mSharedBuffer, getOutput(), false); if (result == NO_ERROR) { cblk = mCblk; cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; @@ -826,13 +856,13 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) // Manage underrun callback if (mActive && (mCblk->framesReady() == 0)) { - LOGV("Underrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag); - if (mCblk->flowControlFlag == 0) { + LOGV("Underrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags); + if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { mCbf(EVENT_UNDERRUN, mUserData, 0); if (mCblk->server == mCblk->frameCount) { mCbf(EVENT_BUFFER_END, mUserData, 0); } - mCblk->flowControlFlag = 1; + mCblk->flags |= CBLK_UNDERRUN_ON; if (mSharedBuffer != 0) return false; } } @@ -932,7 +962,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) while (frames); if (frames == 0) { - mRemainingFrames = mNotificationFrames; + mRemainingFrames = mNotificationFramesAct; } else { mRemainingFrames = frames; } @@ -949,7 +979,7 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const result.append(" AudioTrack::dump\n"); snprintf(buffer, 255, " stream type(%d), left - right volume(%f, %f)\n", mStreamType, mVolume[0], mVolume[1]); result.append(buffer); - snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mFrameCount); + snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%d)\n", mFormat, mChannelCount, mCblk->frameCount); result.append(buffer); snprintf(buffer, 255, " sample rate(%d), status(%d), muted(%d)\n", (mCblk == 0) ? 0 : mCblk->sampleRate, mStatus, mMuted); result.append(buffer); @@ -986,7 +1016,7 @@ audio_track_cblk_t::audio_track_cblk_t() : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0), loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), - flowControlFlag(1), forceReady(0) + flags(0) { } @@ -996,7 +1026,7 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) u += frameCount; // Ensure that user is never ahead of server for AudioRecord - if (out) { + if (flags & CBLK_DIRECTION_MSK) { // If stepServer() has been called once, switch to normal obtainBuffer() timeout period if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; @@ -1013,7 +1043,7 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) this->user = u; // Clear flow control error condition as new data has been written/read to/from buffer. - flowControlFlag = 0; + flags &= ~CBLK_UNDERRUN_MSK; return u; } @@ -1038,7 +1068,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount) uint32_t s = this->server; s += frameCount; - if (out) { + if (flags & CBLK_DIRECTION_MSK) { // Mark that we have read the first buffer so that next time stepUser() is called // we switch to normal obtainBuffer() timeout period if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) { @@ -1089,7 +1119,7 @@ uint32_t audio_track_cblk_t::framesAvailable_l() uint32_t u = this->user; uint32_t s = this->server; - if (out) { + if (flags & CBLK_DIRECTION_MSK) { uint32_t limit = (s < loopStart) ? s : loopStart; return limit + frameCount - u; } else { @@ -1102,7 +1132,7 @@ uint32_t audio_track_cblk_t::framesReady() uint32_t u = this->user; uint32_t s = this->server; - if (out) { + if (flags & CBLK_DIRECTION_MSK) { if (u < loopEnd) { return u - s; } else { |