这里的内部跳转链接好像无效……这里会好一点:https://blog.csdn.net/whshiyun/article/details/79806870
#wmd-preview h1 { color: #0077bb }
android effects笔记
android
sound
effect
- android effects笔记
- 1 相关类及成员说明
- 1.1 AudioPolicyService类
- 1.2 AudioPolicyEffects类
- 1.3 audioEffect相关
- 1.3.1 audioEffect的创建位置
- 1.3.2 audioEffect的关键方法及属性
- 1.3.3 audioEffect的创建过程
- 1.3.4 audioEffect的作用
- 2 effect数据结构及关系
- 2.1 AudioPolicyService与AudioEffect关系图
- 2.2 AudioEffect与AudioModule关系图
- 2.3 audioModule数据结构图
- 3 effect启动加载过程
- 4 effect挂载到音频流过程
- 5 effect工作过程
- 附1 audio_effects.conf文件
- 附2 effect_entry结构分析
- 附3 effectsFactory分析
- 附4 addOutputSessionEffects调用过程
- 附5 effectClient分析
- 1 相关类及成员说明
全文不涉及effect buf相关的内容,这里只记录effect的创建以及接口调用相关内容,effect buf的内容下次有时间单独来记录一下
如果不关心audio effect相关类或者刚开始接触这块的话,可以直接看后面-->effect数据结构及关系,先有个感官上的认识
1 相关类及成员说明
1.1 AudioPolicyService类
AudioPolicyService类中与effect相关的仅仅只有一个成员变量:
sp<AudioPolicyEffects> mAudioPolicyEffects;
AudioPolicyService在创建时会一起创建一个AudioPolicyEffects,并把创建的AudioPolicyEffects保存在mAudioPolicyEffects变量中。
AudioPolicyService在提供给其他地方调用的一些方法中,通过mAudioPolicyEffects,调用了AudioPolicyEffects中的方法,这些方法都在AudioPolicyInterfaceImpl.cpp文件中实现。
对于其他模块,AudioPolicyService提供的对effect的操作仅仅只有几个查询接口,其余再就没什么用了……(AudioPolicyEffects中最核心的创建effect的方法还是AudioPolicyService自己调用的)
所以,这里可以看出,其实真正对于audio effect的操作,根本就跟AudioPolicyService、AudioPolicyEffects没有什么关系……
1.2 AudioPolicyEffects类
先不看方法,里面关键的成员主要就是下面四个
// Automatic input effects are configured per audio_source_t
KeyedVector<audio_source_t,EffectDescVector*> mInputSources;
// Automatic input effects are unique for audio_io_handle_t
KeyedVector<audio_io_handle_t,EffectVector*> mInputs;
// Automatic output effects are organized per audio_stream_type_t
KeyedVector<audio_stream_type_t,EffectDescVector*> mOutputStreams;
// Automatic output effects are unique for audiosession ID
KeyedVector<audio_session_t,EffectVector*> mOutputSessions;
这里主要记录一下mOutputStreams和mOutputSessions两个,另外两个其实就是对应的input。
- EffectDescVector:effect的描述向量,里面记录的是从config文件中读取的effect信息,不是真实可执行的effect
- EffectVector:可执行的effect向量,这里面记录的所有effect都是根据effect描述而创建的实际可执行的effect
- mOutputStreams:把effect与stream type对应绑定的一个键值对。这里要明确三个问题:1、stream type是什么,2、effect是哪来的,里面有些什么。3、这个键值对有什么用?
1、stream type
定义在文件:audio.h中,stream type定义了android所支持的音频流类型,在app侧打开一个音频流时需要指定该stream的类型。
stream type与effect的描述文件(audio_effects.conf)的对应是通过函数
audio_stream_type_tAudioPolicyEffects::streamNameToEnum(constchar*name)
实现的。把描述文件中的字符串如“ring”等,翻译成stream type类型,然后进行匹配,实现stream type与effect的对应绑定
2、effect
effect的类型为:EffectDescVector
在了解EffectDescVector之前需要对audio_effects.conf文件有所了解,关于audio_effects.conf的记录,见后面的附录:audio_effects.conf文件简析,熟悉audio_effects.conf的话,可以直接跳过……
effect里面主要就是存了effet的name,uuid以及params,其中name和uuid比较简单,这里主要记录一下parameters,语言不好描述,这里用一张图来记录:
最下面的键值对实际存储方式与图上画的是不一样的,图上那样画是为了体现他们是键值对,实际的存储方式详见:"audio_effect.h"文件中的typedef struct effect_param_s结构体的注释。
最后,mOutputStreams里面所存储的params信息好像完全没有被使用?!!
至于param的作用,感觉是记录的该effect部分参数的初始值,理论上应该是创建该effect后通过command()配置下去才对
3、作用
其实,audioPolicyEffects类里面解析audio_effects.conf文件我觉得从软件架构上来说是不合理的……不知道google那帮人是怎么想的,也许是我还没有领会他们的用意。
既然我觉得他不合理,是有我个人的理由的,虽然不一定对。从代码中可以看到,在audioPolicyEffects类中,解析了audio_effects.conf文件,并且记录了一个简单的EffectDescVector,这里做解析的函数是audioPolicyEffects类中自己的方法:loadAudioEffectConfig()。可是“effectsFactory.c”中也有一个类似的函数loadEffectConfigFile(),里面实现的内容几乎一样,只是effectsFactory中对audio_effect.conf文件的解析更加彻底,并且建立了一套自己的管理体系,具体分析见这里:effect_entry结构分析。既然这里有了effectsFactory这套体系,是不是可以考虑把audioPolicyEffects类里面的mOutputStreams东西纳入effectsFactory中?这里仅为个人揣测……
那么整个audioPolicyEffects类或者mOutputStreams存在意义到底在哪里?从代码上看,好像最大的作用就是为了支持status_t AudioPolicyEffects::addOutputSessionEffects()
这个函数,这个函数的具体作用就是实际的为指定的session加载音效了,是整个effect中实际创建音效的地方,该函数在doStartOutput()(audioPolicyInterfaceImpl.cpp)中被调用,这里具体的调用过程见附录:addOutputSessionEffects调用过程。
既然addOutputSessionEffects是整个audioPolicyEffects类的核心方法,这里就简单的分析一下。
status_tAudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output,
audio_stream_type_t stream,
audio_session_t audioSession)
{
……
/* 从mOutputStreams中找到对应stream type的预置effects */
ssize_t index = mOutputStreams.indexOfKey(stream);
……
/* 查找新加的session是否已经添加过了 */
ssize_t idx = mOutputSessions.indexOfKey(audioSession);
/* 与该session想对应的,实际的effect的向量列表 */
EffectVector*procDesc;
/* 如果新加的session没有添加过,则创建新的session与EffectVector的对应关系,并添加到mOutputSessions中 */
if(idx <0){
procDesc =newEffectVector(audioSession);
mOutputSessions.add(audioSession, procDesc);
}else{
// EffectVector is existing and we just need to increase ref count
procDesc = mOutputSessions.valueAt(idx);
}
procDesc->mRefCount++;
/* 如果该session对应的effects描述向量procDesc是新建的,则实际创建procDesc中所描述的每一个effects */
if(procDesc->mRefCount ==1){
/* 从mOutputStreams中取出对应流类型的effects描述符 */
Vector<EffectDesc*> effects = mOutputStreams.valueAt(index)->mEffects;
/* 把每个描述符所描述的effects实际创建出来,并挂载到对应的threads中 */
for(size_t i =0; i < effects.size(); i++){
EffectDesc*effect = effects[i];
/* 根据effect的描述符,实际创建一个effect */
sp<AudioEffect> fx =newAudioEffect(NULL,String16("android"),&effect->mUuid,0,0,0,
audioSession, output);
……
/* 把创建好的effect添加到EffectVector向量中 */
procDesc->mEffects.add(fx);
}
/* 设置该向量为使能 */
procDesc->setProcessorEnabled(true);
}
return status;
}
总结来说,上述函数就做了3件事:
1、检查mOutputSessions中,对应session的EffectVector是否已经创建过了,如果没有创建,则创建EffectVector,并且在mOutputSessions中建立当前session与EffectVector的映射
2、根据输入的stream type,从mOutputStreams中创建所有相应的effect
3、把创建的effect添加到EffectVector列表中
关于mOutputSessions,到底有什么用?目前来看,只是在函数
status_t AudioPolicyEffects::queryDefaultOutputSessionEffects()
中被调用,这个函数应该是AudioPolicyEffects提供给上层查询使用的,底层没有调用。除此之外,mOutputSessions就再也没有什么用了……
所以,AudioPolicyEffects类主要就是充当了创建effect的接口,其余再没有什么实质性的作用……包括上层对于这个东西其实也没有什么依赖性,仅仅能够从AudioPolicyEffects中查询一下默认被挂载的音效,至于对音效的控制什么的,完全跟AudioPolicyEffects没有一点关系。
1.3 audioEffect相关
1.3.1 audioEffect的创建位置
1、对于默认挂载的effect,对应的audioEffect是被AudioPolicyEffects所创建的,个人感觉这也是AudioPolicyEffects最大的作用了……
2、android_Effect.cpp里面,关于bassboost(audio_effects.conf里面有定义)之类的音效好像是在这里怎么搞出来的,因为暂时不关心,所以这里暂时不深入研究
3、android_media_AudioEffect.cpp,这里是给上层app提供的effect操作的接口了,这其实就是个jni的接口,里面提供了创建effect,设置effect,使能effect等等相关操作,如果app希望启动一个非默认effect或者去设置已经启动了的effect,应该就是从这里入手~这里面所有函数的操作都是通过调用AudioEffect类的对应方法来完成的。
1.3.2 audioEffect的关键方法及属性
属性:
sp<IEffect> mIEffect; // IEffect binder interface
mIEffect这个属性应该是整个audioEffect中最核心的属性了,mIEffect指向该effect的实体接口。换句话说就是:audioEffect只是一张皮,用来给上层应用调用的(比如android_media_AudioEffect.cpp中),真正的执行者其实是另有其人,这个真实的effect实体实在创建audioEffect时伴随创建的,创建过程在audioEffect的创建过程中。
;
sp<EffectClient> mIEffectClient; // IEffectClient implementation
记录该audioEffect对应的EffectClient,每一个audioEffect都有一个唯一的EffectClient,EffectClient的创建也是伴随audioEffect创建时一起创建的。EffectClient的作用从代码中看,好像是给底层提供audioEffect的调用接口的,effectClient相关的见:effectClient
方法:
- 除了set()方法外,其余真正对effect产生作用的方法,其实都是调用的IEffect中对应的方法,所以这里就暂时不管,后面会简要记录set方法。
1.3.3 audioEffect的创建过程
在addOutputSessionEffects()函数中最关键的一步就是new AudioEffect(),创建一个audioEffect。创建audioEffect的实际执行实在set()方法中完成,这里简要分析一下:
status_tAudioEffect::set(consteffect_uuid_t*type,
consteffect_uuid_t*uuid,
int32_t priority,
effect_callback_t cbf,
void* user,
audio_session_t sessionId,
audio_io_handle_t io)
{
/* 创建一个effectClient */
mIEffectClient =newEffectClient(this);
/* 创建一个effect实体,并且把该effect的实体接口返回给iEffect,这里其实就是返回了一个EffectHandle类型,这里面提供了对该effect实体的实际操作方法 */
iEffect = audioFlinger->createEffect((effect_descriptor_t*)&mDescriptor,
mIEffectClient, priority, io, mSessionId, mOpPackageName,&mStatus,&mId,&enabled);
/* 设置该effect为使能 */
mEnabled =(volatileint32_t)enabled;
/* effect内存相关操作,暂时不分析*/
cblk = iEffect->getCblk();
/* 保存该effect实体接口 */
mIEffect = iEffect;
/* effect内存相关操作,暂时不分析*/
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;
/* binder相关操作,暂时不分析 */
IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient);
mClientPid =IPCThreadState::self()->getCallingPid();
}
这里面最核心的一部就是audioFlinger->createEffect()
这句话,这句话里面最有意义的一句话又是:handle = thread->createEffect_l()
,所以这里稍微来分析一下createEffect_l()这个函数:
sp<AudioFlinger::EffectHandle>AudioFlinger::ThreadBase::createEffect_l(
const sp<AudioFlinger::Client>& client,
const sp<IEffectClient>& effectClient,
int32_t priority,
audio_session_t sessionId,
effect_descriptor_t*desc,
int*enabled,
status_t*status,
bool pinned)
{
sp<EffectModule> effect;
sp<EffectHandle> handle;
sp<EffectChain> chain;
{
// check for existing effect chain with the requested audio session
chain = getEffectChain_l(sessionId);
if(chain ==0){
// create a new chain for this session
chain =newEffectChain(this, sessionId);
addEffectChain_l(chain);
chain->setStrategy(getStrategyForSession_l(sessionId));
chainCreated =true;
}else{
/* 如果该effect chain已经存在,则在chain里面找一下,看看这个effect module是不是已经被创建了 */
effect = chain->getEffectFromDesc_l(desc);
}
/* 如果该effect module没有被创建,则创建这个effect的实体,即:effectModule */
if(effect ==0){
/* 为即将被创建的effectModule获取一个唯一识别号 */
audio_unique_id_t id = mAudioFlinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
// Check CPU and memory usage
/* 这里是因为AudioPolicyManager类里面有一个成员:EffectDescriptorCollection mEffects
这里的注册,其实就是把这个effect封装成一个EffectDescriptor类型,并保存到mEffects中,
该函数的具体实现为:status_t AudioPolicyManager::registerEffect(),EffectDescriptor
主要在AudioPolicyManager中做一些判断的时候使用*/
lStatus =AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
// create a new effect module if none present in the chain
lStatus = chain->createEffect_l(effect,this, desc, id, sessionId, pinned);
/* 给effect module设置一些必要参数 */
effect->setDevice(mOutDevice);
effect->setDevice(mInDevice);
effect->setMode(mAudioFlinger->getMode());
effect->setAudioSource(mAudioSource);
}
/* 创建effectHandle,这里可以看出,其实一个effectModule可以对应多个effectHandle,
所以effectHandle是针对每个使用者而独立存在的,而多个使用者公用一个effectModule */
handle =newEffectHandle(effect, client, effectClient, priority);
/* 把新创建的effectHandle加入到该effectModule所维护的handle列表中 */
if(lStatus == OK){
lStatus = effect->addHandle(handle.get());
}
}
Exit:
return handle;
}
上述代码中其实还是没有看到effectModule被创建的地方,只看到了是被effect chain所创建的,这里继续看一下effect chain中的createEffect_l函数:
status_tAudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
ThreadBase*thread,
effect_descriptor_t*desc,
int id,
audio_session_t sessionId,
bool pinned)
{
Mutex::Autolock _l(mLock);
/* 实例化一个EffectModule,这里才是真正创建一个effectModule,但是这里还并不是真正把effect创建出来的
地方,effectModule其实还不是真正的effect实体,它应该算是effect实体的一个代理,effect实体是在
EffectModule的构造函数中被真正创建的 */
effect =newEffectModule(thread,this, desc, id, sessionId, pinned);
status_t lStatus = effect->status();
if(lStatus == NO_ERROR){
/* 把创建的effectModule存入effect链中 */
lStatus = addEffect_ll(effect);
}
if(lStatus != NO_ERROR){
effect.clear();
}
return lStatus;
}
根据代码里面写的注释,这里需要继续看看EffectModule的构造函数:
AudioFlinger::EffectModule::EffectModule(ThreadBase*thread,
const wp<AudioFlinger::EffectChain>& chain,
effect_descriptor_t*desc,
int id,
audio_session_t sessionId,
bool pinned)
: mPinned(pinned),
mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
mDescriptor(*desc),
// mConfig is set by configure() and not used before then
mEffectInterface(NULL),
mStatus(NO_INIT), mState(IDLE),
// mMaxDisableWaitCnt is set by configure() and not used before then
// mDisableWaitCnt is set by process() and updateState() and not used before then
mSuspended(false),
mAudioFlinger(thread->mAudioFlinger)
{
// create effect engine from effect factory
mStatus =EffectCreate(&desc->uuid, sessionId, thread->id(),&mEffectInterface);
setOffloaded((thread->type()==ThreadBase::OFFLOAD ||
(thread->type()==ThreadBase::DIRECT && thread->mIsDirectPcm)), thread->id());
}
其实EffectModule的构造函数里面真正起作用的就两句话,第一句话,真正的创建effect实体,第二句话设置offload属性,至于第二句话的意义暂时不去深究,总之就是想effect实体发送了一条设置offload的command。
这里EffectCreate()函数引出了一个非常重要的东西EffectFactory。关于effectFactory详见:effectFactory分析。EffectCreate()函数源码如下:
intEffectCreate(consteffect_uuid_t*uuid,int32_t sessionId,int32_t ioId,effect_handle_t*pHandle)
{
/* 初始化真个effectFactory,该函数在多出被调用,只有第一次被调用时才实际执行初始化
其余时候再次被调用则直接返回*/
ret = init();
/* 从配置文件(audio_effects.conf)读入的effects信息中查找所要创建的effect
如果找不到该effect则退出,找到了侧继续创建effect */
ret = findEffect(NULL, uuid,&l,&d);
if(ret <0){
// Sub effects are not associated with the library->effects,
// so, findEffect will fail. Search for the effect in gSubEffectList.
ret = findSubEffect(uuid,&l,&d);
if(ret <0){
gotoexit;
}
}
/* 这里就是调用的effect文件中的create_effect接口(详见audio_effect.h文件,里面的注释已经说的很清楚了)
该接口是每个effect都必须实现的标准接口,这里的返回值其实是itfe,itfe是一个effect_handle_t类型的结构体
该结构体就是这个effect的实际操作接口,该接口的定义也在audio_effect.h文件中,也是每个effect必须实现的 */
// create effect in library
ret = l->desc->create_effect(uuid, sessionId, ioId,&itfe);
/* 创建并填写一个effect_entry_t结构,该结构中就存储了itfe */
// add entry to effect list
fx =(effect_entry_t*)malloc(sizeof(effect_entry_t));
fx->subItfe = itfe;
/* 这里其实是把itfe封装了一次,让effectFactory统一调用itfe,其实落脚点还是itfe…… */
if((*itfe)->process_reverse != NULL){
fx->itfe =(struct effect_interface_s *)&gInterfaceWithReverse;
ALOGV("EffectCreate() gInterfaceWithReverse");
}else{
fx->itfe =(struct effect_interface_s *)&gInterface;
ALOGV("EffectCreate() gInterface");
}
fx->lib = l;
/* 把fx存入gEffectList链表中,该链表为effectFactory自己的全局链表 */
e =(list_elem_t*)malloc(sizeof(list_elem_t));
e->object= fx;
e->next= gEffectList;
gEffectList = e;
/* 其实就是把itfe传了出去 */
*pHandle =(effect_handle_t)fx;
}
至此,一个完整的effect就创建完成了……也与effect lib所提供的接口关联上了。
1.3.4 audioEffect的作用
那么总结下来,audioEffect除了创建了effect实体(即EffectModule)外也没有什么实质性的作用,主要是充当了接口功能,为真正的effect实体提供一个外部接口,并通过mIEffect来调用EffectModule的相关操作,EffectModule通过mIEffectClient来通知外界操作执行完毕。
2 effect数据结构及关系
宏观上来说,android的音效其实可以分为三层:音效框架、音效工厂和plugin音效。这里的名称是我自己起的……不是官方的,大概表述一下应该是这样:
- android音效框架我认为主要职责是让effect系统嵌入到音频框架中,包括对上层app的接口封装,音效的加载,音效的调用、音效的配置以及音频数据流内存管理等等,自己不执行与音效算法相关的任何事情,为音效的plugin提供框架上的支撑
- EffectFactory 这一层起到承上启下的作用,它不关心任何与音频框架相关的事物,只是负责管理好所有的plugin音效,提供对音效算法调用、加载的接口,为音效框架提供操作接口
- plugin effect 这一层由第三方厂家实现(android自己也有部分,但其实都可以归结为外挂的东西,可以不属于android源码本身)。这一层里面,需要实现audio_effect.h中所描述的接口,并且在audio_effect.conf文件中增加相关内容,就可以通过android音效框架提供的接口调用自己的音效了。
这里,只记录和分析android音效框架的数据结构!!EffectFactory相关结构见:effectFactory分析
因为整个effect的数据关系图非常庞大,所以这里分成3个部分来呈现。这三个部分的划分是自上而下的,也就是从提供的对外接口一直到对effect lib的调用。所有关系图中,默认只画了output流,input跟output类似,暂时不画出来。
2.1 AudioPolicyService与AudioEffect关系图
这一部分是最顶层的,直接面对外部接口调用的,其关系图如下:
这里就简单记录一下EffectDescr到AudioEffect的过程:
mOutputCommandThread线程收到START_OUTPUT消息,调用AudioPolicyService::doStartOutput()->AudioPolicyEffects::addOutputSessionEffects()->new AudioEffect()
2.2 AudioEffect与AudioModule关系图
AudioEffect类关系图点击这里:AudioEffect类关系图,往下面拖一点就是。
2.3 audioModule数据结构图
audioModule数据结构里面实际执行操作的其实是effect_entry_t,但effect_entry_t跟audioEffect结构关系不大,所以就不在这里列出,关于effect_entry_t见附录:effect_entry结构分析。
关于audioModule的结构如下图所示:
在一个音频thread当中,一个session对应一个effectChain,每个effectChain可以对应若干个track,只要这些track具有相同的session值。每个effectChain是否真正执行除了要看该effect module的状态外还依赖于effectChain是否有关联的track,即:mTrackCnt是否为零,其关系如下图所示:
只有当mTrackCnt不为零时,该effectChain才可能被执行,这点可以参见函数:void AudioFlinger::EffectChain::process_l()
至于effectChain是如何与track绑定上关系的,这个就跟buffer有关了,Track在创建时会从Thread那里拿到一个默认的MainBuffer,在createTrack_l()时回去匹配相应的effectChain,从effectChain中去获取effectChain的buffer,并设置为mainBuffer,另一方面,每个effectChain在addEffectChain_l()时会去创建(特殊情况下不创建)一个buffer,并去匹配相应的track,为匹配到的track设置mainBuffer。effectChain和track的buffer关系还有其他情况,这里暂时不详细记录,关于effectChain的buffer最终怎么与effectModule对应上的,这里是在EffectChain::addEffect_ll()
中完成的,最终把buffer赋值给mConfig.inputCfg.buffer
,在每次effectModule process时,都会把mConfig中记录的buffer作为in/out buffer来输入输出。
上面只是简单记录一下这里的关系纽带,其实buffer这块比上述的复杂的多,这里不对buffer进行详细记录,等这个整理完了再来整理buffer相关的内容。
3 effect启动加载过程
启动加载其实分为两块,一块是AudioPolicyService发起的加载,两一块是EffectsFactory发起的加载。
1、AudioPolicyService的加载,加载过程如下:
其实这一部分主要是利用audio_effects.conf建立输入输出流与stream type对应的EffectDesc,EffectDesc在创建AudioEffect时会被用到。关于AudioPolicyService、AudioEffect相关内容详见:相关类及成员说明。
2、EffectsFactory的加载,加载过程如下:
这里主要是为了创建gLibraryList和gSubEffectList,关于这部分详见:effect_entry结构分析。
4 effect挂载到音频流过程
effect挂载及创建过程从new AudioEffect()
开始,调用过程如下:
AudioEffect() ---> audioFlinger->createEffect() ---> thread->createEffect_l ---> chain->createEffect_l() ---> new EffectModule() ---> EffectCreate() ---> l->desc->create_effect()
effect的创建位置及更详细的创建过程见:audioEffect相关。
5 effect工作过程
这部分内容参见:effectClient分析的后半部分,在这部分里面,用command进行了举例。
附1 audio_effects.conf文件
音效描述文件其实是分为了3部分:
1、库定义:定义音效库的名称,并将名称与音效的so文件进行绑定,例如:
volume_listener {
path /system/lib/soundfx/libvolumelistener.so
}
这里定义了一个名为volume_listener的音效库,该库的路径为/system/lib/soundfx/libvolumelistener.so
2、音效定义:定义一个音效名称,然后指定该音效使用哪个音效库,例如:
music_helper {
library volume_listener
uuid 08b8b058-0590-11e5-ac71-0025b32654a0
}
ring_helper {
library volume_listener
uuid 0956df94-0590-11e5-bdbe-0025b32654a0
}
这里定义了两个音效,music_helper和ring_helper,都使用了同一个音效库,但是使用的uuid是不同的,一个音效库中可以存在若干个uuid,在做音效处理的时候可以根据不同的uuid采取不同的处理策略,使得音效处理可以针对不同使用者拥有不同的处理方式,而不需要去重新写多个音效库。
3、流类型与音效绑定:定义一个流类型,并且把需要默认挂载到该流类型上的音效绑定到该流上,其中第三部分为可选的,也就是说如果没有针对流类型的默认音效的话,可以没有第三部分,其中第三部分分为两个子模块:output_session_processing和pre_processing,分别对应的其实是输出流和输入流。第三部分的例子:
输出流:
output_session_processing {
music {
music_helper {
}
sixth_music_helper {
}
}
}
输入流:
pre_processing {
voice_communication {
aec {
}
ns {
}
}
}
这里,在music流类型上挂载了两个音效,music_helper和sixth_music_helper,可以看到,这两个音效的名称都是在第二部分定义好的。
这里有一点要注意:就是流类型的名称,例如本例中的music,这个名字不是随便起的,必须是在代码“audio_effects_conf.h”文件中定义好的!!这里的定义如下:
// audio_source_t
#define MIC_SRC_TAG "mic"// AUDIO_SOURCE_MIC
#define VOICE_UL_SRC_TAG "voice_uplink"// AUDIO_SOURCE_VOICE_UPLINK
#define VOICE_DL_SRC_TAG "voice_downlink"// AUDIO_SOURCE_VOICE_DOWNLINK
#define VOICE_CALL_SRC_TAG "voice_call"// AUDIO_SOURCE_VOICE_CALL
#define CAMCORDER_SRC_TAG "camcorder"// AUDIO_SOURCE_CAMCORDER
#define VOICE_REC_SRC_TAG "voice_recognition"// AUDIO_SOURCE_VOICE_RECOGNITION
#define VOICE_COMM_SRC_TAG "voice_communication"// AUDIO_SOURCE_VOICE_COMMUNICATION
#define UNPROCESSED_SRC_TAG "unprocessed"// AUDIO_SOURCE_UNPROCESSED
// audio_stream_type_t
#define AUDIO_STREAM_DEFAULT_TAG "default"
#define AUDIO_STREAM_VOICE_CALL_TAG "voice_call"
#define AUDIO_STREAM_SYSTEM_TAG "system"
#define AUDIO_STREAM_RING_TAG "ring"
#define AUDIO_STREAM_MUSIC_TAG "music"
#define AUDIO_STREAM_ALARM_TAG "alarm"
#define AUDIO_STREAM_NOTIFICATION_TAG "notification"
#define AUDIO_STREAM_BLUETOOTH_SCO_TAG "bluetooth_sco"
#define AUDIO_STREAM_ENFORCED_AUDIBLE_TAG "enforced_audible"
#define AUDIO_STREAM_DTMF_TAG "dtmf"
#define AUDIO_STREAM_TTS_TAG "tts"
这里所使用的music字符就是对应的#define AUDIO_STREAM_MUSIC_TAG "music"
这句话
附2 effect_entry结构分析
相关文件:EffectsFactory.c、EffectsFactory.h
整个effect_entry或者说整个plugin音效与android音效框架的纽带全在这里,这里面关键是要弄清下面三个全局变量的结构:
staticlist_elem_t*gEffectList;// list of effect_entry_t: all currently created effects
staticlist_elem_t*gLibraryList;// list of lib_entry_t: all currently loaded libraries
staticlist_sub_elem_t*gSubEffectList;// list of list_sub_elem_t: all currently created sub effects
- gLibraryList保存了所有已经加载的effect lib,这个lib就是plugin音效编译后的so文件,通过audio_effects.conf来获取lib的路径以及有哪些lib
- gEffectList保存了所有在audio_effects.conf所定义的音效
- gSubEffectList保存了所有在audio_effects.conf所定义的子音效,最常见的一个例子就是proxy lib,里面会再定义hw和sw两个子音效的lib
上述数据结构怎么被创建的在effectFactory分析中分析,这里不记录。
这里关于gLibraryList、gSubEffectList的数据结构如下图所示:
其中有关于subEffect这里,目前最常见的就是proxy effect底下会有两个subEffect,android暂时规定只允许有 两个 subeffect,一个是hw,一个是sw,分别对应offload effect和host effect,这样做的好处我暂时认为是方便effect切换,相当于proxy没有实际处理音频数据,仅仅接受上层配置,根据上层配置在两个effect间切换,同时,对于上层来说看到的只是一个effect,这样上层就完全可以不用关心底下的行为以及管理过多的effect。
还有一点,就是关于effect_descriptor_t这个成员,如果该effect没有sub effect时,descriptor是从自己的lib中获取的,如果存在sub effect则被sub effect中的sw effect给替换掉了,但是UUID仍然为原来的UUID。举个栗子:一个effect使用了proxy lib,那么这个proxy lib在该effect中对应的descriptor的内容将是从libsw中获取的descriptor,而非proxy lib中获取的,但是,UUID还是proxy lib的UUID,这部分可以从代码中看出来。
gEffectList的结构如下所示:
这个比较简单就不多记录了,这个链表是在每次调用effectFactory.c中的EffectCreate()函数时才会向其中增加新的元素,也就是说这里面的元素就是当前正在使用effect实例列表。
附3 effectsFactory分析
看这部分内容之前,要先确认已经了解了effect_entry结构分析里面的数据结构关系。
相关文件:EffectsFactory.c、EffectsFactory.h、audio_effect.h
EffectsFactory.c是链接plugin effect和android effect框架的纽带,两者之间所有打交道的部分全部都是通过EffectFactory来完成的。effectFactory主要职责如下:
- 从audio_effects.conf文件中读取配置,建立gLibraryList及gSubEffectList两个数据列表
- 提供创建、销毁、查询以及执行effect_handle_t的接口
1、建立gLibraryList及gSubEffectList数据列表
该部分由init()函数完成,该函数就是读取audio_effects.conf的配置,根据配置找到相应的动态链接库,然后加载动态链接库,再根据配置文件以及从动态链接库中恢复出来的数据,创建gLibraryList及gSubEffectList,由于此函数比较简单,就不详细记录,这里就记录一点,该函数在几乎所有外部接口中被调用了,同时该函数有一个全局变量标识,保证只实际执行一次,也就是说,effectFactory的外部接口中,哪个接口最先被调用,effectFactory就在哪里被初始化。不过比较遗憾的是暂时还没有找到实际初始化的时间点……
2、外部接口
外部接口调用其实也都比较简单,没有什么需要记录的,看代码可能比记录下来还要来的直接……
附4 addOutputSessionEffects调用过程
addOutputSessionEffects()函数的调用源头在audioPolicyService.cpp提供的对外接口中,这里面的接口最终会被上层应用的api接口所调用,上层调用关系不在这里记录,总之audioPolicyService的主要作用就是音频框架对上提供的一层服务接口。
调用过程:
1、status_t AudioPolicyService::startOutput()被上层应用调用;
2、startOutput()中调用mOutputCommandThread->startOutputCommand(output, stream, session);
3、mOutputCommandThread的创建是在AudioPolicyService::onFirstRef()函数中完成,mOutputCommandThread为一个线程,该线程具体的行为参见:AudioPolicyService解析
4、AudioPolicyService::AudioCommandThread::threadLoop()中收到START_OUTPUT命令后,执行svc->doStartOutput()函数,这里的doStartOutput()函数在文件audioPolicyInterfaceImpl.cpp中。
5、doStartOutput()函数中调用audioPolicyEffects->addOutputSessionEffects(),创建跟session对应的effects,这些effects就是预置的对应流类型的effects。
附5 effectClient分析
classEffectClient:
public android::BnEffectClient,public android::IBinder::DeathRecipient
{
public:
EffectClient(AudioEffect*effect): mEffect(effect){}
// IEffectClient
virtualvoid controlStatusChanged(bool controlGranted){
sp<AudioEffect> effect = mEffect.promote();
if(effect !=0){
effect->controlStatusChanged(controlGranted);
}
}
virtualvoid enableStatusChanged(bool enabled)
virtualvoid commandExecuted()
private:
wp<AudioEffect> mEffect;
};
effectClient干了什么:
这里就简单列一下,其实就是实现了controlStatusChanged,enableStatusChanged,commandExecuted这三个方法,这三个方法简单来说应该是通知该effect的使用者某个动作或者某个消息被执行完成的回调函数,每个函数的实现其实最终还是调用的audioEffect类中的对应函数,在audioEffect类中,其实最终调用的是effect_callback_t mCbf;
,mcbf就是在创建audioeffect的时候外部传入的,如果外部需要获取实行完成的消息,则实现一个mcbf,并在创建audioeffect时传入进来,如果外部不关心,则可以不实现mcbf,那么最终EffectClient其实就是打了个酱油……啥也没干。
谁来使用effectClient的:
这里就用command来做说明。command的最终执行者是EffectModule类,比如说上层要给effect发送一条命令,则是先找到对应的AudioEffect,audioEffect通过自己的属性IEffect(也就是EffectHandle)中提供的command来执行这条命令,EffectHandle则调用其所对应的EffectModule类中的command来执行这条命令,最终effectMoudule将调用mEffectInterface中的command来执行这条命令。mEffectInterface的定义:effect_handle_t mEffectInterface; // Effect module C API
,到这里就和外挂的音效文件接口给对上了。当EffectModule->command被调用时,该函数会调用EffectHandle的commandExecuted(),EffectHandle中又调用了effectClient的commandExecuted(),最终调用AudioEffect的commandExecuted()。
AudioEffect相关的类视图:
commandExecuted的调用过程:
这里有一点,为了简便,我把IEffect类和EffectHandle给等价了,其实在mIEffect应该是IEffect类型,但是EffectHandle是继承自IEffect,并且后面都是用的EffectHandle,所以这里就混用了。
原文地址:https://www.cnblogs.com/RunnigGift/p/8710087.html