From bda7469d9b1ec6d9c9d6da40ddf64dc39ff271a9 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 4 Nov 2009 08:27:26 -0800 Subject: [PATCH] Fix issue 2203561: Sholes: audio playing out of earpiece. Create a new IAudioTrack interface to AudioFlinger when start() fails due to a broken pipe error. Do the same if start fails due to the same error after time out in obtainBuffer(). Do not indicate that the AudioTrack is started to AudioPolicyManager if IAudioTrack start fails. This avoids that an AudioTrack keeps a dead IAudioTrack after a media server crash. Same modifications for AudioRecord. Add a flag to ToneGenerator indicating that the callback thread can call Java. Without it, when the media server crashes and restarts, the AudioSystem error callback will crash in JNI if the IAudiotrack is created from AudioTrack callback thread. --- core/jni/android_media_ToneGenerator.cpp | 2 +- include/media/AudioRecord.h | 6 + include/media/AudioTrack.h | 8 ++ include/media/ToneGenerator.h | 3 +- media/libmedia/AudioRecord.cpp | 129 ++++++++++++------ media/libmedia/AudioTrack.cpp | 165 ++++++++++++++--------- media/libmedia/IAudioRecord.cpp | 13 +- media/libmedia/IAudioTrack.cpp | 13 +- media/libmedia/ToneGenerator.cpp | 19 ++- 9 files changed, 244 insertions(+), 114 deletions(-) diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp index 07bb1abafd325..50aa967df38e1 100644 --- a/core/jni/android_media_ToneGenerator.cpp +++ b/core/jni/android_media_ToneGenerator.cpp @@ -79,7 +79,7 @@ static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) { static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz, jint streamType, jint volume) { - ToneGenerator *lpToneGen = new ToneGenerator(streamType, AudioSystem::linearToLog(volume)); + ToneGenerator *lpToneGen = new ToneGenerator(streamType, AudioSystem::linearToLog(volume), true); env->SetIntField(thiz, fields.context, 0); diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 503cb315246f3..008468c91b092 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -314,6 +314,11 @@ private: }; bool processAudioBuffer(const sp& thread); + status_t openRecord(uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags); sp mAudioRecord; sp mCblkMemory; @@ -341,6 +346,7 @@ private: uint32_t mNewPosition; uint32_t mUpdatePeriod; audio_io_handle_t mInput; + uint32_t mFlags; }; }; // namespace android diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 981c2f6b696ca..14b30ae9cba40 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -391,6 +391,14 @@ private: }; bool processAudioBuffer(const sp& thread); + status_t createTrack(int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp& sharedBuffer, + audio_io_handle_t output); sp mAudioTrack; sp mCblkMemory; diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index c884c2c5b16a4..1ad1f26e108aa 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -151,7 +151,7 @@ public: NUM_SUP_TONES = LAST_SUP_TONE-FIRST_SUP_TONE+1 }; - ToneGenerator(int streamType, float volume); + ToneGenerator(int streamType, float volume, bool threadCanCallJava = false); ~ToneGenerator(); bool startTone(int toneType, int durationMs = -1); @@ -242,6 +242,7 @@ private: static const ToneDescriptor sToneDescriptors[]; + bool mThreadCanCallJava; unsigned int mTotalSmp; // Total number of audio samples played (gives current time) unsigned int mNextSegSmp; // Position of next segment transition expressed in samples // NOTE: because mTotalSmp, mNextSegSmp are stored on 32 bit, current design will operate properly diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 5e355644a65ae..e63c0d2dd2811 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -101,11 +101,6 @@ status_t AudioRecord::set( return INVALID_OPERATION; } - const sp& audioFlinger = AudioSystem::get_audio_flinger(); - if (audioFlinger == 0) { - return NO_INIT; - } - if (inputSource == AUDIO_SOURCE_DEFAULT) { inputSource = AUDIO_SOURCE_MIC; } @@ -171,22 +166,14 @@ status_t AudioRecord::set( notificationFrames = frameCount/2; } - // open record channel - status_t status; - sp record = audioFlinger->openRecord(getpid(), mInput, - sampleRate, format, - channelCount, - frameCount, - ((uint16_t)flags) << 16, - &status); - if (record == 0) { - LOGE("AudioFlinger could not create record track, status: %d", status); + // create the IAudioRecord + status_t status = openRecord(sampleRate, format, channelCount, + frameCount, flags); + + if (status != NO_ERROR) { return status; } - sp cblk = record->getCblk(); - if (cblk == 0) { - return NO_INIT; - } + if (cbf != 0) { mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava); if (mClientRecordThread == 0) { @@ -196,11 +183,6 @@ status_t AudioRecord::set( mStatus = NO_ERROR; - mAudioRecord = record; - mCblkMemory = cblk; - mCblk = static_cast(cblk->pointer()); - mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); - mCblk->out = 0; mFormat = format; // Update buffer size in case it has been limited by AudioFlinger during track creation mFrameCount = mCblk->frameCount; @@ -217,6 +199,7 @@ status_t AudioRecord::set( mNewPosition = 0; mUpdatePeriod = 0; mInputSource = (uint8_t)inputSource; + mFlags = flags; return NO_ERROR; } @@ -284,15 +267,26 @@ status_t AudioRecord::start() if (android_atomic_or(1, &mActive) == 0) { ret = AudioSystem::startInput(mInput); if (ret == NO_ERROR) { - mNewPosition = mCblk->user + mUpdatePeriod; - mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; - mCblk->waitTimeMs = 0; - if (t != 0) { - t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); - } else { - setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); - } ret = mAudioRecord->start(); + if (ret == DEAD_OBJECT) { + LOGV("start() dead IAudioRecord: creating a new one"); + ret = openRecord(mCblk->sampleRate, mFormat, mChannelCount, + mFrameCount, mFlags); + } + if (ret == NO_ERROR) { + mNewPosition = mCblk->user + mUpdatePeriod; + mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + mCblk->waitTimeMs = 0; + if (t != 0) { + t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT); + } else { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + } + } else { + LOGV("start() failed"); + AudioSystem::stopInput(mInput); + android_atomic_and(~1, &mActive); + } } } @@ -396,10 +390,48 @@ status_t AudioRecord::getPosition(uint32_t *position) // ------------------------------------------------------------------------- +status_t AudioRecord::openRecord( + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags) +{ + status_t status; + const sp& audioFlinger = AudioSystem::get_audio_flinger(); + if (audioFlinger == 0) { + return NO_INIT; + } + + sp record = audioFlinger->openRecord(getpid(), mInput, + sampleRate, format, + channelCount, + frameCount, + ((uint16_t)flags) << 16, + &status); + if (record == 0) { + LOGE("AudioFlinger could not create record track, status: %d", status); + return status; + } + sp cblk = record->getCblk(); + if (cblk == 0) { + LOGE("Could not get control block"); + return NO_INIT; + } + mAudioRecord.clear(); + mAudioRecord = record; + mCblkMemory.clear(); + mCblkMemory = cblk; + mCblk = static_cast(cblk->pointer()); + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->out = 0; + + return NO_ERROR; +} + status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { int active; - int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; uint32_t framesReq = audioBuffer->frameCount; @@ -411,25 +443,40 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) uint32_t framesReady = cblk->framesReady(); if (framesReady == 0) { - Mutex::Autolock _l(cblk->lock); + cblk->lock.lock(); goto start_loop_here; while (framesReady == 0) { active = mActive; - if (UNLIKELY(!active)) + if (UNLIKELY(!active)) { + cblk->lock.unlock(); return NO_MORE_BUFFERS; - if (UNLIKELY(!waitCount)) + } + if (UNLIKELY(!waitCount)) { + cblk->lock.unlock(); return WOULD_BLOCK; - timeout = 0; + } result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); if (__builtin_expect(result!=NO_ERROR, false)) { cblk->waitTimeMs += waitTimeMs; if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) { LOGW( "obtainBuffer timed out (is the CPU pegged?) " "user=%08x, server=%08x", cblk->user, cblk->server); - timeout = 1; + cblk->lock.unlock(); + result = mAudioRecord->start(); + if (result == DEAD_OBJECT) { + LOGW("obtainBuffer() dead IAudioRecord: creating a new one"); + result = openRecord(cblk->sampleRate, mFormat, mChannelCount, + mFrameCount, mFlags); + if (result == NO_ERROR) { + cblk = mCblk; + cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + } + } + cblk->lock.lock(); cblk->waitTimeMs = 0; } if (--waitCount == 0) { + cblk->lock.unlock(); return TIMED_OUT; } } @@ -437,13 +484,9 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) start_loop_here: framesReady = cblk->framesReady(); } + cblk->lock.unlock(); } - LOGW_IF(timeout, - "*** SERIOUS WARNING *** obtainBuffer() timed out " - "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) { diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 4b9d272cf60aa..8529a8e46fa1c 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -122,11 +122,6 @@ status_t AudioTrack::set( return INVALID_OPERATION; } - const sp& audioFlinger = AudioSystem::get_audio_flinger(); - if (audioFlinger == 0) { - LOGE("Could not get audioflinger"); - return NO_INIT; - } int afSampleRate; if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { return NO_INIT; @@ -217,28 +212,16 @@ status_t AudioTrack::set( } } - // create the track - status_t status; - sp track = audioFlinger->createTrack(getpid(), - streamType, - sampleRate, - format, - channelCount, - frameCount, - ((uint16_t)flags) << 16, - sharedBuffer, - output, - &status); + mVolume[LEFT] = 1.0f; + mVolume[RIGHT] = 1.0f; + // create the IAudioTrack + status_t status = createTrack(streamType, sampleRate, format, channelCount, + frameCount, flags, sharedBuffer, output); - if (track == 0) { - LOGE("AudioFlinger could not create track, status: %d", status); + if (status != NO_ERROR) { return status; } - sp cblk = track->getCblk(); - if (cblk == 0) { - LOGE("Could not get control block"); - return NO_INIT; - } + if (cbf != 0) { mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); if (mAudioTrackThread == 0) { @@ -249,22 +232,6 @@ status_t AudioTrack::set( mStatus = NO_ERROR; - mAudioTrack = track; - mCblkMemory = cblk; - mCblk = static_cast(cblk->pointer()); - mCblk->out = 1; - // Update buffer size in case it has been limited by AudioFlinger during track creation - mFrameCount = mCblk->frameCount; - 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->volume[0] = mCblk->volume[1] = 0x1000; - mVolume[LEFT] = 1.0f; - mVolume[RIGHT] = 1.0f; mStreamType = streamType; mFormat = format; mChannels = channels; @@ -351,16 +318,27 @@ void AudioTrack::start() } if (android_atomic_or(1, &mActive) == 0) { - AudioSystem::startOutput(getOutput(), (AudioSystem::stream_type)mStreamType); - mNewPosition = mCblk->server + mUpdatePeriod; - mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; - mCblk->waitTimeMs = 0; - if (t != 0) { - t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); - } else { - setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + audio_io_handle_t output = AudioTrack::getOutput(); + status_t 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, output); + } + if (status == NO_ERROR) { + AudioSystem::startOutput(output, (AudioSystem::stream_type)mStreamType); + mNewPosition = mCblk->server + mUpdatePeriod; + mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; + mCblk->waitTimeMs = 0; + if (t != 0) { + t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT); + } else { + setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); + } + } else { + LOGV("start() failed"); + android_atomic_and(~1, &mActive); } - mAudioTrack->start(); } if (t != 0) { @@ -617,10 +595,67 @@ audio_io_handle_t AudioTrack::getOutput() // ------------------------------------------------------------------------- +status_t AudioTrack::createTrack( + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp& sharedBuffer, + audio_io_handle_t output) +{ + status_t status; + const sp& audioFlinger = AudioSystem::get_audio_flinger(); + if (audioFlinger == 0) { + LOGE("Could not get audioflinger"); + return NO_INIT; + } + + sp track = audioFlinger->createTrack(getpid(), + streamType, + sampleRate, + format, + channelCount, + frameCount, + ((uint16_t)flags) << 16, + sharedBuffer, + output, + &status); + + if (track == 0) { + LOGE("AudioFlinger could not create track, status: %d", status); + return status; + } + sp cblk = track->getCblk(); + if (cblk == 0) { + LOGE("Could not get control block"); + return NO_INIT; + } + mAudioTrack.clear(); + mAudioTrack = track; + mCblkMemory.clear(); + mCblkMemory = cblk; + mCblk = static_cast(cblk->pointer()); + mCblk->out = 1; + // Update buffer size in case it has been limited by AudioFlinger during track creation + mFrameCount = mCblk->frameCount; + 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->volumeLR = (int32_t(int16_t(mVolume[LEFT] * 0x1000)) << 16) | int16_t(mVolume[RIGHT] * 0x1000); + + return NO_ERROR; +} + status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { int active; - int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; uint32_t framesReq = audioBuffer->frameCount; @@ -632,17 +667,20 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) uint32_t framesAvail = cblk->framesAvailable(); if (framesAvail == 0) { - Mutex::Autolock _l(cblk->lock); + cblk->lock.lock(); goto start_loop_here; while (framesAvail == 0) { active = mActive; if (UNLIKELY(!active)) { LOGV("Not active and NO_MORE_BUFFERS"); + cblk->lock.unlock(); return NO_MORE_BUFFERS; } - if (UNLIKELY(!waitCount)) + if (UNLIKELY(!waitCount)) { + cblk->lock.unlock(); return WOULD_BLOCK; - timeout = 0; + } + result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); if (__builtin_expect(result!=NO_ERROR, false)) { cblk->waitTimeMs += waitTimeMs; @@ -654,14 +692,23 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) "user=%08x, server=%08x", this, cblk->user, cblk->server); //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140) cblk->lock.unlock(); - mAudioTrack->start(); + result = mAudioTrack->start(); + if (result == DEAD_OBJECT) { + LOGW("obtainBuffer() dead IAudioTrack: creating a new one"); + result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount, + mFrameCount, mFlags, mSharedBuffer, getOutput()); + if (result == NO_ERROR) { + cblk = mCblk; + cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; + } + } cblk->lock.lock(); - timeout = 1; } cblk->waitTimeMs = 0; } if (--waitCount == 0) { + cblk->lock.unlock(); return TIMED_OUT; } } @@ -669,6 +716,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) start_loop_here: framesAvail = cblk->framesAvailable_l(); } + cblk->lock.unlock(); } cblk->waitTimeMs = 0; @@ -684,11 +732,6 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) framesReq = bufferEnd - u; } - LOGW_IF(timeout, - "*** SERIOUS WARNING *** obtainBuffer() timed out " - "but didn't need to be locked. We recovered, but " - "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server); - audioBuffer->flags = mMuted ? Buffer::MUTE : 0; audioBuffer->channelCount = mChannelCount; audioBuffer->frameCount = framesReq; @@ -991,7 +1034,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount) // 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; + bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1; } // It is possible that we receive a flush() // while the mixer is processing a block: in this case, diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp index dacf75aae8739..ba0d55b843d43 100644 --- a/media/libmedia/IAudioRecord.cpp +++ b/media/libmedia/IAudioRecord.cpp @@ -15,6 +15,10 @@ ** limitations under the License. */ +#define LOG_TAG "IAudioRecord" +//#define LOG_NDEBUG 0 +#include + #include #include @@ -42,8 +46,13 @@ public: { Parcel data, reply; data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor()); - remote()->transact(START, data, &reply); - return reply.readInt32(); + status_t status = remote()->transact(START, data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + } else { + LOGW("start() error: %s", strerror(-status)); + } + return status; } virtual void stop() diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index 7f43347a86cb1..01ffd75e56c5b 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -15,6 +15,10 @@ ** limitations under the License. */ +#define LOG_TAG "IAudioTrack" +//#define LOG_NDEBUG 0 +#include + #include #include @@ -45,8 +49,13 @@ public: { Parcel data, reply; data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); - remote()->transact(START, data, &reply); - return reply.readInt32(); + status_t status = remote()->transact(START, data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + } else { + LOGW("start() error: %s", strerror(-status)); + } + return status; } virtual void stop() diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 91d0d00953382..60e3d71171a6c 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -798,7 +798,7 @@ const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONE // none // //////////////////////////////////////////////////////////////////////////////// -ToneGenerator::ToneGenerator(int streamType, float volume) { +ToneGenerator::ToneGenerator(int streamType, float volume, bool threadCanCallJava) { LOGV("ToneGenerator constructor: streamType=%d, volume=%f\n", streamType, volume); @@ -808,6 +808,7 @@ ToneGenerator::ToneGenerator(int streamType, float volume) { LOGE("Unable to marshal AudioFlinger"); return; } + mThreadCanCallJava = threadCanCallJava; mStreamType = streamType; mVolume = volume; mpAudioTrack = 0; @@ -1015,15 +1016,25 @@ bool ToneGenerator::initAudioTrack() { } // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size - mpAudioTrack - = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0); - + mpAudioTrack = new AudioTrack(); if (mpAudioTrack == 0) { LOGE("AudioTrack allocation failed"); goto initAudioTrack_exit; } LOGV("Create Track: %p\n", mpAudioTrack); + mpAudioTrack->set(mStreamType, + 0, + AudioSystem::PCM_16_BIT, + AudioSystem::CHANNEL_OUT_MONO, + 0, + 0, + audioCallback, + this, + 0, + 0, + mThreadCanCallJava); + if (mpAudioTrack->initCheck() != NO_ERROR) { LOGE("AudioTrack->initCheck failed"); goto initAudioTrack_exit;