今天来看看AudioEffect的构造,以及相关的一些函数。
*****************************************源码*************************************************
**********************************************************************************************
源码路径:
frameworks\base\media\tests\mediaframeworktest\src\com\android\mediaframeworktest\functional\MediaAudioEffectTest.java
#######################说明################################
###########################################################
&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
在创建AudioEffect的时候:
1、如果指定了uuid,则会根据uuid 寻找匹配的effect lib来创建effect。
2、若没有指定uuid而指定了type,则会寻找相同type中可用的effect lib。
注意,如果session id为SESSION_OUTPUT_MIX,则优先使用auxiliary的effect。
在指定的type中没有可用的auxiliary的effect的情况下,才会使用insert的effect。
effect lib都被注册到一个列表中。
EffectsFactory中的init函数会将build-in的effect lib添加到该列表。
用户可以调用函数EffectLoadLibrary/EffectUnloadLibrary来注册/删除effect lib。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
*****************************************源码*************************************************
//Test case 1.0: test constructor from effect type and get effect ID
@LargeTest
public void test1_0ConstructorFromType() throws Exception {
boolean result = true;
String msg = "test1_0ConstructorFromType()";
AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
assertTrue(msg+": no effects found", (desc.length != 0));
try {
AudioEffect effect = new AudioEffect(desc[0].type,
AudioEffect.EFFECT_TYPE_NULL,
0,
0);
assertNotNull(msg + ": could not create AudioEffect", effect);
try {
assertTrue(msg +": invalid effect ID", (effect.getId() != 0));
} catch (IllegalStateException e) {
msg = msg.concat(": AudioEffect not initialized");
result = false;
} finally {
effect.release();
}
} catch (IllegalArgumentException e) {
msg = msg.concat(": Effect not found: "+desc[0].name);
result = false;
} catch (UnsupportedOperationException e) {
msg = msg.concat(": Effect library not loaded");
result = false;
}
assertTrue(msg, result);
}
**********************************************************************************************
源码路径:
frameworks\base\media\tests\mediaframeworktest\src\com\android\mediaframeworktest\functional\MediaAudioEffectTest.java
#######################说明################################
//Test case 1.0: test constructor from effect type and get effect ID
@LargeTest
public void test1_0ConstructorFromType() throws Exception {
boolean result = true;
String msg = "test1_0ConstructorFromType()";
AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
assertTrue(msg+": no effects found", (desc.length != 0));
try {
AudioEffect effect = new AudioEffect(desc[0].type,
AudioEffect.EFFECT_TYPE_NULL,
0,
0);
// +++++++++++++++++++++++++++++++AudioEffect+++++++++++++++++++++++++++++++++
/**
* Class constructor.
*
* @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB},
* {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to
* built-in effects are defined by AudioEffect class. Other types
* can be specified provided they correspond an existing OpenSL
* ES interface ID and the corresponsing effect is available on
* the platform. If an unspecified effect type is requested, the
* constructor with throw the IllegalArgumentException. This
* parameter can be set to {@link #EFFECT_TYPE_NULL} in which
* case only the uuid will be used to select the effect.
* @param uuid unique identifier of a particular effect implementation.
* Must be specified if the caller wants to use a particular
* implementation of an effect type. This parameter can be set to
* {@link #EFFECT_TYPE_NULL} in which case only the type will
* be used to select the effect.
* @param priority the priority level requested by the application for
* controlling the effect engine. As the same effect engine can
* be shared by several applications, this parameter indicates
* how much the requesting application needs control of effect
* parameters. The normal priority is 0, above normal is a
* positive number, below normal a negative number.
* @param audioSession system wide unique audio session identifier. If audioSession
* is not 0, the effect will be attached to the MediaPlayer or
* AudioTrack in the same audio session. Otherwise, the effect
* will apply to the output mix.
*
* @throws java.lang.IllegalArgumentException
* @throws java.lang.UnsupportedOperationException
* @throws java.lang.RuntimeException
* @hide
*/
public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
throws IllegalArgumentException, UnsupportedOperationException,
RuntimeException {
int[] id = new int[1];
Descriptor[] desc = new Descriptor[1];
// native initialization
int initResult = native_setup(new WeakReference<AudioEffect>(this),
type.toString(), uuid.toString(), priority, audioSession, id,
desc);
// +++++++++++++++++++++++++android_media_AudioEffect_native_setup+++++++++++++++++++++++++++++++++++++++
static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId, jobjectArray javadesc)
{
LOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
AudioEffect* lpAudioEffect = NULL;
jint* nId = NULL;
const char *typeStr = NULL;
const char *uuidStr = NULL;
effect_descriptor_t desc;
jobject jdesc;
char str[EFFECT_STRING_LEN_MAX];
jstring jdescType;
jstring jdescUuid;
jstring jdescConnect;
jstring jdescName;
jstring jdescImplementor;
if (type != NULL) {
typeStr = env->GetStringUTFChars(type, NULL);
if (typeStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
goto setup_failure;
}
}
if (uuid != NULL) {
uuidStr = env->GetStringUTFChars(uuid, NULL);
if (uuidStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
goto setup_failure;
}
}
// type 和 uuid 必须有一个不为空
// 否则,没办法确定用哪个effect lib
if (typeStr == NULL && uuidStr == NULL) {
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
lpJniStorage = new AudioEffectJniStorage();
if (lpJniStorage == NULL) {
LOGE("setup: Error creating JNI Storage");
goto setup_failure;
}
// ++++++++++++++++++++++++++++AudioEffectJniStorage++++++++++++++++++++++++++++++++++++
struct effect_callback_cookie {
jclass audioEffect_class; // AudioEffect class
jobject audioEffect_ref; // AudioEffect object instance
};
class AudioEffectJniStorage {
public:
effect_callback_cookie mCallbackData;
AudioEffectJniStorage() {
}
~AudioEffectJniStorage() {
}
};
// ----------------------------AudioEffectJniStorage------------------------------------
lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
// we use a weak reference so the AudioEffect object can be garbage collected.
lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this);
LOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p",
lpJniStorage,
lpJniStorage->mCallbackData.audioEffect_ref,
lpJniStorage->mCallbackData.audioEffect_class,
&lpJniStorage->mCallbackData);
if (jId == NULL) {
LOGE("setup: NULL java array for id pointer");
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
// create the native AudioEffect object
lpAudioEffect = new AudioEffect(typeStr,
uuidStr,
priority,
effectCallback,
&lpJniStorage->mCallbackData,
sessionId,
0);
if (lpAudioEffect == NULL) {
LOGE("Error creating AudioEffect");
goto setup_failure;
}
// ++++++++++++++++++++++++++++native AudioEffect++++++++++++++++++++++++++++++++++++
AudioEffect::AudioEffect(const char *typeStr,
const char *uuidStr,
int32_t priority,
effect_callback_t cbf,
void* user,
int sessionId,
audio_io_handle_t output
)
: mStatus(NO_INIT)
{
effect_uuid_t type;
effect_uuid_t *pType = NULL;
effect_uuid_t uuid;
effect_uuid_t *pUuid = NULL;
LOGV("Constructor string\n - type: %s\n - uuid: %s", typeStr, uuidStr);
if (typeStr != NULL) {
if (stringToGuid(typeStr, &type) == NO_ERROR) {
pType = &type;
}
}
if (uuidStr != NULL) {
if (stringToGuid(uuidStr, &uuid) == NO_ERROR) {
pUuid = &uuid;
}
}
mStatus = set(pType, pUuid, priority, cbf, user, sessionId, output);
// +++++++++++++++++++++++++++++AudioEffect::set+++++++++++++++++++++++++++++++++++
status_t AudioEffect::set(const effect_uuid_t *type,
const effect_uuid_t *uuid,
int32_t priority,
effect_callback_t cbf,
void* user,
int sessionId,
audio_io_handle_t output)
{
sp<IEffect> iEffect;
sp<IMemory> cblk;
int enabled;
LOGV("set %p mUserData: %p", this, user);
// 如果effect 对象已经被创建了,返回无效操作
if (mIEffect != 0) {
LOGW("Effect already in use");
return INVALID_OPERATION;
}
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
if (audioFlinger == 0) {
LOGE("set(): Could not get audioflinger");
return NO_INIT;
}
// 再一次检查type和uuid
if (type == NULL && uuid == NULL) {
LOGW("Must specify at least type or uuid");
return BAD_VALUE;
}
mPriority = priority;
// cbf其实是函数effectCallback
mCbf = cbf;
// +++++++++++++++++++++++++++++effectCallback+++++++++++++++++++++++++++++++++++
static void effectCallback(int event, void* user, void *info) {
effect_param_t *p;
int arg1 = 0;
int arg2 = 0;
jobject obj = NULL;
jbyteArray array = NULL;
jbyte *bytes;
bool param;
size_t size;
effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
LOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
callbackInfo,
callbackInfo->audioEffect_ref,
callbackInfo->audioEffect_class);
if (!user || !env) {
LOGW("effectCallback error user %p, env %p", user, env);
return;
}
switch (event) {
case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
if (info == 0) {
LOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
LOGV("EVENT_CONTROL_STATUS_CHANGED");
break;
case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
if (info == 0) {
LOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
LOGV("EVENT_ENABLE_STATUS_CHANGED");
break;
case AudioEffect::EVENT_PARAMETER_CHANGED:
if (info == 0) {
LOGW("EVENT_PARAMETER_CHANGED info == NULL");
goto effectCallback_Exit;
}
p = (effect_param_t *)info;
if (p->psize == 0 || p->vsize == 0) {
goto effectCallback_Exit;
}
// arg1 contains offset of parameter value from start of byte array
arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int);
size = arg1 + p->vsize;
array = env->NewByteArray(size);
if (array == NULL) {
LOGE("effectCallback: Couldn't allocate byte array for parameter data");
goto effectCallback_Exit;
}
bytes = env->GetByteArrayElements(array, NULL);
memcpy(bytes, p, size);
env->ReleaseByteArrayElements(array, bytes, 0);
obj = array;
LOGV("EVENT_PARAMETER_CHANGED");
break;
case AudioEffect::EVENT_ERROR:
LOGW("EVENT_ERROR");
break;
}
env->CallStaticVoidMethod(
callbackInfo->audioEffect_class,
fields.midPostNativeEvent,
callbackInfo->audioEffect_ref, event, arg1, arg2, obj);
effectCallback_Exit:
if (array) {
env->DeleteLocalRef(array);
}
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
// -----------------------------effectCallback-----------------------------------
// user其实是AudioEffectJniStorage对象中mCallbackData成员的地址
mUserData = user;
mSessionId = sessionId;
memset(&mDescriptor, 0, sizeof(effect_descriptor_t));
memcpy(&mDescriptor.type, EFFECT_UUID_NULL, sizeof(effect_uuid_t));
memcpy(&mDescriptor.uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t));
if (type != NULL) {
memcpy(&mDescriptor.type, type, sizeof(effect_uuid_t));
}
if (uuid != NULL) {
memcpy(&mDescriptor.uuid, uuid, sizeof(effect_uuid_t));
}
mIEffectClient = new EffectClient(this);
// +++++++++++++++++++++++++++++EffectClient+++++++++++++++++++++++++++++++++++
// Implements the IEffectClient interface
class EffectClient : public android::BnEffectClient, public android::IBinder::DeathRecipient
{
public:
EffectClient(AudioEffect *effect) : mEffect(effect){}
// IEffectClient
virtual void controlStatusChanged(bool controlGranted) {
mEffect->controlStatusChanged(controlGranted);
}
virtual void enableStatusChanged(bool enabled) {
mEffect->enableStatusChanged(enabled);
}
virtual void commandExecuted(uint32_t cmdCode,
uint32_t cmdSize,
void *pCmdData,
uint32_t replySize,
void *pReplyData) {
mEffect->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who) {mEffect->binderDied();}
private:
AudioEffect *mEffect;
};
// -----------------------------EffectClient-----------------------------------
iEffect = audioFlinger->createEffect(getpid(), (effect_descriptor_t *)&mDescriptor,
mIEffectClient, priority, output, mSessionId, &mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
LOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
return mStatus;
}
// ++++++++++++++++++++++++++AudioFlinger::createEffect++++++++++++++++++++++++++++++++++++++
sp<IEffect> AudioFlinger::createEffect(pid_t pid,
effect_descriptor_t *pDesc,
const sp<IEffectClient>& effectClient,
int32_t priority,
int output,
int sessionId,
status_t *status,
int *id,
int *enabled)
{
status_t lStatus = NO_ERROR;
sp<EffectHandle> handle;
effect_interface_t itfe;
effect_descriptor_t desc;
sp<Client> client;
wp<Client> wclient;
LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d",
pid, effectClient.get(), priority, sessionId, output);
if (pDesc == NULL) {
lStatus = BAD_VALUE;
goto Exit;
}
// check audio settings permission for global effects
if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
// +++++++++++++++++++++++++++++++audio_sessions+++++++++++++++++++++++++++++++++
// special audio session values
enum audio_sessions {
SESSION_OUTPUT_STAGE = -1, // session for effects attached to a particular output stream
// (value must be less than 0)
SESSION_OUTPUT_MIX = 0, // session for effects applied to output mix. These effects can
// be moved by audio policy manager to another output stream
// (value must be 0)
};
// -------------------------------audio_sessions---------------------------------
// Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
// that can only be created by audio policy manager (running in same process)
if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
// check recording permission for visualizer
if ((memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) &&
!recordingAllowed()) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
if (output == 0) {
if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) {
// output must be specified by AudioPolicyManager when using session
// AudioSystem::SESSION_OUTPUT_STAGE
// 如果sessionId为AudioSystem::SESSION_OUTPUT_STAGE,effect必须绑定到特定的output
lStatus = BAD_VALUE;
goto Exit;
} else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
// if the output returned by getOutputForEffect() is removed before we lock the
// mutex below, the call to checkPlaybackThread_l(output) below will detect it
// and we will exit safely
output = AudioSystem::getOutputForEffect(&desc);
// ++++++++++++++++++++++++++++++AudioSystem::getOutputForEffect++++++++++++++++++++++++++++++++++
audio_io_handle_t AudioSystem::getOutputForEffect(effect_descriptor_t *desc)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->getOutputForEffect(desc);
// +++++++++++++++++++++++++++++AudioPolicyService::getOutputForEffect+++++++++++++++++++++++++++++++++++
audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
Mutex::Autolock _l(mLock);
return mpPolicyManager->getOutputForEffect(desc);
// ++++++++++++++++++++++++++++++AudioPolicyManagerBase::getOutputForEffect++++++++++++++++++++++++++++++++++
audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(effect_descriptor_t *desc)
{
LOGV("getOutputForEffect()");
// apply simple rule where global effects are attached to the same output as MUSIC streams
// 所有的effect都会被绑定到MUSIT stream上,所以,这儿只是简单的将MUSIC stream对应的output返回
return getOutput(AudioSystem::MUSIC);
}
// ------------------------------AudioPolicyManagerBase::getOutputForEffect----------------------------------
}
// -----------------------------AudioPolicyService::getOutputForEffect-----------------------------------
}
// ------------------------------AudioSystem::getOutputForEffect----------------------------------
}
}
{
Mutex::Autolock _l(mLock);
if (!EffectIsNullUuid(&pDesc->uuid)) {
// 如果指定了uuid,则根据uuid寻找合适的effect descriptor
// if uuid is specified, request effect descriptor
lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
if (lStatus < 0) {
LOGW("createEffect() error %d from EffectGetDescriptor", lStatus);
goto Exit;
}
// ++++++++++++++++++++++++++++++EffectGetDescriptor++++++++++++++++++++++++++++++++++
路径:frameworks\base\media\libeffects\factory\EffectsFactory.c
int EffectGetDescriptor(effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
{
lib_entry_t *l = NULL;
effect_descriptor_t *d = NULL;
// init 函数我们已经看过
// 如果init已经被调用过,再次进入init函数并不会有真正的动作
// 也就是说,初始化操作只会进行一次
int ret = init();
if (ret < 0) {
return ret;
}
if (pDescriptor == NULL || uuid == NULL) {
return -EINVAL;
}
pthread_mutex_lock(&gLibLock);
ret = findEffect(uuid, &l, &d);
if (ret == 0) {
memcpy(pDescriptor, d, sizeof(effect_descriptor_t));
}
// +++++++++++++++++++++++++++++++findEffect+++++++++++++++++++++++++++++++++
int findEffect(effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc)
{
// gLibraryList是保存所有effect lib的列表
list_elem_t *e = gLibraryList;
lib_entry_t *l = NULL;
effect_descriptor_t *d = NULL;
int found = 0;
int ret = 0;
while (e && !found) {
l = (lib_entry_t *)e->object;
list_elem_t *efx = l->effects;
while (efx) {
d = (effect_descriptor_t *)efx->object;
// 比较是否与给定的uuid一致
if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
found = 1;
break;
}
efx = efx->next;
}
e = e->next;
}
if (!found) {
LOGV("findEffect() effect not found");
ret = -ENOENT;
} else {
LOGV("findEffect() found effect: %s in lib %s", d->name, l->path);
*lib = l;
*desc = d;
}
return ret;
}
// -------------------------------findEffect---------------------------------
pthread_mutex_unlock(&gLibLock);
return ret;
}
// ------------------------------EffectGetDescriptor----------------------------------
} else {
// if uuid is not specified, look for an available implementation
// of the required type in effect factory
// 如果没指定uuid,则寻找一个相同类型的可用的effect lib
if (EffectIsNullUuid(&pDesc->type)) {
LOGW("createEffect() no effect type");
lStatus = BAD_VALUE;
goto Exit;
}
uint32_t numEffects = 0;
effect_descriptor_t d;
bool found = false;
lStatus = EffectQueryNumberEffects(&numEffects);
if (lStatus < 0) {
LOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus);
goto Exit;
}
for (uint32_t i = 0; i < numEffects; i++) {
lStatus = EffectQueryEffect(i, &desc);
if (lStatus < 0) {
LOGW("createEffect() error %d from EffectQueryEffect", lStatus);
continue;
}
if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) {
// If matching type found save effect descriptor. If the session is
// 0 and the effect is not auxiliary, continue enumeration in case
// an auxiliary version of this effect type is available
// 如果session id为0,即AudioSystem::SESSION_OUTPUT_MIX,
// 若找到的effect为auxiliary,则退出,否则的话就继续查找
// 以防止存在可用的该类型的auxiliary的effect。
// 也就是说,对于AudioSystem::SESSION_OUTPUT_MIX的session id来说,
// 会优先使用auxiliary的effect。
found = true;
memcpy(&d, &desc, sizeof(effect_descriptor_t));
if (sessionId != AudioSystem::SESSION_OUTPUT_MIX ||
(desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
break;
}
}
}
if (!found) {
lStatus = BAD_VALUE;
LOGW("createEffect() effect not found");
goto Exit;
}
// For same effect type, chose auxiliary version over insert version if
// connect to output mix (Compliance to OpenSL ES)
// 如果不存在可用的该类型的auxiliary的effect,也只能勉强使用刚才找到的insert的了
if (sessionId == AudioSystem::SESSION_OUTPUT_MIX &&
(d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
memcpy(&desc, &d, sizeof(effect_descriptor_t));
}
}
// auxiliary的effect只能作用在AudioSystem::SESSION_OUTPUT_MIX的session id上。
// Do not allow auxiliary effects on a session different from 0 (output mix)
if (sessionId != AudioSystem::SESSION_OUTPUT_MIX &&
(desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
lStatus = INVALID_OPERATION;
goto Exit;
}
// return effect descriptor
memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
// If output is not specified try to find a matching audio session ID in one of the
// output threads.
// 如果没有指定output,则尝试在output threads中寻找匹配的Session ID。
// If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX
// because of code checking output when entering the function.
// 此时,如果output仍然为0,则说明session ID即不是SESSION_OUTPUT_STAGE,也不是SESSION_OUTPUT_MIX。
// 因为前面我们做过一个检查,如果output为0,若session ID是SESSION_OUTPUT_STAGE,则直接退出;
// 若session ID是SESSION_OUTPUT_MIX,则会调用函数AudioSystem::getOutputForEffect来get一个output
// (从最终的实现可知,函数AudioSystem::getOutputForEffect最终返回的是music stream对应的output)。
if (output == 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) {
output = mPlaybackThreads.keyAt(i);
break;
// ++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::hasAudioSession++++++++++++++++++++++++++++++++++++
uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
{
Mutex::Autolock _l(mLock);
uint32_t result = 0;
if (getEffectChain_l(sessionId) != 0) {
result = EFFECT_SESSION;
}
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
// 在创建track的时候,会传入一个session ID
if (sessionId == track->sessionId() &&
!(track->mCblk->flags & CBLK_INVALID_MSK)) {
result |= TRACK_SESSION;
break;
}
}
return result;
}
// ----------------------------AudioFlinger::PlaybackThread::hasAudioSession------------------------------------
}
}
// 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
// 如果找不到output,默认使用第一个。
// 如果相同session ID的trak被创建了,effect链表会被移动到新的track上。
// effect的移动工作在函数AudioFlinger::createTrack中完成见随后代码
if (output == 0 && mPlaybackThreads.size()) {
output = mPlaybackThreads.keyAt(0);
}
// +++++++++++++++++++++++++++++AudioFlinger::createTrack moveEffectChain_l+++++++++++++++++++++++++++++++++++
if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
if (mPlaybackThreads.keyAt(i) != output) {
// prevent same audio session on different output threads
uint32_t sessions = t->hasAudioSession(*sessionId);
if (sessions & PlaybackThread::TRACK_SESSION) {
lStatus = BAD_VALUE;
goto Exit;
}
// check if an effect with same session ID is waiting for a track to be created
if (sessions & PlaybackThread::EFFECT_SESSION) {
effectThread = t.get();
}
}
}
lSessionId = *sessionId;
} else {
// if no audio session id is provided, create one here
lSessionId = nextUniqueId();
if (sessionId != NULL) {
*sessionId = lSessionId;
}
}
LOGV("createTrack() lSessionId: %d", lSessionId);
track = thread->createTrack_l(client, streamType, sampleRate, format,
channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
// move effect chain to this output thread if an effect on same session was waiting
// for a track to be created
if (lStatus == NO_ERROR && effectThread != NULL) {
Mutex::Autolock _dl(thread->mLock);
Mutex::Autolock _sl(effectThread->mLock);
moveEffectChain_l(lSessionId, effectThread, thread, true);
// +++++++++++++++++++++++++++++AudioFlinger::moveEffectChain_l+++++++++++++++++++++++++++++++++++
// moveEffectChain_l mustbe called with both srcThread and dstThread mLocks held
status_t AudioFlinger::moveEffectChain_l(int session,
AudioFlinger::PlaybackThread *srcThread,
AudioFlinger::PlaybackThread *dstThread,
bool reRegister)
{
LOGV("moveEffectChain_l() session %d from thread %p to thread %p",
session, srcThread, dstThread);
sp<EffectChain> chain = srcThread->getEffectChain_l(session);
if (chain == 0) {
LOGW("moveEffectChain_l() effect chain for session %d not on source thread %p",
session, srcThread);
return INVALID_OPERATION;
}
// remove chain first. This is useful only if reconfiguring effect chain on same output thread,
// so that a new chain is created with correct parameters when first effect is added. This is
// otherwise unecessary as removeEffect_l() will remove the chain when last effect is
// removed.
srcThread->removeEffectChain_l(chain);
// 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
int dstOutput = dstThread->id();
sp<EffectChain> dstChain;
uint32_t strategy;
sp<EffectModule> effect = chain->getEffectFromId_l(0);
while (effect != 0) {
srcThread->removeEffect_l(effect);
dstThread->addEffect_l(effect);
// if the move request is not received from audio policy manager, the effect must be
// re-registered with the new strategy and output
if (dstChain == 0) {
dstChain = effect->chain().promote();
if (dstChain == 0) {
LOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
srcThread->addEffect_l(effect);
return NO_INIT;
}
strategy = dstChain->strategy();
}
if (reRegister) {
AudioSystem::unregisterEffect(effect->id());
AudioSystem::registerEffect(&effect->desc(),
dstOutput,
strategy,
session,
effect->id());
// ++++++++++++++++++++++++++++AudioSystem::registerEffect++++++++++++++++++++++++++++++++++++
status_t AudioSystem::registerEffect(effect_descriptor_t *desc,
audio_io_handle_t output,
uint32_t strategy,
int session,
int id)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->registerEffect(desc, output, strategy, session, id);
// ++++++++++++++++++++++++++++AudioPolicyService::registerEffect++++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::registerEffect(effect_descriptor_t *desc,
audio_io_handle_t output,
uint32_t strategy,
int session,
int id)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
return mpPolicyManager->registerEffect(desc, output, strategy, session, id);
// +++++++++++++++++++++++++++AudioPolicyManagerBase::registerEffect+++++++++++++++++++++++++++++++++++++
status_t AudioPolicyManagerBase::registerEffect(effect_descriptor_t *desc,
audio_io_handle_t output,
uint32_t strategy,
int session,
int id)
{
ssize_t index = mOutputs.indexOfKey(output);
if (index < 0) {
LOGW("registerEffect() unknown output %d", output);
return INVALID_OPERATION;
}
if (mTotalEffectsCpuLoad + desc->cpuLoad > getMaxEffectsCpuLoad()) {
LOGW("registerEffect() CPU Load limit exceeded for Fx %s, CPU %f MIPS",
desc->name, (float)desc->cpuLoad/10);
return INVALID_OPERATION;
}
if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) {
LOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB",
desc->name, desc->memoryUsage);
return INVALID_OPERATION;
}
mTotalEffectsCpuLoad += desc->cpuLoad;
mTotalEffectsMemory += desc->memoryUsage;
LOGV("registerEffect() effect %s, output %d, strategy %d session %d id %d",
desc->name, output, strategy, session, id);
LOGV("registerEffect() CPU %d, memory %d", desc->cpuLoad, desc->memoryUsage);
LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
EffectDescriptor *pDesc = new EffectDescriptor();
memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t));
pDesc->mOutput = output;
pDesc->mStrategy = (routing_strategy)strategy;
pDesc->mSession = session;
mEffects.add(id, pDesc);
return NO_ERROR;
}
// ---------------------------AudioPolicyManagerBase::registerEffect-------------------------------------
}
// ----------------------------AudioPolicyService::registerEffect------------------------------------
}
// ----------------------------AudioSystem::registerEffect------------------------------------
}
effect = chain->getEffectFromId_l(0);
}
return NO_ERROR;
}
// -----------------------------AudioFlinger::moveEffectChain_l-----------------------------------
}
// -----------------------------AudioFlinger::createTrack moveEffectChain_l-----------------------------------
}
LOGV("createEffect() got output %d for effect %s", output, desc.name);
PlaybackThread *thread = checkPlaybackThread_l(output);
if (thread == NULL) {
LOGE("createEffect() unknown output thread");
lStatus = BAD_VALUE;
goto Exit;
}
// TODO: allow attachment of effect to inputs
wclient = mClients.valueFor(pid);
if (wclient != NULL) {
client = wclient.promote();
} else {
client = new Client(this, pid);
mClients.add(pid, client);
}
// create effect on selected output trhead
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
&desc, enabled, &lStatus);
if (handle != 0 && id != NULL) {
*id = handle->id();
}
// ++++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::createEffect_l++++++++++++++++++++++++++++++++++
// PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l(
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
int32_t priority,
int sessionId,
effect_descriptor_t *desc,
int *enabled,
status_t *status
)
{
sp<EffectModule> effect;
sp<EffectHandle> handle;
status_t lStatus;
sp<Track> track;
sp<EffectChain> chain;
bool chainCreated = false;
bool effectCreated = false;
bool effectRegistered = false;
if (mOutput == 0) {
LOGW("createEffect_l() Audio driver not initialized.");
lStatus = NO_INIT;
goto Exit;
}
// Do not allow auxiliary effect on session other than 0
if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY &&
sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
desc->name, sessionId);
lStatus = BAD_VALUE;
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 == AudioSystem::SESSION_OUTPUT_MIX && mType != MIXER) {
LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
desc->name, sessionId);
lStatus = BAD_VALUE;
goto Exit;
}
LOGV("createEffect_l() thread %p effect %s on session %d", this, desc->name, sessionId);
{ // scope for mLock
Mutex::Autolock _l(mLock);
// check for existing effect chain with the requested audio session
chain = getEffectChain_l(sessionId);
if (chain == 0) {
// create a new chain for this session
LOGV("createEffect_l() new effect chain for session %d", sessionId);
chain = new EffectChain(this, sessionId);
addEffectChain_l(chain);
// +++++++++++++++++++++++++++AudioFlinger::PlaybackThread::addEffectChain_l+++++++++++++++++++++++++++++++++++++
status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
{
int session = chain->sessionId();
int16_t *buffer = mMixBuffer;
bool ownsBuffer = false;
LOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
if (session > 0) {
// Only one effect chain can be present in direct output thread and it uses
// the mix buffer as input
if (mType != DIRECT) {
size_t numSamples = mFrameCount * mChannelCount;
buffer = new int16_t[numSamples];
memset(buffer, 0, numSamples * sizeof(int16_t));
LOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session);
ownsBuffer = true;
}
// Attach all tracks with same session ID to this chain.
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (session == track->sessionId()) {
LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
track->setMainBuffer(buffer);
}
}
// indicate all active tracks in the chain
for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
sp<Track> track = mActiveTracks[i].promote();
if (track == 0) continue;
if (session == track->sessionId()) {
LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
chain->startTrack();
}
}
}
chain->setInBuffer(buffer, ownsBuffer);
chain->setOutBuffer(mMixBuffer);
// Effect chain for session AudioSystem::SESSION_OUTPUT_STAGE is inserted at end of effect
// chains list in order to be processed last as it contains output stage effects
// Effect chain for session AudioSystem::SESSION_OUTPUT_MIX is inserted before
// session AudioSystem::SESSION_OUTPUT_STAGE to be processed
// after track specific effects and before output stage
// It is therefore mandatory that AudioSystem::SESSION_OUTPUT_MIX == 0 and
// that AudioSystem::SESSION_OUTPUT_STAGE < AudioSystem::SESSION_OUTPUT_MIX
// Effect chain for other sessions are inserted at beginning of effect
// chains list to be processed before output mix effects. Relative order between other
// sessions is not important
size_t size = mEffectChains.size();
size_t i = 0;
for (i = 0; i < size; i++) {
if (mEffectChains[i]->sessionId() < session) break;
}
mEffectChains.insertAt(chain, i);
return NO_ERROR;
}
// ---------------------------AudioFlinger::PlaybackThread::addEffectChain_l-------------------------------------
chain->setStrategy(getStrategyForSession_l(sessionId));
chainCreated = true;
} else {
effect = chain->getEffectFromDesc_l(desc);
}
LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get());
if (effect == 0) {
int id = mAudioFlinger->nextUniqueId();
// Check CPU and memory usage
lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
if (lStatus != NO_ERROR) {
goto Exit;
}
effectRegistered = true;
// create a new effect module if none present in the chain
effect = new EffectModule(this, chain, desc, id, sessionId);
lStatus = effect->status();
if (lStatus != NO_ERROR) {
goto Exit;
}
lStatus = chain->addEffect_l(effect);
if (lStatus != NO_ERROR) {
goto Exit;
}
effectCreated = true;
// ++++++++++++++++++++++++++++AudioFlinger::EffectModule::EffectModule++++++++++++++++++++++++++++++++++++
AudioFlinger::EffectModule::EffectModule(const wp<ThreadBase>& wThread,
const wp<AudioFlinger::EffectChain>& chain,
effect_descriptor_t *desc,
int id,
int sessionId)
: mThread(wThread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL),
mStatus(NO_INIT), mState(IDLE)
{
LOGV("Constructor %p", this);
int lStatus;
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
return;
}
PlaybackThread *p = (PlaybackThread *)thread.get();
memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
// create effect engine from effect factory
// 这个函数在看函数queryEffects代码的时候已经接触过
mStatus = EffectCreate(&desc->uuid, sessionId, p->id(), &mEffectInterface);
if (mStatus != NO_ERROR) {
return;
}
lStatus = init();
if (lStatus < 0) {
mStatus = lStatus;
goto Error;
}
LOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
return;
Error:
EffectRelease(mEffectInterface);
mEffectInterface = NULL;
LOGV("Constructor Error %d", mStatus);
}
// ----------------------------AudioFlinger::EffectModule::EffectModule------------------------------------
effect->setDevice(mDevice);
// +++++++++++++++++++++++++++AudioFlinger::EffectModule::setDevice+++++++++++++++++++++++++++++++++++++
status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
{
Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
// convert device bit field from AudioSystem to EffectApi format.
device = deviceAudioSystemToEffectApi(device);
if (device == 0) {
return BAD_VALUE;
}
status_t cmdStatus;
uint32_t size = sizeof(status_t);
// 在EffectModule的构造函数中,调用函数EffectCreate对mEffectInterface进行赋值:
// mStatus = EffectCreate(&desc->uuid, sessionId, p->id(), &mEffectInterface);
// 此处调用的command函数其实是调用的具体的effect lib中的command 函数
// 如EffectReverb 中的Reverb_Command函数。
status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_SET_DEVICE,
sizeof(uint32_t),
&device,
&size,
&cmdStatus);
if (status == NO_ERROR) {
status = cmdStatus;
}
}
return status;
}
// ---------------------------AudioFlinger::EffectModule::setDevice-------------------------------------
effect->setMode(mAudioFlinger->getMode());
}
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
lStatus = effect->addHandle(handle);
if (enabled) {
*enabled = (int)effect->isEnabled();
}
}
Exit:
if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
Mutex::Autolock _l(mLock);
if (effectCreated) {
chain->removeEffect_l(effect);
}
if (effectRegistered) {
AudioSystem::unregisterEffect(effect->id());
}
if (chainCreated) {
removeEffectChain_l(chain);
}
handle.clear();
}
if(status) {
*status = lStatus;
}
return handle;
}
// ------------------------------AudioFlinger::PlaybackThread::createEffect_l----------------------------------
}
Exit:
if(status) {
*status = lStatus;
}
return handle;
}
// --------------------------AudioFlinger::createEffect--------------------------------------
mEnabled = (volatile int32_t)enabled;
mIEffect = iEffect;
cblk = iEffect->getCblk();
if (cblk == 0) {
mStatus = NO_INIT;
LOGE("Could not get control block");
return mStatus;
}
mIEffect = iEffect;
mCblkMemory = cblk;
mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer());
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblk->buffer = (uint8_t *)mCblk + bufOffset;
iEffect->asBinder()->linkToDeath(mIEffectClient);
LOGV("set() %p OK effect: %s id: %d status %d enabled %d, ", this, mDescriptor.name, mId, mStatus, mEnabled);
return mStatus;
}
// -----------------------------AudioEffect::set-----------------------------------
}
// ----------------------------native AudioEffect------------------------------------
lStatus = translateError(lpAudioEffect->initCheck());
if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
LOGE("AudioEffect initCheck failed %d", lStatus);
goto setup_failure;
}
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
if (nId == NULL) {
LOGE("setup: Error retrieving id pointer");
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
nId[0] = lpAudioEffect->id();
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
nId = NULL;
if (typeStr) {
env->ReleaseStringUTFChars(type, typeStr);
typeStr = NULL;
}
if (uuidStr) {
env->ReleaseStringUTFChars(uuid, uuidStr);
uuidStr = NULL;
}
// get the effect descriptor
desc = lpAudioEffect->descriptor();
AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
jdescType = env->NewStringUTF(str);
AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
jdescUuid = env->NewStringUTF(str);
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
jdescConnect = env->NewStringUTF("Auxiliary");
} else {
jdescConnect = env->NewStringUTF("Insert");
}
jdescName = env->NewStringUTF(desc.name);
jdescImplementor = env->NewStringUTF(desc.implementor);
jdesc = env->NewObject(fields.clazzDesc,
fields.midDescCstor,
jdescType,
jdescUuid,
jdescConnect,
jdescName,
jdescImplementor);
env->DeleteLocalRef(jdescType);
env->DeleteLocalRef(jdescUuid);
env->DeleteLocalRef(jdescConnect);
env->DeleteLocalRef(jdescName);
env->DeleteLocalRef(jdescImplementor);
if (jdesc == NULL) {
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
goto setup_failure;
}
env->SetObjectArrayElement(javadesc, 0, jdesc);
env->SetIntField(thiz, fields.fidNativeAudioEffect, (int)lpAudioEffect);
env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
return AUDIOEFFECT_SUCCESS;
// failures:
setup_failure:
if (nId != NULL) {
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
if (lpAudioEffect) {
delete lpAudioEffect;
}
env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);
if (lpJniStorage) {
delete lpJniStorage;
}
env->SetIntField(thiz, fields.fidJniData, 0);
if (uuidStr != NULL) {
env->ReleaseStringUTFChars(uuid, uuidStr);
}
if (typeStr != NULL) {
env->ReleaseStringUTFChars(type, typeStr);
}
return lStatus;
}
// -------------------------android_media_AudioEffect_native_setup---------------------------------------
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
Log.e(TAG, "Error code " + initResult
+ " when initializing AudioEffect.");
switch (initResult) {
case ERROR_BAD_VALUE:
throw (new IllegalArgumentException("Effect type: " + type
+ " not supported."));
case ERROR_INVALID_OPERATION:
throw (new UnsupportedOperationException(
"Effect library not loaded"));
default:
throw (new RuntimeException(
"Cannot initialize effect engine for type: " + type
+ "Error: " + initResult));
}
}
mId = id[0];
mDescriptor = desc[0];
synchronized (mStateLock) {
mState = STATE_INITIALIZED;
}
}
// --------------------------------AudioEffect--------------------------------
assertNotNull(msg + ": could not create AudioEffect", effect);
try {
assertTrue(msg +": invalid effect ID", (effect.getId() != 0));
} catch (IllegalStateException e) {
msg = msg.concat(": AudioEffect not initialized");
result = false;
} finally {
effect.release();
}
} catch (IllegalArgumentException e) {
msg = msg.concat(": Effect not found: "+desc[0].name);
result = false;
} catch (UnsupportedOperationException e) {
msg = msg.concat(": Effect library not loaded");
result = false;
}
assertTrue(msg, result);
}
###########################################################
&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
在创建AudioEffect的时候:
1、如果指定了uuid,则会根据uuid 寻找匹配的effect lib来创建effect。
2、若没有指定uuid而指定了type,则会寻找相同type中可用的effect lib。
注意,如果session id为SESSION_OUTPUT_MIX,则优先使用auxiliary的effect。
在指定的type中没有可用的auxiliary的effect的情况下,才会使用insert的effect。
effect lib都被注册到一个列表中。
EffectsFactory中的init函数会将build-in的effect lib添加到该列表。
用户可以调用函数EffectLoadLibrary/EffectUnloadLibrary来注册/删除effect lib。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&