diff options
Diffstat (limited to 'services/audioflinger')
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 89 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 6 | ||||
-rw-r--r-- | services/audioflinger/Effects.cpp | 49 | ||||
-rw-r--r-- | services/audioflinger/Effects.h | 9 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 30 | ||||
-rw-r--r-- | services/audioflinger/Tracks.cpp | 10 |
6 files changed, 136 insertions, 57 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 626b5c2..8fbac42 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -98,7 +98,6 @@ size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault; size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault; #endif -//TODO: remove when effect offload is implemented // In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off // we define a minimum time during which a global effect is considered enabled. static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200); @@ -2084,20 +2083,6 @@ sp<IEffect> AudioFlinger::createEffect( goto Exit; } - if (io == 0) { - if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) { - // output must be specified by AudioPolicyManager when using session - // AUDIO_SESSION_OUTPUT_STAGE - lStatus = BAD_VALUE; - goto Exit; - } else if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { - // if the output returned by getOutputForEffect() is removed before we lock the - // mutex below, the call to checkPlaybackThread_l(io) below will detect it - // and we will exit safely - io = AudioSystem::getOutputForEffect(&desc); - } - } - { Mutex::Autolock _l(mLock); @@ -2181,20 +2166,35 @@ sp<IEffect> AudioFlinger::createEffect( // because of code checking output when entering the function. // Note: io is never 0 when creating an effect on an input if (io == 0) { - // look for the thread where the specified audio session is present - for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { - io = mPlaybackThreads.keyAt(i); - break; - } + if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) { + // output must be specified by AudioPolicyManager when using session + // AUDIO_SESSION_OUTPUT_STAGE + lStatus = BAD_VALUE; + goto Exit; + } + if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { + // if the output returned by getOutputForEffect() is removed before we lock the + // mutex below, the call to checkPlaybackThread_l(io) below will detect it + // and we will exit safely + io = AudioSystem::getOutputForEffect(&desc); + ALOGV("createEffect got output %d", io); } if (io == 0) { - for (size_t i = 0; i < mRecordThreads.size(); i++) { - if (mRecordThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { - io = mRecordThreads.keyAt(i); + // look for the thread where the specified audio session is present + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { + io = mPlaybackThreads.keyAt(i); break; } } + if (io == 0) { + for (size_t i = 0; i < mRecordThreads.size(); i++) { + if (mRecordThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { + io = mRecordThreads.keyAt(i); + break; + } + } + } } // If no output thread contains the requested session ID, default to // first output. The effect chain will be moved to the correct output @@ -2254,9 +2254,7 @@ status_t AudioFlinger::moveEffects(int sessionId, audio_io_handle_t srcOutput, Mutex::Autolock _dl(dstThread->mLock); Mutex::Autolock _sl(srcThread->mLock); - moveEffectChain_l(sessionId, srcThread, dstThread, false); - - return NO_ERROR; + return moveEffectChain_l(sessionId, srcThread, dstThread, false); } // moveEffectChain_l must be called with both srcThread and dstThread mLocks held @@ -2283,13 +2281,18 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId, // transfer all effects one by one so that new effect chain is created on new thread with // correct buffer sizes and audio parameters and effect engines reconfigured accordingly - audio_io_handle_t dstOutput = dstThread->id(); sp<EffectChain> dstChain; uint32_t strategy = 0; // prevent compiler warning sp<EffectModule> effect = chain->getEffectFromId_l(0); + Vector< sp<EffectModule> > removed; + status_t status = NO_ERROR; while (effect != 0) { srcThread->removeEffect_l(effect); - dstThread->addEffect_l(effect); + removed.add(effect); + status = dstThread->addEffect_l(effect); + if (status != NO_ERROR) { + break; + } // removeEffect_l() has stopped the effect if it was active so it must be restarted if (effect->state() == EffectModule::ACTIVE || effect->state() == EffectModule::STOPPING) { @@ -2301,15 +2304,15 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId, dstChain = effect->chain().promote(); if (dstChain == 0) { ALOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get()); - srcThread->addEffect_l(effect); - return NO_INIT; + status = NO_INIT; + break; } strategy = dstChain->strategy(); } if (reRegister) { AudioSystem::unregisterEffect(effect->id()); AudioSystem::registerEffect(&effect->desc(), - dstOutput, + dstThread->id(), strategy, sessionId, effect->id()); @@ -2317,10 +2320,24 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId, effect = chain->getEffectFromId_l(0); } - return NO_ERROR; + if (status != NO_ERROR) { + for (size_t i = 0; i < removed.size(); i++) { + srcThread->addEffect_l(removed[i]); + if (dstChain != 0 && reRegister) { + AudioSystem::unregisterEffect(removed[i]->id()); + AudioSystem::registerEffect(&removed[i]->desc(), + srcThread->id(), + strategy, + sessionId, + removed[i]->id()); + } + } + } + + return status; } -bool AudioFlinger::isGlobalEffectEnabled_l() +bool AudioFlinger::isNonOffloadableGlobalEffectEnabled_l() { if (mGlobalEffectEnableTime != 0 && ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) { @@ -2330,14 +2347,14 @@ bool AudioFlinger::isGlobalEffectEnabled_l() for (size_t i = 0; i < mPlaybackThreads.size(); i++) { sp<EffectChain> ec = mPlaybackThreads.valueAt(i)->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX); - if (ec != 0 && ec->isEnabled()) { + if (ec != 0 && ec->isNonOffloadableEnabled()) { return true; } } return false; } -void AudioFlinger::onGlobalEffectEnable() +void AudioFlinger::onNonOffloadableGlobalEffectEnable() { Mutex::Autolock _l(mLock); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 0992308..b41d480 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -466,9 +466,8 @@ private: void removeClient_l(pid_t pid); void removeNotificationClient(pid_t pid); - //TODO: remove when effect offload is implemented - bool isGlobalEffectEnabled_l(); - void onGlobalEffectEnable(); + bool isNonOffloadableGlobalEffectEnabled_l(); + void onNonOffloadableGlobalEffectEnable(); class AudioHwDevice { public: @@ -645,7 +644,6 @@ public: private: bool mIsLowRamDevice; bool mIsDeviceTypeKnown; - //TODO: remove when effect offload is implemented nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled }; diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 86671a9..0ca2107 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -764,6 +764,46 @@ bool AudioFlinger::EffectModule::purgeHandles() return enabled; } +status_t AudioFlinger::EffectModule::setOffloaded(bool offloaded, audio_io_handle_t io) +{ + Mutex::Autolock _l(mLock); + if (mStatus != NO_ERROR) { + return mStatus; + } + status_t status = NO_ERROR; + if ((mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0) { + status_t cmdStatus; + uint32_t size = sizeof(status_t); + effect_offload_param_t cmd; + + cmd.isOffload = offloaded; + cmd.ioHandle = io; + status = (*mEffectInterface)->command(mEffectInterface, + EFFECT_CMD_OFFLOAD, + sizeof(effect_offload_param_t), + &cmd, + &size, + &cmdStatus); + if (status == NO_ERROR) { + status = cmdStatus; + } + mOffloaded = (status == NO_ERROR) ? offloaded : false; + } else { + if (offloaded) { + status = INVALID_OPERATION; + } + mOffloaded = false; + } + ALOGV("setOffloaded() offloaded %d io %d status %d", offloaded, io, status); + return status; +} + +bool AudioFlinger::EffectModule::isOffloaded() const +{ + Mutex::Autolock _l(mLock); + return mOffloaded; +} + void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; @@ -932,14 +972,13 @@ status_t AudioFlinger::EffectHandle::enable() } mEnabled = false; } else { - //TODO: remove when effect offload is implemented - if (thread != 0) { + if (thread != 0 && !mEffect->isOffloadable()) { if ((thread->type() == ThreadBase::OFFLOAD)) { PlaybackThread *t = (PlaybackThread *)thread.get(); t->invalidateTracks(AUDIO_STREAM_MUSIC); } if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) { - thread->mAudioFlinger->onGlobalEffectEnable(); + thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable(); } } } @@ -1728,12 +1767,12 @@ void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModul } } -bool AudioFlinger::EffectChain::isEnabled() +bool AudioFlinger::EffectChain::isNonOffloadableEnabled() { Mutex::Autolock _l(mLock); size_t size = mEffects.size(); for (size_t i = 0; i < size; i++) { - if (mEffects[i]->isEnabled()) { + if (mEffects[i]->isEnabled() && !mEffects[i]->isOffloadable()) { return true; } } diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h index bac50f2..c35cff0 100644 --- a/services/audioflinger/Effects.h +++ b/services/audioflinger/Effects.h @@ -111,6 +111,10 @@ public: bool purgeHandles(); void lock() { mLock.lock(); } void unlock() { mLock.unlock(); } + bool isOffloadable() const + { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; } + status_t setOffloaded(bool offloaded, audio_io_handle_t io); + bool isOffloaded() const; void dump(int fd, const Vector<String16>& args); @@ -144,6 +148,7 @@ mutable Mutex mLock; // mutex for process, commands and handl // sending disable command. 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 }; // The EffectHandle class implements the IEffect interface. It provides resources @@ -303,8 +308,8 @@ public: void clearInputBuffer(); - // At least one effect in the chain is enabled - bool isEnabled(); + // At least one non offloadable effect in the chain is enabled + bool isNonOffloadableEnabled(); void dump(int fd, const Vector<String16>& args); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 4234965..7d0ecac 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -702,14 +702,22 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( goto Exit; } - // Do not allow effects with session ID 0 on direct output or duplicating threads - // TODO: add rule for hw accelerated effects on direct outputs with non PCM format - if (sessionId == AUDIO_SESSION_OUTPUT_MIX && mType != MIXER) { - ALOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", - desc->name, sessionId); - lStatus = BAD_VALUE; - goto Exit; + // Allow global effects only on offloaded and mixer threads + if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { + switch (mType) { + case MIXER: + case OFFLOAD: + break; + case DIRECT: + case DUPLICATING: + case RECORD: + default: + ALOGW("createEffect_l() Cannot add global effect %s on thread %s", desc->name, mName); + lStatus = BAD_VALUE; + goto Exit; + } } + // Only Pre processor effects are allowed on input threads and only on input threads if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) { ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d", @@ -752,6 +760,8 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( if (lStatus != NO_ERROR) { goto Exit; } + effect->setOffloaded(mType == OFFLOAD, mId); + lStatus = chain->addEffect_l(effect); if (lStatus != NO_ERROR) { goto Exit; @@ -813,6 +823,10 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect) sp<EffectChain> chain = getEffectChain_l(sessionId); bool chainCreated = false; + ALOGD_IF((mType == OFFLOAD) && !effect->isOffloadable(), + "addEffect_l() on offloaded thread %p: effect %s does not support offload flags %x", + this, effect->desc().name, effect->desc().flags); + if (chain == 0) { // create a new chain for this session ALOGV("addEffect_l() new effect chain for session %d", sessionId); @@ -829,6 +843,8 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect) return BAD_VALUE; } + effect->setOffloaded(mType == OFFLOAD, mId); + status_t status = chain->addEffect_l(effect); if (status != NO_ERROR) { if (chainCreated) { diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 3b1874e..57aad1e 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -553,12 +553,12 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { - //TODO: remove when effect offload is implemented if (isOffloaded()) { Mutex::Autolock _laf(thread->mAudioFlinger->mLock); Mutex::Autolock _lth(thread->mLock); sp<EffectChain> ec = thread->getEffectChain_l(mSessionId); - if (thread->mAudioFlinger->isGlobalEffectEnabled_l() || (ec != 0 && ec->isEnabled())) { + if (thread->mAudioFlinger->isNonOffloadableGlobalEffectEnabled_l() || + (ec != 0 && ec->isNonOffloadableEnabled())) { invalidate(); return PERMISSION_DENIED; } @@ -797,7 +797,11 @@ status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId) return INVALID_OPERATION; } srcThread->removeEffect_l(effect); - playbackThread->addEffect_l(effect); + status = playbackThread->addEffect_l(effect); + if (status != NO_ERROR) { + srcThread->addEffect_l(effect); + return INVALID_OPERATION; + } // removeEffect_l() has stopped the effect if it was active so it must be restarted if (effect->state() == EffectModule::ACTIVE || effect->state() == EffectModule::STOPPING) { |