diff options
-rw-r--r-- | media/libmediaplayerservice/nuplayer/GenericSource.cpp | 6 | ||||
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 70 | ||||
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayer.h | 5 | ||||
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp | 62 | ||||
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h | 15 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 78 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 20 | ||||
-rw-r--r-- | services/audioflinger/Effects.cpp | 28 | ||||
-rw-r--r-- | services/audioflinger/Effects.h | 3 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 20 | ||||
-rw-r--r-- | services/audioflinger/Threads.h | 3 | ||||
-rw-r--r-- | services/audioflinger/TrackBase.h | 1 | ||||
-rw-r--r-- | services/audioflinger/Tracks.cpp | 26 | ||||
-rw-r--r-- | services/camera/libcameraservice/device3/Camera3Device.cpp | 4 |
14 files changed, 237 insertions, 104 deletions
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index d8ed836..511871d 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -1176,12 +1176,14 @@ void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) { void NuPlayer::GenericSource::readBuffer( media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) { Track *track; + size_t maxBuffers = 1; switch (trackType) { case MEDIA_TRACK_TYPE_VIDEO: track = &mVideoTrack; break; case MEDIA_TRACK_TYPE_AUDIO: track = &mAudioTrack; + maxBuffers = 64; break; case MEDIA_TRACK_TYPE_SUBTITLE: track = &mSubtitleTrack; @@ -1214,7 +1216,7 @@ void NuPlayer::GenericSource::readBuffer( options.setNonBlocking(); } - for (;;) { + for (size_t numBuffers = 0; numBuffers < maxBuffers; ) { MediaBuffer *mbuf; status_t err = track->mSource->read(&mbuf, &options); @@ -1245,7 +1247,7 @@ void NuPlayer::GenericSource::readBuffer( sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs); track->mPackets->queueAccessUnit(buffer); - break; + ++numBuffers; } else if (err == WOULD_BLOCK) { break; } else if (err == INFO_FORMAT_CHANGED) { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index df3e992..9020a8d 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -50,6 +50,10 @@ namespace android { +// TODO optimize buffer size for power consumption +// The offload read buffer size is 32 KB but 24 KB uses less power. +const size_t NuPlayer::kAggregateBufferSizeBytes = 24 * 1024; + struct NuPlayer::Action : public RefBase { Action() {} @@ -730,7 +734,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { if (err == -EWOULDBLOCK) { if (mSource->feedMoreTSData() == OK) { - msg->post(10000ll); + msg->post(10 * 1000ll); } } } else if (what == Decoder::kWhatEOS) { @@ -995,6 +999,7 @@ void NuPlayer::finishFlushIfPossible() { ALOGV("both audio and video are flushed now."); mPendingAudioAccessUnit.clear(); + mAggregateBuffer.clear(); if (mTimeDiscontinuityPending) { mRenderer->signalTimeDiscontinuity(); @@ -1256,14 +1261,8 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { // Aggregate smaller buffers into a larger buffer. // The goal is to reduce power consumption. // Unfortunately this does not work with the software AAC decoder. - // TODO optimize buffer size for power consumption - // The offload read buffer size is 32 KB but 24 KB uses less power. - const int kAudioBigBufferSizeBytes = 24 * 1024; - bool doBufferAggregation = (audio && mOffloadAudio); - sp<ABuffer> biggerBuffer; + bool doBufferAggregation = (audio && mOffloadAudio);; bool needMoreData = false; - int numSmallBuffers = 0; - bool gotTime = false; bool dropAccessUnit; do { @@ -1279,14 +1278,10 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { } if (err == -EWOULDBLOCK) { - if (biggerBuffer == NULL) { - return err; - } else { - break; // Reply with data that we already have. - } + return err; } else if (err != OK) { if (err == INFO_DISCONTINUITY) { - if (biggerBuffer != NULL) { + if (mAggregateBuffer != NULL) { // We already have some data so save this for later. mPendingAudioErr = err; mPendingAudioAccessUnit = accessUnit; @@ -1401,46 +1396,45 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { size_t smallSize = accessUnit->size(); needMoreData = false; - if (doBufferAggregation && (biggerBuffer == NULL) + if (doBufferAggregation && (mAggregateBuffer == NULL) // Don't bother if only room for a few small buffers. - && (smallSize < (kAudioBigBufferSizeBytes / 3))) { + && (smallSize < (kAggregateBufferSizeBytes / 3))) { // Create a larger buffer for combining smaller buffers from the extractor. - biggerBuffer = new ABuffer(kAudioBigBufferSizeBytes); - biggerBuffer->setRange(0, 0); // start empty + mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes); + mAggregateBuffer->setRange(0, 0); // start empty } - if (biggerBuffer != NULL) { + if (mAggregateBuffer != NULL) { int64_t timeUs; + int64_t dummy; bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs); + bool bigTimestampValid = mAggregateBuffer->meta()->findInt64("timeUs", &dummy); // Will the smaller buffer fit? - size_t bigSize = biggerBuffer->size(); - size_t roomLeft = biggerBuffer->capacity() - bigSize; + size_t bigSize = mAggregateBuffer->size(); + size_t roomLeft = mAggregateBuffer->capacity() - bigSize; // Should we save this small buffer for the next big buffer? // If the first small buffer did not have a timestamp then save // any buffer that does have a timestamp until the next big buffer. if ((smallSize > roomLeft) - || (!gotTime && (numSmallBuffers > 0) && smallTimestampValid)) { + || (!bigTimestampValid && (bigSize > 0) && smallTimestampValid)) { mPendingAudioErr = err; mPendingAudioAccessUnit = accessUnit; accessUnit.clear(); } else { + // Grab time from first small buffer if available. + if ((bigSize == 0) && smallTimestampValid) { + mAggregateBuffer->meta()->setInt64("timeUs", timeUs); + } // Append small buffer to the bigger buffer. - memcpy(biggerBuffer->base() + bigSize, accessUnit->data(), smallSize); + memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize); bigSize += smallSize; - biggerBuffer->setRange(0, bigSize); + mAggregateBuffer->setRange(0, bigSize); - // Keep looping until we run out of room in the biggerBuffer. + // Keep looping until we run out of room in the mAggregateBuffer. needMoreData = true; - // Grab time from first small buffer if available. - if ((numSmallBuffers == 0) && smallTimestampValid) { - biggerBuffer->meta()->setInt64("timeUs", timeUs); - gotTime = true; - } - - ALOGV("feedDecoderInputData() #%d, smallSize = %zu, bigSize = %zu, capacity = %zu", - numSmallBuffers, smallSize, bigSize, biggerBuffer->capacity()); - numSmallBuffers++; + ALOGV("feedDecoderInputData() smallSize = %zu, bigSize = %zu, capacity = %zu", + smallSize, bigSize, mAggregateBuffer->capacity()); } } } while (dropAccessUnit || needMoreData); @@ -1459,9 +1453,11 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { mCCDecoder->decode(accessUnit); } - if (biggerBuffer != NULL) { - ALOGV("feedDecoderInputData() reply with aggregated buffer, %d", numSmallBuffers); - reply->setBuffer("buffer", biggerBuffer); + if (mAggregateBuffer != NULL) { + ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu", + mAggregateBuffer->size()); + reply->setBuffer("buffer", mAggregateBuffer); + mAggregateBuffer.clear(); } else { reply->setBuffer("buffer", accessUnit); } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 89ae11c..2e951bd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -67,6 +67,8 @@ struct NuPlayer : public AHandler { status_t getSelectedTrack(int32_t type, Parcel* reply) const; status_t selectTrack(size_t trackIndex, bool select); + static const size_t kAggregateBufferSizeBytes; + protected: virtual ~NuPlayer(); @@ -158,8 +160,11 @@ private: // notion of time has changed. bool mTimeDiscontinuityPending; + // Used by feedDecoderInputData to aggregate small buffers into + // one large buffer. sp<ABuffer> mPendingAudioAccessUnit; status_t mPendingAudioErr; + sp<ABuffer> mAggregateBuffer; FlushStatus mFlushingAudio; FlushStatus mFlushingVideo; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp index ab7906a..f7aacdd 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp @@ -30,8 +30,10 @@ namespace android { -static const int kMaxPendingBuffers = 10; -static const int kMaxCachedBytes = 200000; +static const size_t kMaxCachedBytes = 200000; +// The buffers will contain a bit less than kAggregateBufferSizeBytes. +// So we can start off with just enough buffers to keep the cache full. +static const size_t kMaxPendingBuffers = 1 + (kMaxCachedBytes / NuPlayer::kAggregateBufferSizeBytes); NuPlayer::DecoderPassThrough::DecoderPassThrough( const sp<AMessage> ¬ify) @@ -39,7 +41,8 @@ NuPlayer::DecoderPassThrough::DecoderPassThrough( mNotify(notify), mBufferGeneration(0), mReachedEOS(true), - mPendingBuffers(0), + mPendingBuffersToFill(0), + mPendingBuffersToDrain(0), mCachedBytes(0), mComponentName("pass through decoder") { mDecoderLooper = new ALooper; @@ -79,12 +82,13 @@ bool NuPlayer::DecoderPassThrough::supportsSeamlessFormatChange( void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) { ALOGV("[%s] onConfigure", mComponentName.c_str()); - mPendingBuffers = 0; mCachedBytes = 0; + mPendingBuffersToFill = 0; + mPendingBuffersToDrain = 0; mReachedEOS = false; ++mBufferGeneration; - requestABuffer(); + requestMaxBuffers(); sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatOutputFormatChanged); @@ -98,12 +102,15 @@ bool NuPlayer::DecoderPassThrough::isStaleReply(const sp<AMessage> &msg) { return generation != mBufferGeneration; } -void NuPlayer::DecoderPassThrough::requestABuffer() { - if (mCachedBytes >= kMaxCachedBytes || mReachedEOS) { - ALOGV("[%s] mReachedEOS=%d, max pending buffers(%d:%d)", - mComponentName.c_str(), (mReachedEOS ? 1 : 0), - mPendingBuffers, kMaxPendingBuffers); - return; +bool NuPlayer::DecoderPassThrough::requestABuffer() { + if (mCachedBytes >= kMaxCachedBytes) { + ALOGV("[%s] mCachedBytes = %zu", + mComponentName.c_str(), mCachedBytes); + return false; + } + if (mReachedEOS) { + ALOGV("[%s] reached EOS", mComponentName.c_str()); + return false; } sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id()); @@ -113,19 +120,16 @@ void NuPlayer::DecoderPassThrough::requestABuffer() { notify->setInt32("what", kWhatFillThisBuffer); notify->setMessage("reply", reply); notify->post(); - mPendingBuffers++; + mPendingBuffersToFill++; + ALOGV("requestABuffer: #ToFill = %zu, #ToDrain = %zu", mPendingBuffersToFill, + mPendingBuffersToDrain); - // pending buffers will already result in requestABuffer - if (mPendingBuffers < kMaxPendingBuffers) { - sp<AMessage> message = new AMessage(kWhatRequestABuffer, id()); - message->setInt32("generation", mBufferGeneration); - message->post(); - } - return; + return true; } void android::NuPlayer::DecoderPassThrough::onInputBufferFilled( const sp<AMessage> &msg) { + --mPendingBuffersToFill; if (mReachedEOS) { return; } @@ -153,11 +157,16 @@ void android::NuPlayer::DecoderPassThrough::onInputBufferFilled( notify->setBuffer("buffer", buffer); notify->setMessage("reply", reply); notify->post(); + ++mPendingBuffersToDrain; + ALOGV("onInputBufferFilled: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu", + mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes); } void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) { - mPendingBuffers--; + --mPendingBuffersToDrain; mCachedBytes -= size; + ALOGV("onBufferConsumed: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu", + mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes); requestABuffer(); } @@ -167,11 +176,20 @@ void NuPlayer::DecoderPassThrough::onFlush() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); - mPendingBuffers = 0; + mPendingBuffersToFill = 0; + mPendingBuffersToDrain = 0; mCachedBytes = 0; mReachedEOS = false; } +void NuPlayer::DecoderPassThrough::requestMaxBuffers() { + for (size_t i = 0; i < kMaxPendingBuffers; i++) { + if (!requestABuffer()) { + break; + } + } +} + void NuPlayer::DecoderPassThrough::onShutdown() { ++mBufferGeneration; @@ -229,7 +247,7 @@ void NuPlayer::DecoderPassThrough::onMessageReceived(const sp<AMessage> &msg) { case kWhatResume: { - requestABuffer(); + requestMaxBuffers(); break; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h index 8590856..fb20257 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h @@ -55,19 +55,26 @@ private: sp<AMessage> mNotify; sp<ALooper> mDecoderLooper; - void requestABuffer(); + /** Returns true if a buffer was requested. + * Returns false if at EOS or cache already full. + */ + bool requestABuffer(); bool isStaleReply(const sp<AMessage> &msg); void onConfigure(const sp<AMessage> &format); void onFlush(); void onInputBufferFilled(const sp<AMessage> &msg); void onBufferConsumed(int32_t size); + void requestMaxBuffers(); void onShutdown(); int32_t mBufferGeneration; - bool mReachedEOS; - int32_t mPendingBuffers; - int32_t mCachedBytes; + bool mReachedEOS; + // TODO mPendingBuffersToFill and mPendingBuffersToDrain are only for + // debugging. They can be removed when the power investigation is done. + size_t mPendingBuffersToFill; + size_t mPendingBuffersToDrain; + size_t mCachedBytes; AString mComponentName; DISALLOW_EVIL_CONSTRUCTORS(DecoderPassThrough); diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 1843722..e200857 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -418,6 +418,13 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) mRecordThreads.valueAt(i)->dump(fd, args); } + // dump orphan effect chains + if (mOrphanEffectChains.size() != 0) { + write(fd, " Orphan Effect Chains\n", strlen(" Orphan Effect Chains\n")); + for (size_t i = 0; i < mOrphanEffectChains.size(); i++) { + mOrphanEffectChains.valueAt(i)->dump(fd, args); + } + } // dump all hardware devs for (size_t i = 0; i < mAudioHwDevs.size(); i++) { audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice(); @@ -796,9 +803,14 @@ status_t AudioFlinger::setMicMute(bool state) } AutoMutex lock(mHardwareLock); - audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; - ret = dev->set_mic_mute(dev, state); + for (size_t i = 0; i < mAudioHwDevs.size(); i++) { + audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice(); + status_t result = dev->set_mic_mute(dev, state); + if (result != NO_ERROR) { + ret = result; + } + } mHardwareStatus = AUDIO_HW_IDLE; return ret; } @@ -1416,7 +1428,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( *sessionId = lSessionId; } } - ALOGV("openRecord() lSessionId: %d", lSessionId); + ALOGV("openRecord() lSessionId: %d input %d", lSessionId, input); // TODO: the uid should be passed in as a parameter to openRecord recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask, @@ -2022,6 +2034,16 @@ status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input) } ALOGV("closeInput() %d", input); + { + // If we still have effect chains, it means that a client still holds a handle + // on at least one effect. We must keep the chain alive in case a new record + // thread is opened for a new capture on the same session + Mutex::Autolock _sl(thread->mLock); + Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l(); + for (size_t i = 0; i < effectChains.size(); i++) { + putOrphanEffectChain_l(effectChains[i]); + } + } audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL); mRecordThreads.removeItem(input); } @@ -2451,6 +2473,13 @@ sp<IEffect> AudioFlinger::createEffect( lStatus = BAD_VALUE; goto Exit; } + } else { + // Check if one effect chain was awaiting for an effect to be created on this + // session and used it instead of creating a new one. + sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)sessionId); + if (chain != 0) { + thread->addEffectChain_l(chain); + } } sp<Client> client = registerPid(pid); @@ -2623,6 +2652,49 @@ void AudioFlinger::onNonOffloadableGlobalEffectEnable() } +status_t AudioFlinger::putOrphanEffectChain_l(const sp<AudioFlinger::EffectChain>& chain) +{ + audio_session_t session = (audio_session_t)chain->sessionId(); + ssize_t index = mOrphanEffectChains.indexOfKey(session); + ALOGV("putOrphanEffectChain_l session %d index %d", session, index); + if (index >= 0) { + ALOGW("putOrphanEffectChain_l chain for session %d already present", session); + return ALREADY_EXISTS; + } + mOrphanEffectChains.add(session, chain); + return NO_ERROR; +} + +sp<AudioFlinger::EffectChain> AudioFlinger::getOrphanEffectChain_l(audio_session_t session) +{ + sp<EffectChain> chain; + ssize_t index = mOrphanEffectChains.indexOfKey(session); + ALOGV("getOrphanEffectChain_l session %d index %d", session, index); + if (index >= 0) { + chain = mOrphanEffectChains.valueAt(index); + mOrphanEffectChains.removeItemsAt(index); + } + return chain; +} + +bool AudioFlinger::updateOrphanEffectChains(const sp<AudioFlinger::EffectModule>& effect) +{ + Mutex::Autolock _l(mLock); + audio_session_t session = (audio_session_t)effect->sessionId(); + ssize_t index = mOrphanEffectChains.indexOfKey(session); + ALOGV("updateOrphanEffectChains session %d index %d", session, index); + if (index >= 0) { + sp<EffectChain> chain = mOrphanEffectChains.valueAt(index); + if (chain->removeEffect_l(effect) == 0) { + ALOGV("updateOrphanEffectChains removing effect chain at index %d", index); + mOrphanEffectChains.removeItemsAt(index); + } + return true; + } + return false; +} + + struct Entry { #define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav char mName[MAX_NAME]; diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 753314f..1003017 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -569,6 +569,23 @@ private: bool isNonOffloadableGlobalEffectEnabled_l(); void onNonOffloadableGlobalEffectEnable(); + // Store an effect chain to mOrphanEffectChains keyed vector. + // Called when a thread exits and effects are still attached to it. + // If effects are later created on the same session, they will reuse the same + // effect chain and same instances in the effect library. + // return ALREADY_EXISTS if a chain with the same session already exists in + // mOrphanEffectChains. Note that this should never happen as there is only one + // chain for a given session and it is attached to only one thread at a time. + status_t putOrphanEffectChain_l(const sp<EffectChain>& chain); + // Get an effect chain for the specified session in mOrphanEffectChains and remove + // it if found. Returns 0 if not found (this is the most common case). + sp<EffectChain> getOrphanEffectChain_l(audio_session_t session); + // Called when the last effect handle on an effect instance is removed. If this + // effect belongs to an effect chain in mOrphanEffectChains, the chain is updated + // and removed from mOrphanEffectChains if it does not contain any effect. + // Return true if the effect was found in mOrphanEffectChains, false otherwise. + bool updateOrphanEffectChains(const sp<EffectModule>& effect); + class AudioHwDevice { public: enum Flags { @@ -713,6 +730,9 @@ private: Vector < sp<SyncEvent> > mPendingSyncEvents; // sync events awaiting for a session // to be created + // Effect chains without a valid thread + DefaultKeyedVector< audio_session_t , sp<EffectChain> > mOrphanEffectChains; + private: sp<Client> registerPid(pid_t pid); // always returns non-0 diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 365f271..15f1f23 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -68,7 +68,8 @@ AudioFlinger::EffectModule::EffectModule(ThreadBase *thread, mStatus(NO_INIT), mState(IDLE), // mMaxDisableWaitCnt is set by configure() and not used before then // mDisableWaitCnt is set by process() and updateState() and not used before then - mSuspended(false) + mSuspended(false), + mAudioFlinger(thread->mAudioFlinger) { ALOGV("Constructor %p", this); int lStatus; @@ -197,9 +198,19 @@ size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIf // destructor before we exit sp<EffectModule> keep(this); { - sp<ThreadBase> thread = mThread.promote(); - if (thread != 0) { - thread->disconnectEffect(keep, handle, unpinIfLast); + if (removeHandle(handle) == 0) { + if (!isPinned() || unpinIfLast) { + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + thread->removeEffect_l(this); + } + sp<AudioFlinger> af = mAudioFlinger.promote(); + if (af != 0) { + af->updateOrphanEffectChains(this); + } + AudioSystem::unregisterEffect(mId); + } } } return mHandles.size(); @@ -1911,4 +1922,13 @@ bool AudioFlinger::EffectChain::isNonOffloadableEnabled() return false; } +void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread) +{ + Mutex::Autolock _l(mLock); + mThread = thread; + for (size_t i = 0; i < mEffects.size(); i++) { + mEffects[i]->setThread(thread); + } +} + }; // namespace android diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h index 4170fd4..eaf90e7 100644 --- a/services/audioflinger/Effects.h +++ b/services/audioflinger/Effects.h @@ -153,6 +153,7 @@ mutable Mutex mLock; // mutex for process, commands and handl uint32_t mDisableWaitCnt; // current process() calls count during disable period. bool mSuspended; // effect is suspended: temporarily disabled by framework bool mOffloaded; // effect is currently offloaded to the audio DSP + wp<AudioFlinger> mAudioFlinger; }; // The EffectHandle class implements the IEffect interface. It provides resources @@ -347,6 +348,8 @@ protected: void clearInputBuffer_l(sp<ThreadBase> thread); + void setThread(const sp<ThreadBase>& thread); + wp<ThreadBase> mThread; // parent mixer thread Mutex mLock; // mutex protecting effect list Vector< sp<EffectModule> > mEffects; // list of effect modules diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 97b1753..3d17c89 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -1147,21 +1147,6 @@ void AudioFlinger::ThreadBase::setMode(audio_mode_t mode) } } -void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect, - EffectHandle *handle, - bool unpinIfLast) { - - Mutex::Autolock _l(mLock); - ALOGV("disconnectEffect() %p effect %p", this, effect.get()); - // delete the effect module if removing last handle on it - if (effect->removeHandle(handle) == 0) { - if (!effect->isPinned() || unpinIfLast) { - removeEffect_l(effect); - AudioSystem::unregisterEffect(effect->id()); - } - } -} - void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config) { config->type = AUDIO_PORT_TYPE_MIX; @@ -2278,7 +2263,7 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& c } } } - + chain->setThread(this); chain->setInBuffer(buffer, ownsBuffer); chain->setOutBuffer(reinterpret_cast<int16_t*>(mEffectBufferEnabled ? mEffectBuffer : mSinkBuffer)); @@ -6188,10 +6173,11 @@ status_t AudioFlinger::RecordThread::addEffectChain_l(const sp<EffectChain>& cha { // only one chain per input thread if (mEffectChains.size() != 0) { + ALOGW("addEffectChain_l() already one chain %p on thread %p", chain.get(), this); return INVALID_OPERATION; } ALOGV("addEffectChain_l() %p on thread %p", chain.get(), this); - + chain->setThread(this); chain->setInBuffer(NULL); chain->setOutBuffer(NULL); diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 648502b..fd025b5 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -283,9 +283,6 @@ public: effect_descriptor_t *desc, int *enabled, status_t *status /*non-NULL*/); - void disconnectEffect(const sp< EffectModule>& effect, - EffectHandle *handle, - bool unpinIfLast); // return values for hasAudioSession (bit field) enum effect_state { diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 864daa5..98bf96e 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -166,6 +166,7 @@ protected: sp<NBAIO_Source> mTeeSource; bool mTerminated; track_type mType; // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ... + audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to }; // PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 6cbab04..c0a75b9 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -96,7 +96,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mServerProxy(NULL), mId(android_atomic_inc(&nextTrackId)), mTerminated(false), - mType(type) + mType(type), + mThreadIoHandle(thread->id()) { // if the caller is us, trust the specified uid if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) { @@ -482,14 +483,15 @@ void AudioFlinger::PlaybackThread::Track::destroy() // this Track with its member mTrack. sp<Track> keep(this); { // scope for mLock + bool wasActive = false; sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { Mutex::Autolock _l(thread->mLock); PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); - bool wasActive = playbackThread->destroyTrack_l(this); - if (isExternalTrack() && !wasActive) { - AudioSystem::releaseOutput(thread->id()); - } + wasActive = playbackThread->destroyTrack_l(this); + } + if (isExternalTrack() && !wasActive) { + AudioSystem::releaseOutput(mThreadIoHandle); } } } @@ -2050,7 +2052,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop() if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); if (recordThread->stop(this) && isExternalTrack()) { - AudioSystem::stopInput(recordThread->id(), (audio_session_t)mSessionId); + AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId); } } } @@ -2060,14 +2062,14 @@ void AudioFlinger::RecordThread::RecordTrack::destroy() // see comments at AudioFlinger::PlaybackThread::Track::destroy() sp<RecordTrack> keep(this); { + if (isExternalTrack()) { + if (mState == ACTIVE || mState == RESUMING) { + AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId); + } + AudioSystem::releaseInput(mThreadIoHandle, (audio_session_t)mSessionId); + } sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { - if (isExternalTrack()) { - if (mState == ACTIVE || mState == RESUMING) { - AudioSystem::stopInput(thread->id(), (audio_session_t)mSessionId); - } - AudioSystem::releaseInput(thread->id(), (audio_session_t)mSessionId); - } Mutex::Autolock _l(thread->mLock); RecordThread *recordThread = (RecordThread *) thread.get(); recordThread->destroyTrack_l(this); diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 690f675..6a7f9e7 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -1092,6 +1092,10 @@ status_t Camera3Device::waitUntilDrainedLocked() { ALOGV("%s: Camera %d: Waiting until idle", __FUNCTION__, mId); status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout); + if (res != OK) { + SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res), + res); + } return res; } |