android audio effects架構筆記


 

這里的內部跳轉鏈接好像無效……這里會好一點: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類

先不看方法,里面關鍵的成員主要就是下面四個
 
  1. // Automatic input effects are configured per audio_source_t
  2. KeyedVector<audio_source_t,EffectDescVector*> mInputSources;
  3. // Automatic input effects are unique for audio_io_handle_t
  4. KeyedVector<audio_io_handle_t,EffectVector*> mInputs;
  5. // Automatic output effects are organized per audio_stream_type_t
  6. KeyedVector<audio_stream_type_t,EffectDescVector*> mOutputStreams;
  7. // Automatic output effects are unique for audiosession ID
  8. 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)的對應是通過函數

 
  1. 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,語言不好描述,這里用一張圖來記錄:
effectDescVector.jpg-189.3kB
最下面的鍵值對實際存儲方式與圖上畫的是不一樣的,圖上那樣畫是為了體現他們是鍵值對,實際的存儲方式詳見:"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類的核心方法,這里就簡單的分析一下。

 
  1. status_tAudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output,
  2. audio_stream_type_t stream,
  3. audio_session_t audioSession)
  4. {
  5. ……
  6. /* 從mOutputStreams中找到對應stream type的預置effects */
  7. ssize_t index = mOutputStreams.indexOfKey(stream);
  8. ……
  9. /* 查找新加的session是否已經添加過了 */
  10. ssize_t idx = mOutputSessions.indexOfKey(audioSession);
  11. /* 與該session想對應的,實際的effect的向量列表 */
  12. EffectVector*procDesc;
  13. /* 如果新加的session沒有添加過,則創建新的session與EffectVector的對應關系,並添加到mOutputSessions中 */
  14. if(idx <0){
  15. procDesc =newEffectVector(audioSession);
  16. mOutputSessions.add(audioSession, procDesc);
  17. }else{
  18. // EffectVector is existing and we just need to increase ref count
  19. procDesc = mOutputSessions.valueAt(idx);
  20. }
  21. procDesc->mRefCount++;
  22. /* 如果該session對應的effects描述向量procDesc是新建的,則實際創建procDesc中所描述的每一個effects */
  23. if(procDesc->mRefCount ==1){
  24. /* 從mOutputStreams中取出對應流類型的effects描述符 */
  25. Vector<EffectDesc*> effects = mOutputStreams.valueAt(index)->mEffects;
  26. /* 把每個描述符所描述的effects實際創建出來,並掛載到對應的threads中 */
  27. for(size_t i =0; i < effects.size(); i++){
  28. EffectDesc*effect = effects[i];
  29. /* 根據effect的描述符,實際創建一個effect */
  30. sp<AudioEffect> fx =newAudioEffect(NULL,String16("android"),&effect->mUuid,0,0,0,
  31. audioSession, output);
  32. ……
  33. /* 把創建好的effect添加到EffectVector向量中 */
  34. procDesc->mEffects.add(fx);
  35. }
  36. /* 設置該向量為使能 */
  37. procDesc->setProcessorEnabled(true);
  38. }
  39. return status;
  40. }

總結來說,上述函數就做了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()方法中完成,這里簡要分析一下:

 
  1. status_tAudioEffect::set(consteffect_uuid_t*type,
  2. consteffect_uuid_t*uuid,
  3. int32_t priority,
  4. effect_callback_t cbf,
  5. void* user,
  6. audio_session_t sessionId,
  7. audio_io_handle_t io)
  8. {
  9. /* 創建一個effectClient */
  10. mIEffectClient =newEffectClient(this);
  11. /* 創建一個effect實體,並且把該effect的實體接口返回給iEffect,這里其實就是返回了一個EffectHandle類型,這里面提供了對該effect實體的實際操作方法 */
  12. iEffect = audioFlinger->createEffect((effect_descriptor_t*)&mDescriptor,
  13. mIEffectClient, priority, io, mSessionId, mOpPackageName,&mStatus,&mId,&enabled);
  14. /* 設置該effect為使能 */
  15. mEnabled =(volatileint32_t)enabled;
  16. /* effect內存相關操作,暫時不分析*/
  17. cblk = iEffect->getCblk();
  18. /* 保存該effect實體接口 */
  19. mIEffect = iEffect;
  20. /* effect內存相關操作,暫時不分析*/
  21. mCblkMemory = cblk;
  22. mCblk =static_cast<effect_param_cblk_t*>(cblk->pointer());
  23. int bufOffset =((sizeof(effect_param_cblk_t)-1)/sizeof(int)+1)*sizeof(int);
  24. mCblk->buffer =(uint8_t*)mCblk + bufOffset;
  25. /* binder相關操作,暫時不分析 */
  26. IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient);
  27. mClientPid =IPCThreadState::self()->getCallingPid();
  28. }

這里面最核心的一部就是audioFlinger->createEffect()這句話,這句話里面最有意義的一句話又是:handle = thread->createEffect_l(),所以這里稍微來分析一下createEffect_l()這個函數:

 
  1. sp<AudioFlinger::EffectHandle>AudioFlinger::ThreadBase::createEffect_l(
  2. const sp<AudioFlinger::Client>& client,
  3. const sp<IEffectClient>& effectClient,
  4. int32_t priority,
  5. audio_session_t sessionId,
  6. effect_descriptor_t*desc,
  7. int*enabled,
  8. status_t*status,
  9. bool pinned)
  10. {
  11. sp<EffectModule> effect;
  12. sp<EffectHandle> handle;
  13. sp<EffectChain> chain;
  14. {
  15. // check for existing effect chain with the requested audio session
  16. chain = getEffectChain_l(sessionId);
  17. if(chain ==0){
  18. // create a new chain for this session
  19. chain =newEffectChain(this, sessionId);
  20. addEffectChain_l(chain);
  21. chain->setStrategy(getStrategyForSession_l(sessionId));
  22. chainCreated =true;
  23. }else{
  24. /* 如果該effect chain已經存在,則在chain里面找一下,看看這個effect module是不是已經被創建了 */
  25. effect = chain->getEffectFromDesc_l(desc);
  26. }
  27. /* 如果該effect module沒有被創建,則創建這個effect的實體,即:effectModule */
  28. if(effect ==0){
  29. /* 為即將被創建的effectModule獲取一個唯一識別號 */
  30. audio_unique_id_t id = mAudioFlinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
  31. // Check CPU and memory usage
  32. /* 這里是因為AudioPolicyManager類里面有一個成員:EffectDescriptorCollection mEffects
  33. 這里的注冊,其實就是把這個effect封裝成一個EffectDescriptor類型,並保存到mEffects中,
  34. 該函數的具體實現為:status_t AudioPolicyManager::registerEffect(),EffectDescriptor
  35. 主要在AudioPolicyManager中做一些判斷的時候使用*/
  36. lStatus =AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
  37. // create a new effect module if none present in the chain
  38. lStatus = chain->createEffect_l(effect,this, desc, id, sessionId, pinned);
  39. /* 給effect module設置一些必要參數 */
  40. effect->setDevice(mOutDevice);
  41. effect->setDevice(mInDevice);
  42. effect->setMode(mAudioFlinger->getMode());
  43. effect->setAudioSource(mAudioSource);
  44. }
  45. /* 創建effectHandle,這里可以看出,其實一個effectModule可以對應多個effectHandle,
  46. 所以effectHandle是針對每個使用者而獨立存在的,而多個使用者公用一個effectModule */
  47. handle =newEffectHandle(effect, client, effectClient, priority);
  48. /* 把新創建的effectHandle加入到該effectModule所維護的handle列表中 */
  49. if(lStatus == OK){
  50. lStatus = effect->addHandle(handle.get());
  51. }
  52. }
  53. Exit:
  54. return handle;
  55. }

上述代碼中其實還是沒有看到effectModule被創建的地方,只看到了是被effect chain所創建的,這里繼續看一下effect chain中的createEffect_l函數:

 
  1. status_tAudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
  2. ThreadBase*thread,
  3. effect_descriptor_t*desc,
  4. int id,
  5. audio_session_t sessionId,
  6. bool pinned)
  7. {
  8. Mutex::Autolock _l(mLock);
  9. /* 實例化一個EffectModule,這里才是真正創建一個effectModule,但是這里還並不是真正把effect創建出來的
  10. 地方,effectModule其實還不是真正的effect實體,它應該算是effect實體的一個代理,effect實體是在
  11. EffectModule的構造函數中被真正創建的 */
  12. effect =newEffectModule(thread,this, desc, id, sessionId, pinned);
  13. status_t lStatus = effect->status();
  14. if(lStatus == NO_ERROR){
  15. /* 把創建的effectModule存入effect鏈中 */
  16. lStatus = addEffect_ll(effect);
  17. }
  18. if(lStatus != NO_ERROR){
  19. effect.clear();
  20. }
  21. return lStatus;
  22. }

根據代碼里面寫的注釋,這里需要繼續看看EffectModule的構造函數:

 
  1. AudioFlinger::EffectModule::EffectModule(ThreadBase*thread,
  2. const wp<AudioFlinger::EffectChain>& chain,
  3. effect_descriptor_t*desc,
  4. int id,
  5. audio_session_t sessionId,
  6. bool pinned)
  7. : mPinned(pinned),
  8. mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
  9. mDescriptor(*desc),
  10. // mConfig is set by configure() and not used before then
  11. mEffectInterface(NULL),
  12. mStatus(NO_INIT), mState(IDLE),
  13. // mMaxDisableWaitCnt is set by configure() and not used before then
  14. // mDisableWaitCnt is set by process() and updateState() and not used before then
  15. mSuspended(false),
  16. mAudioFlinger(thread->mAudioFlinger)
  17. {
  18. // create effect engine from effect factory
  19. mStatus =EffectCreate(&desc->uuid, sessionId, thread->id(),&mEffectInterface);
  20. setOffloaded((thread->type()==ThreadBase::OFFLOAD ||
  21. (thread->type()==ThreadBase::DIRECT && thread->mIsDirectPcm)), thread->id());
  22. }

其實EffectModule的構造函數里面真正起作用的就兩句話,第一句話,真正的創建effect實體,第二句話設置offload屬性,至於第二句話的意義暫時不去深究,總之就是想effect實體發送了一條設置offload的command。

這里EffectCreate()函數引出了一個非常重要的東西EffectFactory。關於effectFactory詳見:effectFactory分析。EffectCreate()函數源碼如下:

 
  1. intEffectCreate(consteffect_uuid_t*uuid,int32_t sessionId,int32_t ioId,effect_handle_t*pHandle)
  2. {
  3. /* 初始化真個effectFactory,該函數在多出被調用,只有第一次被調用時才實際執行初始化
  4. 其余時候再次被調用則直接返回*/
  5. ret = init();
  6. /* 從配置文件(audio_effects.conf)讀入的effects信息中查找所要創建的effect
  7. 如果找不到該effect則退出,找到了側繼續創建effect */
  8. ret = findEffect(NULL, uuid,&l,&d);
  9. if(ret <0){
  10. // Sub effects are not associated with the library->effects,
  11. // so, findEffect will fail. Search for the effect in gSubEffectList.
  12. ret = findSubEffect(uuid,&l,&d);
  13. if(ret <0){
  14. gotoexit;
  15. }
  16. }
  17. /* 這里就是調用的effect文件中的create_effect接口(詳見audio_effect.h文件,里面的注釋已經說的很清楚了)
  18. 該接口是每個effect都必須實現的標准接口,這里的返回值其實是itfe,itfe是一個effect_handle_t類型的結構體
  19. 該結構體就是這個effect的實際操作接口,該接口的定義也在audio_effect.h文件中,也是每個effect必須實現的 */
  20. // create effect in library
  21. ret = l->desc->create_effect(uuid, sessionId, ioId,&itfe);
  22. /* 創建並填寫一個effect_entry_t結構,該結構中就存儲了itfe */
  23. // add entry to effect list
  24. fx =(effect_entry_t*)malloc(sizeof(effect_entry_t));
  25. fx->subItfe = itfe;
  26. /* 這里其實是把itfe封裝了一次,讓effectFactory統一調用itfe,其實落腳點還是itfe…… */
  27. if((*itfe)->process_reverse != NULL){
  28. fx->itfe =(struct effect_interface_s *)&gInterfaceWithReverse;
  29. ALOGV("EffectCreate() gInterfaceWithReverse");
  30. }else{
  31. fx->itfe =(struct effect_interface_s *)&gInterface;
  32. ALOGV("EffectCreate() gInterface");
  33. }
  34. fx->lib = l;
  35. /* 把fx存入gEffectList鏈表中,該鏈表為effectFactory自己的全局鏈表 */
  36. e =(list_elem_t*)malloc(sizeof(list_elem_t));
  37. e->object= fx;
  38. e->next= gEffectList;
  39. gEffectList = e;
  40. /* 其實就是把itfe傳了出去 */
  41. *pHandle =(effect_handle_t)fx;
  42. }

至此,一個完整的effect就創建完成了……也與effect lib所提供的接口關聯上了。

 

1.3.4 audioEffect的作用

那么總結下來,audioEffect除了創建了effect實體(即EffectModule)外也沒有什么實質性的作用,主要是充當了接口功能,為真正的effect實體提供一個外部接口,並通過mIEffect來調用EffectModule的相關操作,EffectModule通過mIEffectClient來通知外界操作執行完畢。

2 effect數據結構及關系

宏觀上來說,android的音效其實可以分為三層:音效框架、音效工廠和plugin音效。這里的名稱是我自己起的……不是官方的,大概表述一下應該是這樣:
effectOverView.jpg-37kB

  • 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關系圖

這一部分是最頂層的,直接面對外部接口調用的,其關系圖如下:
audioPolicyService.jpg-204kB
這里就簡單記錄一下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的結構如下圖所示:
effectChain.jpg-90.5kB
在一個音頻thread當中,一個session對應一個effectChain,每個effectChain可以對應若干個track,只要這些track具有相同的session值。每個effectChain是否真正執行除了要看該effect module的狀態外還依賴於effectChain是否有關聯的track,即:mTrackCnt是否為零,其關系如下圖所示:
mTrackCnt.jpg-119.9kB
只有當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的加載,加載過程如下:
loadEffects1.jpg-100.1kB
其實這一部分主要是利用audio_effects.conf建立輸入輸出流與stream type對應的EffectDesc,EffectDesc在創建AudioEffect時會被用到。關於AudioPolicyService、AudioEffect相關內容詳見:相關類及成員說明

2、EffectsFactory的加載,加載過程如下:
loadEffects2.jpg-49.6kB
這里主要是為了創建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文件進行綁定,例如:

 
  1. volume_listener {
  2. path /system/lib/soundfx/libvolumelistener.so
  3. }

這里定義了一個名為volume_listener的音效庫,該庫的路徑為/system/lib/soundfx/libvolumelistener.so
2、音效定義:定義一個音效名稱,然后指定該音效使用哪個音效庫,例如:

 
  1. music_helper {
  2. library volume_listener
  3. uuid 08b8b058-0590-11e5-ac71-0025b32654a0
  4. }
  5. ring_helper {
  6. library volume_listener
  7. uuid 0956df94-0590-11e5-bdbe-0025b32654a0
  8. }

這里定義了兩個音效,music_helper和ring_helper,都使用了同一個音效庫,但是使用的uuid是不同的,一個音效庫中可以存在若干個uuid,在做音效處理的時候可以根據不同的uuid采取不同的處理策略,使得音效處理可以針對不同使用者擁有不同的處理方式,而不需要去重新寫多個音效庫。
3、流類型與音效綁定:定義一個流類型,並且把需要默認掛載到該流類型上的音效綁定到該流上,其中第三部分為可選的,也就是說如果沒有針對流類型的默認音效的話,可以沒有第三部分,其中第三部分分為兩個子模塊:output_session_processing和pre_processing,分別對應的其實是輸出流和輸入流。第三部分的例子:

輸出流:

 
  1. output_session_processing {
  2. music {
  3. music_helper {
  4. }
  5. sixth_music_helper {
  6. }
  7. }
  8. }

輸入流:

 
  1. pre_processing {
  2. voice_communication {
  3. aec {
  4. }
  5. ns {
  6. }
  7. }
  8. }

這里,在music流類型上掛載了兩個音效,music_helper和sixth_music_helper,可以看到,這兩個音效的名稱都是在第二部分定義好的。
這里有一點要注意:就是流類型的名稱,例如本例中的music,這個名字不是隨便起的,必須是在代碼“audio_effects_conf.h”文件中定義好的!!這里的定義如下:

 
  1. // audio_source_t
  2. #define MIC_SRC_TAG "mic"// AUDIO_SOURCE_MIC
  3. #define VOICE_UL_SRC_TAG "voice_uplink"// AUDIO_SOURCE_VOICE_UPLINK
  4. #define VOICE_DL_SRC_TAG "voice_downlink"// AUDIO_SOURCE_VOICE_DOWNLINK
  5. #define VOICE_CALL_SRC_TAG "voice_call"// AUDIO_SOURCE_VOICE_CALL
  6. #define CAMCORDER_SRC_TAG "camcorder"// AUDIO_SOURCE_CAMCORDER
  7. #define VOICE_REC_SRC_TAG "voice_recognition"// AUDIO_SOURCE_VOICE_RECOGNITION
  8. #define VOICE_COMM_SRC_TAG "voice_communication"// AUDIO_SOURCE_VOICE_COMMUNICATION
  9. #define UNPROCESSED_SRC_TAG "unprocessed"// AUDIO_SOURCE_UNPROCESSED
  10. // audio_stream_type_t
  11. #define AUDIO_STREAM_DEFAULT_TAG "default"
  12. #define AUDIO_STREAM_VOICE_CALL_TAG "voice_call"
  13. #define AUDIO_STREAM_SYSTEM_TAG "system"
  14. #define AUDIO_STREAM_RING_TAG "ring"
  15. #define AUDIO_STREAM_MUSIC_TAG "music"
  16. #define AUDIO_STREAM_ALARM_TAG "alarm"
  17. #define AUDIO_STREAM_NOTIFICATION_TAG "notification"
  18. #define AUDIO_STREAM_BLUETOOTH_SCO_TAG "bluetooth_sco"
  19. #define AUDIO_STREAM_ENFORCED_AUDIBLE_TAG "enforced_audible"
  20. #define AUDIO_STREAM_DTMF_TAG "dtmf"
  21. #define AUDIO_STREAM_TTS_TAG "tts"

這里所使用的music字符就是對應的#define AUDIO_STREAM_MUSIC_TAG "music"這句話

附2 effect_entry結構分析

相關文件:EffectsFactory.c、EffectsFactory.h
整個effect_entry或者說整個plugin音效與android音效框架的紐帶全在這里,這里面關鍵是要弄清下面三個全局變量的結構:

 
  1. staticlist_elem_t*gEffectList;// list of effect_entry_t: all currently created effects
  2. staticlist_elem_t*gLibraryList;// list of lib_entry_t: all currently loaded libraries
  3. 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的數據結構如下圖所示:
effectLists.jpg-426.2kB
其中有關於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的結構如下所示:
gEffectList.jpg-133.2kB
這個比較簡單就不多記錄了,這個鏈表是在每次調用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分析

 
  1. classEffectClient:
  2. public android::BnEffectClient,public android::IBinder::DeathRecipient
  3. {
  4. public:
  5. EffectClient(AudioEffect*effect): mEffect(effect){}
  6. // IEffectClient
  7. virtualvoid controlStatusChanged(bool controlGranted){
  8. sp<AudioEffect> effect = mEffect.promote();
  9. if(effect !=0){
  10. effect->controlStatusChanged(controlGranted);
  11. }
  12. }
  13. virtualvoid enableStatusChanged(bool enabled)
  14. virtualvoid commandExecuted()
  15. private:
  16. wp<AudioEffect> mEffect;
  17. };

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相關的類視圖:
EffectClient.jpg-176.7kB
commandExecuted的調用過程:
EffectClientSeq.jpg-89.5kB
這里有一點,為了簡便,我把IEffect類和EffectHandle給等價了,其實在mIEffect應該是IEffect類型,但是EffectHandle是繼承自IEffect,並且后面都是用的EffectHandle,所以這里就混用了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM