Android audio(6)-audiopolicyservice介绍

AudioPolicyService 是策略的制定者,比如某种 Stream 类型不同设备的音量(index/DB)是多少、某种 Stream 类型的音频数据流对应什么设备等等。而 AudioFlinger 则是策略的执行者,例如具体如何与音频设备通信,维护现有系统中可用的音频设备,以及多个音频流的混音处理,音频数据流的算法处理(重采样,音效),音量调节,音频数据搬运等等都由它完成。

AudioPolicyService 根据用户配置来指导 AudioFlinger 加载设备接口,起到路由功能。在 Android Audio 系统中主要完成以下几个任务:
1)管理输入输出设备,包括设备的连接/断开状态,设备的选择和切换等
2)管理系统的音频策略,比如通话时播放音乐、或者播放音乐时来电话的一系列处理
3)管理系统的音量/静音
4)上层的一些音频参数也可以通过AudioPolicyService设置到底层
5)管理音效和流的配置

一、初始化

与AudioPolicyService 生命周期关联的有三个函数, 构造函数 AudioPolicyService、onFirstRef 和初始化有关, 析构函数 ~AudioPolicyService 和销毁有关。通过对android代码的学习,我们可以看到与成员变量相关的初始化都是 在构造函数里面做,而与业务相关的初始化都是在onFirstRef 里面完成。这是关注点分离思想的一种体现。

源码位置:/frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp

1、构造函数

AudioPolicyService::AudioPolicyService(): BnAudioPolicyService(), // 定义在IAudioPolicyService.h中, 作为Binder调用的Bn端mAudioPolicyManager(NULL),  // APMmAudioPolicyClient(NULL), mPhoneState(AUDIO_MODE_INVALID), // 通话状态 mCaptureStateNotifier(false) {
}

2、onFirstRef

void AudioPolicyService::onFirstRef()
{{Mutex::Autolock _l(mLock);//一个系统的服务通常都需要线程去承载,不断处理命令。// 启动音频命令线程,audio命令相关,如音量控制、音频参数设置mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);// 启动输出命令线程,Output管理mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);mAudioPolicyClient = new AudioPolicyClient(this);mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);}sp<AudioPolicyEffects> audioPolicyEffects = new AudioPolicyEffects();sp<UidPolicy> uidPolicy = new UidPolicy(this);sp<SensorPrivacyPolicy> sensorPrivacyPolicy = new SensorPrivacyPolicy(this);{Mutex::Autolock _l(mLock);mAudioPolicyEffects = audioPolicyEffects;mUidPolicy = uidPolicy;mSensorPrivacyPolicy = sensorPrivacyPolicy;}uidPolicy->registerSelf();sensorPrivacyPolicy->registerSelf();
}

可以看到这里创建了两个 AudioCommandThread 和 AudioPolicyManager,而在创建 AudioPolicyManager 时创建了AudioPolicyClient作为参数传入其中。

3、析构函数

AudioPolicyService::~AudioPolicyService()
{mAudioCommandThread->exit();mOutputCommandThread->exit();destroyAudioPolicyManager(mAudioPolicyManager);delete mAudioPolicyClient;mNotificationClients.clear();mAudioPolicyEffects.clear();mUidPolicy->unregisterSelf();mSensorPrivacyPolicy->unregisterSelf();mUidPolicy.clear();mSensorPrivacyPolicy.clear();
}

这里主要是做销毁,情基本是对初始化创建对象的回收。

二、AudioPolicyManager

AudioPolicyManager是 AudioPolicyService 服务进程下的功能模块,主要负责解析各种 Audio 配置 xml 文件,例如 audio_policy_configuration.xml 中音频的设备、流以及路由关系等。从audiopolicyservice和audiopolicymanager的设计我们可以学到,服务承载实体(进程,service等)可以和服务的业务逻辑代码分离!!!!! 服务实体是为了实现异步以及序列化操作

1、AudioPolicyManager创建
上面的代码可以看到 AudioPolicyManager 是在 AudioPolicyService 的 onFirstRef 中调用 createAudioPolicyManager() 方法创建的。传入参数为 new AudioPolicyClient(this)。这样audiopolicymanager就可以调用audiopolicyservice的接口。

createAudioPolicyManager

源码位置:/frameworks/av/services/audiopolicy/manager/AudioPolicyFactory.cpp

extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{AudioPolicyManager *apm = new AudioPolicyManager(clientInterface);status_t status = apm->initialize();if (status != NO_ERROR) {delete apm;apm = nullptr;}return apm;
}

AudioPolicyClient
AudioPolicyClient 类定义在 AudioPolicyService.h 中。源码位置:/frameworks/av/services/audiopolicy/service/AudioPolicyService.h

class AudioPolicyClient : public AudioPolicyClientInterface
{
public:
explicit AudioPolicyClient(AudioPolicyService *service) : mAudioPolicyService(service) {}

}

它的实现在 frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp 中。

AudioPolicyManager

源码位置:/frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface, bool /*forTesting*/):mUidCached(AID_AUDIOSERVER), // no need to call getuid(), there's only one of us running.mpClientInterface(clientInterface),mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),mA2dpSuspended(false),mConfig(mHwModulesAll, mOutputDevicesAll, mInputDevicesAll, mDefaultOutputDevice),mAudioPortGeneration(1),mBeaconMuteRefCount(0),mBeaconPlayingRefCount(0),mBeaconMuted(false),mTtsOutputAvailable(false),mMasterMono(false),mMusicEffectOutput(AUDIO_IO_HANDLE_NONE)
{
}AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();
}void AudioPolicyManager::loadConfig() {// 处理音频配置xmlif (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {ALOGE("无法加载音频策略配置文件,设置默认值");getConfig().setDefault();}
}

对于解析音频配置相关的 xml 文件我们会在后面单独分析。

三、Engine创建

Engine 是主要负责音频路由/音量相关的业务。Engine 的创建是在 AudioPolicyManager 的 initialize() 中。

status_t AudioPolicyManager::initialize() {{auto engLib = EngineLibrary::load("libaudiopolicyengine" + getConfig().getEngineLibraryNameSuffix() + ".so");……mEngine = engLib->createEngine();……mEngine->setObserver(this);status_t status = mEngine->initCheck();……}……return status;
}

EngineLibrary.cpp:createEngine
源码位置:/frameworks/av/services/audiopolicy/managerdefault/EngineLibrary.cpp

bool EngineLibrary::init(std::string libraryPath)
{……mCreateEngineInstance = (EngineInterface* (*)())dlsym(mLibraryHandle, "createEngineInstance");……return true;}EngineInstance EngineLibrary::createEngine()
{if (mCreateEngineInstance == nullptr || mDestroyEngineInstance == nullptr) {return EngineInstance();}return EngineInstance(mCreateEngineInstance(),[lib = shared_from_this(), destroy = mDestroyEngineInstance] (EngineInterface* e) {destroy(e);});
}

EngineInstance .cpp:createEngineInstance
源码位置:/frameworks/av/services/audiopolicy/engineconfigurable/src/EngineInstance.cpp

EngineInstance *EngineInstance::getInstance()
{static EngineInstance instance;return &instance;
}Engine *EngineInstance::getEngine() const
{static Engine engine;return &engine;
}template <>
EngineInterface *EngineInstance::queryInterface() const
{return getEngine()->queryInterface<EngineInterface>();
}extern "C" EngineInterface* createEngineInstance()
{return audio_policy::EngineInstance::getInstance()->queryInterface<EngineInterface>();
}

在这里最后创建了 Engine。
Engine.cpp
源码位置:/frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp

Engine::Engine()
{auto result = EngineBase::loadAudioPolicyEngineConfig();auto legacyStrategy = getLegacyStrategy();for (const auto &strategy : legacyStrategy) {mLegacyStrategyMap[getProductStrategyByName(strategy.name)] = strategy.id;}
}

这里调用了一个比较重要的函数 loadAudioPolicyEngineConfig()。

EngineBase.cpp: loadAudioPolicyEngineConfig()

engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{auto loadVolumeConfig = [](auto &volumeGroups, auto &volumeConfig) {// 确保名称唯一LOG_ALWAYS_FATAL_IF(std::any_of(std::begin(volumeGroups), std::end(volumeGroups), [&volumeConfig](const auto &volumeGroup) {return volumeConfig.name == volumeGroup.second->getName(); }), "group name %s defined twice, review the configuration", volumeConfig.name.c_str());// 表示当前VolumeGroup还没有被加载过,开始创建加载sp<VolumeGroup> volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin, volumeConfig.indexMax);volumeGroups[volumeGroup->getId()] = volumeGroup;for (auto &configCurve : volumeConfig.volumeCurves) {device_category deviceCat = DEVICE_CATEGORY_SPEAKER;if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, deviceCat)) {continue;}sp<VolumeCurve> curve = new VolumeCurve(deviceCat);for (auto &point : configCurve.curvePoints) {curve->add({point.index, point.attenuationInMb});}volumeGroup->add(curve);}return volumeGroup;};auto addSupportedAttributesToGroup = [](auto &group, auto &volumeGroup, auto &strategy) {for (const auto &attr : group.attributesVect) {strategy->addAttributes({group.stream, volumeGroup->getId(), attr});volumeGroup->addSupportedAttributes(attr);}};auto checkStreamForGroups = [](auto streamType, const auto &volumeGroups) {const auto &iter = std::find_if(std::begin(volumeGroups), std::end(volumeGroups), [&streamType](const auto &volumeGroup) {const auto& streams = volumeGroup.second->getStreamTypes();return std::find(std::begin(streams), std::end(streams), streamType) != std::end(streams);});return iter != end(volumeGroups);};// 这里开始进行解析,最终会解析出来策略、标准、标准类型以及音量组四个内容auto result = engineConfig::parse();if (result.parsedConfig == nullptr) {// 如果上面没有解析没有找到配置,使用默认的配置engineConfig::Config config = gDefaultEngineConfig;android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);result = {std::make_unique<engineConfig::Config>(config), static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};}……engineConfig::VolumeGroup defaultVolumeConfig;engineConfig::VolumeGroup defaultSystemVolumeConfig;// 循环解析所有的音量组for (auto &volumeConfig : result.parsedConfig->volumeGroups) {// 保存未在配置中定义的流的默认音量配置 将music和patch作为未定义流类型的默认配置if (volumeConfig.name.compare("AUDIO_STREAM_MUSIC") == 0) {defaultVolumeConfig = volumeConfig;}if (volumeConfig.name.compare("AUDIO_STREAM_PATCH") == 0) {defaultSystemVolumeConfig = volumeConfig;}// 这里调用上面第一个lamabda表达式// 这里定义的mVolumeGroups是一个map容器,其中second是VolumeGroup指针,定义在VolumeGroup.h中// 这里调用这个lambda表达式是为了讲volumeConfig中包含的volumeGroups解析到mVolumeGroups中loadVolumeConfig(mVolumeGroups, volumeConfig);}// 循环遍历所有的音频策略for (auto& strategyConfig : result.parsedConfig->productStrategies) {sp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);// 查找该策略是否有相应的音量组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(); });sp<VolumeGroup> volumeGroup = nullptr;// 如果没有为此策略提供音量组,则使用音乐音量组配置创建一个新的音量组(视为默认设置) if (iter == end(mVolumeGroups)) {engineConfig::VolumeGroup volumeConfig;if (group.stream >= AUDIO_STREAM_PUBLIC_CNT) {volumeConfig = defaultSystemVolumeConfig;} else {volumeConfig = defaultVolumeConfig;}volumeConfig.name = group.volumeGroup;volumeGroup = loadVolumeConfig(mVolumeGroups, volumeConfig);} else {volumeGroup = iter->second;}if (group.stream != AUDIO_STREAM_DEFAULT) {// 可以将旧流一次分组 LOG_ALWAYS_FATAL_IF(checkStreamForGroups(group.stream, mVolumeGroups), "stream %s already assigned to a volume group, " "review the configuration", toString(group.stream).c_str());volumeGroup->addSupportedStream(group.stream);}// 为策略添加相应的属性addSupportedAttributesToGroup(group, volumeGroup, strategy);}product_strategy_t strategyId = strategy->getId();mProductStrategies[strategyId] = strategy;}// 将新创建的strategy保存到mProductStrategies中并分配一个单独的IDmProductStrategies.initialize();return result;
}

总结

通过前面几篇文章的学习,我们也了解了音频服务初始化的大致流程,下面总结一下:

audioserver实例化audioflinger
audioserver实例化audiopolicyservice
audiopolicyservice创建audiopolicymanager
audiopolicymanager解析xml配置文件
audiopolicymanager创建engine
audiopolicymanager调用audioflinger接口打开audio interface
audiopolicymanager调用audioflinger接口打开音频通道(output,注意区分stream和output的概念)

经过上面的流程系统音频服务已经启动处于ready状态,如果有应用需要播放则会通过服务选择合适的硬件播出。

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

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

相关文章

Boost库搜索引擎项目(版本1)

Boost库搜索引擎 项目开源地址 Github&#xff1a;https://github.com/H0308/BoostSearchingEngine Gitee&#xff1a;https://gitee.com/EPSDA/BoostSearchingEngine 版本声明 当前为最初版本&#xff0c;后续会根据其他内容对当前项目进行修改&#xff0c;具体见后续版本…

git分支合并信息查看

TortoiseGit工具 1、选择"Revision graph" 2、勾选view中的 Show branchings and merges Arrows point towards merges 3、图案说明 红色部分‌&#xff1a;代表当前分支 橙色部分‌&#xff1a;代表远程分支 黄色部分‌&#xff1a;代表一个tag 绿色部分‌&#xf…

Java学习笔记(多线程):ReentrantLock 源码分析

本文是自己的学习笔记&#xff0c;主要参考资料如下 JavaSE文档 1、AQS 概述1.1、锁的原理1.2、任务队列1.2.1、结点的状态变化 1.3、加锁和解锁的简单流程 2、ReentrantLock2.1、加锁源码分析2.1.1、tryAcquire()的具体实现2.1.2、acquirQueued()的具体实现2.1.3、tryLock的具…

在C++11及后续标准中,auto和decltype是用于类型推导的关键特性,它们的作用和用法。

在C11及后续标准中&#xff0c;auto和decltype是用于类型推导的关键特性&#xff0c;它们的作用和用法有所不同。以下是详细说明&#xff1a; 1. auto 关键字 基本作用 自动推导变量的类型&#xff08;根据初始化表达式&#xff09;主要用于简化代码&#xff0c;避免显式书写…

Linux:进程程序替换execl

目录 引言 1.单进程版程序替换 2.程序替换原理 3.6种替换函数介绍 3.1 函数返回值 3.2 命名理解 3.3 环境变量参数 引言 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支)&#xff0c;我们所创建的所有的子进程&#xff0c;执行的代码&#x…

LeetCode.02.04.分割链表

分割链表 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x …

Johnson算法 流水线问题 java实现

某印刷厂有 6项加工任务J1&#xff0c;J2&#xff0c;J3&#xff0c;J4&#xff0c;J5&#xff0c;J6&#xff0c;需要在两台机器Mi和M2上完 成。 在机器Mi上各任务所需时间为5,1,8,5,3,4单位; 在机器M2上各任务所需时间为7,2,2,4,7,4单位。 即时间矩阵为&#xff1a; T1 {5, …

按键++,--在操作uint8_t类型(一个取值为1~10的数)中,在LCD中显示两位数字问题

问题概况 在执行按键&#xff0c;--过程中&#xff0c;本来数值为1~10.但是在执行过程中&#xff0c;发现数值在经过10数值后&#xff0c;后面的“0”会一直在LCD显示屏中显示。 就是执行操作中&#xff0c;从1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xf…

【QT】QTreeWidgetItem的checkState/setCheckState函数和isSelected/setSelected函数

目录 1、函数原型1.1 checkState/setCheckState1.2 isSelected/setSelected2、功能用途3、示例QTreeWidget的checkState/setCheckState函数和isSelected/setSelected这两组函数有着不同的用途,下面具体说明: 1、函数原型 1.1 checkState/setCheckState Qt::CheckState QTr…

005 vue项目结构 vue请求页面执行流程(vue2)

文章目录 vue项目结构vue请求页面执行流程main.jsrouterHelloWorld.vueApp.vueindex.html vue项目结构 config目录存放的是配置文件&#xff0c;比如index.js可以配置端口 node_modules存放的是该项目依赖的模块&#xff0c;这些依赖的模块在package.json中指定 src目录分析 1…

汇丰xxx

1. Spring Boot 的了解&#xff0c;解决什么问题&#xff1f; 我的理解&#xff1a; Spring Boot 是一个基于 Spring 框架的快速开发脚手架&#xff0c;它简化了 Spring 应用的初始搭建和开发过程。解决的问题&#xff1a; 简化配置&#xff1a; 传统的 Spring 应用需要大量的…

基于 Spring Boot 瑞吉外卖系统开发(一)

基于 Spring Boot 瑞吉外卖系统开发&#xff08;一&#xff09; 系统概述 系统功能 技术选型 初始项目和数据准备 初始项目和SQL文件下载 创建数据库并导入数据 打开reggie项目 运行效果 主函数启动项目&#xff0c;访问URL&#xff1a; http://127.0.0.1:8080/backend/pag…

大型语言模型智能应用Coze、Dify、FastGPT、MaxKB 对比,选择合适自己的LLM工具

大型语言模型智能应用Coze、Dify、FastGPT、MaxKB 对比&#xff0c;选择合适自己的LLM工具 Coze、Dify、FastGPT 和 MaxKB 都是旨在帮助用户构建基于大型语言模型 (LLM) 的智能应用的平台。它们各自拥有独特的功能和侧重点&#xff0c;以下是对它们的简要对比&#xff1a; Coz…

【项目管理】第6章 信息管理概论 --知识点整理

项目管理 相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 &#xff08;一&#xff09;知识总览 项目管理知识域 知识点&#xff1a; &#xff08;项目管理概论、立项管理、十大知识域、配置与变更管理、绩效域&#xff09; 对应&…

Zapier MCP:重塑跨应用自动化协作的技术实践

引言&#xff1a;数字化协作的痛点与突破 在当今多工具协同的工作环境中&#xff0c;开发者与办公人员常常面临数据孤岛、重复操作等效率瓶颈。Zapier推出的MCP&#xff08;Model Context Protocol&#xff09;协议通过标准化数据交互框架&#xff0c;为跨应用自动化提供了新的…

echart实现动态折线图(vue3+ts)

最近接到个任务&#xff0c;需要用vue3实现动态折线图。之前没有用过&#xff0c;所以一路坎坷&#xff0c;现在记录一下&#xff0c;以后也好回忆一下。 之前不清楚echart的绘制方式&#xff0c;以为是在第一秒的基础上绘制第二秒&#xff0c;后面实验过后&#xff0c;发现并…

Java学习——day24(反射进阶:注解与动态代理)

文章目录 1. 反射与注解2. 动态代理3. 实践&#xff1a;编写动态代理示例4. 注解定义与使用5. 动态代理6. 小结与思考 1. 反射与注解 注解&#xff1a;注解是 Java 提供的用于在代码中添加元数据的机制。它不会影响程序的执行&#xff0c;但可以在运行时通过反射获取和处理。反…

C++之nullptr

文章目录 前言 一、NULL 1、代码 2、结果 二、nullptr 1、代码 2、结果 总结 前言 当我们谈论空指针时,很难避免谈及nullptr。nullptr是C++11引入的一个关键字,用来表示空指针。在C++中,空指针一直是一个容易引起混淆的问题,因为在早期版本的C++中,通常使用NULL来…

JavaScript惰性加载优化实例

这是之前的一位朋友的酒桌之谈&#xff0c;他之前负责的一个电商项目&#xff0c;刚刚开发万&#xff0c;首页加载时间特别长&#xff0c;体验很差&#xff0c;所以就开始排查&#xff0c;发现是在首页一次性加载所有js导致的问题&#xff0c;这个问题在自己学习的时候并不明显…

苹果内购支付 Java 接口

支付流程&#xff0c;APP支付成功后 前端调用后端接口&#xff0c;后端接口将前端支付成功后拿到的凭据传给苹果服务器检查&#xff0c;如果接口返回成功了&#xff0c;就视为支付。 代码&#xff0c;productId就是苹果开发者后台提前设置好的 产品id public CommonResult<S…