Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5baf2af5 authored by Eric Laurent's avatar Eric Laurent
Browse files

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
parent 9a98b6de
Loading
Loading
Loading
Loading
+48 −11
Original line number Diff line number Diff line
@@ -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 */


+2 −2
Original line number Diff line number Diff line
@@ -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;
            }
+53 −36
Original line number Diff line number Diff line
@@ -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);

@@ -2180,6 +2165,20 @@ sp<IEffect> AudioFlinger::createEffect(
        // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX
        // because of code checking output when entering the function.
        // Note: io is never 0 when creating an effect on an input
        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;
            }
            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) {
                // look for the thread where the specified audio session is present
                for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
@@ -2196,6 +2195,7 @@ sp<IEffect> AudioFlinger::createEffect(
                        }
                    }
                }
            }
            // If no output thread contains the requested session ID, default to
            // first output. The effect chain will be moved to the correct output
            // thread when a track with the same session ID is created
@@ -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);

+2 −4
Original line number Diff line number Diff line
@@ -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
};

+44 −5
Original line number Diff line number Diff line
@@ -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;
        }
    }
Loading