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.pswp.cn/news/900631.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900631.shtml
英文地址,請注明出處:http://en.pswp.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…