Android Audio分区——音频分区加载流程(三)

        前面文章介绍了车载多区音频基础,并且介绍了音频分区相关类及对应功能,这里我们就来看一下音频分区的解析过程。

一、音频分区加载

        音频分区的加载是在 CarAudioService 的初始化函数 init() 流程中进行的。

1、CarAudioService.java

源码位置:/packages/services/Car/service/src/com/android/car/audio/CarAudioService.java

init

@Override
public void init() {synchronized (mImplLock) {// 获取CarOccupantZoneService服务,该服务在车辆网中介绍过mOccupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class);Car car = new Car(mContext, /* service= */null, /* handler= */ null);mOccupantZoneManager = new CarOccupantZoneManager(car, mOccupantZoneService);if (mUseDynamicRouting) {// 设置动态音频路由setupDynamicRoutingLocked();// 设置HAL音频焦点监听器setupHalAudioFocusListenerLocked();} else {// 动态路由为其用,设置传统模式下的音量变化监听器setupLegacyVolumeChangedListener();}// 恢复主静音状态if (mPersistMasterMuteState) {// 获取存储的主静音状态boolean storedMasterMute = mCarAudioSettings.getMasterMute();// 设置主静音状态setMasterMute(storedMasterMute, 0);}// 设置支持的系统用途mAudioManager.setSupportedSystemUsages(SYSTEM_USAGES);}
}

        这里主要是根据动态音频路由是否启用来进行不同的配置,确保了系统能够正确地处理音频输出和音频焦点的变化。 

setupDynamicRoutingLocked

private void setupDynamicRoutingLocked() {// 创建音频策略构建器final AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);builder.setLooper(Looper.getMainLooper());// 加载音频区域loadCarAudioZonesLocked();// 同步音频区域增益索引for (CarAudioZone zone : mCarAudioZones) {// 确保HAL获得初始值zone.synchronizeCurrentGainIndex();}// 设置动态路由规则final CarAudioDynamicRouting dynamicRouting = new CarAudioDynamicRouting(mCarAudioZones);dynamicRouting.setupAudioDynamicRouting(builder);// 设置音频策略音量回调builder.setAudioPolicyVolumeCallback(mAudioPolicyVolumeCallback);// 配置音频焦点处理if (sUseCarAudioFocus) {mFocusHandler = new CarZonesAudioFocus(mAudioManager, mContext.getPackageManager(),mCarAudioZones, mCarAudioSettings, ENABLE_DELAYED_AUDIO_FOCUS);builder.setAudioPolicyFocusListener(mFocusHandler);builder.setIsAudioFocusPolicy(true);}mAudioPolicy = builder.build();if (sUseCarAudioFocus) {// 连接音频策略和焦点监听器mFocusHandler.setOwningPolicy(this, mAudioPolicy);}// 注册音频策略int r = mAudioManager.registerAudioPolicy(mAudioPolicy);if (r != AudioManager.SUCCESS) {throw new RuntimeException("registerAudioPolicy failed " + r);}// 设置乘员区域信息setupOccupantZoneInfo();
}

        该方法用于设置动态音频路由,包括配置动态路由规则、设置音频策略、处理音频焦点事件等。这里我们主要看一下音频区的加载。

loadCarAudioZonesLocked

private CarAudioZone[] mCarAudioZones;
private String mCarAudioConfigurationPath;private void loadCarAudioZonesLocked() {// 生成音频设备信息List<CarAudioDeviceInfo> carAudioDeviceInfos = generateCarAudioDeviceInfos();// 获取音频配置路径mCarAudioConfigurationPath = getAudioConfigurationPath();if (mCarAudioConfigurationPath != null) {// 从音频配置文件中加载音频区域mCarAudioZones = loadCarAudioConfigurationLocked(carAudioDeviceInfos);} else {// 从音量组配置中加载音频区域mCarAudioZones = loadVolumeGroupConfigurationWithAudioControlLocked(carAudioDeviceInfos);}// 验证加载的音频区域是否有效CarAudioZonesValidator.validate(mCarAudioZones);
}

        该方法用于加载所有可用的音频区域(CarAudioZone)。它首先生成音频设备信息,然后根据是否存在特定的音频配置路径来决定如何加载音频区域。这里我们先来看一下获取音频配置路径,然后再看音频区的加载流程。

getAudioConfigurationPath

private static final String[] AUDIO_CONFIGURATION_PATHS = new String[] {"/vendor/etc/car_audio_configuration.xml","/system/etc/car_audio_configuration.xml"
};private String getAudioConfigurationPath() {for (String path : AUDIO_CONFIGURATION_PATHS) {File configuration = new File(path);// 检查文件是否存在if (configuration.exists()) {return path;}}return null;
}

        可以看到这里查找流程,先找到 /vendor/etc/ 路径下是否存在 car_audio_configuration.xml 文件,如果存在直接返回,不存在则查找 /system/etc/ 下的对应文件。vendor 和 system 代表厂商定制和系统配置,而 car_audio_configuration.xml 文件正是我们前面介绍的车载音频配置文件。

loadCarAudioConfigurationLocked

private CarAudioZone[] loadCarAudioConfigurationLocked(List<CarAudioDeviceInfo> carAudioDeviceInfos) {// 获取所有输入设备AudioDeviceInfo[] inputDevices = getAllInputDevices();// 打开音频配置文件try (InputStream inputStream = new FileInputStream(mCarAudioConfigurationPath)) {// 创建CarAudioZonesHelper实例CarAudioZonesHelper zonesHelper = new CarAudioZonesHelper(mCarAudioSettings,inputStream, carAudioDeviceInfos, inputDevices);// 加载音频区域映射,获取音频区域ID到乘员区域ID的映射。mAudioZoneIdToOccupantZoneIdMapping = zonesHelper.getCarAudioZoneIdToOccupantZoneIdMapping();// 加载音频区域return zonesHelper.loadAudioZones();} catch (IOException | XmlPullParserException e) {throw new RuntimeException("Failed to parse audio zone configuration", e);}
}

        该方法用于从音频配置文件中加载所有可用的音频区域(CarAudioZone)。它首先读取音频配置文件的内容,然后使用 CarAudioZonesHelper 类的 loadAudioZones() 函数来解析配置文件并加载音频区域。

2、CarAudioZonesHelper

源码位置:/packages/services/Car/service/src/com/android/car/audio/CarAudioZonesHelper.java

loadAudioZones

CarAudioZone[] loadAudioZones() throws IOException, XmlPullParserException {List<CarAudioZone> carAudioZones = new ArrayList<>();// 解析音频区域parseCarAudioZones(carAudioZones, mInputStream);return carAudioZones.toArray(new CarAudioZone[0]);
}

        这里首先创建一个 ArrayList 来存储解析得到的音频区域,然后调用 parseCarAudioZones() 方法来解析输入流中的数据,并将结果转换为 CarAudioZone 数组返回。 

parseCarAudioZones

private static final String TAG_ROOT = "carAudioConfiguration";
private static final String TAG_AUDIO_ZONES = "zones";
private static final int INVALID_VERSION = -1;
private static final int SUPPORTED_VERSION_2 = 2;private void parseCarAudioZones(List<CarAudioZone> carAudioZones, InputStream stream)throws XmlPullParserException, IOException {// 创建XML解析器final XmlPullParser parser = Xml.newPullParser();parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, NAMESPACE != null);parser.setInput(stream, null);// 跳过文档中的注释或空白parser.nextTag();// 确保当前元素是<carAudioConfiguration>parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_ROOT);// 获取XML文档中的版本号final int versionNumber = Integer.parseInt(parser.getAttributeValue(NAMESPACE, ATTR_VERSION));if (SUPPORTED_VERSIONS.get(versionNumber, INVALID_VERSION) == INVALID_VERSION) {throw new IllegalArgumentException("Latest Supported version:" + SUPPORTED_VERSION_2 + " , got version:" + versionNumber);}mCurrentVersion = versionNumber;// 遍历XML文档直到遇到根元素的结束标签while (parser.next() != XmlPullParser.END_TAG) {if (parser.getEventType() != XmlPullParser.START_TAG) continue;// 如果当前元素是<audioZones>,则调用parseAudioZones()方法解析音频区域。if (TAG_AUDIO_ZONES.equals(parser.getName())) {// 解析音频区域parseAudioZones(parser, carAudioZones);} else {// 跳过该元素及其子元素skip(parser);}}
}

        这里首先确保 XML 文档的根元素是 <carAudioConfiguration>,然后检查版本号是否支持,接着解析所有的音频区域配置。 

parseAudioZones

private static final String TAG_AUDIO_ZONE = "zone";private void parseAudioZones(XmlPullParser parser, List<CarAudioZone> carAudioZones)throws XmlPullParserException, IOException {while (parser.next() != XmlPullParser.END_TAG) {if (parser.getEventType() != XmlPullParser.START_TAG) continue;if (TAG_AUDIO_ZONE.equals(parser.getName())) {carAudioZones.add(parseAudioZone(parser));} else {skip(parser);}}Preconditions.checkArgument(mHasPrimaryZone, "Requires one primary zone");carAudioZones.sort(Comparator.comparing(CarAudioZone::getId));
}

       与上面的解析流程基本相同,只是解析的标签不同,这里通过解析 <zone>  标签并将数据保存到 carAudioZones 中。

二、车载音频分区配置

        对于前面文章中的 xml 文件解析,是否注意到了对应的文件路径 "/vendor/etc/" 和 "/system/etc/",在源码中并未找到该路径,是不是有些疑惑。其实这是设备的内存路径,对应的源码文件并不在次,而是通过代码拷贝过去的。

1、system文件

源码位置:/device/google/trout/hal/audio/6.0/car_audio_configuration.xml

<!--定义汽车中的音频配置,包括- 音频区- 上下文到音频总线的映射- 音量组在汽车环境中。
-->
<carAudioConfiguration version="2"><zones><zone name="primary zone" isPrimary="true" occupantZoneId="0"><volumeGroups><group><device address="bus0_media_out"><context context="music"/></device><device address="bus3_call_ring_out"><context context="call_ring"/></device><device address="bus6_notification_out"><context context="notification"/></device><device address="bus7_system_sound_out"><context context="system_sound"/><context context="emergency"/><context context="safety"/><context context="vehicle_status"/><context context="announcement"/></device></group><group><device address="bus1_navigation_out"><context context="navigation"/></device><device address="bus2_voice_command_out"><context context="voice_command"/></device></group><group><device address="bus4_call_out"><context context="call"/></device></group><group><device address="bus5_alarm_out"><context context="alarm"/></device></group></volumeGroups></zone><zone name="rear seat zone 1" audioZoneId="1"><volumeGroups><group><device address="bus100_audio_zone_1"><context context="music"/><context context="navigation"/><context context="voice_command"/><context context="call_ring"/><context context="call"/><context context="alarm"/><context context="notification"/><context context="system_sound"/><context context="emergency"/><context context="safety"/><context context="vehicle_status"/><context context="announcement"/></device></group></volumeGroups></zone><zone name="rear seat zone 2"  audioZoneId="2"><volumeGroups><group><device address="bus200_audio_zone_2"><context context="music"/><context context="navigation"/><context context="voice_command"/><context context="call_ring"/><context context="call"/><context context="alarm"/><context context="notification"/><context context="system_sound"/><context context="emergency"/><context context="safety"/><context context="vehicle_status"/><context context="announcement"/></device></group></volumeGroups></zone></zones>
</carAudioConfiguration>

文件拷贝

 源码位置:/device/google/trout/aosp_trout_common.mk

LOCAL_AUDIO_PRODUCT_COPY_FILES ?= \device/google/trout/hal/audio/6.0/car_audio_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/car_audio_configuration.xml \

2、vendor文件

        对于 vendor 下的配置文件都是车企定制的配置,根据需要去设置分组及音频流等信息,并且同样需要在 mk 文件中进行拷贝操作,这里就不再展示代码了。

源码位置:device/{硬件平台}/audio/{产品}/config/audio/car_audio_configuration.xml

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

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

相关文章

【ragflow】安装2:源码安装依赖

中文文档【ragflow】安装1: docker:失败官方说的成功 docker 安装的启动失败 重新来一遍,不会重新拉取: root@k8s-master-pfsrv:/home/zhangbin/perfwork/rag# cd ragflow/ root@k8s-master-pfsrv:/home/

USB3202N多功能数据采集卡16位模拟量250K频率LabVIEW采集卡

品牌&#xff1a;阿尔泰科技 系列&#xff1a;多功能数据采集卡 概述&#xff1a; USB3202N多功能数据采集卡&#xff0c;LabVIEW无缝连接&#xff0c;提供图形化API函数&#xff0c;提供8通道&#xff08;RSE、NRSE&#xff09;、4通道&#xff08;DIFF&#xff09;模拟量输…

向量和矩阵学习笔记

向量和矩阵学习笔记 Ps:因为本人实力有限&#xff0c;有一部分可能不太详细&#xff0c;若有补充评论区回复&#xff0c;QWQ 向量 向量的定义 首先&#xff0c;因为我刚刚学到高中的向量&#xff0c;对向量的看法呢就是一条有长度和方向的线&#xff0c;不过这在数学上的定…

C/C++入门案例01

文章目录 写在前面1. 你好&#xff0c;世界&#xff01; (Hello, World!)2. 基本的算术运算3. 判断奇偶数4. 计算数组元素的和5. 求最大值和最小值6. 简单的计算器7. 字符串反转8. 计算阶乘9. 查找质数10. 冒泡排序 系列推荐 写在前面 以下是10个适合初学者的C语言入门案例&am…

【如何在MacOS升级ruby版本】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

C++期末知识点概述

《大学 C知识点概述》 在大学的计算机课程中&#xff0c;C作为一门重要的编程语言&#xff0c;有着广泛的应用和丰富的知识点。 一、基础语法 数据类型&#xff1a;C包含多种数据类型&#xff0c;如整数类型&#xff08;int、short、long 等&#xff09;、浮点类型&#xff…

Unity(2022.3.41LTS) - 音频

目录 一、音频系统概述 二、音频资源类型 三、音频组件 四、音频空间定位 五、音频效果处理 六.音乐框架设计 一、音频系统概述 Unity 的音频系统允许开发者在游戏中添加各种声音效果&#xff0c;包括背景音乐、音效、环境音等。它提供了丰富的功能来控制音频的播放、音…

MIT 6.5940 EfficientML.ai Fall 2023: Lab 1 Pruning

EfficientML.ai Lec 3 - Pruning and Sparsity (Part I) MIT 6.5940, Fall 2023, Zoom 本文是EfficientML.ai Fall 2023课程作业1练习答案&#xff0c;在本次练习里将会对经典的分类神经网络进行剪枝处理&#xff0c;减少模型大小和延迟。The goals of this assignment are as …

python破解[5分钟解决拼多多商家后台字体加密]

可【QQ群】拿源码 进入经营总览想把数据存下来发现返回的json数据部分空白如下 这可怎么办 稳住应该是字体的问题&#xff0c;可能是多多自己实现了某种字体&#xff0c;我们去找他的js 发现如我们所想&#xff0c;进行跟踪&#xff0c;发现的确是在css端进行了字体替换&am…

Servlet, Filter, Listener 启动与执行顺序

Servlet, Filter, Listener 启动与执行顺序 1、启动顺序 **Listener -> Filter -> Servlet**2、记忆口诀3、执行顺序 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java Web应用中&#xff0c;Servlet、Filter和Listener的启动与执…

从0开始深度学习(2)——自动微分

1 微积分 1.1 导数和微分 略 1.2 偏导数 略 1.3 梯度&#xff08;gradient&#xff09; 1.3.1 定义 对于一个多变量函数 f ( x 1 , x 2 , … , x n ) f\left(x_{1}, x_{2}, \ldots, x_{n}\right) f(x1​,x2​,…,xn​)其中点 a ( a 1 , a 2 , … , a n ) \mathbf{a}(a_…

【卷起来】VUE3.0教程-01-环境搭建与安装

​分享不易&#xff0c;耗时耗力&#xff0c;麻烦给个不要钱的关注和赞吧 &#x1f332; 什么是VUE Vue 是一个框架&#xff0c;也是一个生态。其功能覆盖了大部分前端开发常见的需求。但 Web 世界是十分多样化的&#xff0c;不同的开发者在 Web 上构建的东西可能在形式和规模…

SpringSecurity笔记整理

自定义登录页面 编写登录页面<!DOCTYPE html> <html xmlns"http://www.w3.org/1999/xhtml" xmlns:th"https://www.thymeleaf.org"> <head><title>Please Log In</title> </head> <body> <h1>Please Log …

鸿蒙OpenHarmony、HarmonyOS、HarmonyOS NEXT的区别

鸿蒙OpenHarmony、HarmonyOS、HarmonyOS NEXT的区别 OpenHarmony&#xff1a;开源底层。HarmonyOS&#xff1a;闭源手机系统&#xff0c;兼容安卓生态。HarmonyOS NEXT&#xff1a;纯血鸿蒙&#xff0c;不兼容安卓。 OpenHarmony&#xff08;开源&#xff09; 开源地址&…

Question mutiple pdf‘s using openai, pinecone, langchain

题意&#xff1a;使用 OpenAI、Pinecone 和 LangChain 对多个 PDF 文件进行提问。 问题背景&#xff1a; I am trying to ask questions against a multiple pdf using pinecone and openAI but I dont know how to. 我正在尝试使用 Pinecone 和 OpenAI 对多个 PDF 文件进行提…

【Linux】保姆级 Linux 常见命令使用

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. Linux 是什么1.1 Linux 是什么1.2 关于 Linux 我们需要学什么 2. 需提前准备的东西2.1 环境 —— 如何获取…

使用 Eigen 库中的 Kronecker 积运算

前言 在数值计算和线性代数的众多应用中&#xff0c;Kronecker 积&#xff08;Kronecker Product&#xff09;是一种常用的矩阵运算。Eigen 是一个高性能的 C 数值计算库&#xff0c;广泛用于科学计算和工程应用中。在 Eigen 库中&#xff0c;Kronecker 积运算属于不常用的扩展…

【QNX+Android虚拟化方案】114 - QNX /dev/switch 节点创建 及 读写功能实现实例

【QNX+Android虚拟化方案】114 - QNX /dev/switch 节点创建 及 读写功能实现实例 一、/dev/switch 节点创建代码分解1. 头文件包含2. 创建节 /dev/switch 节点代码3. /dev/switch 节点读函数实现(cat /dev/switch)4. /dev/switch 节点写函数实现(echo "abcdef" &g…

构建高效微服务架构:Spring Cloud中的注册中心与负载均衡实践

一、注册中心的重要性 服务发现&#xff1a; 服务注册/注销&#xff1a;注册中心维护着所有服务提供者和服务消费者的元数据信息。服务订阅/取消订阅&#xff1a;服务消费者可以通过订阅来获取服务提供者的信息&#xff0c;并且注册中心应当支持实时推送更新。服务路由&#…

Linux 进程概念

冯诺依曼体系结构 我们常见的计算机&#xff0c;大部分都遵守冯诺依曼体系结构 关于冯诺依曼的注意点 1.这里的存储器指的是内存 2.不考虑缓冲情况&#xff0c;这里的CPU能且只能对内存进行读写&#xff0c;不能访问外设(输入或输出设备) 3.外设(输入或输出设备)要输入或者输出…