這里的內部跳轉鏈接好像無效……這里會好一點:https://blog.csdn.net/whshiyun/article/details/79806870
android effects筆記
android sound effect
全文不涉及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_tKeyedVector<audio_source_t,EffectDescVector*> mInputSources;// Automatic input effects are unique for audio_io_handle_tKeyedVector<audio_io_handle_t,EffectVector*> mInputs;// Automatic output effects are organized per audio_stream_type_tKeyedVector<audio_stream_type_t,EffectDescVector*> mOutputStreams;// Automatic output effects are unique for audiosession IDKeyedVector<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 countprocDesc = 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 sessionchain = getEffectChain_l(sessionId);if(chain ==0){// create a new chain for this sessionchain =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 chainlStatus = 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 thenmEffectInterface(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 thenmSuspended(false),mAudioFlinger(thread->mAudioFlinger){// create effect engine from effect factorymStatus =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 libraryret = l->desc->create_effect(uuid, sessionId, ioId,&itfe);/* 創建並填寫一個effect_entry_t結構,該結構中就存儲了itfe */// add entry to effect listfx =(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_listeneruuid 08b8b058-0590-11e5-ac71-0025b32654a0}ring_helper {library volume_listeneruuid 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 effectsstaticlist_elem_t*gLibraryList;// list of lib_entry_t: all currently loaded librariesstaticlist_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){}// IEffectClientvirtualvoid 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,所以這里就混用了。
