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_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文件解析相关推荐
- Android开发历程_18(XML文件解析)
前言 本文主要介绍在Android中怎样来解析XML文件.主要采用的是SAX机制,SAX全称为Simple API for XML,它既是一种接口,也是一个软件包.作为接口,SAX是事件驱动型XML ...
- java怎么xml文件解析_Java对Xml文件解析
JAVA 解析 XML 通常有两种方式,DOM 和 SAX. DOM 虽然是 W3C 的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一 ...
- Python3将xml文件解析为Python对象
一.说明 从最开始写javascript开始,我就很烦感使用getElementById()等函数来获取节点的方法,获取了一个节点要访问其子孙节点要么child半天要么就再来一个getElementB ...
- 【VOC格式xml文件解析】——Python
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2021/4/26 12:49 # @Author : @linlianqin # @S ...
- dom4j工具类_基于DOM4J的XML文件解析类
XML文件解析分四类方式:DOM解析:SAX解析:JDOM解析:DOM4J解析.其中前两种属于基础方法,是官方提供的平台无关的解析方式:后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于ja ...
- XML文件解析-DOM4J方式和SAX方式
最近遇到的工作内容都是和xml内容解析相关的. 1图片数据以base64编码的方式保存在xml的一个标签中,xml文件通过接口的方式发送给我,然后我去解析出图片数据,对图片进行进一步处理. 2.xml ...
- Android学习笔记之AndroidManifest.xml文件解析(摘自皮狼的博客)
Android学习笔记之AndroidManifest.xml文件解析 一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件.它 ...
- XML解析 (JAVA解析xml文件)java+Dom4j+Xpath xml文件解析根据子节点得到父节点 查找校验xml文件中相同的节点属性值 java遍历文件夹解析XML
XML解析 (JAVA解析xml文件)java+Dom4j+Xpath xml文件解析根据子节点得到父节点 以及查找xml文件中相同的节点属性值 项目背景:这是本人实习中所碰到的项目,当时感觉很棘手, ...
- XML - XML学习/XML文件解析器(C++)实现
XML - XML学习/XML文件解析器(C++)实现 XML概述 XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识.它也是元标记语言,用于定义其他与特定领域有关的, ...
- android基础知识13:AndroidManifest.xml文件解析【转载】
注:本文转载于:http://blog.csdn.NET/xianming01/article/details/7526987 AndroidManifest.xml文件解析. 1.重要性 Andro ...
最新文章
- 信息系统项目管理师-信息系统范围管理核心知识点思维脑图
- 如何使用puttygen基于pem文件生成可供登录的ppk文件
- mysql简单语句_MySQL 简单的语句
- Linux 自动化运维工具 ansible
- vue实现搜索框记录搜索历史_使用JS location实现搜索框历史记录功能_苏颜_前端开发者...
- java业务类_Java_业务层开发
- js html转盘点名,原生JS实现简易随机点名功能
- myeclipse激活+Aptana安装配置
- WS2812B全彩灯板
- 如何根据光学中像差(相位)求出其点扩散函数
- html mint ui,移动端UI库对比 vant mint-ui
- 【MySQL学习】数据库问题及着重点汇总
- win10便签常驻桌面_Win10上自带超好用的便利贴
- 小孩上了半年小学,针对老师的评语总结,如何对症优化教育培养策略?chatGPT搜了一下,AI震惊了我
- 复旦的新衣再登Nature!穿在身上能为手机充电
- 2009年经典语录雷人总汇
- 《电子商务与企业经营管理》
- 基于SpringBoot酒店房间管理系统
- 吉林大学软件学院数据库系统原理复习
- druid多数据源配置
热门文章
- 偶然还是必然——读小蚂蚁的罗马人故事II
- 计算机为什么采用二进制?
- linux中命令tat,文件管理类命令(ls,tat,glob,cp,touch等)
- CSS3实战 - 3d转换 - 超级立方体
- js 字串转换HTML,js转化html字符
- 升级opengl和显卡驱动_opengl驱动 OpenGL版本或显卡驱动版本太低的解决方法介绍_网络-游戏圈...
- 根据收入计算个人所得税
- Linux虚拟内存空间分布
- asp.net常用的命名空间及含义
- ASP.NET 基于asp.net设计项目框架