audio_policy_configuration.xml文件解析
audio_policy_configuration.xml文件解析
簡介
audio音頻數據從一個源走到一個目的都是需要根據配置文件來決定,所以理解configuration配置文件中各個標簽項轉化為c++實體類的及各成員至關重要,本文先直接給出各標簽和對應實體類的結果,后再簡單分析其解析過程
audio_policy_configuration.xml文件對應C++實體類
configuration文件(audio_policy_configuration的縮寫)為音頻audio的設備、流以及路由等配置文件,里面寫明了audio音頻部分有哪些設備、哪些流以及它們支持的編碼、格式以及通道存儲布局等等;
文件通常保存在odm/etc、/vendor/etc、/system/etc目錄下,文件內容大致如下:
查看源碼,在AudioPolicyManager初始化的時候,在方法deserializeAudioPolicyXmlConfig中,當解析正確完第一個configuration文件就會return,所以應該不會解析完所有的config文件;以上xml配置最終轉化為以下c++類AudioPolicyConfig:
class AudioPolicyConfig {std::string mSource; //為config字符串目錄,一般在odm/etc、/vendor/etc、/system/etc下的audio_policy_configuration.xmlHwModuleCollection &mHwModules; //保存了配置文件中所有的所有module標簽集合,每個module標簽對應一個HwModule類DeviceVector &mAvailableOutputDevices; //attchedDevices標簽中,設備名稱名字和devicePort標簽的tagName相同,且type中有OUT字眼的DeviceDescriptor實體類集合,如上SpeakerDeviceVector &mAvailableInputDevices; //同mAvailableOutputDevices一樣,只不過type中有IN的DeviceDescriptor實體類集合,如上Built-In Micsp<DeviceDescriptor> &mDefaultOutputDevice; // 保存defaultOutputDevice標簽內名字和devicePort標簽的tagName相同,如Speaker }module標簽
每個module標簽對應有自己的hal,也就是hal的源碼實現都不一樣,如primary、usb、a2dp等
<module name="primary" halVersion="3.0"><mixport name="compressed_offload" role="source"...><profile name="" format="AUDIO_FORMAT_MP3" .../><profile name="" format="AUDIO_FORMAT_AAC_LC" .../></mixport><mixport name="...." role="sink"/><deviceport .../><route .../> </module>module標簽對應C++實體類HWModule
class HWModule {mName = "primary"mHalVersion = 3.0OutputProfileCollection mOutputProfiles; //mixport標簽role為source類型,對應IOProfle實體類集合InputProfileCollection mInputProfiles; //mixport標簽role為sink的類型,對應IOProfle實體類集合DeviceVector mDeclaredDevices; //所有的deviceport標簽,對應DeviceDescriptor實體類的集合AudioPortVector mPorts; //所有的mixport,deviceport標簽對應的實體類,因為IOProfle和DeviceDescriptor都繼承了AudioPort,所以相當于這是一個AudioPort集合AudioRouteVector mRoutes; //所有的route }MixPort標簽
mixport標簽可以理解為stream流,流配置了自己的格式、采樣率以及mask,并且氛圍輸出、輸入流
<mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/><profile name="" format="AUDIO_FORMAT_AAC"samplingRates="8000,11025,12000,16000" channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/> </mixport>注意:一個mixPort標簽可能有多個profile屬性,也就是支持很多編碼格式屬性
每個mixport標簽對應一個IOProfile實體類
class IOProfile : public AudioPort {/* *同時存活的流的最大數量,默認為1* 標簽中flag會影響該值,如果role為sink,且flag標記為AUDIO_INPUT_FLAG_MMAP_NOIRQ,則賦值為0,表示無窮大* */int maxActiveCount; /** 當前流支持的設備集合;* 如果是sink輸入流,查找規則如下:* 1. 遍歷其父類的成員mRoutes,因為是輸入流,所以遍歷mRoute集合中sink為自己的route,也就是找有哪些源source設備把數據傳給自己。* 2. 找到route后,根據route中source保存的對象,且對象type是AUDIO_PORT_TYPE_DEVICE類型(就是devicesPort標簽對應的實體類DeviceDescriptor)* 3. 把DeviceDescriptor保存在集合中,保存在以下mSupportedDevices中,作為其支持的設備;* 輸出流,同理;最終的結果就是:* 作為輸出流source,mSupportedDevices保存此流可以輸出到對應的device,stream -> device* 作為輸入流sink,mSupportedDevices保存了其他device能輸出數據到此流, device -> stream**/DeviceVector mSupportedDevices; }class AudioPort {mName = "primary output" //對應name(枚舉,下同)audio_port_type_t mType = AUDIO_PORT_TYPE_MIX //此處固定audio_port_role_t mRole = AUDIO_PORT_ROLE_SOURCE/AUDIO_PORT_ROLE_SINK //由config的role決定AudioProfileVector mProfiles; //AudioProfile的集合,對應mixport里面的多個profile/* ** 標簽中flag會影響該值,flag中有INPUT和OUTPUT字眼,如果mixport的role為source,則會去枚舉enum為* OutputFlagConverter::Table查找對比獲取枚舉值;反之則會去InputFlagConverter::Table去對比查找;* 最后將枚舉值設置到這里來;如果role是source角色,則判斷flag包含AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD* 離線加載,則flag |= AUDIO_OUTPUT_FLAG_DIRECT,最后在賦值* */mFlag = flags sp<HwModule> mModule //通過attach函數與HwModule綁定AudioRouteVector mRoutes //相關連的route標簽集合,多個route里面可能都會包含同一個name的mixport,所以這里是集合 }mixport內部的Profile標簽
在解析以上標簽至profile時,會單獨創建AudioProfile,如上xml配置會創建:
class AudioProfile {mName = "" //空串audio_format_t mFormat ; //format字符對應enum的枚舉值,enum在TypeConverter.cpp的FormatConverte的mTable中ChannelsVector mChannelMasks = //同上,也是枚舉值,而不是字符串,定義在OutputChannelConverter、InputChannelConverter和ChannelIndexConverter的mTable中SampleRateVector mSamplingRates = //同上//以下三個對應上面三位,如果三位都有值,則為false固定的,如果xml沒有指定值,則為true表示是動態的值bool mIsDynamicFormat = falsebool mIsDynamicChannels = false;bool mIsDynamicRate = false; }DevicePort標簽
devicePort標簽可以理解為一個device設備,設備也分output和input,但是不在像mixport那樣以role來分,而是以type中有關鍵字“IN”和“OUT”來分,如下:
<devicePort tagName="BT A2DP Headphones" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES" role="sink"encodedFormats="AUDIO_FORMAT_LDAC AUDIO_FORMAT_APTX AUDIO_FORMAT_APTX_HD AUDIO_FORMAT_AAC AUDIO_FORMAT_SBC"><profile name="" format="AUDIO_FORMAT_PCM_16_BIT"samplingRates="44100,48000,88200,96000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/></devicePort>對應實體類DeviceDescriptor
class DeviceDescriptor : public AudioPort, public AudioPortConfig {/* *AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES字符串對應的enum的整型值,* enum定義在system下的audio-base.h,根據其字符串中就有OUT和IN兩種類型* audio_devices_t其實也是一個整型,判斷一個device是in或者out也是根據這個判斷的;* role標志只是會在audioPort中的mRole保存* */audio_devices_t mDeviceType;String8 mTagName = "BT A2DP Headphones"FormatVector mEncodedFormats = Vector<int>上面encodedFormats轉換的枚舉值 }class AudioPort {mName = ""audio_port_type_t mType = AUDIO_PORT_TYPE_DEVICE //固定值audio_port_role_t mRole = AUDIO_PORT_ROLE_SOURCE/AUDIO_PORT_ROLE_SINK //由role決定AudioProfileVector mProfiles = //對應deviceport里面的多個profile標簽,AudioProfile的集合,sp<HwModule> mModule = null //目前沒有attach到HwModule上AudioRouteVector mRoutes //相關連的route標簽集合,多個route里面可能都會包含同一個name的deviceport標簽,所以這里是集合 }同上MixPort一樣,也會在解析內部profile標簽,創建新的AudioProfile,如下:
class AudioProfile {mName = "" 空串audio_format_t mFormat; //同上mixport中的audioprofileChannelsVector mChannelMasks;SampleRateVector mSamplingRates;//對應上面三位,如果三位都有值,則為false固定的,如果xml沒有指定值,則為true表示是動態的值bool mIsDynamicFormat = falsebool mIsDynamicChannels = false;bool mIsDynamicRate = false; }route標簽
route是把deviceport和mixport連接起來的路由,數據由一個stream輸出到另一個device,或者從一個device輸出到另一個stream;
<route type="mix" sink="Speaker"sources="primary output,raw,deep_buffer,compressed_offload,mmap_no_irq_out,voip_rx"/>對應的AudioRoute類:
class AudioRoute {audio_route_type_t mType = AUDIO_ROUTE_MIX/AUDIO_ROUTE_MUX//根據type而定是互斥還是可融合sp<AudioPort> mSink; //所有的deviceport、mixport標簽轉化的實體類都保存到HwModule的mPorts成員了,所以是用name去mPorts里面查找;AudioPortVector mSources; //同上,只是source可能是多個,這里用集合保存 }configuration配置文件中關鍵點理解
devicePort和mixport如何通過route串聯
route路由決定了哪些mixport的流數據可以傳到devicePort的設備里,建立他們之間的連接關系;在代碼中的體現就是通過mixport標簽對應的實體類IOProfile,在IOProfile里面有一個mSupportedDevices成員,它是一個DeviceDescriptor集合類型,意思也就是IOProfile支持的設備集合,這些設備集合可以把音頻數據傳遞給IOProfile或IOProfile可以把數據傳給device;那IOProfile是如何找到他的DeviceDescriptor的?
主要是通過route標簽對應AudioRoute,只要route標簽內,不管sink或source內容只要有自己的名字,就把這條route保存到自己IOProfile的mRoutes中去,最后通過遍歷mRoute來查找自己支持的設備DeviceDescriptor,如下代碼:
DeviceVector sourceDevices; //input stream to sink device for (const auto& route : stream->getRoutes()) {sp<AudioPort> sink = route->getSink();if (sink == 0 || stream != sink) {ALOGE("%s: Invalid route attached to input stream", __FUNCTION__);continue;}//過濾route里面的source中的deviceport而不是mixportDeviceVector sourceDevicesForRoute = getRouteSourceDevices(route);if (sourceDevicesForRoute.isEmpty()) {ALOGE("%s: invalid source devices for %s", __FUNCTION__, stream->getName().string());continue;}sourceDevices.add(sourceDevicesForRoute); }DeviceVector HwModule::getRouteSourceDevices(const sp<AudioRoute> &route) const {DeviceVector sourceDevices;for (const auto& source : route->getSources()) {//type在AudioPort里面,過濾得到deviceport而不是mixportif (source->getType() == AUDIO_PORT_TYPE_DEVICE) {sourceDevices.add(mDeclaredDevices.getDeviceFromTagName(source->getTagName()));}}return sourceDevices; }上面是一個sink輸入流案例,查找規則如下:
輸出流,同理;最終的結果就是:
作為輸出流source,mSupportedDevices保存此流可以輸出到對應的device,stream -> device
作為輸入流sink,mSupportedDevices保存了其他device能輸出數據到此流, device -> stream
輸出流source同理,就不在闡述了,最后層級依賴大致如下:
MixPort中的flag
| AUDIO_OUTPUT_FLAG_PRIMARY | 表示音頻流需要輸出到主輸出設備,一般用于鈴聲類聲音 |
| AUDIO_OUTPUT_FLAG_DIRECT | 表示音頻流直接輸出到音頻設備,不需要軟件混音,一般用于 HDMI 設備聲音輸出 |
| AUDIO_OUTPUT_FLAG_FAST | 表示音頻流需要快速輸出到音頻設備,一般用于按鍵音、游戲背景音等對時延要求高的場景 |
| AUDIO_OUTPUT_FLAG_DEEP_BUFFER | 表示音頻流輸出可以接受較大的時延,一般用于音樂、視頻播放等對時延要求不高的場景 |
| AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | 表示音頻流沒有經過軟件解碼,需要輸出到硬件解碼器,由硬件解碼器進行解碼 |
在TypeConveter的OutputFlagConverter和InputFlagConverter還有定義的很多flag,如下:
AUDIO_OUTPUT_FLAG_NON_BLOCKING AUDIO_OUTPUT_FLAG_HW_AV_SYNC AUDIO_OUTPUT_FLAG_TTS AUDIO_OUTPUT_FLAG_RAW AUDIO_OUTPUT_FLAG_SYNC AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO AUDIO_OUTPUT_FLAG_DIRECT_PCM AUDIO_OUTPUT_FLAG_MMAP_NOIRQ AUDIO_OUTPUT_FLAG_VOIP_RX AUDIO_OUTPUT_FLAG_INCALL_MUSIC不是很懂這些flag,希望懂的朋友交流下!
解析xml文件標簽代碼架構
這里不談具體的解析過程,而是探討Android源碼中這塊的設計框架,源碼在/frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp中,博主覺得它設計很精巧,使用template模板來減少大量的冗余代碼,同時將各個模塊類串聯起來;
首先,它為mixport、deviceport所有標簽分別創建單獨的模塊,如MixPortTraits,定義標簽名字屬性和解析方法:
同時,也創建了deviceport的DevicePortTraits模塊,但是deserialize方法形參和返回值均相同; 而Attributes則根據自己的標簽內容定義,其他route、profile也有對應的獨立模塊,相互之間互不干擾;
其次,用一個模板函數將每個模塊連接起來,如下:
template <class Trait> status_t deserializeCollection(const xmlNode *cur,typename Trait::Collection *collection,typename Trait::PtrSerializingCtx serializingContext)使用deserializeCollection<MixPortTraits>來發起調用,在函數內部用模板調用模塊內部deserialize就串聯起來了,這樣看起來清晰易讀,結構也分明,以后的設計可參考參考此類型設計,相互獨立模塊,又相互聯系,具體的解析又是一致的場景
解析架構圖
總結
以上是生活随笔為你收集整理的audio_policy_configuration.xml文件解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NOIP / CSP-S/J初赛基础知识
- 下一篇: cocos creator播放声音控制台