日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

audio_policy_configuration.xml文件解析

發布時間:2023/12/16 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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目錄下,文件內容大致如下:

<module name="a"><attachedDevices><item>Speaker</item><item>Built-In Mic</item><item>Built-In Back Mic</item></attachedDevices><defaultOutputDevice>Speaker</defaultOutputDevice><devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" .../><devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BACK_MIC" .../><mixport .../>....<route .../> </module> <module name="b"></module>

查看源碼,在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輸入流案例,查找規則如下:

  • 遍歷其父類的成員mRoutes,因為是輸入流,所以遍歷mRoute集合中sink為自己的route,也就是找有哪些源source設備把數據傳給自己。
  • 找到route后,根據route中source保存的對象,且對象type是AUDIO_PORT_TYPE_DEVICE類型(就是devicesPort標簽對應的實體類DeviceDescriptor)
  • 把DeviceDescriptor保存在集合中,保存在以下mSupportedDevices中,作為其支持的設備;
    輸出流,同理;最終的結果就是:
    作為輸出流source,mSupportedDevices保存此流可以輸出到對應的device,stream -> device
    作為輸入流sink,mSupportedDevices保存了其他device能輸出數據到此流, device -> stream
  • 輸出流source同理,就不在闡述了,最后層級依賴大致如下:


    MixPort中的flag

    AUDIO_OUTPUT_FLAGDescription
    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,定義標簽名字屬性和解析方法:

    struct MixPortTraits : public AndroidCollectionTraits<IOProfile, IOProfileCollection> {static constexpr const char *tag = "mixPort";static constexpr const char *collectionTag = "mixPorts";struct Attributes{static constexpr const char *name = "name";static constexpr const char *role = "role";static constexpr const char *roleSource = "source"; /**< <attribute role source value>. */static constexpr const char *flags = "flags";static constexpr const char *maxOpenCount = "maxOpenCount";static constexpr const char *maxActiveCount = "maxActiveCount";};static Return<Element> deserialize(const xmlNode *cur, PtrSerializingCtx serializingContext);// Children: GainTraits };

    同時,也創建了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文件解析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。