diff options
-rw-r--r-- | include/media/AudioRecord.h | 2 | ||||
-rw-r--r-- | include/media/AudioTrack.h | 2 | ||||
-rw-r--r-- | include/media/ToneGenerator.h | 2 | ||||
-rw-r--r-- | include/private/media/AudioTrackShared.h | 9 | ||||
-rw-r--r-- | media/libmedia/AudioRecord.cpp | 46 | ||||
-rw-r--r-- | media/libmedia/AudioTrack.cpp | 82 |
6 files changed, 89 insertions, 54 deletions
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 71744be..dd585c9 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -275,7 +275,7 @@ public: STOPPED = 1 }; - status_t obtainBuffer(Buffer* audioBuffer, bool blocking); + status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount); void releaseBuffer(Buffer* audioBuffer); diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index f382451..fd62daa 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -358,7 +358,7 @@ public: STOPPED = 1 }; - status_t obtainBuffer(Buffer* audioBuffer, bool blocking); + status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount); void releaseBuffer(Buffer* audioBuffer); diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index 0ddfb8e..0cfdeec 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -134,7 +134,7 @@ private: Condition mWaitCbkCond; // condition enabling interface to wait for audio callback completion after a change is requested float mVolume; // Volume applied to audio track int mStreamType; // Audio stream used for output - int mProcessSize; // Size of audio blocks generated at a time by audioCallback() (in PCM frames). + unsigned int mProcessSize; // Size of audio blocks generated at a time by audioCallback() (in PCM frames). bool initAudioTrack(); static void audioCallback(int event, void* user, void *info); diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h index 72ed281..1991aa7 100644 --- a/include/private/media/AudioTrackShared.h +++ b/include/private/media/AudioTrackShared.h @@ -28,6 +28,11 @@ namespace android { #define MAX_SAMPLE_RATE 65535 #define THREAD_PRIORITY_AUDIO_CLIENT (ANDROID_PRIORITY_AUDIO) +// Maximum cumulated timeout milliseconds before restarting audioflinger thread +#define MAX_STARTUP_TIMEOUT_MS 3000 // Longer timeout period at startup to cope with A2DP init time +#define MAX_RUN_TIMEOUT_MS 1000 +#define WAIT_PERIOD_MS 10 + struct audio_track_cblk_t { @@ -55,9 +60,11 @@ struct audio_track_cblk_t int16_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 bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger + uint16_t waitTimeMs; // Cumulated wait time // Padding ensuring that data buffer starts on a cache line boundary (32 bytes). // See AudioFlinger::TrackBase constructor - int32_t Padding[4]; + int32_t Padding[3]; audio_track_cblk_t(); uint32_t stepUser(uint32_t frameCount); diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index bbb9548..3d39181 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -245,6 +245,8 @@ status_t AudioRecord::start() if (android_atomic_or(1, &mActive) == 0) { mNewPosition = mCblk->user + mUpdatePeriod; + mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + mCblk->waitTimeMs = 0; if (t != 0) { t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { @@ -342,7 +344,7 @@ status_t AudioRecord::getPosition(uint32_t *position) // ------------------------------------------------------------------------- -status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) +status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { int active; int timeout = 0; @@ -362,14 +364,21 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) active = mActive; if (UNLIKELY(!active)) return NO_MORE_BUFFERS; - if (UNLIKELY(!blocking)) + if (UNLIKELY(!waitCount)) return WOULD_BLOCK; timeout = 0; - result = cblk->cv.waitRelative(cblk->lock, seconds(1)); + result = cblk->cv.waitRelative(cblk->lock, milliseconds(WAIT_PERIOD_MS)); if (__builtin_expect(result!=NO_ERROR, false)) { - LOGW( "obtainBuffer timed out (is the CPU pegged?) " - "user=%08x, server=%08x", cblk->user, cblk->server); - timeout = 1; + cblk->waitTimeMs += WAIT_PERIOD_MS; + if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", cblk->user, cblk->server); + timeout = 1; + cblk->waitTimeMs = 0; + } + if (--waitCount == 0) { + return TIMED_OUT; + } } // read the server count again start_loop_here: @@ -382,6 +391,8 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, bool blocking) "but didn't need to be locked. We recovered, but " "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); + cblk->waitTimeMs = 0; + if (framesReq > framesReady) { framesReq = framesReady; } @@ -430,7 +441,9 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t); - status_t err = obtainBuffer(&audioBuffer, true); + // Calling obtainBuffer() with a negative wait count causes + // an (almost) infinite wait time. + status_t err = obtainBuffer(&audioBuffer, -1); if (err < 0) { // out of buffers, return #bytes written if (err == status_t(NO_MORE_BUFFERS)) @@ -457,7 +470,7 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) { Buffer audioBuffer; uint32_t frames = mRemainingFrames; - size_t readSize = 0; + size_t readSize; // Manage marker callback if (mMarkerPosition > 0) { @@ -477,17 +490,19 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) do { audioBuffer.frameCount = frames; - status_t err = obtainBuffer(&audioBuffer, false); + // Calling obtainBuffer() with a wait count of 1 + // limits wait time to WAIT_PERIOD_MS. This prevents from being + // stuck here not being able to handle timed events (position, markers). + status_t err = obtainBuffer(&audioBuffer, 1); if (err < NO_ERROR) { - if (err != WOULD_BLOCK) { + if (err != TIMED_OUT) { LOGE("Error obtaining an audio buffer, giving up."); return false; } + break; } if (err == status_t(STOPPED)) return false; - if (audioBuffer.size == 0) break; - size_t reqSize = audioBuffer.size; mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); readSize = audioBuffer.size; @@ -514,13 +529,6 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) } } - // If no data was read, it is likely that obtainBuffer() did - // not find available data in PCM buffer: we release the processor for - // a few millisecond before polling again for available data. - if (readSize == 0) { - usleep(5000); - } - if (frames == 0) { mRemainingFrames = mNotificationFrames; } else { diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index ce65312..f9f8568 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -330,6 +330,8 @@ void AudioTrack::start() if (android_atomic_or(1, &mActive) == 0) { mNewPosition = mCblk->server + mUpdatePeriod; + mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; + mCblk->waitTimeMs = 0; if (t != 0) { t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); } else { @@ -572,7 +574,7 @@ status_t AudioTrack::reload() // ------------------------------------------------------------------------- -status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) +status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { int active; int timeout = 0; @@ -594,15 +596,23 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) LOGV("Not active and NO_MORE_BUFFERS"); return NO_MORE_BUFFERS; } - if (UNLIKELY(!blocking)) + if (UNLIKELY(!waitCount)) return WOULD_BLOCK; timeout = 0; - result = cblk->cv.waitRelative(cblk->lock, seconds(1)); - if (__builtin_expect(result!=NO_ERROR, false)) { - LOGW( "obtainBuffer timed out (is the CPU pegged?) " - "user=%08x, server=%08x", cblk->user, cblk->server); - mAudioTrack->start(); // FIXME: Wake up audioflinger - timeout = 1; + result = cblk->cv.waitRelative(cblk->lock, milliseconds(WAIT_PERIOD_MS)); + if (__builtin_expect(result!=NO_ERROR, false)) { + cblk->waitTimeMs += WAIT_PERIOD_MS; + if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { + LOGW( "obtainBuffer timed out (is the CPU pegged?) " + "user=%08x, server=%08x", cblk->user, cblk->server); + mAudioTrack->start(); // FIXME: Wake up audioflinger + timeout = 1; + cblk->waitTimeMs = 0; + } + ; + if (--waitCount == 0) { + return TIMED_OUT; + } } // read the server count again start_loop_here: @@ -610,6 +620,8 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, bool blocking) } } + cblk->waitTimeMs = 0; + if (framesReq > framesAvail) { framesReq = framesAvail; } @@ -667,8 +679,9 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) if (mFormat == AudioSystem::PCM_16_BIT) { audioBuffer.frameCount >>= 1; } - - status_t err = obtainBuffer(&audioBuffer, true); + // Calling obtainBuffer() with a negative wait count causes + // an (almost) infinite wait time. + status_t err = obtainBuffer(&audioBuffer, -1); if (err < 0) { // out of buffers, return #bytes written if (err == status_t(NO_MORE_BUFFERS)) @@ -706,7 +719,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) { Buffer audioBuffer; uint32_t frames; - size_t writtenSize = 0; + size_t writtenSize; // Manage underrun callback if (mActive && (mCblk->framesReady() == 0)) { @@ -756,18 +769,20 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) do { audioBuffer.frameCount = frames; - - status_t err = obtainBuffer(&audioBuffer, false); + + // Calling obtainBuffer() with a wait count of 1 + // limits wait time to WAIT_PERIOD_MS. This prevents from being + // stuck here not being able to handle timed events (position, markers, loops). + status_t err = obtainBuffer(&audioBuffer, 1); if (err < NO_ERROR) { - if (err != WOULD_BLOCK) { + if (err != TIMED_OUT) { LOGE("Error obtaining an audio buffer, giving up."); return false; } + break; } if (err == status_t(STOPPED)) return false; - if (audioBuffer.size == 0) break; - // Divide buffer size by 2 to take into account the expansion // due to 8 to 16 bit conversion: the callback must fill only half // of the destination buffer @@ -802,13 +817,6 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) } while (frames); - // If no data was written, it is likely that obtainBuffer() did - // not find room in PCM buffer: we release the processor for - // a few millisecond before polling again for available room. - if (writtenSize == 0) { - usleep(5000); - } - if (frames == 0) { mRemainingFrames = mNotificationFrames; } else { @@ -872,7 +880,12 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) u += frameCount; // Ensure that user is never ahead of server for AudioRecord - if (!out && u > this->server) { + if (out) { + // If stepServer() has been called once, switch to normal obtainBuffer() timeout period + if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) { + bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + } + } else if (u > this->server) { LOGW("stepServer occured after track reset"); u = this->server; } @@ -909,13 +922,20 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount) uint32_t s = this->server; s += frameCount; - // It is possible that we receive a flush() - // while the mixer is processing a block: in this case, - // stepServer() is called After the flush() has reset u & s and - // we have s > u - if (out && s > this->user) { - LOGW("stepServer occured after track reset"); - s = this->user; + if (out) { + // 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) { + bufferTimeoutMs = MAX_RUN_TIMEOUT_MS - 1; + } + // It is possible that we receive a flush() + // while the mixer is processing a block: in this case, + // stepServer() is called After the flush() has reset u & s and + // we have s > u + if (s > this->user) { + LOGW("stepServer occured after track reset"); + s = this->user; + } } if (s >= loopEnd) { |