Android 11 Audio strategy配置解析

在启动AudioPolicyService时,通过EngineBase的loadAudioPolicyEngineConfig函数去解析strategy配置。其调用流程如下

在这里插入图片描述
接下来就对loadAudioPolicyEngineConfig展开分析
1,解析volume标签

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{//省略auto result = engineConfig::parse();//1if (result.parsedConfig == nullptr) {engineConfig::Config config = gDefaultEngineConfig;//2android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);//3result = {std::make_unique<engineConfig::Config>(config),static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};} else {//省略}//省略
}

注释1处,parse函数会去解析/vendor/etc/audio_policy_engine_configuration.xml 文件,我的Android 11 源码环境没有这个文件,所以会进入if分支。注释2处设置默认配置为gDefaultEngineConfig。注释3处调用parseLegacyVolumes去解析audio_policy_configuration.xml文件下的volume标签。

parseLegacyVolumes找到audio_policy_configuration.xml文件后,调用到deserializeLegacyVolumeCollection函数继续解析

//frameworks/av/services/audiopolicy/engine/config/src/EngineConfig.cpp
static status_t deserializeLegacyVolumeCollection(_xmlDoc *doc, const _xmlNode *cur,VolumeGroups &volumeGroups,size_t &nbSkippedElement)
{std::map<std::string, VolumeCurves> legacyVolumeMap;for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {if (xmlStrcmp(cur->name, (const xmlChar *)legacyVolumecollectionTag)) {//legacyVolumecollectionTag:volumescontinue;}const xmlNode *child = cur->xmlChildrenNode;for (; child != NULL; child = child->next) {if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) {//legacyVolumeTag:volumestatus_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap);//1if (status != NO_ERROR) {nbSkippedElement += 1;}}}}for (const auto &volumeMapIter : legacyVolumeMap) {//省略int indexMin = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 0 : -1;int indexMax = streamType >= AUDIO_STREAM_PUBLIC_CNT ? 100 : -1;volumeGroups.push_back({ volumeMapIter.first, indexMin, indexMax, volumeMapIter.second });//2}return NO_ERROR;
}

注释1处,对volumes标签下的每个volume,调用deserializeLegacyVolume处理,解析的结果放在legacyVolumeMap这个容器中。注释2处将map容器的内容保存到volumeGroups中

deserializeLegacyVolume

//frameworks/av/services/audiopolicy/engine/config/src/EngineConfig.cpp
status_t deserializeLegacyVolume(_xmlDoc *doc, const _xmlNode *cur,std::map<std::string, VolumeCurves> &legacyVolumes)
{std::string streamTypeLiteral = getXmlAttribute(cur, "stream");//解析streamstd::string deviceCategoryLiteral = getXmlAttribute(cur, "deviceCategory");//解析deviceCategorystd::string referenceName = getXmlAttribute(cur, "ref");//引用其它文件的内容,解析过程是一样的const xmlNode *ref = NULL;if (!referenceName.empty()) {getReference(xmlDocGetRootElement(doc), ref, referenceName, legacyVolumecollectionTag);}CurvePoints curvePoints;for (const xmlNode *child = referenceName.empty() ?cur->xmlChildrenNode : ref->xmlChildrenNode; child != NULL; child = child->next) {if (!xmlStrcmp(child->name, (const xmlChar *)VolumeTraits::volumePointTag)) {xmlCharUnique pointXml(xmlNodeListGetString(doc, child->xmlChildrenNode, 1), xmlFree);std::vector<int> point;collectionFromString<DefaultTraits<int>>(reinterpret_cast<const char*>(pointXml.get()), point, ",");curvePoints.push_back({point[0], point[1]});//将解析point标签的结果保存在curvePoints中}}legacyVolumes[streamTypeLiteral].push_back({ deviceCategoryLiteral, curvePoints });//保存到legacyVolumes中return NO_ERROR;
}

以以下内容场景为例

<volumes><volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET"><point>0,-4200</point><point>33,-2800</point><point>66,-1400</point><point>100,0</point></volume><volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_SPEAKER"><point>0,-2400</point><point>33,-1600</point><point>66,-800</point><point>100,0</point></volume><volume stream="AUDIO_STREAM_SYSTEM" deviceCategory="DEVICE_CATEGORY_HEADSET"><point>1,-3000</point><point>33,-2600</point><point>66,-2200</point><point>100,-1800</point></volume>//省略
<volumes>

最后的解析结果为
在这里插入图片描述
再来看一下图中各结构体的定义。源码路径:frameworks/av/services/audiopolicy/engine/config/include/EngineConfig.h

VolumeCurve

struct VolumeCurve {std::string deviceCategory;//volume标签下的deviceCategory字段CurvePoints curvePoints;//volume标签下的point字段的集合
};

VolumeGroup

struct VolumeGroup {std::string name;//来自于volume标签下的stream字段int indexMin;int indexMax;VolumeCurves volumeCurves;//VolumeCurve 的集合
};

2,遍历volumeGroups,结果保存到EngineBase的mVolumeGroups集合中

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{//省略for (auto &volumeConfig : result.parsedConfig->volumeGroups) {//遍历// save default volume config for streams not defined in configurationif (volumeConfig.name.compare("AUDIO_STREAM_MUSIC") == 0) {defaultVolumeConfig = volumeConfig;}if (volumeConfig.name.compare("AUDIO_STREAM_PATCH") == 0) {defaultSystemVolumeConfig = volumeConfig;}loadVolumeConfig(mVolumeGroups, volumeConfig);}//省略
}

loadVolumeConfig

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{auto loadVolumeConfig = [](auto &volumeGroups, auto &volumeConfig) {//省略sp<VolumeGroup> volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin,volumeConfig.indexMax);//创建VolumeGroup对象volumeGroups[volumeGroup->getId()] = volumeGroup;//放入mVolumeGroups集合中for (auto &configCurve : volumeConfig.volumeCurves) {device_category deviceCat = DEVICE_CATEGORY_SPEAKER;//省略sp<VolumeCurve> curve = new VolumeCurve(deviceCat);//创建VolumeCurve对象for (auto &point : configCurve.curvePoints) {curve->add({point.index, point.attenuationInMb});}volumeGroup->add(curve);//添加到volumeGroup对象中}return volumeGroup;};

可以看出,最后,EngineBase的mVolumeGroups保存了配置文件中volume字段的信息。

3,解析ProductStrategies,保存在EngineBase的mProductStrategies集合中
在分析之前,需要先了解几个结构体
ProductStrategy

struct ProductStrategy {std::string name;AttributesGroups attributesGroups;
};

AttributesGroup

struct AttributesGroup {std::string name;audio_stream_type_t stream;std::string volumeGroup;AttributesVector attributesVect;//using AttributesVector = std::vector<audio_attributes_t>;
};

audio_attributes_t

typedef struct {audio_content_type_t content_type;audio_usage_t        usage;audio_source_t       source;audio_flags_mask_t   flags;char                 tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE]; /* UTF8 */
} __attribute__((packed)) audio_attributes_t; // sent through Binder;

gDefaultEngineConfig

//frameworks/av/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
const engineConfig::Config gDefaultEngineConfig = {1.0,gOrderedStrategies,{},{},{}
};

接着来看解析ProductStrategies的过程

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{//省略for (auto& strategyConfig : result.parsedConfig->productStrategies) {//1,,result.parsedConfig指向前面说的默认配置gDefaultEngineConfigsp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);//1,创建ProductStrategy类对象for (const auto &group : strategyConfig.attributesGroups) {//遍历const auto &iter = std::find_if(begin(mVolumeGroups), end(mVolumeGroups),[&group](const auto &volumeGroup) {return group.volumeGroup == volumeGroup.second->getName(); });//3,根据名字查找sp<VolumeGroup> volumeGroup = nullptr;if (iter == end(mVolumeGroups)) {//没有找到的//省略} else {volumeGroup = iter->second;//找到了}addSupportedAttributesToGroup(group, volumeGroup, strategy);//4,将支持的属性添加到volumeGroup和strategy中}product_strategy_t strategyId = strategy->getId();mProductStrategies[strategyId] = strategy;//5,保存在mProductStrategies中
}

注释1处遍历的就是gOrderedStrategies 集合。注释2处,创建ProductStrategy对象时,传入的名字就是gOrderedStrategies中各元素的名字,如:“STRATEGY_PHONE”,“STRATEGY_SONIFICATION” 。注释3处,遍历前面得到的mVolumeGroups集合,根据名字进行匹配(用volumeGroup 的名字和“AUDIO_STREAM_VOICE_CALL”,“AUDIO_STREAM_BLUETOOTH_SCO”等进行匹配),找到匹配的volumeGroup 。注释4处将支持的属性添加到volumeGroup和strategy中,注释5处将该strategy保存到EngineBase的mProductStrategies集合中

//frameworks/av/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
const engineConfig::ProductStrategies gOrderedStrategies = {{"STRATEGY_PHONE",{{"phone", AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT, 0,""}},},{"sco", AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO,""}},}},},{"STRATEGY_SONIFICATION",{{"ring", AUDIO_STREAM_RING, "AUDIO_STREAM_RING",{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,AUDIO_SOURCE_DEFAULT, 0, ""}}},{"alarm", AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT, 0, ""}},}},},//省略

继续来看一下addSupportedAttributesToGroup函数

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{//省略auto addSupportedAttributesToGroup = [](auto &group, auto &volumeGroup, auto &strategy) {for (const auto &attr : group.attributesVect) {strategy->addAttributes({group.stream, volumeGroup->getId(), attr});//保存在strategy的mAttributesVector中volumeGroup->addSupportedAttributes(attr);//保存在volumeGroup中}};//省略
}

总结一下上面得到的结果
在这里插入图片描述
除了保存在ProductStrategy中之外,还会将attr保存到匹配的volumeGroup中(根据名字匹配)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/20401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch Lighting 库的学习 mvsplat 的笔记

变量理解&#xff1a; context_image&#xff1a; 表示投影的 refrence image Epipolar Transformer vs Swin Transformer : 不同于 Pixel Splat 使用的是 Epipolar Transformer. MVspalt 使用的是 Swin Transformer&#xff0c; 但是作者在 Code 里面 也使用了 Epipolar Tran…

容器项目之前后端分离

容器化部署ruoyi项目 #需要的镜像nginx、java、mysql、redis、 #导入maven镜像、Java镜像和node镜像 docker load -i java-8u111-jdk.tar docker load -i maven-3.8.8-sapmachine-11.tar docker load -i node-18.20.3-alpine3.20.tar #拉取MySQL和nginx镜像 docker pull mysql…

echarts学习:基本使用和组件封装

前言 我在工作中使用echarts较少&#xff0c;这就导致每次使用时都要从头再来&#xff0c;这让我很头疼。因此我决心编写一系列文章将我参与工作后几次使用echarts所用到的知识记录下来&#xff0c;以便将来可以快速查阅。 一、基本使用 像我一样的新手&#xff0c;想要入门e…

【Java】还有人不懂继承?25 个 Case 包教包会

还有人不懂继承&#xff1f;25 个 Case 包教包会 1.Implement single inheritance2.Implement multilevel inheritance3.Implement hierarchical inheritance4.Override a base class method into a derived class5.Demonstrate the protected access specifier6.Create an Stu…

《面试笔记》——MySQL终结篇30

三大范式&#xff1f; 第一范式&#xff1a;字段具有原子性&#xff0c;不可再分&#xff08;字段单一职责&#xff09; 第二范式&#xff1a;满足第一范式&#xff0c;每行应该被唯一区分&#xff0c;加一列存放每行的唯一标识符&#xff0c;称为主键&#xff08;都要依赖主…

10- Redis 键值对数据库是怎么实现的?

在开始将数据结构之前&#xff0c;先给介绍下 Redis 是怎样实现键值对&#xff08;key-value&#xff09;数据库的。 Redis 的键值对中的 key 就是字符串对象&#xff0c;而 value 可以是字符串对象&#xff0c;也可以是集合数据类型的对象&#xff0c;比如 List 对象&#xf…

Django序列化器中is_valid和validate

今天上班的时候分配了一个任务&#xff0c;是修复前端的一个提示优化&#xff0c;如下图所示&#xff1a; 按照以往的经验我以为可以直接在validate上进行校验&#xff0c;如何抛出一个异常即可 &#xff0c;例如&#xff1a; class CcmSerializer(serializers.ModelSerialize…

体验Photoshop:无需下载,直接在浏览器编辑图片

搜索Photoshop时&#xff0c;映入眼帘的是PS软件下载&#xff0c;自学PS软件需要多长时间&#xff0c;学PS软件有必要报班吗...PS软件的设计功能很多&#xff0c;除了常见的图像处理功能外&#xff0c;还涉及图形、文本、视频、出版等。不管你是平面设计师&#xff0c;UI/UX设计…

Servlet搭建博客系统

现在我们可以使用Servlet来搭建一个动态(前后端可以交互)的博客系统了(使用Hexo只能实现一个纯静态的网页,即只能在后台自己上传博客)。有一种"多年媳妇熬成婆"的感觉。 一、准备工作 首先创建好项目,引入相关依赖。具体过程在"Servlet的创建"中介绍了。…

FreeRTOS【14】软件定时器使用

1.开发背景 基于以上的章节&#xff0c;这个篇章主题是软件定时器使用&#xff0c;能使用 FreeRTOS 的基本都是从裸机 MCU 过来的&#xff0c;基本都知道 MCU 最基本的功能之一就是定时器&#xff0c;确切的说是硬件定时器&#xff0c;外围电路已经构建好的&#xff0c;精度很高…

【实战JVM】-实战篇-05-内存泄漏及分析

【实战JVM】-实战篇-05-内存泄漏及分析 1 内存溢出和内存泄漏1.1 常见场景1.2 解决内存溢出的方法1.2.1 发现问题1.2.1.1 top1.2.1.2 ViusalVM1.2.1.3 arthas1.2.1.4 PrometheusGrafana 1.2.2 堆内存状况对比1.2.3 内存泄漏原因-代码中1.2.3.1 equals()-hashCode()1.2.3.2 内部…

小程序-富文本编辑框的注意事项

富文本编辑框官网位置 表单组件 / editor (qq.com)https://developers.weixin.qq.com/miniprogram/dev/component/editor.html &#xff08;一&#xff09;富文本编辑框的作用 1.适用于一些表单的提交 2.这些表单内容需要自定义图片大小&#xff0c;编辑文字样式 主要用到的是…

【C++】10.list

list这个迭代器是双向迭代器&#xff0c;与vector的迭代器具有很大的区别&#xff0c;主要在于双向迭代器不支持&#xff0b;- 操作 正由于list的双向迭代器&#xff0c;因此<algorithm>中的sort()函数无法使用&#xff0c;list单独实现了一个sort()函数&#xff0c;但效…

统计信号处理基础 习题解答10-5

题目 通过令 并进行计算来重新推导MMSE估计量。提示&#xff1a;利用结果 解答 首先需要明确的是&#xff1a; 上式是关于观测值x 的函数 其次需要说明一下这个结果 和教材一样&#xff0c;我们用求期望&#xff0c;需要注意的是&#xff0c;在贝叶斯情况下&#xff0c;是个…

创刊即王炸?首个IF近7分,稳坐中科院1区!同领域全球第一!

【欧亚科睿学术】 01 期刊基本概况 【期刊类型】经济类SSCI 【出版社】SPRINGER出版社 【期刊概况】IF&#xff1a;8.0-9.0&#xff0c;JCR1区&#xff0c;中科院1区 【版面类型】正刊&#xff0c;仅少量版面 【预警情况】2020-2024年无预警记录 【收录年份】2016年被WO…

Facebook开户|Facebook做落地页的标准和建议

哈喽呀家人们下午好~今天Zoey来跟大家带来Facebook做落地页的标准和建议&#xff01;需要的家人建议点赞收藏啦&#xff01;&#xff01;用户通过点击你的推广链接、搜索引擎搜索结果页面的快照链接、社交媒体中的网页链接、电子邮件中的链接等进入你网站的特定页面&#xff0c…

tcp链接中的三次挥手是什么原因

一、tcp链接中的正常四次挥手过程&#xff1f; 刚开始双方都处于 ESTABLISHED 状态&#xff0c;假如是客户端先发起关闭请求。四次挥手的过程如下&#xff1a; 1、客户端打算关闭连接&#xff0c;此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文&#xff0c;也即 FIN 报文…

pytorch项目实战-分类模型李宏毅 21 机器学习第三次作业代码详解 CNN图片分类任务

CNN 卷积神经网络食物分类任务 前言一、数据集介绍二、CNN模型整体框架三、卷积神经网络代码详解3.1 导入需要使用的包3.2 数据集&#xff0c;数据加载器&#xff0c;数据增强操作3.2.1 数据增强3.2.2 数据集构建3.2.3 加载器构建 3.3 卷积神经网络构建3.4 训练代码3.4.1 半监督…

jmeter多用户登录并退出教程

有时候为了模拟更真实的场景&#xff0c;在项目中需要多用户登录并退出操作&#xff0c;大致参考如下 多用户登录前面已经实现&#xff1a;参考博文 多用户登录并退出jmx文件&#xff1a;百度网盘 提取码&#xff1a;0000 一、多用户退出操作 添加一个setUp线程组&#xff0…

Perplexity 搜索引擎刚刚推出了新的页面功能——维基百科可以扔了

Perplexity 允许用户根据搜索结果创建自定义页面 人工智能搜索引擎初创公司 Perplexity 推出了一项新功能&#xff0c;使其结果更具粘性&#xff0c;允许用户将研究转变为易于共享的页面。页面建立在 Perplexity 中现有的人工智能驱动的搜索功能之上&#xff0c;该功能使用与 …