/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "EffectProxy"
//#define LOG_NDEBUG 0
#include <cutils/log.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <EffectProxy.h>
#include <utils/threads.h>
#include <media/EffectsFactoryApi.h>
namespace android {
// This is a dummy proxy descriptor just to return to Factory during the initial
// GetDescriptor call. Later in the factory, it is replaced with the
// SW sub effect descriptor
// proxy UUID af8da7e0-2ca1-11e3-b71d-0002a5d5c51b
const effect_descriptor_t gProxyDescriptor = {
EFFECT_UUID_INITIALIZER, // type
{0xaf8da7e0, 0x2ca1, 0x11e3, 0xb71d, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }}, // uuid
EFFECT_CONTROL_API_VERSION, //version of effect control API
(EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST |
EFFECT_FLAG_VOLUME_CTRL), // effect capability flags
0, // CPU load
1, // Data memory
"Proxy", //effect name
"AOSP", //implementor name
};
static const effect_descriptor_t *const gDescriptors[] =
{
&gProxyDescriptor,
};
int EffectProxyCreate(const effect_uuid_t *uuid,
int32_t sessionId,
int32_t ioId,
effect_handle_t *pHandle) {
effect_descriptor_t* desc;
audio_effect_library_t** aeli;
sub_effect_entry_t** sube;
EffectContext* pContext;
if (pHandle == NULL || uuid == NULL) {
ALOGE("EffectProxyCreate() called with NULL pointer");
return -EINVAL;
}
ALOGV("EffectProxyCreate start..");
pContext = new EffectContext;
pContext->sessionId = sessionId;
pContext->ioId = ioId;
pContext->uuid = *uuid;
pContext->common_itfe = &gEffectInterface;
// The sub effects will be created in effect_command when the first command
// for the effect is received
pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL;
// Get the HW and SW sub effect descriptors from the effects factory
desc = new effect_descriptor_t[SUB_FX_COUNT];
aeli = new audio_effect_library_t*[SUB_FX_COUNT];
sube = new sub_effect_entry_t*[SUB_FX_COUNT];
pContext->sube = new sub_effect_entry_t*[SUB_FX_COUNT];
pContext->desc = new effect_descriptor_t[SUB_FX_COUNT];
pContext->aeli = new audio_effect_library_t*[SUB_FX_COUNT];
int retValue = EffectGetSubEffects(uuid, sube, SUB_FX_COUNT);
// EffectGetSubEffects returns the number of sub-effects copied.
if (retValue != SUB_FX_COUNT) {
ALOGE("EffectCreate() could not get the sub effects");
delete[] sube;
delete[] desc;
delete[] aeli;
delete[] pContext->sube;
delete[] pContext->desc;
delete[] pContext->aeli;
return -EINVAL;
}
// Check which is the HW descriptor and copy the descriptors
// to the Context desc array
// Also check if there is only one HW and one SW descriptor.
// HW descriptor alone has the HW_TUNNEL flag.
desc[0] = *(effect_descriptor_t*)(sube[0])->object;
desc[1] = *(effect_descriptor_t*)(sube[1])->object;
aeli[0] = sube[0]->lib->desc;
aeli[1] = sube[1]->lib->desc;
if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
!(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
pContext->sube[SUB_FX_OFFLOAD] = sube[0];
pContext->desc[SUB_FX_OFFLOAD] = desc[0];
pContext->aeli[SUB_FX_OFFLOAD] = aeli[0];
pContext->sube[SUB_FX_HOST] = sube[1];
pContext->desc[SUB_FX_HOST] = desc[1];
pContext->aeli[SUB_FX_HOST] = aeli[1];
}
else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
!(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
pContext->sube[SUB_FX_HOST] = sube[0];
pContext->desc[SUB_FX_HOST] = desc[0];
pContext->aeli[SUB_FX_HOST] = aeli[0];
pContext->sube[SUB_FX_OFFLOAD] = sube[1];
pContext->desc[SUB_FX_OFFLOAD] = desc[1];
pContext->aeli[SUB_FX_OFFLOAD] = aeli[1];
}
delete[] desc;
delete[] aeli;
delete[] sube;
#if (LOG_NDEBUG == 0)
effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid;
ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X"
"%02X%02X\n",uuid_print.timeLow, uuid_print.timeMid,
uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0],
uuid_print.node[1], uuid_print.node[2], uuid_print.node[3],
uuid_print.node[4], uuid_print.node[5]);
ALOGV("EffectCreate() UUID of OFFLOAD: %08X-%04X-%04X-%04X-%02X%02X%02X%02X"
"%02X%02X\n", uuid_print.timeLow, uuid_print.timeMid,
uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0],
uuid_print.node[1], uuid_print.node[2], uuid_print.node[3],
uuid_print.node[4], uuid_print.node[5]);
#endif
pContext->replySize = PROXY_REPLY_SIZE_DEFAULT;
pContext->replyData = (char *)malloc(PROXY_REPLY_SIZE_DEFAULT);
*pHandle = (effect_handle_t)pContext;
ALOGV("EffectCreate end");
return 0;
} //end EffectProxyCreate
int EffectProxyRelease(effect_handle_t handle) {
EffectContext * pContext = (EffectContext *)handle;
if (pContext == NULL) {
ALOGV("ERROR : EffectRelease called with NULL pointer");
return -EINVAL;
}
ALOGV("EffectRelease");
delete[] pContext->desc;
free(pContext->replyData);
if (pContext->eHandle[SUB_FX_HOST])
pContext->aeli[SUB_FX_HOST]->release_effect(pContext->eHandle[SUB_FX_HOST]);
if (pContext->eHandle[SUB_FX_OFFLOAD])
pContext->aeli[SUB_FX_OFFLOAD]->release_effect(pContext->eHandle[SUB_FX_OFFLOAD]);
delete[] pContext->aeli;
delete[] pContext->sube;
delete pContext;
pContext = NULL;
return 0;
} /*end EffectProxyRelease */
int EffectProxyGetDescriptor(const effect_uuid_t *uuid,
effect_descriptor_t *pDescriptor) {
const effect_descriptor_t *desc = NULL;
if (pDescriptor == NULL || uuid == NULL) {
ALOGV("EffectGetDescriptor() called with NULL pointer");
return -EINVAL;
}
desc = &gProxyDescriptor;
*pDescriptor = *desc;
return 0;
} /* end EffectProxyGetDescriptor */
/* Effect Control Interface Implementation: Process */
int Effect_process(effect_handle_t self,
audio_buffer_t *inBuffer,
audio_buffer_t *outBuffer) {
EffectContext *pContext = (EffectContext *) self;
int ret = 0;
if (pContext != NULL) {
int index = pContext->index;
// if the index refers to HW , do not do anything. Just return.
if (index == SUB_FX_HOST) {
ret = (*pContext->eHandle[index])->process(pContext->eHandle[index],
inBuffer, outBuffer);
}
}
return ret;
} /* end Effect_process */
/* Effect Control Interface Implementation: Command */
int Effect_command(effect_handle_t self,
uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
uint32_t *replySize,
void *pReplyData) {
EffectContext *pContext = (EffectContext *) self;
int status = 0;
if (pContext == NULL) {
ALOGV("Effect_command() Proxy context is NULL");
return -EINVAL;
}
if (pContext->eHandle[SUB_FX_HOST] == NULL) {
ALOGV("Effect_command() Calling HOST EffectCreate");
status = pContext->aeli[SUB_FX_HOST]->create_effect(
&pContext->desc[SUB_FX_HOST].uuid,
pContext->sessionId, pContext->ioId,
&(pContext->eHandle[SUB_FX_HOST]));
if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) {
ALOGV("Effect_command() Error creating SW sub effect");
return status;
}
}
if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) {
ALOGV("Effect_command() Calling OFFLOAD EffectCreate");
status = pContext->aeli[SUB_FX_OFFLOAD]->create_effect(
&pContext->desc[SUB_FX_OFFLOAD].uuid,
pContext->sessionId, pContext->ioId,
&(pContext->eHandle[SUB_FX_OFFLOAD]));
if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) {
ALOGV("Effect_command() Error creating HW effect");
pContext->eHandle[SUB_FX_OFFLOAD] = NULL;
// Do not return error here as SW effect is created
// Return error if the CMD_OFFLOAD sends the index as OFFLOAD
}
pContext->index = SUB_FX_HOST;
}
// EFFECT_CMD_OFFLOAD used to (1) send whether the thread is offload or not
// (2) Send the ioHandle of the effectThread when the effect
// is moved from one type of thread to another.
// pCmdData points to a memory holding effect_offload_param_t structure
if (cmdCode == EFFECT_CMD_OFFLOAD) {
ALOGV("Effect_command() cmdCode = EFFECT_CMD_OFFLOAD");
if (cmdSize == 0 || pCmdData == NULL) {
ALOGV("effectsOffload: Effect_command: CMD_OFFLOAD has no data");
*(int*)pReplyData = FAILED_TRANSACTION;
return FAILED_TRANSACTION;
}
effect_offload_param_t* offloadParam = (effect_offload_param_t*)pCmdData;
// Assign the effect context index based on isOffload field of the structure
pContext->index = offloadParam->isOffload ? SUB_FX_OFFLOAD : SUB_FX_HOST;
// if the index is HW and the HW effect is unavailable, return error
// and reset the index to SW
if (pContext->eHandle[pContext->index] == NULL) {
ALOGV("Effect_command()CMD_OFFLOAD sub effect unavailable");
*(int*)pReplyData = FAILED_TRANSACTION;
return FAILED_TRANSACTION;
}
pContext->ioId = offloadParam->ioHandle;
ALOGV("Effect_command()CMD_OFFLOAD index:%d io %d", pContext->index, pContext->ioId);
// Update the DSP wrapper with the new ioHandle.
// Pass the OFFLOAD command to the wrapper.
// The DSP wrapper needs to handle this CMD
if (pContext->eHandle[SUB_FX_OFFLOAD]) {
ALOGV("Effect_command: Calling OFFLOAD command");
return (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
pCmdData, replySize, pReplyData);
}
*(int*)pReplyData = NO_ERROR;
ALOGV("Effect_command OFFLOAD return 0, replyData %d",
*(int*)pReplyData);
return NO_ERROR;
}
int index = pContext->index;
if (index != SUB_FX_HOST && index != SUB_FX_OFFLOAD) {
ALOGV("Effect_command: effect index is neither offload nor host");
return -EINVAL;
}
// Getter commands are only sent to the active sub effect.
int *subStatus[SUB_FX_COUNT];
uint32_t *subReplySize[SUB_FX_COUNT];
void *subReplyData[SUB_FX_COUNT];
uint32_t tmpSize;
int tmpStatus;
// grow temp reply buffer if needed
if (replySize != NULL) {
tmpSize = pContext->replySize;
while (tmpSize < *replySize && tmpSize < PROXY_REPLY_SIZE_MAX) {
tmpSize *= 2;
}
if (tmpSize > pContext->replySize) {
ALOGV("Effect_command grow reply buf to %d", tmpSize);
pContext->replyData = (char *)realloc(pContext->replyData, tmpSize);
pContext->replySize = tmpSize;
}
if (tmpSize > *replySize) {
tmpSize = *replySize;
}
} else {
tmpSize = 0;
}
// tmpSize is now the actual reply size for the non active sub effect
// Send command to sub effects. The command is sent to all sub effects so that their internal
// state is kept in sync.
// Only the reply from the active sub effect is returned to the caller. The reply from the
// other sub effect is lost in pContext->replyData
for (int i = 0; i < SUB_FX_COUNT; i++) {
if (pContext->eHandle[i] == NULL) {
continue;
}
if (i == index) {
subStatus[i] = &status;
subReplySize[i] = replySize;
subReplyData[i] = pReplyData;
} else {
subStatus[i] = &tmpStatus;
subReplySize[i] = replySize == NULL ? NULL : &tmpSize;
subReplyData[i] = pReplyData == NULL ? NULL : pContext->replyData;
}
*subStatus[i] = (*pContext->eHandle[i])->command(
pContext->eHandle[i], cmdCode, cmdSize,
pCmdData, subReplySize[i], subReplyData[i]);
}
return status;
} /* end Effect_command */
/* Effect Control Interface Implementation: get_descriptor */
int Effect_getDescriptor(effect_handle_t self,
effect_descriptor_t *pDescriptor) {
EffectContext * pContext = (EffectContext *) self;
const effect_descriptor_t *desc;
ALOGV("Effect_getDescriptor");
if (pContext == NULL || pDescriptor == NULL) {
ALOGV("Effect_getDescriptor() invalid param");
return -EINVAL;
}
if (pContext->desc == NULL) {
ALOGV("Effect_getDescriptor() could not get descriptor");
return -EINVAL;
}
desc = &pContext->desc[SUB_FX_HOST];
*pDescriptor = *desc;
pDescriptor->uuid = pContext->uuid; // Replace the uuid with the Proxy UUID
// Also set/clear the EFFECT_FLAG_OFFLOAD_SUPPORTED flag based on the sub effects availability
if (pContext->eHandle[SUB_FX_OFFLOAD] != NULL)
pDescriptor->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
else
pDescriptor->flags &= ~EFFECT_FLAG_OFFLOAD_SUPPORTED;
return 0;
} /* end Effect_getDescriptor */
} // namespace android
__attribute__ ((visibility ("default")))
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
.tag = AUDIO_EFFECT_LIBRARY_TAG,
.version = EFFECT_LIBRARY_API_VERSION,
.name = "Effect Proxy",
.implementor = "AOSP",
.create_effect = android::EffectProxyCreate,
.release_effect = android::EffectProxyRelease,
.get_descriptor = android::EffectProxyGetDescriptor,
};