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输入流案例,查找规则如下:

  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

输出流source同理,就不在阐述了,最后层级依赖大致如下:


MixPort中的flag

AUDIO_OUTPUT_FLAG Description
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文件解析相关推荐

  1. Android开发历程_18(XML文件解析)

    前言  本文主要介绍在Android中怎样来解析XML文件.主要采用的是SAX机制,SAX全称为Simple API for XML,它既是一种接口,也是一个软件包.作为接口,SAX是事件驱动型XML ...

  2. java怎么xml文件解析_Java对Xml文件解析

    JAVA 解析 XML 通常有两种方式,DOM 和 SAX. DOM 虽然是 W3C 的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一 ...

  3. Python3将xml文件解析为Python对象

    一.说明 从最开始写javascript开始,我就很烦感使用getElementById()等函数来获取节点的方法,获取了一个节点要访问其子孙节点要么child半天要么就再来一个getElementB ...

  4. 【VOC格式xml文件解析】——Python

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2021/4/26 12:49 # @Author : @linlianqin # @S ...

  5. dom4j工具类_基于DOM4J的XML文件解析类

    XML文件解析分四类方式:DOM解析:SAX解析:JDOM解析:DOM4J解析.其中前两种属于基础方法,是官方提供的平台无关的解析方式:后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于ja ...

  6. XML文件解析-DOM4J方式和SAX方式

    最近遇到的工作内容都是和xml内容解析相关的. 1图片数据以base64编码的方式保存在xml的一个标签中,xml文件通过接口的方式发送给我,然后我去解析出图片数据,对图片进行进一步处理. 2.xml ...

  7. Android学习笔记之AndroidManifest.xml文件解析(摘自皮狼的博客)

    Android学习笔记之AndroidManifest.xml文件解析 一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件.它 ...

  8. XML解析 (JAVA解析xml文件)java+Dom4j+Xpath xml文件解析根据子节点得到父节点 查找校验xml文件中相同的节点属性值 java遍历文件夹解析XML

    XML解析 (JAVA解析xml文件)java+Dom4j+Xpath xml文件解析根据子节点得到父节点 以及查找xml文件中相同的节点属性值 项目背景:这是本人实习中所碰到的项目,当时感觉很棘手, ...

  9. XML - XML学习/XML文件解析器(C++)实现

    XML - XML学习/XML文件解析器(C++)实现 XML概述 ​ XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识.它也是元标记语言,用于定义其他与特定领域有关的, ...

  10. android基础知识13:AndroidManifest.xml文件解析【转载】

    注:本文转载于:http://blog.csdn.NET/xianming01/article/details/7526987 AndroidManifest.xml文件解析. 1.重要性 Andro ...

最新文章

  1. 信息系统项目管理师-信息系统范围管理核心知识点思维脑图
  2. 如何使用puttygen基于pem文件生成可供登录的ppk文件
  3. mysql简单语句_MySQL 简单的语句
  4. Linux 自动化运维工具 ansible
  5. vue实现搜索框记录搜索历史_使用JS location实现搜索框历史记录功能_苏颜_前端开发者...
  6. java业务类_Java_业务层开发
  7. js html转盘点名,原生JS实现简易随机点名功能
  8. myeclipse激活+Aptana安装配置
  9. WS2812B全彩灯板
  10. 如何根据光学中像差(相位)求出其点扩散函数
  11. html mint ui,移动端UI库对比 vant mint-ui
  12. 【MySQL学习】数据库问题及着重点汇总
  13. win10便签常驻桌面_Win10上自带超好用的便利贴
  14. 小孩上了半年小学,针对老师的评语总结,如何对症优化教育培养策略?chatGPT搜了一下,AI震惊了我
  15. 复旦的新衣再登Nature!穿在身上能为手机充电
  16. 2009年经典语录雷人总汇
  17. 《电子商务与企业经营管理》
  18. 基于SpringBoot酒店房间管理系统
  19. 吉林大学软件学院数据库系统原理复习
  20. druid多数据源配置

热门文章

  1. 偶然还是必然——读小蚂蚁的罗马人故事II
  2. 计算机为什么采用二进制?
  3. linux中命令tat,文件管理类命令(ls,tat,glob,cp,touch等)
  4. CSS3实战 - 3d转换 - 超级立方体
  5. js 字串转换HTML,js转化html字符
  6. 升级opengl和显卡驱动_opengl驱动 OpenGL版本或显卡驱动版本太低的解决方法介绍_网络-游戏圈...
  7. 根据收入计算个人所得税
  8. Linux虚拟内存空间分布
  9. asp.net常用的命名空间及含义
  10. ASP.NET 基于asp.net设计项目框架