国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android13音頻子系統(tǒng)分析(三)---音效算法集成框架

這篇具有很好參考價(jià)值的文章主要介紹了Android13音頻子系統(tǒng)分析(三)---音效算法集成框架。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

目錄

一、Android音效C/S架構(gòu)

二、EffectHAL音效框架的初始化與使用

2.1音效框架初始化

2.2創(chuàng)建并加載音效算法

2.3執(zhí)行音效算法

三、AudioFlinger對(duì)音效框架的二次封裝

四、Device音效的綁定過(guò)程

4.1 DeviceHAL處理音效數(shù)據(jù)的方式

4.2 AudioFlinger處理音效數(shù)據(jù)的方式

五、Stream音效的綁定過(guò)程

六、StreamHAL處理音效數(shù)據(jù)的方式

七、Track音效的綁定過(guò)程

八、Aux音效的綁定過(guò)程

九、所有音效處理的數(shù)據(jù)傳輸流程圖


????????Android的音效架構(gòu)中,將所有的音效算法全部掛載到AudioHAL進(jìn)程中運(yùn)行。根據(jù)音效算法應(yīng)用的位置,可以分為三類:Track音效、Stream音效、Device音效。即:針對(duì)某一個(gè)Track的處理音效、針對(duì)某種StreamType的處理音效、針對(duì)某個(gè)Device的處理音效。在Android13音頻子系統(tǒng)分析(一)---整體架構(gòu)中我們已經(jīng)看到,Track只是在APP層和AudioServer框架層中的概念,到了AudioHAL層已經(jīng)沒(méi)有Track的概念,只有Stream和Device。也就是說(shuō)AudioFlinger模塊中已經(jīng)將多個(gè)Track的數(shù)據(jù)混合到一起再輸出給StreamOut了。所以,可以想到,針對(duì)Track的音效處理,必然會(huì)跨進(jìn)程調(diào)用,為了跨進(jìn)程傳輸Track的音頻數(shù)據(jù)給音效算法處理,然后將算法處理后的數(shù)據(jù)拿回來(lái),就會(huì)導(dǎo)致額外的共享內(nèi)存的創(chuàng)建開(kāi)銷,增加內(nèi)存的消耗。這是我認(rèn)為Android音效架構(gòu)設(shè)計(jì)不合理的地方。下面我們一起來(lái)看一下Android音效架構(gòu)的設(shè)計(jì)原理。

一、Android音效C/S架構(gòu)

? ? ? ? 既然Android將所有音效算法運(yùn)行在AudioHAL進(jìn)程中,而核心的控制和使用者又是AudioServer進(jìn)程中的AudioFlinger,所以,必然會(huì)通過(guò)Binder機(jī)制設(shè)計(jì)成一個(gè)C/S架構(gòu)。在Server端,它提供了兩個(gè)主要的HIDL接口:IEffectsFactory.hal和IEffect.hal。在AudioHAL進(jìn)程初始化時(shí)(/hardware/interfaces/audio/common/all-versions/default/service.cpp中的源碼),會(huì)將IEffectsFactory作為一個(gè)HIDL服務(wù)端,增加到ServiceManager中??蛻舳送ㄟ^(guò)IEffectsFactory可以獲取到指定的某種音效算法引擎:IEffect。以下是EffectHAL架構(gòu)類圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

EffectHAL架構(gòu)分為三層:

  • Effect客戶端代理層:圖中黃色背景的模塊。它們編譯打包在libaudiohal.so庫(kù)中,由AudioFlinger調(diào)用,運(yùn)行在AudioServer進(jìn)程中。源碼位置:/frameworks/av/media/libaudiohal/
  • Effect服務(wù)端實(shí)現(xiàn)層:圖中藍(lán)色背景的模塊。運(yùn)行在AudioHAL進(jìn)程中。源碼位置:/hardware/interfaces/audio/effect/all-versions/default/。其中EffectsFactory.c是用于解析音效配置文件audio_effects.xml,并加載調(diào)用音效算法的模塊。其源碼位于:/frameworks/av/media/libeffects/factory/。會(huì)被編譯打包成libeffects.so庫(kù)。所以,如果我們想讓音效算法運(yùn)行在AudioServer進(jìn)程中,不跨進(jìn)程調(diào)用,完全可以直接加載這個(gè)libeffects.so庫(kù),通過(guò)調(diào)用EffectsFactory.c模塊來(lái)實(shí)現(xiàn)。當(dāng)然,改動(dòng)不會(huì)小?;诂F(xiàn)有的Android音效框架修改的好處是提供了給上層APP的算法控制接口。我們也可以圖省事,直接在AudioMixer混音器中新增加一種自定義類型的AudioBufferProvider,這樣就可以針對(duì)單個(gè)AudioTrack進(jìn)行自定義的音效處理。
  • 提供給算法廠商的接口定義層:只有兩個(gè)主要結(jié)構(gòu)體,audio_effect_library_t和effect_interface_s。effect_handle_t是指向effect_interface_s結(jié)構(gòu)體的指針。它們?nèi)慷x在/hardware/libhardware/include/hardware/audio_effect.h文件中。Android音效架構(gòu)允許算法廠商將自己的多個(gè)算法編譯打包到一個(gè)so庫(kù)中,所以,audio_effect_library_t結(jié)構(gòu)體中定義的是獲取該so庫(kù)中某個(gè)算法引擎的接口。而effect_interface_s結(jié)構(gòu)體定義就是一個(gè)具體音效算法需要實(shí)現(xiàn)的接口。
  • 圖中的白色部分:AudioBuffer類(/hardware/interfaces/audio/effect/7.0/types.hal文件中定義)和audio_buffer_t結(jié)構(gòu)體(/system/media/audio/include/system/audio_effect.h文件中定義),都是代表的共享內(nèi)存塊。里面封裝了指向共享內(nèi)存地址的指針。只是AudioBuffer類供C++代碼使用,audio_buffer_t結(jié)構(gòu)體供C代碼使用。
  • 除了上述的幾個(gè)模塊外,還有一個(gè)常用的模塊在圖中沒(méi)有體現(xiàn)。它就是供C++代碼使用的EffectDescriptor類(/hardware/interfaces/audio/effect/7.0/types.hal文件中定義)和供C代碼使用的effect_descriptor_t結(jié)構(gòu)體(/system/media/audio/include/system/audio_effect.h文件中定義)。它們都是用于描述某個(gè)音效算法的信息,包括typeUUID(由OpenSL ES定義的某種類型的音效算法,比如降噪算法全部定義的是一個(gè)TypeUUID,不管是哪家算法廠商實(shí)現(xiàn)的)、UUID(代表的是某個(gè)具體實(shí)現(xiàn)的降噪算法的唯一標(biāo)識(shí)符,不同廠商實(shí)現(xiàn)的降噪算法UUID不會(huì)相同)、flags(代表此算法支持的能力,詳細(xì)的解釋可以參見(jiàn)/hardware/interfaces/audio/effect/7.0/types.hal文件)。剩下的如cpuLoadmemoryUsage字段,代表的是此算法所占用的CPU和內(nèi)存情況,但是有些算法實(shí)現(xiàn)廠商并不會(huì)給出這部分信息描述,比如Android自己實(shí)現(xiàn)的多聲道轉(zhuǎn)雙聲道的算法EffectDownmix就沒(méi)有。AudioPolicyService會(huì)基于這兩個(gè)字段來(lái)統(tǒng)計(jì)監(jiān)控所有算法的CPU和內(nèi)存使用情況,但是顯然是不準(zhǔn)確的。

? ? ? ?Android雖然支持第三方算法廠商基于audio_effect.h->effect_interface_s接口實(shí)現(xiàn)自己的算法,同時(shí)也自己默認(rèn)實(shí)現(xiàn)了一些音效算法,供上層使用。它們的源碼位于/frameworks/av/media/libeffects/目錄中。主要包括以下幾種:

  • EffectDownmix:用于將多聲道轉(zhuǎn)換成立體聲雙聲道。
  • DynamicsProcessing:動(dòng)態(tài)范圍調(diào)節(jié)音效。
  • EffectHapticGenerator:基于音頻數(shù)據(jù)生成震動(dòng)效果數(shù)據(jù)。
  • LoudnessEnhancer:音量放大音效。
  • PreProcessing:音頻上行錄制通路使用的前處理算法。包含AGC自動(dòng)增益算法、AEC回聲消除算法、NS降噪算法。也就是通話場(chǎng)景下常說(shuō)的3A算法。因?yàn)镹S降噪算法有的地方也叫ANS。這三種算法Android都是調(diào)用WebRTC中的音效模塊實(shí)現(xiàn)的。顯然,針對(duì)通話場(chǎng)景,直接在DSP芯片中運(yùn)行3A算法會(huì)更高效。所以,實(shí)踐中各個(gè)手機(jī)廠商也沒(méi)有人會(huì)使用Android提供的這個(gè)PreProcessing算法。
  • Visualizer:將音頻PCM數(shù)據(jù)轉(zhuǎn)換成可視化的波形圖。
  • BassBoost:低音增強(qiáng)音效。
  • PresetReverb:環(huán)境混響音效。主要適用于音樂(lè)播放場(chǎng)景。
  • EnvironmentalReverb:環(huán)境混響音效。提供更多的混響參數(shù)設(shè)置。適用于游戲播放場(chǎng)景。
  • Equalizer:EQ均衡器音效。
  • Virtualizer:空間感音效。
  • EffectPoxy:Android定義的音效代理接口。對(duì)應(yīng)音效配置文件audio_effects.xml中的<effectProxy>節(jié)點(diǎn),它保存了兩種音效子類,一種是<libsw>節(jié)點(diǎn)中定義的運(yùn)行在AudioHAL進(jìn)程中的普通音效。一種是<libhw>節(jié)點(diǎn)中定義的與DSP中運(yùn)行的音效算法通信的代理音效。也就是說(shuō),Android音效架構(gòu)提供了接口用于控制運(yùn)行在DSP中的音效算法。但是需要音效算法廠商把控制端基于audio_effect.h->effect_interface_s接口實(shí)現(xiàn),并且在audio_effects.xml文件中配置。

二、EffectHAL音效框架的初始化與使用

? ? ? ? 前面有提到audio_effects.xml音效配置文件,那么Android音效框架是在什么時(shí)候解析加載的這個(gè)配置文件中的音效的呢?它采用的是類似"懶加載模式",并不是在EffectsFactory.cpp創(chuàng)建并添加進(jìn)HIDL服務(wù)端時(shí)初始化的,而是在客戶端有人調(diào)用EffectsFactoryHalInterface.h接口中的函數(shù)時(shí),才會(huì)初始化。也就是有人使用的時(shí)候才會(huì)初始化。

下面我將以AudioMixer中的DownmixerBufferProvider為例,呈現(xiàn)音效框架的初始化過(guò)程。AudioMixer是供AudioFlinger使用的、用于將AudioTrack的音頻數(shù)據(jù)進(jìn)行聲道數(shù)轉(zhuǎn)換、Format采樣精度轉(zhuǎn)換、采樣率轉(zhuǎn)換的混音器。其中DownmixerBufferProvider是它的一個(gè)子模塊,用于將多聲道轉(zhuǎn)換成雙聲道,轉(zhuǎn)換算法使用的是音效框架中提供的EffectDownmix。它們的源碼位于/frameworks/av/media/libaudioprocessing/目錄中。我之所以選擇DownmixerBufferProvider為例講解音效框架的初始化過(guò)程,是因?yàn)樗苯邮褂玫膌ibaudiohal.so庫(kù)中的EffectsFactoryHalInterface.h接口和EffectHalInterface.h接口,沒(méi)有經(jīng)過(guò)AudioFlinger。所以,也就沒(méi)有AudioFlinger中涉及的sessionId、EffectChain、AudioEffect這些概念。可以更加簡(jiǎn)單清晰的理解如何通過(guò)libaudiohal.so庫(kù)調(diào)用Android音效框架。

音效框架的使用流程主要就三步:

  1. 查詢是否存在聲道轉(zhuǎn)換音效?
  2. 初始化聲道轉(zhuǎn)換音效。包括創(chuàng)建音效代理接口對(duì)象EffectHalInterface、創(chuàng)建共享內(nèi)存EffectBufferHalInterface、設(shè)置音效算法的參數(shù)。
  3. 聲道轉(zhuǎn)換音效算法處理。先將準(zhǔn)備好的音頻數(shù)據(jù)拷貝到共享內(nèi)存中,然后通過(guò)EffectHalInterface->process()接口通知音效算法進(jìn)行處理。

2.1音效框架初始化

DownmixerBufferProvider是在它的init()函數(shù)中進(jìn)行查詢的,它采用的方法是先通過(guò)EffectsFactoryHalInterface->queryNumberEffects()接口,查詢目前音效框架中存在的音效算法總數(shù)量,然后循環(huán)遍歷EffectsFactoryHalInterface->getDescriptor(uint32_t index,effect_descriptor_t *pDescriptor)接口,獲取每個(gè)音效算法的描述對(duì)象effect_descriptor_t,最后看一下這些effect_descriptor_t中是否存在Downmix的TypeUUID。下面我們跟蹤下它所使用的音效框架的第一個(gè)接口queryNumberEffects(),看看是如何觸發(fā)音效框架初始化的。

BufferProviders.cpp->DownmixerBufferProvider::init()
    |->EffectsFactoryHalHidl.cpp->queryNumberEffects()
        |->EffectsFactoryHalHidl.cpp->queryAllDescriptors()
            |->EffectsFactory.cpp->getAllDescriptors()//Binder跨進(jìn)程調(diào)用
                |->EffectsFactory.c->EffectQueryNumberEffects()//libeffects庫(kù)中的代碼
                    |->EffectsFactory.c->init()

可以看出,最終調(diào)用的是EffectsFactory.c(/frameworks/av/media/libeffects/factory/)的EffectQueryNumberEffects()接口。EffectsFactory.c對(duì)外提供的接口函數(shù)中,都會(huì)先調(diào)用自己的init()函數(shù),用于初始化音效框架。init()函數(shù)中使用了一個(gè)全局變量gInitDone,所以init()函數(shù)只會(huì)執(zhí)行一次。在init()函數(shù)中,它會(huì)先查詢?"ro.audio.ignore_effects"系統(tǒng)屬性,如果這個(gè)系統(tǒng)屬性配置為了true,就不會(huì)初始化解析audio_effects.xml音效配置文件,也就不會(huì)初始化所有的音效算法。我們?cè)贒ebug設(shè)備的音頻播放性能時(shí),可以把這個(gè)系統(tǒng)屬性設(shè)置為true,即可bypass掉所有的音效處理。下面接著看下init()函數(shù)做了什么事情。

EffectsFactory.c->init()
    |->EffectsXmlConfigLoader.cpp->EffectLoadXmlEffectConfig()
        |->EffectsConfig.cpp->parse()//解析audio_effects.xml配置文件,保存到EffectsConfig.h->Config結(jié)構(gòu)體中。

        |->EffectsXmlConfigLoader.cpp->loadLibraries()//將加載成功的音效so庫(kù)所對(duì)應(yīng)的libeffects/EffectsFactory.h->lib_entry_t結(jié)構(gòu)體保存到gLibraryList列表中。
            |->EffectsXmlConfigLoader.cpp->loadLibrary()//從"/vendor/lib64/soundfx"目錄中加載音效so庫(kù)。獲取該so庫(kù)中的聲明為AUDIO_EFFECT_LIBRARY_INFO_SYM的結(jié)構(gòu)體,從而獲取到audio_effect_library_t結(jié)構(gòu)體句柄。
            
        |->EffectsXmlConfigLoader.cpp->loadEffects()//將獲取的effect_descriptor_t數(shù)據(jù)保存到lib_entry_t->effects列表中
            |->EffectsXmlConfigLoader.cpp->loadEffect()
                |->audio_effect.h->audio_effect_library_t.get_descriptor()
                    |->EffectDownMix.cpp->DownmixLib_GetDescriptor()//實(shí)際調(diào)用的是音效so庫(kù)中的函數(shù),用于獲取effect_descriptor_t結(jié)構(gòu)體數(shù)據(jù)

    |->EffectsFactory.c->updateNumEffects()//將所有l(wèi)ib中effect_descriptor_t的總數(shù)匯總后保存到gNumEffects變量中

當(dāng)這段代碼執(zhí)行完成了,音效框架就完成了audio_effects.xml文件的解析、加載了所有的音效so庫(kù)、并創(chuàng)建了每個(gè)音效對(duì)應(yīng)的描述對(duì)象effect_descriptor_t。但是此時(shí)并沒(méi)有創(chuàng)建音效算法對(duì)應(yīng)的處理對(duì)象effect_handle_t,要等到有人真正使用時(shí)才會(huì)創(chuàng)建。

下面看一下audioeffects.xml配置文件,這個(gè)配置文件包括了5種節(jié)點(diǎn):

<libraries>節(jié)點(diǎn)。里面包含的是所有音效so庫(kù)的名稱。EffectsFactory.c會(huì)通過(guò)這個(gè)名稱在"/vendor/lib64/soundfx"目錄中加載so庫(kù)。然后獲取該so庫(kù)中的聲明為AUDIO_EFFECT_LIBRARY_INFO_SYM的結(jié)構(gòu)體,從而獲取到audio_effect_library_t結(jié)構(gòu)體的句柄。

<libraries>
? ? <library name="downmix" path="libdownmix.so"/>
? ? <library name="proxy" path="libeffectproxy.so"/>
    <library name="bundle" path="libbundlewrapper.so"/>
    <library name="offload_bundle" path="libqcompostprocbundle.so"/>
</libraries>

<effects>節(jié)點(diǎn)。里面包含了所有的音效算法聲明。每個(gè)音效算法都需要指定它所屬的so庫(kù)名稱和自己的唯一UUID。通過(guò)這個(gè)UUID,我們就可以通過(guò)audio_effect_library_t->get_descriptor(uuid,effect_descriptor_t *pDescriptor)接口獲取到該音效對(duì)應(yīng)的描述結(jié)構(gòu)體effect_descriptor_t。也可以通過(guò)audio_effect_library_t->create_effect(uuid,effect_handle_t *pHandle)獲取到其對(duì)應(yīng)的使用接口effect_handle_t。

<effects>
? ? ? ? <effect name="downmix" library="downmix" uuid="93f04452-e4fe-41cc-91f9-e475b6d1d69f"/>
? ? ? ? <effectProxy name="bassboost" library="proxy" uuid="14804144-a5ee-4d24-aa88-0002a5d5c51b">
? ? ? ? ? ? <libsw library="bundle" uuid="8631f300-72e2-11df-b57e-0002a5d5c51b"/>
? ? ? ? ? ? <libhw library="offload_bundle" uuid="2c4a8c24-1581-487f-94f6-0002a5d5c51b"/>
? ? ? ? </effectProxy>
</effects>

<postprocess>節(jié)點(diǎn)。用于為某個(gè)streamType綁定多個(gè)音效算法。AudioPolicyService會(huì)在初始化時(shí)解析此節(jié)點(diǎn)。下面這個(gè)配置,代表的意思是所有申明為AUDIO_STREAM_MUSIC的AudioTrack,在播放時(shí)都會(huì)加載bassboost低音增強(qiáng)音效和loudness_enhancer音量放大音效。相當(dāng)于配置了一個(gè)全局音效。

<postprocess>
    <stream type="music">
        <apply effect="bassboost"/>
        <apply effect="loudness_enhancer"/>
    </stream>
</postprocess>

<preprocess>節(jié)點(diǎn)。用于為某個(gè)inputSource綁定多個(gè)音效算法。AudioPolicyService會(huì)在初始化時(shí)解析此節(jié)點(diǎn)。下面這個(gè)配置,代表的意思是所有申明了AUDIO_SOURCE_VOICE_COMMUNICATION的AudioRecord(即VoIP語(yǔ)聊場(chǎng)景),在錄制音頻數(shù)據(jù)時(shí)都會(huì)加載AEC算法和NS算法。

<preprocess>
    <stream type="voice_communication">
        <apply effect="aec"/>
        <apply effect="ns"/>
    </stream>
</preprocess>

<deviceEffects>節(jié)點(diǎn)。用于為某個(gè)設(shè)備綁定多個(gè)音效算法。AudioPolicyService會(huì)在初始化時(shí)解析此節(jié)點(diǎn)。下面這個(gè)配置,代表的意思是為內(nèi)置麥克風(fēng)綁定一個(gè)AGC算法。也就是不管什么場(chǎng)景的錄音,都會(huì)執(zhí)行AGC算法。

<deviceEffects>
    <devicePort type="AUDIO_DEVICE_IN_BUILTIN_MIC" address="bottom">
        <apply effect="agc"/>
    </devicePort>
</deviceEffects>

從上面可以看出,audioeffects.xml文件包含了兩部分配置內(nèi)容。一部分是配置申明音效算法,由AudioHAL負(fù)責(zé)解析。一部分是配置全局音效:Stream音效和Device音效,由AudioPolicyService負(fù)責(zé)解析。在EffectsFactory.c完成初始化后,AudioHAL進(jìn)程的本地內(nèi)存中就保存了audio_effect_library_t列表和effect_descriptor_t列表,供上層查詢使用。

2.2創(chuàng)建并加載音效算法

在DownmixerBufferProvider的構(gòu)造函數(shù)中,會(huì)使用EffectsFactoryHalInterface->createEffect(uuid,sessionId,ioId,deviceId,sp<EffectHalInterface> *effect)接口來(lái)創(chuàng)建IEffect.hal的代理對(duì)象EffectHalHidl。其中ioId指是PlaybackThread中StreamOutHAL對(duì)應(yīng)的Id。seesionId、ioId、deviceId都可以不指定。它們主要是告訴音效算法自己處理的數(shù)據(jù)是為誰(shuí)服務(wù)的。必須指定的只有UUID這個(gè)??梢圆碌?,EffectsFactory.c正是通過(guò)UUID來(lái)查找并創(chuàng)建該音效對(duì)應(yīng)的effect_handle_t接口的。下面簡(jiǎn)單看一下調(diào)用流程:

BufferProviders.cpp->DownmixerBufferProvider::DownmixerBufferProvider()
    |->EffectsFactoryHalHidl.cpp->createEffect()//創(chuàng)建EffectHalHidl對(duì)象
        EffectsFactory.cpp->createEffect()//Binder跨進(jìn)程調(diào)用,獲取IEffect對(duì)象
            |->EffectsFactory.cpp->createEffectImpl()//創(chuàng)建IEffect對(duì)象(DownmixEffect對(duì)象),生成effectId。并保存到EffectMap集合中。
                |->EffectsFactory.c->EffectCreate()//libeffects庫(kù)中。獲取effect_handle_t句柄
                    |->EffectsFactory.c->doEffectCreate()
                        |->audio_effect.h->audio_effect_library_t.create_effect()
                            |->EffectDownMix.cpp->DownmixLib_Create()//返回的是downmix_module_t結(jié)構(gòu)體句柄
                                |->EffectDownMix.cpp->Downmix_Init()//設(shè)置輸入源的數(shù)據(jù)格式為:7.1聲道、浮點(diǎn)型、44.1K采樣率。輸出的數(shù)據(jù)格式為:雙聲道、浮點(diǎn)型、44.1K
        
                |->EffectsFactory.cpp->dispatchEffectInstanceCreation()//根據(jù)effect_descriptor_t結(jié)構(gòu)體中的type(即OpenSL ES中定義的音效類型UUID),創(chuàng)建對(duì)象。new DownmixEffect對(duì)象。

當(dāng)這段代碼執(zhí)行完成后,在客戶端,EffectsFactoryHalHidl.cpp創(chuàng)建了一個(gè)EffectHalHidl對(duì)象,供DownmixerBufferProvider調(diào)用使用。EffectHalHidl有一個(gè)成員變量IEffect(由IEffectFactory返回的),指向的是服務(wù)端AudioHAL進(jìn)程中的DownmixEffect.cpp對(duì)象,DownmixEffect有一個(gè)成員變量Effect.cpp,Effect有一個(gè)成員變量effect_handle_t,effect_handle_t指針指向的是EffectDownMix.cpp的downmix_module_t結(jié)構(gòu)體。這樣最終DownmixerBufferProvider就和EffectDownMix算法建立了聯(lián)系。下面是調(diào)用關(guān)系圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

上面圖標(biāo)為綠色的DownmixerBufferProvider代表的是音效算法的使用方。標(biāo)為紅色的downmix_module_t代表的是音效算法的實(shí)現(xiàn)方,中間白色部分是前文提到的音效框架。

????????創(chuàng)建完EffectHalHidl對(duì)象后,就需要?jiǎng)?chuàng)建共享內(nèi)存了。我們從effect_handle_t接口中的音效算法處理函數(shù)process(effect_handle_t self,audio_buffer_t *inBuffer,audio_buffer_t *outBuffer)中,可以看出音效算法處理時(shí)是需要兩塊內(nèi)存空間,一個(gè)用于保存輸入給音效算法的數(shù)據(jù)內(nèi)存塊inBuffer,一個(gè)用于音效算法處理完成后輸出的數(shù)據(jù)內(nèi)存塊outBuffer。我們可以通過(guò)EffectsFactoryHalHidl的mirrorBuffer(void* external,size_t size,sp<EffectBufferHalInterface>* buffer)接口,創(chuàng)建一塊共享內(nèi)存。其中external指針指向的是本地?cái)?shù)據(jù)內(nèi)存塊。保存在EffectBufferHalHidl對(duì)象的mExternalData成員變量中。然后EffectBufferHalHidl會(huì)通過(guò)IAllocator接口創(chuàng)建一塊共享內(nèi)存,保存在AudioBuffer.data中,它會(huì)被傳遞給Effect服務(wù)端。它的調(diào)用代碼很簡(jiǎn)單如下:

BufferProviders.cpp->DownmixerBufferProvider::DownmixerBufferProvider()
    |->EffectsFactoryHalHidl.cpp->mirrorBuffer(&mInBuffer)//創(chuàng)建輸入數(shù)據(jù)共享內(nèi)存塊
        |->EffectBufferHalHidl.cpp->mirror()//創(chuàng)建EffectBufferHalHidl對(duì)象
            |->EffectBufferHalHidl.cpp->init()//創(chuàng)建共享內(nèi)存

    |->EffectsFactoryHalHidl.cpp->mirrorBuffer(&mOutBuffer)//創(chuàng)建輸出數(shù)據(jù)共享內(nèi)存塊
        |->EffectBufferHalHidl.cpp->mirror()//創(chuàng)建EffectBufferHalHidl對(duì)象
            |->EffectBufferHalHidl.cpp->init()//創(chuàng)建共享內(nèi)存

    |->EffectHalHidl.cpp->setInBuffer(mInBuffer)//為音效綁定輸入共享內(nèi)存塊
    |->EffectHalHidl.cpp->setOutBuffer(mOutBuffer)//為音效綁定輸出共享內(nèi)存塊

當(dāng)上述代碼執(zhí)行完了后,音效使用方就新創(chuàng)建了兩個(gè)共享內(nèi)存塊。供算法使用。

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

上圖就是當(dāng)使用音效算法時(shí),音頻數(shù)據(jù)的傳遞路徑:從"本地內(nèi)存"拷貝到"輸入共享內(nèi)存",音效算法讀取處理完成后,將新數(shù)據(jù)寫(xiě)入到"輸出共享內(nèi)存",EffectBufferHalHidl會(huì)將"輸出共享內(nèi)存"中的數(shù)據(jù)拷貝回"本地內(nèi)存",供音效調(diào)用方后續(xù)使用。當(dāng)然,如果音效處理前后的數(shù)據(jù)所需的內(nèi)存大小完全一樣,也可以將輸入和輸出指向同一塊共享內(nèi)存。但是使用Android架構(gòu)的音效處理時(shí),仍然至少要?jiǎng)?chuàng)建一份共享內(nèi)存塊。所以,這就是我認(rèn)為Android音效架構(gòu)設(shè)計(jì)不合理的地方。因?yàn)樵贏ndroid音效架構(gòu)中,Track音效的使用方是AudioFlinger。所以,必然會(huì)出現(xiàn)因?yàn)榭邕M(jìn)程通信,需要額外創(chuàng)建共享內(nèi)存的情況。

? ? ? ? 新創(chuàng)建完共享內(nèi)存并傳遞給了EffectHalHidl后,就需要對(duì)算法進(jìn)行配置,比如說(shuō)明輸入的音頻數(shù)據(jù)的采樣率、聲道數(shù)、Format精度;要求輸出的音頻數(shù)據(jù)格式等。調(diào)用的是EffectHalHidl->command()接口。因?yàn)檫壿嫳容^簡(jiǎn)單,這里就不列出代碼調(diào)用流程了。

2.3執(zhí)行音效算法

DownmixerBufferProvider是通過(guò)EffectHalHidl.cpp->process()接口,來(lái)通知音效算法執(zhí)行的,最終通過(guò)層層調(diào)用,會(huì)調(diào)用到effect_handle_t.process()接口。EffectDownMix算法針對(duì)該接口的實(shí)現(xiàn)是EffectDownMix.cpp->Downmix_Process()函數(shù)。

查看Downmix_Process()函數(shù)源碼你會(huì)發(fā)現(xiàn),EffectDownMix并沒(méi)有自己寫(xiě)處理算法,而是調(diào)用的/system/media/audio_utils/include/audio_utils/ChannelMix.h->process()接口。并且與DownmixerBufferProvider類似的、提供相同功能的ChannelMixBufferProvider模塊,也是使用ChannelMix.h進(jìn)行處理的。而AudioMixer卻把DownmixerBufferProvider作為多聲道轉(zhuǎn)雙聲道處理的首選模塊,顯然是沒(méi)有必要的,除非我們自己實(shí)現(xiàn)了一個(gè)聲道轉(zhuǎn)換算法,配置在audioeffects.xml文件中。否則建議大家修改一下AudioMixer代碼(AudioMixer.cpp->prepareForDownmix()函數(shù)中),將多聲道轉(zhuǎn)換雙聲道處理的模塊默認(rèn)改成ChannelMixBufferProvider模塊,效果完全一樣,卻不需要跨進(jìn)程調(diào)用,也不用創(chuàng)建新的共享內(nèi)存,系統(tǒng)資源消耗的更少。

BufferProviders.cpp->DownmixerBufferProvider::copyFrames()
    |->EffectBufferHalHidl.cpp->setExternalData()//設(shè)置本地內(nèi)存的指針,因?yàn)樵跇?gòu)造函數(shù)中沒(méi)有指定。
    |->EffectBufferHalHidl.cpp->update()//將待處理的數(shù)據(jù)從本地內(nèi)存拷貝到共享內(nèi)存中

    |->EffectHalHidl.cpp->process()//通知音效算法處理
        |->EffectHalHidl.cpp->processImpl()
            |->EffectHalHidl.cpp->prepareForProcessing()
                |->DownmixEffect.cpp->prepareForProcessing()//Binder跨進(jìn)程調(diào)用
                    |->Effect.cpp->prepareForProcessing()//創(chuàng)建并運(yùn)行"effect"線程
            |->EffectHalHidl.cpp->needToResetBuffers()
            |->EffectHalHidl.cpp->setProcessBuffers()
                |->DownmixEffect.cpp->setProcessBuffers()//Binder跨進(jìn)程調(diào)用
                    |->Effect.cpp->setProcessBuffers()
                        |->AudioBufferManager.cpp->wrap()//將創(chuàng)建的AudioBufferWrapper對(duì)象保存到mBuffers集合中。
                            |->new AudioBufferWrapper()
                            |->AudioBufferManager.cpp->AudioBufferWrapper::init()//構(gòu)建audio_buffer_t結(jié)構(gòu)體,將audio_buffer_t.raw指針指向AudioBuffer共享內(nèi)存
            
            |->Effect.cpp->ProcessThread::threadLoop()
                |->audio_effect.h->effect_interface_s.process()
                    |->EffectDownMix.cpp->Downmix_Process()
                        |->ChannelMix.h->process()

    |->EffectBufferHalHidl.cpp->commit()//將共享內(nèi)存中的數(shù)據(jù)拷貝給本地內(nèi)存

上面是音效處理的調(diào)用流程代碼,需要說(shuō)明的是:

  • 在第一次處理數(shù)據(jù)時(shí),服務(wù)端Effect.cpp會(huì)新創(chuàng)建一個(gè)獨(dú)立線程名為"effect"。也就是每個(gè)音效算法在執(zhí)行時(shí)都運(yùn)行在自己的獨(dú)立線程中。
  • 客戶端EffectHalHidl.cpp與服務(wù)端Effect.cpp會(huì)基于MessageQueue來(lái)實(shí)現(xiàn)同步機(jī)制,當(dāng)服務(wù)端沒(méi)有處理完數(shù)據(jù)時(shí),EffectHalHidl.cpp->process()函數(shù)會(huì)讓當(dāng)前調(diào)用線程休眠等待,當(dāng)服務(wù)端處理完數(shù)據(jù),通過(guò)MessageQueue喚醒客戶端后,EffectHalHidl.cpp->process()函數(shù)才會(huì)返回。也就是說(shuō),EffectHalHidl.cpp->process()函數(shù)是阻塞式的。

三、AudioFlinger對(duì)音效框架的二次封裝

? ? ? ? AudioFlinger做為EffectHAL音效框架的核心控制方,為上層APP提供了一個(gè)AudioEffect.java接口,讓用戶可以啟動(dòng)一個(gè)音效并控制音效算法的相關(guān)參數(shù)。然后在自己模塊的內(nèi)部,定義了EffectHandle、EffectModule、EffectChain這三個(gè)內(nèi)部類。

EffectHandle作為AudioEffect的服務(wù)端存在,與AudioEffect進(jìn)行跨進(jìn)程通信。它們是1:1的關(guān)聯(lián)關(guān)系。

EffectModule作為真實(shí)音效算法的控制端存在,與EffectHalHidl進(jìn)行關(guān)聯(lián),如前文所述,有了EffectHalHidl對(duì)象,EffectModule就與EffectHAL層建立了關(guān)聯(lián)關(guān)系。能夠真正的控制一個(gè)音效算法了。EffectModule與EffectHalHidl是1:1的關(guān)聯(lián)關(guān)系。EffectModule與EffectHandle是1:n的關(guān)聯(lián)關(guān)系。

EffectChain是一個(gè)列表集合,里面包含了一組EffectModule對(duì)象。也就是說(shuō)EffectChain列表中包含了多個(gè)音效算法。目的是將這一組音效算法綁定到指定數(shù)據(jù)傳輸位置上。這樣一個(gè)AudioTrack,就可以綁定多個(gè)音效算法了。同時(shí),每個(gè)PlaybackThread里面會(huì)包含多個(gè)EffectChain對(duì)象,讓每個(gè)EffectChain可以綁定到不同的數(shù)據(jù)傳輸位置上。

以下是它們的類圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

上面的類圖因?yàn)橐w現(xiàn)出繼承和真實(shí)的關(guān)聯(lián)關(guān)系,所以嵌套的層級(jí)比較多,不容易直觀的看出AudioEffect、EffectHandle、EffectModule、EffectChain它們之間的關(guān)聯(lián)關(guān)系。以下是簡(jiǎn)化后的關(guān)系圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

EffectHandle、EffectModule、EffectChain都是在AudioFlinger模塊(/frameworks/av/services/audioflinger/目錄)中的Effect.h文件中定義,在Effects.cpp文件中實(shí)現(xiàn)的。當(dāng)查看Effect.h文件時(shí),你會(huì)發(fā)現(xiàn),Android還定義了一個(gè)DeviceEffectProxy類,它和EffectModule一樣,都繼承自EffectBase。DeviceEffectProxy代表的是綁定到Device的音效。當(dāng)使用Device音效時(shí),EffectHandle就和DeviceEffectProxy建立的關(guān)聯(lián)關(guān)系,而不是直接和EffectModule。同時(shí),在DeviceEffectManager.h文件中,還定義了一個(gè)DeviceEffectManager類,用于管理所有的DeviceEffectProxy對(duì)象。DeviceEffectProxy類有一個(gè)成員變量mHalEffect,它關(guān)聯(lián)的是EffectModule對(duì)象。也就說(shuō)做,DeviceEffectProxy最終還是通過(guò)EffectModule去控制真實(shí)的音效算法。下面是Device音效情況下的關(guān)系圖,圖中標(biāo)紅的是有變化的部分。

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

了解完AudioFlinger針對(duì)音效二次封裝的架構(gòu),我們?cè)賮?lái)看一下AudioFlinger是如何綁定音效到指定位置的。為了綁定音效,Android引入了sessionId這個(gè)概念。AudioTrack正是通過(guò)sessionId與EffectChain進(jìn)行綁定的。AndroidFlinger在接收到AudioTrack的創(chuàng)建請(qǐng)求時(shí)(AudioFlinger.cpp->createTrack()函數(shù)中),會(huì)為每個(gè)Track新分配一個(gè)唯一的seesionId,它是一個(gè)正整數(shù),根據(jù)Track創(chuàng)建的先后順序遞增,也就是說(shuō)越晚創(chuàng)建的Track,它被分配的seesionId的數(shù)值越大。

之所以說(shuō)明seesionId的數(shù)值,是因?yàn)锳udioFlinger模塊會(huì)根據(jù)sessionId的大小來(lái)對(duì)EffectChain進(jìn)行排序,按照從大到小的順序排序。同時(shí),/system/media/audio/include/system/audio.haudio_session_t枚舉中,定義了三種全局音效:AUDIO_SESSION_OUTPUT_MIX(值為0)、AUDIO_SESSION_OUTPUT_STAGE(值為-1)、AUDIO_SESSION_DEVICE(值為-2)。這樣經(jīng)過(guò)排序后,就可以得到多個(gè)EffectChain之間的執(zhí)行先后順序,EffectChain-Track最先執(zhí)行,然后是EffectChain-Mix,然后是EffectChain-Stage、最后是EffectChain-Device。以下是執(zhí)行順序圖:

EffectChain-Track2---》EffectChain-Track1---》EffectChain-Mix---》EffectChain-Stage---》EffectChain-Device

AUDIO_SESSION_OUTPUT_MIX這個(gè)seesionId會(huì)被用于Aux音效的綁定。

AUDIO_SESSION_OUTPUT_STAGE這個(gè)seesionId會(huì)被用于空間音頻(Spatializer)相關(guān)音效的綁定。

AUDIO_SESSION_DEVICE這個(gè)sessionId會(huì)被用于Device音效的綁定。

上層應(yīng)用APP在創(chuàng)建自己的AudioTrack時(shí),也可以指定一個(gè)已知的seesionId,通過(guò)已有的AudioTrack對(duì)象的AudioTrack.java->getAudioSessionId()函數(shù)獲取,然后在創(chuàng)建新AudioTrack對(duì)象時(shí)通過(guò)AudioTrack.java->Builder.setSessionId()函數(shù)設(shè)置。這樣,就把多個(gè)AudioTrack綁定到同一個(gè)sessionId上了。也就實(shí)現(xiàn)了AudioTrack與sessionId的n:1關(guān)系。但是,AudioTrack不能指定負(fù)數(shù)的sessionId。也就是說(shuō)AudioTrack不能綁定到全局音效中。因?yàn)樵?/span>AudioTrack.java->Builder.setSessionId()函數(shù)、AudioTrack.java的構(gòu)造函數(shù)、AudioFlinger.cpp->createTrack()函數(shù)中都做了檢查校驗(yàn)。

那么sessionId又是如何與Effect關(guān)聯(lián)上的呢?我們?cè)谛聞?chuàng)建一個(gè)AudioEffect對(duì)象時(shí),需要指定一個(gè)sessionId。然后會(huì)通知AudioFlinger模塊去創(chuàng)建該AudioEffect對(duì)應(yīng)的AudioHandle(AudioFlinger.cpp->createEffect()函數(shù)中)。AudioFlinger模塊會(huì)先根據(jù)seesionId查找與之對(duì)應(yīng)的Track,找到了Track,也就找到了播放Track對(duì)應(yīng)的PlaybackThread。然后從PlaybackThread的mEffectChains列表中,根據(jù)此sessionId找到對(duì)應(yīng)的EffectChain對(duì)象。如果沒(méi)有,就新創(chuàng)建一個(gè)EffectChain對(duì)象。所以,sessionId和EffectChain是1:1的對(duì)應(yīng)關(guān)系。最后,為這個(gè)AudioEffect創(chuàng)建對(duì)應(yīng)的EffectModule對(duì)象,并將它添加到EffectChain列表中。至此,AudioTrack就和EffectModule基于sessionId建立起了n:n的關(guān)聯(lián)關(guān)系。以下是關(guān)系圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

從圖中可以看出,同一個(gè)音效,它綁定到不同的seesionId時(shí),會(huì)被重新創(chuàng)建一個(gè)新的EffectModule對(duì)象。比如BassBoost低音增強(qiáng)音效,它被分別綁定到QQ音樂(lè)和酷狗音樂(lè)的sessionId時(shí),就會(huì)創(chuàng)建兩個(gè)不同的BassBoost對(duì)象。這樣兩個(gè)音效處理就會(huì)各不干擾。

前面講了多個(gè)EffectChain之間的執(zhí)行順序,那么在EffectChain中的多個(gè)EffectModule,它們的執(zhí)行順序是什么呢?AudioFlinger是根據(jù)effect_descriptor_t結(jié)構(gòu)體(音效算法描述類)中的flags來(lái)控制的。在你實(shí)現(xiàn)的音效算法指定的flags中可以指定下面flag的任意一種:

EFFECT_FLAG_INSERT_FIRST:表示將自己這個(gè)音效算法插入到EffectChain最前面。即:最先執(zhí)行。如果有多個(gè)音效算法指定了這個(gè)flag,就按照創(chuàng)建EffectModule的時(shí)間先后排序,后創(chuàng)建的放在后面執(zhí)行。

EFFECT_FLAG_INSERT_LAST:表示將這個(gè)音效算法插入到EffectChain的最后面執(zhí)行。如果有多個(gè)音效算法聲明此flag,后創(chuàng)建的EffectModule會(huì)插入到上次那個(gè)的前面。

EFFECT_FLAG_INSERT_ANY:表示將這個(gè)音效算法插入到INSERT_FIRSTINSERT_LAST之間。有多個(gè)時(shí),按照加入的時(shí)間依次向后插入。

EFFECT_FLAG_INSERT_EXCLUSIVE:表示排它。意思是插入的EffectChain中,只能包含這一個(gè)音效算法。

以下就是EffectChain中多個(gè)EffectModule的執(zhí)行順序:

INSERT_FIRST1--》INSERT_FIRST2--》INSERT_ANY?1--》INSERT_ANY?2--》INSERT_LAST2--》INSERT_LAST1

? ? ? ? 當(dāng)AudioFlinger中的PlaybackThread在執(zhí)行時(shí),會(huì)創(chuàng)建三個(gè)本地內(nèi)存塊:mMixerBuffer、mEffectBuffer、mSinkBuffer。

mMixerBuffer內(nèi)存塊中保存的是所有沒(méi)有綁定任何音效算法的Track的音頻數(shù)據(jù)。

mEffectBuffer內(nèi)存塊中保存的是所有經(jīng)過(guò)音效算法處理后的Track的音頻數(shù)據(jù)。

mSinkBuffer內(nèi)存塊中保存的是最終需要輸出給AudioHAL進(jìn)程中StreamOut的音頻數(shù)據(jù)。

所有的Track數(shù)據(jù),都會(huì)先傳遞給AudioMixer混音器進(jìn)行多聲道轉(zhuǎn)換、采樣精度調(diào)整、重采樣后,才會(huì)傳遞給綁定的音效算法處理或傳遞給mMixerBuffer內(nèi)存塊。

當(dāng)此次PlaybackThread播放時(shí)所有的Track都沒(méi)有綁定音效算法,PlaybackThread會(huì)將mMixerBuffer中的數(shù)據(jù)直接拷貝到mSinkBuffer中。

當(dāng)此次PlaybackTread播放時(shí)所有的Track都綁定的有音效算法,mMixerBuffer內(nèi)存塊中就沒(méi)有數(shù)據(jù),就不會(huì)將其拷貝搬運(yùn)到任何地方。

當(dāng)此次PlaybackTread播放時(shí)即有綁定音效算法的Track,又有沒(méi)有綁定的Track,PlaybackThread就會(huì)先將mMixerBuffer中的數(shù)據(jù)拷貝到mEffectBuffer中,再將mEffectBuffer的數(shù)據(jù)拷貝到mSinkBuffer中。

以下是音頻數(shù)據(jù)傳輸流程圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

四、Device音效的綁定過(guò)程

????????Android中支持兩種方式,將一個(gè)音效算法綁定到Device上處理。一種是動(dòng)態(tài)創(chuàng)建一個(gè)AudioEffect對(duì)象,并給該對(duì)象指定的seesionId為AUDIO_SESSION_DEVICE。另一種是在audio_effects.xml文件的<deviceEffects>節(jié)點(diǎn)中申明,然后由AudioPolicyService解析后,通知AudioFlinger進(jìn)行綁定。下面我將說(shuō)明一下通過(guò)audio_effects.xml配置文件的綁定方式。

AudioPolicyService在它的初始化函數(shù)onFirstRef()中,會(huì)創(chuàng)建AudioPolicyEffects對(duì)象,AudioPolicyEffects在其構(gòu)造函數(shù)中就會(huì)進(jìn)行audio_effects.xml文件的解析,然后通知AudioFlinger綁定音效。以下是代碼調(diào)用流程:

AudioPolicyService.cpp->onFirstRef()
    |->new AudioPolicyEffects()
        |->AudioPolicyEffects.cpp->loadAudioEffectXmlConfig()//mInputSources集合中保存的是<preprocess>節(jié)點(diǎn)中的配置。mOutputStreams集合中保存的是<postprocess>節(jié)點(diǎn)中的配置。mDeviceEffects集合中保存的是<deviceEffects>節(jié)點(diǎn)中的配置。
            |->EffectsConfig.cpp->parse()//libeffectsconfig

        |->AudioPolicyEffects.cpp->initDefaultDeviceEffects()

AudioPolicyEffects與EffectHAL一樣,也是調(diào)用EffectsConfig.cpp->parse()來(lái)進(jìn)行audio_effects.xml文件的解析,它會(huì)將<deviceEffects>節(jié)點(diǎn)中的配置保存到mDeviceEffects集合中,將<preprocess>節(jié)點(diǎn)中的配置保存到mInputSources集合,將<postprocess>節(jié)點(diǎn)中的配置保存到mOutputStreams集合中。

然后會(huì)調(diào)用initDefaultDeviceEffects()函數(shù),通知AudioFlinger進(jìn)行Device音效綁定。在該函數(shù)中,會(huì)為每個(gè)待綁定的音效算法,創(chuàng)建一個(gè)AudioEffect.cpp對(duì)象,并將sessionID設(shè)置為AUDIO_SESSION_DEVICE,將待綁定的DeviceType設(shè)置到AudioEffect對(duì)象中。然后調(diào)用AudioEffect->setEnabled(true)函數(shù)。這樣做之后,AudioEffect對(duì)象在它的初始化時(shí)就會(huì)通知AudioFlinger綁定Device音效。以下是代碼調(diào)用流程:

AudioPolicyEffects.cpp->initDefaultDeviceEffects()//綁定Device音效。為每個(gè)Device對(duì)應(yīng)的所有Effect,都分別創(chuàng)建一個(gè)包名為android的客戶端AudioEffect對(duì)象。并初始化它。
    |->AudioEffect.cpp->set(AUDIO_SESSION_DEVICE)
        |->AudioFlinger.cpp->createEffect()//Binder跨進(jìn)程調(diào)用
            |->DeviceEffectManager.cpp->AudioFlinger::DeviceEffectManager::createEffect_l()
                |->new DeviceEffectProxy()//從mDeviceEffects列表中找不到此音效時(shí),新創(chuàng)建一個(gè)。 
                |->new EffectHandle()//mEffect引用的是DeviceEffectProxy對(duì)象

                |->Effects.cpp->AudioFlinger::DeviceEffectProxy::init()
                    |->Effects.cpp->AudioFlinger::DeviceEffectProxy::onCreatePatch()
                        |->Effects.cpp->AudioFlinger::DeviceEffectProxy::checkPort()//為當(dāng)前Device綁定音效 
    
    |->AudioEffect.cpp->setEnabled(true)         

從上面的代碼流程可以看出,AudioFlinger通過(guò)自己的成員變量DeviceEffectManager創(chuàng)建了一個(gè)DeviceEffectProxy對(duì)象。而實(shí)現(xiàn)Device音效綁定的地方,是在DeviceEffectProxy::checkPort()函數(shù)中。這個(gè)函數(shù)為Device音效實(shí)現(xiàn)了兩種綁定方式:

  • 一種是讓AudioHAL進(jìn)程中的DeviceHAL直接處理音效數(shù)據(jù)。通過(guò)調(diào)用audio.h->audio_hw_device_t.add_device_effect(audio_port_handle_t device, effect_handle_t effect)接口實(shí)現(xiàn)。這種方式在音效算法處理過(guò)程中,就不會(huì)出現(xiàn)跨進(jìn)程調(diào)用和共享內(nèi)存創(chuàng)建的情況了。也就是說(shuō)會(huì)由AudioHAL進(jìn)程處理音效算法生成的數(shù)據(jù),而不是AudioFlinger處理。AudioFlinger只負(fù)責(zé)控制音效算法的參數(shù)。
  • 另一種方法是找到這個(gè)Device對(duì)應(yīng)的PlaybackThread,由PlaybackThread去創(chuàng)建EffectModule對(duì)象,并插入到AUDIO_SESSION_DEVICE這個(gè)sessionId對(duì)應(yīng)的EffectChain中,最終由PlaybackThread在傳輸音頻數(shù)據(jù)時(shí)調(diào)用EffectChain->process_l()函數(shù)去處理音效算法生成的數(shù)據(jù)。也就是說(shuō)由AudioServer進(jìn)程負(fù)責(zé)處理音效數(shù)據(jù)。這種方式會(huì)出現(xiàn)跨進(jìn)程調(diào)用和額外的共享內(nèi)存創(chuàng)建。

4.1 DeviceHAL處理音效數(shù)據(jù)的方式

????????采用這種方式,加載的音效算法必須聲明為前處理音效類型(EFFECT_FLAG_TYPE_PRE_PROC)或后處理音效類型(EFFECT_FLAG_TYPE_POST_PROC)。并且聲明EFFECT_FLAG_HW_ACC_TUNNEL這個(gè)flag。它們都是通過(guò)在音效算法的實(shí)現(xiàn)代碼中,設(shè)置effect_descriptor_t->flags來(lái)聲明的。

在DeviceEffectProxy::checkPort()函數(shù)中,判斷待綁定的音效算法如果包含EFFECT_FLAG_HW_ACC_TUNNEL?flag,就會(huì)新創(chuàng)建一個(gè)EffectModule對(duì)象,將其保存到自己的DeviceEffectProxy::mHalEffect變量中。

下面我們看看AudioEffect.cpp->setEnabled(true)函數(shù)做了什么事?

AudioPolicyEffects.cpp->initDefaultDeviceEffects()
    |->AudioEffect.cpp->setEnabled(true)
        |->Effects.cpp->AudioFlinger::EffectHandle::enable()//對(duì)應(yīng)的是DeviceEffectProxy
            |->Effects.cpp->AudioFlinger::DeviceEffectProxy::setEnabled()
                |->Effects.cpp->AudioFlinger::EffectBase::setEnabled()//執(zhí)行的是DeviceEffectProxy對(duì)象的方法
                    |->Effects.cpp->AudioFlinger::EffectBase::setEnabled_l()//mState = STARTING,設(shè)置的是DeviceEffectProxy對(duì)象狀態(tài)
                    |->Effects.cpp->AudioFlinger::DeviceEffectProxy::ProxyCallback::onEffectEnable(sp<EffectBase>& effectBase)//傳入的是DeviceEffectProxy對(duì)象,所以直接返回null

                |->Effects.cpp->AudioFlinger::EffectHandle::enable()//執(zhí)行的DeviceEffectProxy的mEffectHandles集合中保存的EffectHandle對(duì)象,對(duì)應(yīng)的是EffectModule對(duì)象
                    |->Effects.cpp->AudioFlinger::EffectBase::setEnabled()//執(zhí)行的是EffectModule對(duì)象的方法           
                        |->Effects.cpp->AudioFlinger::EffectBase::setEnabled_l()//mState = STARTING,設(shè)置的是EffectModule對(duì)象狀態(tài)
                        |->Effects.cpp->AudioFlinger::DeviceEffectProxy::ProxyCallback::onEffectEnable(sp<EffectBase>& effectBase)傳入的是EffectModule對(duì)象
                            |->Effects.cpp->AudioFlinger::EffectModule::start()
                                |->Effects.cpp->AudioFlinger::EffectModule::start_l()
                                    |->EffectHalHidl.cpp->command(EFFECT_CMD_ENABLE)//通知EffectHAL層enable 
                                    |->Effects.cpp->AudioFlinger::EffectModule::addEffectToHal_l()//只有EFFECT_FLAG_TYPE_PRE_PROC或EFFECT_FLAG_TYPE_POST_PROC才執(zhí)行下面的代碼
                                        |->Effects.cpp->AudioFlinger::DeviceEffectProxy::ProxyCallback::addEffectToHal()
                                            |->Effects.cpp->AudioFlinger::DeviceEffectProxy::addEffectToHal()  
                                                |->DeviceEffectManager.h->DeviceEffectManagerCallback::addEffectToHal()
                                                    |->DeviceEffectManager.h->DeviceEffectManager::addEffectToHal()
                                                        |->AudioFlinger.cpp->addEffectToHal()
                                                            |->DeviceHalHidl.cpp->addDeviceEffect()
                                                                |->Device.cpp->addDeviceEffect()//Binder跨進(jìn)程調(diào)用
                                                                    |->EffectMap.cpp->EffectMap::getInstance().get(effectId)//源碼在/hardware/interfaces/audio/common/all-versions/default/目錄
                                                                    |->audio.h->audio_hw_device_t.add_device_effect()                              

從上面這份代碼調(diào)用流程可以看出,Effects.cpp文件中Effect和Callback之間的嵌套關(guān)系還是挺深的,最終通過(guò)層層調(diào)用,終于調(diào)用到了audio.h->audio_hw_device_t.add_device_effect(audio_port_handle_t device, effect_handle_t effect)接口。成功將此音效算法的effect_handle_t 句柄傳遞給DeviceHAL的audio_hw_device_t進(jìn)行關(guān)聯(lián)綁定。

4.2 AudioFlinger處理音效數(shù)據(jù)的方式

在DeviceEffectProxy::checkPort()函數(shù)中,會(huì)通過(guò)PatchPanel::Patch對(duì)象,找到需要綁定音效的Device對(duì)應(yīng)的PlaybackThread,然后調(diào)用createEffect_l(AUDIO_SESSION_DEVICE)函數(shù),進(jìn)行音效加載。

Effects.cpp->AudioFlinger::DeviceEffectProxy::checkPort()
    |->Threads.cpp->AudioFlinger::ThreadBase::createEffect_l(AUDIO_SESSION_DEVICE)
        |->Threads.cpp->AudioFlinger::PlaybackThread::checkEffectCompatibility_l()//FAST線程要求AUDIO_SESSION_DEVICE只能應(yīng)用EFFECT_FLAG_TYPE_POST_PROC音效。
        |->new EffectChain()
        |->Threads.cpp->AudioFlinger::PlaybackThread::addEffectChain_l()//初始化EffectChain
            |->EffectsFactoryHalHidl.cpp->mirrorBuffer()
                |->EffectBufferHalHidl.cpp->mirror()//創(chuàng)建共享內(nèi)存,mExternalData指向的是PlaybackThread的mEffectBuffer
            |->halOutBuffer = halInBuffer;//輸入和輸出共享內(nèi)存都指向的是mEffectBuffer
            |->Effects.h->EffectChain::setInBuffer(halInBuffer)
            |->Effects.h->EffectChain::setOutBuffer(halOutBuffer)
            |->mEffectChains.insertAt(chain, i)//按照sessionId從大到小插入。全局session都是負(fù)數(shù),所以在最后。順序:track-EffectChain、MIX-EffectChain、STAGE-EffectChain、DEVICE-EffectChain
            
        |->Effects.cpp->AudioFlinger::EffectChain::createEffect_l()//創(chuàng)建EffectModule
            |->new EffectModule()
                |->Effects.cpp->AudioFlinger::EffectChain::EffectCallback::createEffectHal()
                    |->EffectsFactoryHalHidl.cpp->createEffect()//通知EffectHAL去創(chuàng)建真正的音效算法對(duì)象
            |->Effects.cpp->AudioFlinger::EffectChain::addEffect_ll(){
                    effect->setInBuffer(mInBuffer);
                    if (idx_insert == previousSize) {
                        if (idx_insert != 0) {
                            mEffects[idx_insert-1]->setOutBuffer(mInBuffer);
                        }
                        effect->setOutBuffer(mOutBuffer);
                    } else {
                    effect->setOutBuffer(mInBuffer);
                    }
                }

從上面的調(diào)用代碼流程可以看出,在PlaybackThread中新創(chuàng)建了一個(gè)EffectChain,并創(chuàng)建了EffectChain的輸入和輸出共享內(nèi)存塊,它們都指向的是mEffectBuffer本地內(nèi)存塊。而此EffectChain中包含的所有EffectModule對(duì)象,它們的輸入和輸出共享內(nèi)存塊都使用的是EffectChain的輸入內(nèi)存塊,只有最后一個(gè)EffectModule的輸出內(nèi)存塊使用的是EffectChain的輸出內(nèi)存塊。但是AUDIO_SESSION_DEVICE對(duì)應(yīng)的這個(gè)EffectChain在創(chuàng)建共享內(nèi)存塊時(shí),輸入和輸出是同一個(gè),所以,也就無(wú)所謂了。以下是音頻數(shù)據(jù)傳輸流程圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

五、Stream音效的綁定過(guò)程

? ? ? Stream音效的綁定方式,也支持兩種。一種是在audio_effects.xml文件的<postprocess>節(jié)點(diǎn)中申明,這樣AudioPolicyEffects模塊解析后就會(huì)將配置信息保存到mOutputStreams集合中。

另一種動(dòng)態(tài)綁定的方式,是通過(guò)創(chuàng)建StreamDefaultEffect.java對(duì)象,并在該類的構(gòu)造函數(shù)中指定待綁定音效的TypeUUID、UUID、AudioAttributes.Usage。在其構(gòu)造函數(shù)中,會(huì)通過(guò)層層調(diào)用,最終調(diào)用到AudioPolicyEffects的addStreamDefaultEffect()函數(shù),AudioPolicyEffects就會(huì)通過(guò)AudioPolicyManager查找AudioAttributes.Usage對(duì)應(yīng)的StreamType,然后根據(jù)UUID從EffectHAL中找到對(duì)應(yīng)的音效描述對(duì)象effect_descriptor_t。最終將StreamType和effect_descriptor_t一起保存到mOutputStreams集合中。

下面看一下AudioPolicyEffects如何通過(guò)mOutputStreams集合來(lái)進(jìn)行Stream音效綁定的。

在AudioTrack.java的play()函數(shù)中,會(huì)調(diào)用到PlaybackThread的addTrack_l()函數(shù),addTrack_l()函數(shù)又會(huì)調(diào)用到AudioPolicyService::startOutput()函數(shù),startOutput()函數(shù)最終會(huì)調(diào)用到AudioPolicyEffects::addOutputSessionEffects()函數(shù)。所以,進(jìn)行綁定的是addOutputSessionEffects()函數(shù)。

AudioTrack.java->play()
    |->Threads.cpp->AudioFlinger::PlaybackThread::addTrack_l()
        |->AudioPolicyInterfaceImpl.cpp->AudioPolicyService::startOutput()
            |->AudioPolicyEffects.cpp->AudioPolicyEffects::addOutputSessionEffects(
                         audio_io_handle_t output,
                         audio_stream_type_t stream,
                         audio_session_t audioSession)
                |->new AudioEffect()
                |->AudioEffect.cpp->set()
                |->AudioPolicyEffects.cpp->AudioPolicyEffects::EffectVector::setProcessorEnabled(true)
                    |->AudioEffect.cpp->setEnabled(true)
                        |->Effects.cpp->AudioFlinger::EffectBase::setEnabled_l()//mState = STARTING
                    

addOutputSessionEffects()函數(shù)中傳入的三個(gè)參數(shù),分別是此AudioTrack對(duì)應(yīng)的PlaybackThread的Id(output)、此AudioTrack對(duì)應(yīng)的StreamType(stream)、此AudioTrack的seesionId(audioSession)。這樣,addOutputSessionEffects()函數(shù)就可以根據(jù)StreamType從mOutputStreams集合中找到它需要綁定的音效算法描述對(duì)象effect_descriptor_t,也就找到了音效算法的UUID。然后addOutputSessionEffects()函數(shù)會(huì)創(chuàng)建一個(gè)AudioEffect.cpp對(duì)象,并向該對(duì)象指定Effect的UUID和AudioTrack的seesionId。

可以看出,Android綁定Stream音效的方法和綁定Track音效沒(méi)有區(qū)別。即:在此StreamType對(duì)應(yīng)的每個(gè)AudioTrack準(zhǔn)備播放時(shí),為它們關(guān)聯(lián)的seesionId都新創(chuàng)建一個(gè)AudioEffect。

之所以這樣設(shè)計(jì),我認(rèn)為的原因是同一種StreamType對(duì)應(yīng)的多個(gè)AudioTrack,可以指定到不同的PlaybackThread中去處理,這樣兩個(gè)AudioTrack就沒(méi)法放在同一個(gè)線程對(duì)應(yīng)的mEffectBuffer內(nèi)存塊中去處理了。

從上面可以看出,Stream音效嚴(yán)格意義來(lái)說(shuō)其實(shí)是StreamType音效。它并不是直接綁定到一個(gè)StreamOut流中的。

<preprocess>節(jié)點(diǎn)的綁定過(guò)程類似,這里就不再贅述了。

六、StreamHAL處理音效數(shù)據(jù)的方式

? ? ? ?當(dāng)我查看/hardware/libhardware/include/hardware/audio.h接口文件時(shí),發(fā)現(xiàn)audio_stream_t結(jié)構(gòu)體定義了一個(gè)函數(shù):audio_stream_t->add_audio_effect(effect_handle_t effect)。意思是可以為StreamOut流綁定音效?即:像DeviceHAL處理音效數(shù)據(jù)那樣,讓StreamHAL直接處理音效數(shù)據(jù),不進(jìn)行共享內(nèi)存的創(chuàng)建和搬運(yùn)?答案是肯定的。但是,當(dāng)我下面分析完AudioFlinger中的設(shè)置流程后,我們就會(huì)發(fā)現(xiàn),這個(gè)流程設(shè)計(jì)的很不合理,而且有漏洞。如果我理解的不對(duì),歡迎各位指正!
? ? ? ? 如果想讓一個(gè)音效算法綁定到StreamHAL中處理。只需在自己的音效算法中聲明為前處理音效類型(EFFECT_FLAG_TYPE_PRE_PROC)或后處理音效類型(EFFECT_FLAG_TYPE_POST_PROC)就可以了。AudioFlinger最終就會(huì)調(diào)用到StreamHAL層的audio_stream_t->add_audio_effect(effect_handle_t effect)接口進(jìn)行綁定。

AudioFlinger模塊調(diào)用的入口是在PlaybackThread的音頻數(shù)據(jù)處理函數(shù)threadLoop()中。即播放線程的循環(huán)執(zhí)行函數(shù)中。

AudioFlinger::PlaybackThread::threadLoop()
    |->Effects.cpp->AudioFlinger::EffectChain::process_l()
        |->Effects.cpp->AudioFlinger::EffectModule::process()
            |->Effects.h->EffectBase::isProcessImplemented()//判斷effect_descriptor_t->flags是否包含EFFECT_FLAG_NO_PROCESS
        
        |->Effects.cpp->AudioFlinger::EffectModule::updateState()
            |->Effects.cpp->AudioFlinger::EffectModule::start_l()
                |->AudioFlinger::EffectModule::addEffectToHal_l()//只有EFFECT_FLAG_TYPE_PRE_PROC或EFFECT_FLAG_TYPE_POST_PROC才執(zhí)行下面的代碼
                    |->AudioFlinger::EffectChain::EffectCallback::addEffectToHal()
                        |->StreamHalHidl.cpp->addEffect()
                            |->Stream.cpp->addEffect(effectId)//Binder跨進(jìn)程調(diào)用
                                |->EffectMap.cpp->EffectMap::getInstance().get(effectId)//源碼在/hardware/interfaces/audio/common/all-versions/default/目錄
                                |->audio.h->audio_stream_t.add_audio_effect()
        

從上面的代碼流程可以看出,EffectModule在它的start_l()函數(shù)中,會(huì)調(diào)用StreamHAL的綁定接口。是否調(diào)用的唯一判斷條件就是該音效算法是否聲明了前處理音效類型(EFFECT_FLAG_TYPE_PRE_PROC)或后處理音效類型(EFFECT_FLAG_TYPE_POST_PROC)。

只要音效算法申明了,就會(huì)進(jìn)行綁定。根本不管此音效算法關(guān)聯(lián)的seesionId是什么類型,也就是說(shuō)關(guān)聯(lián)到AudioTrack sessionId、AUDIO_SESSION_OUTPUT_MIX、AUDIO_SESSION_OUTPUT_STAGE,都可以。

只有AUDIO_SESSION_DEVICE不行,因?yàn)锳UDIO_SESSION_DEVICE對(duì)應(yīng)的EffectModule,它的EffectModule::mCallback變量指向的是DeviceEffectProxy::ProxyCallback,而不是EffectChain::EffectCallback,所以最終綁定到的是DeviceHAL。

這樣簡(jiǎn)單粗暴的設(shè)計(jì),我能想到的漏洞有兩個(gè):

  1. 同一個(gè)StreamHAL,會(huì)綁定多次相同類型的音效算法。比如我在同一個(gè)PlaybackThread中被處理的AudioTrack1的sessonId1中綁定了低音增強(qiáng)算法,在AudioTrack2的seesionId2中又綁定了低音增強(qiáng)算法。它們兩個(gè)算法的UUID完全一樣,但會(huì)有兩個(gè)不同的EffectModule對(duì)象,所以最終audio_stream_t->add_audio_effect(effect_handle_t effect)接口會(huì)被執(zhí)行兩次。而且effect_handle_t的句柄是不相同的對(duì)象。補(bǔ)救辦法是修改/hardware/interfaces/audio/core/all-versions/default/Stream.cpp->addEffect()函數(shù)。增加一個(gè)相同算法UUID的判斷,同一個(gè)UUID的音效算法如果已綁定過(guò),就不再調(diào)用audio_stream_t->add_audio_effect(effect_handle_t effect)接口。
  2. AudioFlinger和StreamHAL會(huì)同時(shí)處理音效數(shù)據(jù)。因?yàn)樵贓ffectModule::process()函數(shù)中,沒(méi)有進(jìn)行音效處理類型(EFFECT_FLAG_TYPE_PRE_PROC或EFFECT_FLAG_TYPE_POST_PROC)的判斷,只判斷了此音效算法是否聲明了EFFECT_FLAG_NO_PROCESS flag。只有聲明了EFFECT_FLAG_NO_PROCESS flag,才不會(huì)處理音效數(shù)據(jù)。也就是說(shuō)沒(méi)有聲明EFFECT_FLAG_NO_PROCESS?flag的音效算法所生成的數(shù)據(jù),會(huì)被AudioFlinger和StreamHAL同時(shí)處理。而Device音效不會(huì)出現(xiàn)這種情況的原因是綁定到DeviceHAL的EffectModule,它的mInBuffer和mOutBuffer都是null,即:沒(méi)有創(chuàng)建共享內(nèi)存數(shù)據(jù)。也就無(wú)法處理。補(bǔ)救辦法有兩種:一種是在音效算法的實(shí)現(xiàn)代碼中聲明一下EFFECT_FLAG_NO_PROCESS flag,但是需要確保SoC廠商實(shí)現(xiàn)的StreamHAL不會(huì)根據(jù)這個(gè)flag來(lái)判斷是否讓音效算法處理。另一種方法是修改EffectModule::process()函數(shù),添加音效處理類型(EFFECT_FLAG_TYPE_PRE_PROC或EFFECT_FLAG_TYPE_POST_PROC)的判斷,包含這兩種flag的EffectModule,就不要通知音效算法處理數(shù)據(jù)了。

七、Track音效的綁定過(guò)程

? ? ? ? 通過(guò)前面的介紹,為一個(gè)AudioTrack綁定音效的方法,基本可以猜到了。只需執(zhí)行兩步:

  1. 創(chuàng)建一個(gè)AudioEffect.java對(duì)象。在其構(gòu)造函數(shù)中,指定從AudioTrack.java->getAudioSessionId()獲取到的AudioTrack sessionId、指定音效算法的UUID或TypeUUID。但是AudioEffect.java的構(gòu)造函數(shù)是@hide類型的,不支持3rd APP調(diào)用。而Android將系統(tǒng)支持的音效算法都創(chuàng)建了一個(gè)java文件,比如BassBoost.java。這些音效類都是繼承的AudioEffect.java。并默認(rèn)將自己的TypeUUID指定到了AudioEffect.java父類中。所以,普通的3rd APP可以直接new?BassBoost對(duì)象就可以了,只需指定seesionId。也就是說(shuō),普通的3rd APP無(wú)法綁定設(shè)備廠商自己擴(kuò)展的音效算法類型。除非通過(guò)Java反射機(jī)制直接創(chuàng)建AudioEffect.java對(duì)象。
  2. 調(diào)用AudioEffect.java->setEnabled(true)函數(shù),啟動(dòng)音效算法。

下面我以BassBoost低音增強(qiáng)算法舉例,來(lái)看看它與AudioTrack的綁定過(guò)程。

new BassBoost(AudioTrackSessionId)
    |->AudioEffect.java->AudioEffect(EFFECT_TYPE_BASS_BOOST,AudioTrackSessionId)
        |->AudioEffect.java->native_setup()
            |->android_media_AudioEffect.cpp->android_media_AudioEffect_native_setup()
                |->new AudioEffect(attributionSource)
                |->AudioEffect.cpp->set()
                    |->AudioFlinger.cpp->createEffect()//Binder跨進(jìn)程調(diào)用
                        |->Threads.cpp->AudioFlinger::ThreadBase::createEffect_l()
                            |->Threads.cpp->AudioFlinger::PlaybackThread::checkEffectCompatibility_l()
                            |->Threads.cpp->AudioFlinger::PlaybackThread::addEffectChain_l()
                                |->EffectsFactoryHalHidl.cpp->mirrorBuffer()//mExternalData指向的是PlaybackThread的mEffectBuffer
                                |->halOutBuffer = halInBuffer
                                |->halInBuffer=EffectsFactoryHalHidl.cpp->allocateBuffer()//新創(chuàng)建共享內(nèi)存塊
                                |->PlaybackTracks.h->Track::setMainBuffer()//設(shè)置所有關(guān)聯(lián)此seesionId的track的mMainBuffer,設(shè)置的是halInBuffer中的共享內(nèi)存。
                                |->Effects.h->EffectChain::setInBuffer()
                                |->Effects.h->EffectChain::setOutBuffer()//halOutBuffer::mExternalData指向的是PlaybackThread的mEffectBuffer
                            
                            |->Effects.cpp->AudioFlinger::EffectChain::createEffect_l()
                                |->Effects.cpp->AudioFlinger::EffectChain::addEffect_ll(){
                                    effect->setInBuffer(mInBuffer);
                                    if (idx_insert == previousSize) {
                                        if (idx_insert != 0) {
                                            mEffects[idx_insert-1]->setOutBuffer(mInBuffer);
                                            }
                                        effect->setOutBuffer(mOutBuffer);
                                    } else {
                                        effect->setOutBuffer(mInBuffer);
                                        }
                                  }

執(zhí)行完上面的代碼流程后,AudioFlinger會(huì)為EffectChain創(chuàng)建一個(gè)新的輸入共享內(nèi)存塊halInBuffer和輸出共享內(nèi)存塊halOutBuffer。halOutBuffer的mExternalData指向的是PlaybackThread的mEffectBuffer。EffectChain中包含的所有EffectModule對(duì)象,它們的輸入和輸出共享內(nèi)存塊都使用的是EffectChain的輸入內(nèi)存塊,只有最后一個(gè)EffectModule的輸出內(nèi)存塊使用的是EffectChain的輸出內(nèi)存塊。

綁定到該sessionId上的所有AudioTrack,它們的mMainBuffer全部指向的是EffectChain的輸入共享內(nèi)存halInBuffer。AudioMixer混音器處理完每個(gè)AudioTrack的數(shù)據(jù)后,會(huì)將數(shù)據(jù)拷貝到mMainBuffer指向的內(nèi)存塊中。

這樣就成功建立了音頻數(shù)據(jù)傳輸通道。以下是傳輸流程圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

那么,當(dāng)有兩個(gè)運(yùn)行在不同PlaybackThread的AudioTrack,綁定到同一個(gè)sessionId上時(shí),會(huì)如何處理呢?

比如我先創(chuàng)建一個(gè)AudioTrackA,它在DeepBuffer PlayackThread中運(yùn)行。然后創(chuàng)建一個(gè)AudioEffect,將其綁定到TrackA-sessionId中。最后再創(chuàng)建一個(gè)AudioTrackB,讓其運(yùn)行在Primary PlaybackThread中,并將TrackA-sessionId指定給AudioTrackB。

這種情況,出現(xiàn)的結(jié)果是AudioEffect最終會(huì)被綁定到AudioTrackB上執(zhí)行,AudioTrackA會(huì)被解綁掉此AudioEffect音效。關(guān)鍵的處理代碼是在AudioFlinger創(chuàng)建AudioTrackB的函數(shù)AudioFlinger.cpp->createTrack()中。如下:

        // 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) {
            // no risk of deadlock because AudioFlinger::mLock is held
            Mutex::Autolock _dl(thread->mLock);
            Mutex::Autolock _sl(effectThread->mLock);
            if (moveEffectChain_l(sessionId, effectThread, thread) == NO_ERROR) {
                effectThreadId = thread->id();
                effectIds = thread->getEffectIds_l(sessionId);
            }
        }

而AudioFlinger.cpp->moveEffectChain_l()函數(shù)最終會(huì)調(diào)用PlaybackThread::removeEffectChain_l()執(zhí)行刪除EffectChain的操作。代碼如下:

size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& chain)
{
    audio_session_t session = chain->sessionId();

    ALOGV("removeEffectChain_l() %p from thread %p for session %d", chain.get(), this, session);

    for (size_t i = 0; i < mEffectChains.size(); i++) {
        if (chain == mEffectChains[i]) {
            mEffectChains.removeAt(i);
            // detach all active tracks from the chain
            for (const sp<Track> &track : mActiveTracks) {
                if (session == track->sessionId()) {
                    ALOGV("removeEffectChain_l(): stopping track on chain %p for session Id: %d",
                            chain.get(), session);
                    chain->decActiveTrackCnt();
                }
            }

            // detach all tracks with same session ID from this chain
            for (size_t i = 0; i < mTracks.size(); ++i) {
                sp<Track> track = mTracks[i];
                if (session == track->sessionId()) {
                    track->setMainBuffer(reinterpret_cast<effect_buffer_t*>(mSinkBuffer));
                    chain->decTrackCnt();
                }
            }
            break;
        }
    }
    return mEffectChains.size();
}

可以從上面的代碼看到,PlaybackThread刪除了AudioTrackA關(guān)聯(lián)的所有音效,并將AudioTrackA的mainBuffer指向了mSinkBuffer。

八、Aux音效的綁定過(guò)程

? ? ? ? Android中支持AudioTrack綁定一個(gè)Aux音效(auxiliary effect),當(dāng)AudioTrack綁定了Aux音效后,AudioMixer混音器在處理音頻數(shù)據(jù)時(shí),就會(huì)將AudioTrack的數(shù)據(jù)拷貝兩份,一份拷貝到它綁定的普通Track音效中處理,另一份拷貝到Aux音效中處理,最后由PlaybackThread進(jìn)行合并。我認(rèn)為這是Android的一個(gè)Bug,因?yàn)樵创a中,編寫(xiě)Threads.cpp代碼的人在AudioTrack綁定到普通Track音效時(shí),會(huì)禁用掉它綁定的Aux音效。但是寫(xiě)Effects.cpp代碼的人在音效禁用的實(shí)現(xiàn)函數(shù)中,又明確不禁用Aux音效。所以,最終其實(shí)是沒(méi)有禁用,導(dǎo)致普通Track音效和Aux音效可以同時(shí)處理同一個(gè)AudioTrack的數(shù)據(jù)。下面我會(huì)詳細(xì)說(shuō)明代碼邏輯。

定義為Aux音效的算法,必須申明EFFECT_FLAG_TYPE_AUXILIARY flag。否則無(wú)法被當(dāng)做Aux音效綁定給AudioTrack使用。

所有Aux音效的EffectModule都會(huì)被放入到MIX sessionId的EffectChain中。也就是說(shuō)Aux音效只能關(guān)聯(lián)到AUDIO_SESSION_OUTPUT_MIX中。并且MIX sessionId也只能關(guān)聯(lián)Aux音效。

綁定Aux音效的方法,主要分為四步:

  1. 創(chuàng)建一個(gè)Aux音效的控制對(duì)象AudioEffect,指定sessionId為AUDIO_SESSION_OUTPUT_MIX。需要APP擁有"android.permission.MODIFY_AUDIO_SETTINGS"權(quán)限。
  2. 啟動(dòng)Aux音效:調(diào)用AudioEffect.java->setEnabled(true)接口。
  3. 為AudioTrack綁定音效:調(diào)用AudioTrack.java->attachAuxEffect(int effectId)接口。其中的入?yún)ffectId可以通過(guò)AudioEffect.java->getId()函數(shù)獲取到。一個(gè)AudioTrack只能綁定一個(gè)Aux音效。如果需要取消綁定,在AudioTrack.java->attachAuxEffect(int effectId)函數(shù)中傳入0即可。
  4. 設(shè)置Aux音效處理的數(shù)據(jù)音量大小,默認(rèn)是0,也就是Aux音效不會(huì)處理數(shù)據(jù)。通過(guò)調(diào)用AudioTrack.java->setAuxEffectSendLevel(float level)接口設(shè)置。

以下是Aux音效的數(shù)據(jù)傳輸流程圖:

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

下面我以EnvironmentalReverb為例,分析下Aux音效的創(chuàng)建流程。

//創(chuàng)建Aux音效
new EnvironmentalReverb(AUDIO_SESSION_OUTPUT_MIX)
    |->AudioEffect.java->AudioEffect(EFFECT_TYPE_ENV_REVERB,AUDIO_SESSION_OUTPUT_MIX)
        |->AudioFlinger.cpp->createEffect()//Binder跨進(jìn)程調(diào)用
            |->ServiceUtilities.cpp->settingsAllowed()//將Effect綁定到MIX SessionId時(shí),需要APP擁有"android.permission.MODIFY_AUDIO_SETTINGS"權(quán)限。沒(méi)有聲明此權(quán)限就會(huì)直接返回。
            |->AudioFlinger.cpp->getEffectDescriptor(uuid,type,EFFECT_FLAG_TYPE_AUXILIARY)//找到聲明了EFFECT_FLAG_TYPE_AUXILIARY標(biāo)識(shí)的音效描述對(duì)象,沒(méi)有聲明的就會(huì)直接返回。

            |->AudioSystem.cpp->getOutputForEffect(effect_descriptor_t)//為此音效找到合適的PlaybackThreadId
                |->AudioPolicyIntefaceImpl.cpp->AudioPolicyService::getOutputForEffect()
                    |->AudioPolicyManager.cpp->getOutputForEffect()
                        |->AudioPolicyManager.cpp->selectOutputForMusicEffects()//查找outputstream的邏輯是先找到STRATEGY_MEDIA現(xiàn)在對(duì)應(yīng)的播放Device,再根據(jù)Device找到可播放到該Device的outputStream。有多個(gè)outputStream時(shí),按照以下順序返回:1: An offloaded output. 2: A deep buffer output 3: The primary output 4: the first output in the list
                            |->Engine.cpp->getOutputDevicesForAttributes(AUDIO_USAGE_MEDIA)
                        |->mEffects.moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output);
                        |->AudioPolicyClientImpl.cpp->AudioPolicyService::AudioPolicyClient::moveEffects(AUDIO_SESSION_OUTPUT_MIX, mMusicEffectOutput, output)
                        |->AudioFlinger.cpp->moveEffects()
                            |->AudioFlinger.cpp->moveEffectChain_l(AUDIO_SESSION_OUTPUT_MIX,srcThread,dstThread)//通知AudioFlinger,將AUDIO_SESSION_OUTPUT_MIX對(duì)應(yīng)的EffectChain,從舊的PlaybackThread移動(dòng)到指定的PlaybackThread中。

            |->Threads.cpp->AudioFlinger::ThreadBase::createEffect_l()
                |->Threads.cpp->AudioFlinger::PlaybackThread::addEffectChain_l()
                    |->EffectsFactoryHalHidl.cpp->mirrorBuffer(&halInBuffer)//mExternalData指向的是PlaybackThread的mEffectBuffer
                    |->halOutBuffer = halInBuffer;
                    |->Effects.h->EffectChain::setInBuffer(halInBuffer)
                    |->Effects.h->EffectChain::setOutBuffer(halOutBuffer)
                    |->Threads.cpp->AudioFlinger::ThreadBase::checkSuspendOnAddEffectChain_l(chain)//檢查一下自己這個(gè)EffectChain對(duì)應(yīng)的sessionId是否被禁用音效了

                |->Effects.cpp->AudioFlinger::EffectChain::createEffect_l()
                    |->Effects.cpp->AudioFlinger::EffectChain::addEffect_ll(){
                        mEffects.insertAt(effect, 0);//每次都將新添加的Aux音效插入到隊(duì)列的最前面。
                        EffectsFactoryHalHidl.cpp->allocateBuffer(halBuffer)//新創(chuàng)建一塊共享內(nèi)存
                        effect->setInBuffer(halBuffer);
                        effect->setOutBuffer(mInBuffer);
                        }

從上面的代碼調(diào)用流程中可以看出,與Track音效的創(chuàng)建流程主要有三點(diǎn)不同。

一個(gè)是創(chuàng)建Aux音效時(shí),AudioFlinger會(huì)從AudioPolicyManager那里查詢出一個(gè)合適的PlaybackThread去綁定。因?yàn)榇薃ux音效對(duì)應(yīng)的是全局MIX-sessionId,而不是像Track音效那樣對(duì)應(yīng)的是Track-sessionId,這樣就無(wú)法根據(jù)具體的Track對(duì)象找到它關(guān)聯(lián)的PlaybackThread了。

AudioPolicyManager返回的是MUSIC streamType對(duì)應(yīng)的PlaybackThread。等到有AudioTrack綁定此Aux音效時(shí),AudioFlinger會(huì)將此Aux音效的EffectModule移動(dòng)到AudioTrack對(duì)應(yīng)的PlaybackThread中。

第二點(diǎn)不同是創(chuàng)建的音效共享內(nèi)存塊不同。AudioFlinger會(huì)為每個(gè)Aux音效都新創(chuàng)建一個(gè)輸入共享內(nèi)存塊。然后將Aux音效的輸出內(nèi)存指向EffectChain-MIX的輸入\輸出共享內(nèi)存塊。因?yàn)镋ffectChain-MIX的輸入內(nèi)存塊與輸出內(nèi)存塊是同一個(gè)。

第三點(diǎn)不同是AudioFlinger會(huì)限制MIX sessionId對(duì)應(yīng)的EffectChain只能存在一個(gè)PlaybackThread中。即:MIXsessionId-EffectChain是全局唯一的。也就是說(shuō)所有的Aux音效,只能在同一個(gè)PlaybackThread中處理。以下是相關(guān)限制的代碼,在AudioFlinger.cpp->createEffect()函數(shù)中。

        } else if (checkPlaybackThread_l(io) != nullptr
                        && sessionId != AUDIO_SESSION_OUTPUT_STAGE) {
            // allow only one effect chain per sessionId on mPlaybackThreads.
            for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
                const audio_io_handle_t checkIo = mPlaybackThreads.keyAt(i);
                if (io == checkIo) {
                    if (hapticPlaybackRequired
                            && mPlaybackThreads.valueAt(i)
                                    ->hapticChannelMask() == AUDIO_CHANNEL_NONE) {
                        ALOGE("%s: haptic playback thread is required while the required playback "
                              "thread(io=%d) doesn't support", __func__, (int)io);
                        lStatus = BAD_VALUE;
                        goto Exit;
                    }
                    continue;
                }
                const uint32_t sessionType =
                        mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId);
                if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) {
                    ALOGE("%s: effect %s io %d denied because session %d effect exists on io %d",
                          __func__, descOut.name, (int) io, (int) sessionId, (int) checkIo);
                    android_errorWriteLog(0x534e4554, "123237974");
                    lStatus = BAD_VALUE;
                    goto Exit;
                }
            }
        }

下面看一下Aux音效的綁定流程:

//綁定Aux音效
AudioTrack.java->attachAuxEffect(int effectId)
	AudioTrack.cpp->attachAuxEffect()
		Tracks.cpp->AudioFlinger::TrackHandle::attachAuxEffect()
			Tracks.cpp->AudioFlinger::PlaybackThread::Track::attachAuxEffect()
				AudioFlinger.cpp->moveAuxEffectToIo(EffectId,dstThread)//只將此EffectModule從原來(lái)PlaybackThread-MIXsessionId-EffectChain中移動(dòng)到此AudioTrack運(yùn)行的PlaybackThread的MIXsessionId-EffectChain中。此時(shí)就會(huì)有兩個(gè)MIXsessionId-EffectChain
				Threads.cpp->AudioFlinger::PlaybackThread::attachAuxEffect()
					Tracks.cpp->AudioFlinger::PlaybackThread::Track::setAuxBuffer(effect->inBuffer())//將此AudioTrack的AuxBuffer指向EffectModule的輸入共享內(nèi)存塊。

從上面的代碼可以看出,AudioFlinger將Track的mAuxBuffer指針指向了Aux音效的輸入共享內(nèi)存塊。同時(shí),會(huì)將Aux音效EffectModule移動(dòng)到此AudioTrack對(duì)應(yīng)的PlaybackThread中。

我認(rèn)為Aux音效綁定流程這樣設(shè)計(jì)會(huì)存在漏洞。它假設(shè)了一個(gè)前提,所有使用Aux音效的AudioTrack都是Music StreamType類型的。因?yàn)镸usic StreamType類型的AudioTrack在創(chuàng)建時(shí),AudioPolicyManager會(huì)將其強(qiáng)制綁定到DeepBuffer PlaybackThread中(AudioPolicyManager.cpp->selectOutputForMusicEffects()函數(shù))。所以,根本不會(huì)出現(xiàn)Aux音效的移動(dòng)。因?yàn)樗蠱usic AudioTrack都在DeepBuffer PlaybackThread中,而所有Aux音效在創(chuàng)建時(shí)也都在DeepBuffer PlaybackThread中。因?yàn)锳ux音效的AudioEffect在創(chuàng)建時(shí),AudioPolicyManger為它選擇的也是DeepBuffer PlaybackThread(AudioPolicyManager.cpp->selectOutputForMusicEffects()函數(shù))。

實(shí)際上,Aux音效一般也確實(shí)是為Music StreamType場(chǎng)景服務(wù)的,這里的場(chǎng)景包括音樂(lè)播放、視頻播放、游戲播放。

但是,假設(shè)已經(jīng)有一個(gè)Music StreamType的AudioTrack綁定到了此Aux音效上,它運(yùn)行在DeepBuffer PlaybackThread中?,F(xiàn)在我們?cè)購(gòu)?qiáng)制創(chuàng)建一個(gè)System StreamType的audioTrack,讓其運(yùn)行在Primary PlaybackThread中,然后也綁定這個(gè)Aux音效,就會(huì)導(dǎo)致移動(dòng)產(chǎn)生,EffectModule從DeepBuffer Thread移動(dòng)到Primary Thread,對(duì)應(yīng)MIX sessionId的EffectChain也移動(dòng)到了Primary Thread。而之前Music StreamType的AudioTrack就會(huì)被解綁此Aux音效,而沒(méi)有Aux音效處理了。如果此后再新創(chuàng)建一個(gè)Aux音效,比如new?PresetReverb()對(duì)象,AudioFlinger在createEffect()函數(shù)中,又會(huì)從AudioPolicyManger那獲取到DeepBuffer PlaybackThread,然后進(jìn)行綁定,但是在AudioFlinger.cpp->createEffect()函數(shù)中限制了MIX sessionId對(duì)應(yīng)的EffectChain只能存在一個(gè)PlaybackThread中(上面第三點(diǎn)),導(dǎo)致PresetReverb音效創(chuàng)建失敗了。也就是說(shuō)后面創(chuàng)建的Aux音效全部會(huì)失敗,再也無(wú)法創(chuàng)建Aux音效

所以,我認(rèn)為Android設(shè)計(jì)的綁定Aux音效流程有漏洞。我覺(jué)得簡(jiǎn)單的修改方法是既然設(shè)計(jì)者想讓MIX sessionId只綁定Aux音效,并且MIX sessionId對(duì)應(yīng)的EffectChain是全局唯一的。那就不如直接new一個(gè)EffectChain,綁定到MIX sessionId,然后將這個(gè)EffectChain作為AudioFlinger的成員變量保存住。DeepBuffer類型的PlaybackThread在操作Aux音效時(shí),直接從AudioFlinger的成員變量EffectChain中獲取,沒(méi)有必要在各個(gè)類型的PlaybackThread之間移動(dòng)EffectChain對(duì)象了。

下面我們?cè)賮?lái)看一下我發(fā)現(xiàn)的另外一個(gè)Bug,也就是章節(jié)最開(kāi)始提到的:當(dāng)有普通Track音效啟動(dòng),會(huì)禁用Aux音效,但是卻禁用失敗的問(wèn)題。

我們看一下普通Track音效的啟動(dòng)流程,在AudioEffect.cpp->setEnabled(true)函數(shù)中。

//Track音效啟動(dòng)
AudioEffect.cpp->setEnabled(true)
	Effects.cpp->AudioFlinger::EffectHandle::enable()
		Effects.cpp->AudioFlinger::EffectChain::EffectCallback::checkSuspendOnEffectEnabled(true)
			Threads.cpp->AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(enabled, effect->sessionId())
				Threads.cpp->AudioFlinger::ThreadBase::setEffectSuspended_l(NULL, enabled, AUDIO_SESSION_OUTPUT_MIX)//當(dāng)enable的是Track音效時(shí),通知AUDIO_SESSION_OUTPUT_MIX對(duì)應(yīng)的EffectChain禁用!
					Effects.cpp->AudioFlinger::EffectChain::setEffectSuspendedAll_l(true)
						desc = new SuspendedEffectDesc();
						mSuspendedEffects.add((int)kKeyForSuspendAll, desc);
						Effects.cpp->AudioFlinger::EffectChain::getSuspendEligibleEffects()
							Effects.cpp->AudioFlinger::EffectChain::isEffectEligibleForSuspend()//綁定到AUDIO_SESSION_OUTPUT_MIX中的Aux音效不會(huì)被禁用,直接返回了!
					Threads.cpp->AudioFlinger::ThreadBase::updateSuspendedSessions_l(NULL, true, AUDIO_SESSION_OUTPUT_MIX)//向mSuspendedSessions集合中添加kKeyForSuspendAll。SuspendedSessionDesc.mType=0
						desc = new SuspendedSessionDesc()//SuspendedSessionDesc.mType=0
						sessionEffects.add(kKeyForSuspendAll, desc);
						mSuspendedSessions.replaceValueFor(sessionId, sessionEffects)

上面代碼流程中兩個(gè)關(guān)鍵的函數(shù)是Threads.cpp->AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(enabled, effect->sessionId())Effects.cpp->AudioFlinger::EffectChain::isEffectEligibleForSuspend()。以下是這兩個(gè)函數(shù)的關(guān)鍵代碼:

void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(bool enabled,
                                                           audio_session_t sessionId,
                                                           bool threadLocked) {
    if (mType != RECORD) {
        // suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
        // another session. This gives the priority to well behaved effect control panels
        // and applications not using global effects.
        // Enabling post processing in AUDIO_SESSION_OUTPUT_STAGE session does not affect
        // global effects
        if (!audio_is_global_session(sessionId)) {
            setEffectSuspended_l(NULL, enabled, AUDIO_SESSION_OUTPUT_MIX);
        }
    }

}



bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
{
    // auxiliary effects and visualizer are never suspended on output mix
    if ((mSessionId == AUDIO_SESSION_OUTPUT_MIX) &&
        (((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) ||
         (memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) ||
         (memcmp(&desc.type, SL_IID_VOLUME, sizeof(effect_uuid_t)) == 0) ||
         (memcmp(&desc.type, SL_IID_DYNAMICSPROCESSING, sizeof(effect_uuid_t)) == 0))) {
        return false;
    }
    return true;
}

也就是說(shuō),在PlaybackThread中,發(fā)現(xiàn)有Track音效啟動(dòng)后,就會(huì)通知MIX sessionId的EffectChain去禁用掉它里面的所有Aux音效。并且還增加了注釋來(lái)說(shuō)明禁用的原因:

? ? ? ? // suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
? ? ? ? // another session. This gives the priority to well behaved effect control panels
? ? ? ? // and applications not using global effects.
? ? ? ? // Enabling post processing in AUDIO_SESSION_OUTPUT_STAGE session does not affect
? ? ? ? // global effects

而EffectChain在執(zhí)行禁用操作時(shí),卻專門(mén)增加了判斷,如果是請(qǐng)求禁用MIX sessionID對(duì)應(yīng)的Aux音效,不處理!并且也專門(mén)增加了注釋說(shuō)明這一點(diǎn):

// auxiliary effects and visualizer are never suspended on output mix

所以,最終的結(jié)果是一個(gè)AudioTrack可以同時(shí)綁定Track音效和Aux音效。

九、所有音效處理的數(shù)據(jù)傳輸流程圖

android13上音頻重采樣,Android13音頻系統(tǒng),android,音頻

以上是所有音效類型處理時(shí)的數(shù)據(jù)傳輸流程圖,其中沒(méi)有包含AUDIO_SESSION_OUTPUT_STAGE的處理,目前AUDIO_SESSION_OUTPUT_STAGE只會(huì)與空間音頻(Spatializer)關(guān)聯(lián)使用,待我寫(xiě)空間音頻的框架設(shè)計(jì)時(shí),進(jìn)行說(shuō)明。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-771763.html

到了這里,關(guān)于Android13音頻子系統(tǒng)分析(三)---音效算法集成框架的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【W(wǎng)indows 11】安裝 Android子系統(tǒng) 和 Linux子系統(tǒng)

    【W(wǎng)indows 11】安裝 Android子系統(tǒng) 和 Linux子系統(tǒng)

    本文使用電腦系統(tǒng): 主要就是安裝一個(gè)名為: 適用于Android的Windows子系統(tǒng) (WSA)的軟件。 首先在電腦的設(shè)置里面:時(shí)間和語(yǔ)言——語(yǔ)言和地區(qū)里面把地區(qū)改為美國(guó)。 然后到微軟商店搜索: Amazon AppStore 。 安裝亞馬遜應(yīng)用商店的時(shí)候,會(huì)首先提示你安裝前面說(shuō)的WSA。如此,我

    2024年02月09日
    瀏覽(31)
  • Android相機(jī)-HAL子系統(tǒng)

    Android相機(jī)-HAL子系統(tǒng)

    應(yīng)用框架要通過(guò)拍照預(yù)覽攝像獲得照片或者視頻,就需要向相機(jī)子系統(tǒng)發(fā)出請(qǐng)求, 一個(gè)請(qǐng)求對(duì)應(yīng)一組結(jié)果 一次可發(fā)起多個(gè)請(qǐng)求,并且提交請(qǐng)求是非阻塞的,始終按照接收的順序以隊(duì)列的形式先進(jìn)先出地進(jìn)行順序處理 一個(gè)請(qǐng)求包含了拍攝和拍照配置的所有信息,以及處理這些的

    2024年02月11日
    瀏覽(17)
  • 一、LED子系統(tǒng)框架分析

    個(gè)人主頁(yè):董哥聊技術(shù) 我是董哥,嵌入式領(lǐng)域新星創(chuàng)作者 創(chuàng)作理念:專注分享高質(zhì)量嵌入式文章,讓大家讀有所得!

    2023年04月09日
    瀏覽(20)
  • OpenHarmony3.1安全子系統(tǒng)-簽名系統(tǒng)分析

    OpenHarmony3.1安全子系統(tǒng)-簽名系統(tǒng)分析

    應(yīng)用簽名系統(tǒng)主要負(fù)責(zé)鴻蒙hap應(yīng)用包的簽名完整性校驗(yàn),以及應(yīng)用來(lái)源識(shí)別等功能。 子系統(tǒng)間接口: 應(yīng)用完整性校驗(yàn)?zāi)K給其他模塊提供的接口; 完整性校驗(yàn): 通過(guò)驗(yàn)簽,保障應(yīng)用包完整性,防篡改; 應(yīng)用來(lái)源識(shí)別: 通過(guò)匹配簽名證書(shū)鏈與可信源列表,識(shí)別應(yīng)用來(lái)源。

    2024年02月05日
    瀏覽(20)
  • 【分析筆記】Linux 4.9 backlight 子系統(tǒng)分析

    【分析筆記】Linux 4.9 backlight 子系統(tǒng)分析

    內(nèi)核版本:Linux version 4.9.56 驅(qū)動(dòng)文件:licheelinux-4.9driversvideobacklightbacklight.c 對(duì)上,面對(duì)應(yīng)用層提供統(tǒng)一的設(shè)備節(jié)點(diǎn)入口 同級(jí),面對(duì)驅(qū)動(dòng)層提供設(shè)備驅(qū)動(dòng)加載卸載通知事件,以及背光控制接口。 對(duì)下,面對(duì)硬件層提供背光控制調(diào)節(jié)的回調(diào)接口 監(jiān)聽(tīng) frambuffer 事件, 實(shí)現(xiàn)清屏聯(lián)

    2024年02月11日
    瀏覽(17)
  • 五分鐘Win11安裝安卓(Android)子系統(tǒng)

    五分鐘Win11安裝安卓(Android)子系統(tǒng)

    十分鐘,完成win11安裝安卓子系統(tǒng) Win+i 進(jìn)入設(shè)置頁(yè)面,選擇 時(shí)間和語(yǔ)言 - 語(yǔ)言和區(qū)域 - 區(qū)域-美國(guó) 訪問(wèn)如下連接,install即可 安卓子系統(tǒng) 在開(kāi)始菜單找到子系統(tǒng),點(diǎn)開(kāi),做如下配置: 打開(kāi)應(yīng)用商店,安卓APK安裝程序 下載應(yīng)用寶手機(jī)版,APK文件,雙擊,有什么點(diǎn)什么,即可

    2024年02月02日
    瀏覽(29)
  • windows11安裝Android子系統(tǒng),安裝apk教程。

    windows11安裝Android子系統(tǒng),安裝apk教程。

    系統(tǒng):windows11TPM2.0 硬件:內(nèi)存大于8GB 瀏覽器地址欄輸入:“https://store.rg-adguard.net/” 搜索框輸入:“https://www.microsoft.com/store/productId/9P3395VX91NR” 然后在右邊下拉列表選擇“Slow” ,然后點(diǎn)擊最后面的“√”。注:不要翻譯界面! 找到MicrosoftCorporationII.WindowsSubsystemForAndroid_1

    2024年02月06日
    瀏覽(24)
  • 【W(wǎng)indows優(yōu)化系列】Windows11安裝Android子系統(tǒng)

    【W(wǎng)indows優(yōu)化系列】Windows11安裝Android子系統(tǒng)

    Q:為什么要在Windows安裝Android系統(tǒng)?直接在手機(jī)使用不好嗎? A:在電腦刷酷安不比拿著手機(jī)刷酷安爽嗎?在電腦版的酷安碼字不比手機(jī)上碼字爽嗎?不用打開(kāi)手機(jī)也可以在電腦上點(diǎn)餓了么外賣不方便嗎?手機(jī)上似乎不能使用tiktok,電腦上就可以使用tiktok。 并且,使用微軟的

    2024年02月07日
    瀏覽(23)
  • 字符設(shè)備驅(qū)動(dòng)之輸入子系統(tǒng)分析(二)

    作者: Bright-Ho 聯(lián)系方式: 836665637@qq.com input 輸入子系統(tǒng)框架分析(純軟件方面): ? 上一節(jié),我們簡(jiǎn)單的描述的 什么是輸入子系統(tǒng) ; 什么是字符設(shè)備 ; 以及其作用 ;重點(diǎn)是我們講到分析 輸入子系統(tǒng)必須結(jié)合硬件設(shè)備來(lái)分析 ;那么這一節(jié),我們主要講解輸入子系統(tǒng)的 軟

    2024年02月15日
    瀏覽(24)
  • 字符設(shè)備驅(qū)動(dòng)之輸入子系統(tǒng)分析(一)

    作者: Bright-Ho 聯(lián)系方式: 836665637@qq.com 前言背景描述: 雖然在網(wǎng)上看了很多有關(guān)輸入子系統(tǒng)的資料和視頻,但是真正的,系統(tǒng)的,全面的,來(lái)弄清輸入子系統(tǒng),還是要花些時(shí)間和精力的!現(xiàn)在我以一個(gè)初學(xué)者的角度來(lái)分析 input 輸入子系統(tǒng); 那么分析 input 輸入子系統(tǒng)之前,

    2024年02月15日
    瀏覽(23)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包