Android 11 Audio strategy配置解析

news2024/10/6 2:14:40

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

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

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{
	//省略
	auto result = engineConfig::parse();//1
    if (result.parsedConfig == nullptr) {
        engineConfig::Config config = gDefaultEngineConfig;//2
        android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);//3
        result = {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:volumes
            continue;
        }
        const xmlNode *child = cur->xmlChildrenNode;
        for (; child != NULL; child = child->next) {
            if (!xmlStrcmp(child->name, (const xmlChar *)legacyVolumeTag)) {//legacyVolumeTag:volume

                status_t status = deserializeLegacyVolume(doc, child, legacyVolumeMap);//1
                if (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");//解析stream
    
    std::string deviceCategoryLiteral = getXmlAttribute(cur, "deviceCategory");//解析deviceCategory
   
    std::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 configuration
        if (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指向前面说的默认配置gDefaultEngineConfig
        sp<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.coloradmin.cn/o/1717302.html

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

相关文章

构建 VPC 并启动 Web 服务器

实验 2&#xff1a;构建 VPC 并启动 Web 服务器 目标 完成本实验后&#xff0c;您可以&#xff1a; 创建 VPC。创建子网。配置安全组。在 VPC 中启动 EC2 实例。任务 1&#xff1a;创建 VPC 在本任务中&#xff0c;您将使用 VPC 向导在单个可用区中创建一个 VPC、一个互联网网关…

【vueCms】vueCms后台管理系统安装问题集合

开源项目地址: https://www.vuecms.cn/ 开源代码地址: https://gitee.com/derekgo/vue-cms_xg 问题一 如果出现提示少了个index.html。如下图 解决办法: 重新安装前端(vue3_vite)项目依赖 问题二 npm版本高无法解析依赖树导致依赖下载失败 解决方案: npm install --legacy…

Web程序设计-实验05 DOM与BOM编程

题目 【实验主题】 影视网站后台影视记录管理页设计 【实验任务】 1、浏览并分析多个网站后台的列表页面、编辑页面&#xff08;详见参考资源&#xff0c;建议自行搜索更多后台页面&#xff09;的主要元素构成和版面设计&#xff0c;借鉴并构思预期效果。 2、新建 index.h…

ODBC访问达梦数据库Ubuntu18.04 x86-x64(亲测有效)

ODBC访问达梦数据库Ubuntu18.04 x86-x64 第1步&#xff1a;安装unixodbc驱动,使用下面命令。第2步&#xff1a;拷贝已经安装好的达梦数据库驱动程序第3步&#xff1a;配置ODBC必要的参数文件&#xff0c;如下图第4步&#xff1a;设置环境变量第5步&#xff1a;连接测试 说明&am…

Linux实验六:进程间通信(二)

目录 一、实验目的二、实验内容三、实验环境四、参考代码五、实验步骤步骤1. 编辑源代码test6.c步骤2. 编译源代码test6.c步骤3. 运行可执行程序test6步骤4. 进一步调试源代码test6.c 六、实验结果七、实验总结 一、实验目的 1、理解 POSIX 和 System V 提供的 IPC 相关概念&a…

Unity 自定义房间布局系统 设计与实现一个灵活的房间放置系统 ——自定义房间区域功能

自定义房间区域功能 效果&#xff1a; 功能&#xff1a; 能够自定义房间的大小一键生成放置区域可控的放置网格点当物体放置到区域内可自动吸附物体是否可放置&#xff0c;放置时如果与其他物体交叉则不可放置&#xff08;纯算法计算&#xff09;管理房间内的物体&#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 内部…

相机等效焦距

1. 背景 物理焦距我们很熟悉,但是在接触实际的相机参数时,相机厂家会提到一个参数等效焦距,甚至有时候不提供物理焦距,这时候如果我们得到真实的物理焦距需要进行一定的转换.在介绍两者之间的转换关系前,先介绍一下等效焦距的由来. 如上图,假设在某一个镜头,其成像面会出现图…

Linux配置java,maven,marshalsec环境

文章目录 一. Linux配置java环境1.下载jdk文件2.解压tar.gz文件3.设置java环境变量4.验证是否成功 二. Linux配置maven环境1.下载压缩包2.解压tar.gz3. 配置环境变量 三. Linux配置marshalsec环境 一. Linux配置java环境 1.下载jdk文件 mkdir /opt/javawget https://repo.hua…

【设计模式深度剖析】【5】【结构型】【桥接模式】| 以电视和遥控器为例加深理解

&#x1f448;️上一篇:组合模式 | 下一篇:外观模式&#x1f449;️ 设计模式-专栏&#x1f448;️ 目 录 桥接模式(Bridge Pattern)定义英文原话是&#xff1a;直译理解 4个角色UML类图代码示例 应用优点缺点使用场景 示例解析&#xff1a;电视和遥控器UML类图 桥接模式…

【漏洞复现】DT-高清车牌识别摄像机 任意文件读取漏洞

0x01 产品简介 DT-高清 车牌识别摄像机是一款先进的安防设备&#xff0c;采用高清图像传感器和先进的识别算法&#xff0c;能够精准、快速地识别车牌信息。其高清晰该摄像机结合了智能识别技术&#xff0c;支持实时监宴图像质量确保在各种光照和天气条件下都能准确捕捉车牌信息…

【设计模式】JAVA Design Patterns——Factory Method(虚拟构造器模式)

&#x1f50d;目的 为创建一个对象定义一个接口&#xff0c;但是让子类决定实例化哪个类。工厂方法允许类将实例化延迟到子类 &#x1f50d;解释 真实世界例子 铁匠生产武器。精灵需要精灵武器&#xff0c;而兽人需要兽人武器。根据客户来召唤正确类型的铁匠。 通俗描述 它为类…

视频汇聚管理平台EasyCVR程序报错“create jwtSecret del server class:0xf98b6040”的原因排查与解决

国标GB28181协议EasyCVR安防视频监控平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流…

【学习笔记】Windows GDI绘图(八)画笔Pen与画刷Brush

文章目录 关于Pen改变Pen的宽度width和对齐方式Alignment带线帽的线段连接线条LineJoin自定义虚线用纹理填充线条 关于BrushHatchBrush阴影LinearGradientBrush线性渐变PathGradientBrush 详细示例Pen与Brush的属性与方法 关于Pen 改变Pen的宽度width和对齐方式Alignment 可以…

IntelliJ IDEA Ultimate 2024.1 Mac激活码 Java开发首选IDE

IntelliJ IDEA Ultimate 2024 搜Mac软件之家下载IDEA Mac中文版 IntelliJ IDEA Ultimate 2024是JetBrains公司推出的一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为专业开发者设计&#xff0c;支持多种编程语言和框架。它提供了一系列高级功能&…

【免费Web系列】JavaWeb实战项目案例五

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 新增员工 前面我们已经实现了员工信息的条件分页查询。 那今天我们要实现的是新增员工的功能实现&#xff0c;页面原型如下&#xff1a; ​ 首先我们先完成"新增员工"的功能开发&#xff0…

Linux--线程的分离、线程库的地址关系的理解、线程的简单封装(二)

线程系列&#xff1a; 线程的认识&#xff1a;讲解线程的概念和线程的基本控制 线程的分离 线程分离是指将一个线程从主线程中分离出来&#xff0c;使其能够独立运行。当一个线程被设置为分离状态时&#xff0c;它结束时系统会自动回收其资源&#xff0c;而不需要其他线程使用…

【喜报】科大睿智服务企业通过CMMI3级认证

​北京建投科信科技发展股份有限公司&#xff08;以下简称“北京建投科技” &#xff09;前身为北京银帝科技发展公司&#xff0c;成立于1993年&#xff0c;注册资本6,000万元&#xff0c;为中国建银投资有限责任公司&#xff08;简称“中国建投”&#xff09;的成员企业建投华…

ovs-vsctl错误:Port does not contain a column whoes name matches “--id“

出错的命令是: ovs-vsctl -- set Bridge br-int mirrors=@m -- --id=@snooper0 get Port snooper0\ -- --id=@patch-tun get Port patch-tun -- --id=@m create Mirror name=mymirror \ select

微软Edge浏览器深度解析:功能、同步、隐私与安全

微软Edge浏览器是微软公司开发的一款网页浏览器,它基于Chromium内核,提供了快速、安全和兼容性良好的网页浏览体验。以下是关于微软Edge浏览器的详细信息和使用指南: 微软Edge浏览器的主要特点: 1. 基于Chromium内核: 渲染引擎:Chromium内核是基于开源项目Blink的,它…