summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2013-09-12 17:37:00 -0700
committerEric Laurent <elaurent@google.com>2013-09-18 11:52:40 -0700
commit5baf2af52cd186633b7173196c1e4a4cd3435f22 (patch)
tree9c6752456812c4a927574a71e1d2ec2495ff6ce3
parent9a98b6de791aeb130192df10744f5b35f8b6ef1a (diff)
downloadframeworks_av-5baf2af52cd186633b7173196c1e4a4cd3435f22.zip
frameworks_av-5baf2af52cd186633b7173196c1e4a4cd3435f22.tar.gz
frameworks_av-5baf2af52cd186633b7173196c1e4a4cd3435f22.tar.bz2
more support for audio effect offload
Offloading of audio effects is now enabled for offloaded output threads. If an effect not supporting offload is enabled, the AudioTrack is invalidated so that it can be recreated in PCM mode. Fix some issues in effect proxy related to handling of effect commands to offloaded and non offloaded effects. Also fixed a bug on capture index in software Visualizer effect. Bug: 8174034. Change-Id: Ib23d3c2d5a652361b0aaec7faee09102f2b18fce
-rw-r--r--media/libeffects/proxy/EffectProxy.cpp59
-rw-r--r--media/libeffects/visualizer/EffectVisualizer.cpp4
-rw-r--r--services/audioflinger/AudioFlinger.cpp89
-rw-r--r--services/audioflinger/AudioFlinger.h6
-rw-r--r--services/audioflinger/Effects.cpp49
-rw-r--r--services/audioflinger/Effects.h9
-rw-r--r--services/audioflinger/Threads.cpp30
-rw-r--r--services/audioflinger/Tracks.cpp10
8 files changed, 186 insertions, 70 deletions
diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp
index 77c6e89..41640da 100644
--- a/media/libeffects/proxy/EffectProxy.cpp
+++ b/media/libeffects/proxy/EffectProxy.cpp
@@ -48,6 +48,21 @@ static const effect_descriptor_t *const gDescriptors[] =
&gProxyDescriptor,
};
+static inline bool isGetterCmd(uint32_t cmdCode)
+{
+ switch (cmdCode) {
+ case EFFECT_CMD_GET_PARAM:
+ case EFFECT_CMD_GET_CONFIG:
+ case EFFECT_CMD_GET_CONFIG_REVERSE:
+ case EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS:
+ case EFFECT_CMD_GET_FEATURE_CONFIG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
int EffectProxyCreate(const effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId,
@@ -155,7 +170,6 @@ int Effect_process(effect_handle_t self,
int index = pContext->index;
// if the index refers to HW , do not do anything. Just return.
if (index == SUB_FX_HOST) {
- ALOGV("Calling CoreProcess");
ret = (*pContext->eHandle[index])->process(pContext->eHandle[index],
inBuffer, outBuffer);
}
@@ -172,7 +186,7 @@ int Effect_command(effect_handle_t self,
void *pReplyData) {
EffectContext *pContext = (EffectContext *) self;
- int status;
+ int status = 0;
if (pContext == NULL) {
ALOGV("Effect_command() Proxy context is NULL");
return -EINVAL;
@@ -237,23 +251,46 @@ int Effect_command(effect_handle_t self,
ALOGV("Effect_command: effect index is neither offload nor host");
return -EINVAL;
}
- ALOGV("Effect_command: pContext->eHandle[%d]: %p",
- index, pContext->eHandle[index]);
- if (pContext->eHandle[SUB_FX_HOST])
- (*pContext->eHandle[SUB_FX_HOST])->command(
+
+ // Getter commands are only sent to the active sub effect.
+ uint32_t hostReplySize = replySize != NULL ? *replySize : 0;
+ bool hostReplied = false;
+ int hostStatus = 0;
+ uint32_t offloadReplySize = replySize != NULL ? *replySize : 0;
+ bool offloadReplied = false;
+ int offloadStatus = 0;
+
+ if (pContext->eHandle[SUB_FX_HOST] && (!isGetterCmd(cmdCode) || index == SUB_FX_HOST)) {
+ hostStatus = (*pContext->eHandle[SUB_FX_HOST])->command(
pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize,
- pCmdData, replySize, pReplyData);
- if (pContext->eHandle[SUB_FX_OFFLOAD]) {
+ pCmdData, replySize != NULL ? &hostReplySize : NULL, pReplyData);
+ hostReplied = true;
+ }
+ if (pContext->eHandle[SUB_FX_OFFLOAD] && (!isGetterCmd(cmdCode) || index == SUB_FX_OFFLOAD)) {
// In case of SET CMD, when the offload stream is unavailable,
// we will store the effect param values in the DSP effect wrapper.
// When the offload effects get enabled, we send these values to the
// DSP during Effect_config.
// So,we send the params to DSP wrapper also
- (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
+ offloadStatus = (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
- pCmdData, replySize, pReplyData);
+ pCmdData, replySize != NULL ? &offloadReplySize : NULL, pReplyData);
+ offloadReplied = true;
}
- return 0;
+ // By convention the offloaded implementation reply is returned if command is processed by both
+ // host and offloaded sub effects
+ if (offloadReplied){
+ status = offloadStatus;
+ if (replySize) {
+ *replySize = offloadReplySize;
+ }
+ } else if (hostReplied) {
+ status = hostStatus;
+ if (replySize) {
+ *replySize = hostReplySize;
+ }
+ }
+ return status;
} /* end Effect_command */
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index e7eccf1..96935e3 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -61,7 +61,7 @@ struct VisualizerContext {
uint32_t mCaptureSize;
uint32_t mScalingMode;
uint8_t mState;
- uint8_t mLastCaptureIdx;
+ uint32_t mLastCaptureIdx;
uint32_t mLatency;
struct timespec mBufferUpdateTime;
uint8_t mCaptureBuf[CAPTURE_BUF_SIZE];
@@ -499,7 +499,7 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
memcpy(pReplyData,
pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
size);
- pReplyData += size;
+ pReplyData = (char *)pReplyData + size;
captureSize -= size;
capturePoint = 0;
}
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) {