Android Audio基礎——音頻配置xml文件加載(七)

??????? 通過前面的文章,我們知道在 AudioPolicyManager 初始化的時候回調用 loadConfig() 方法去加載 Audio 相關的配置信息,這里我們就來詳細看一下。

一、配置文件加載

1、AudioPolicyManager

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

loadConfig

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface): AudioPolicyManager(clientInterface, false /*forTesting*/)
{loadConfig();
}void AudioPolicyManager::loadConfig() {// 處理音頻配置xmlif (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {ALOGE("無法加載音頻策略配置文件,設置默認值");getConfig().setDefault();}
}

deserializeAudioPolicyXmlConfig

#define AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH 128
#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];std::vector<const char*> fileNames;status_t ret;……// 保存要解析的Audio配置文件名fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);for (const char* fileName : fileNames) {// 獲取配置文件路徑for (const auto& path : audio_get_configuration_paths()) {snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), "%s/%s", path.c_str(), fileName);// 開始解析文件ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);if (ret == NO_ERROR) {config.setSource(audioPolicyXmlConfigFile);return ret;}}}return ret;
}

????? 這里首先保存了要解析的 Audio 配置文件的名稱,然后獲取通過文件名獲取文件存儲路徑,最后開始解析文件。我們先來看一下配置文件路徑的獲取 audio_get_configuration_paths() 方法。

2、audio_config.h

源碼位置:/system/media/audio/include/system/audio_config.h

// 返回一個路徑向量,其中音頻配置文件必須按提供的順序搜索。
static inline std::vector<std::string> audio_get_configuration_paths() {static const std::vector<std::string> paths = []() {char value[PROPERTY_VALUE_MAX] = {};// 獲取屬性ro.boot.product.vendor.sku的值if (property_get("ro.boot.product.vendor.sku", value, "") <= 0) { // 使用默認路徑return std::vector<std::string>({"/odm/etc", "/vendor/etc", "/system/etc"});} else {// 增加了一個配置路徑return std::vector<std::string>({"/odm/etc", std::string("/vendor/etc/audio/sku_") + value, "/vendor/etc", "/system/etc"});}}();return paths;
}

??????? 這里主用通過系統屬性 ro.boot.product.vendor.sku 的值判斷是否定制配置文件路徑。這里一般使用系統默認路徑。

  • /odm/etc:這個路徑通常用于 OEM(原始設備制造商)的專有或私有數據,比如那些在設備制造過程中預裝的、與設備固件緊密相關的配置文件。ODM 數據通常不被用戶修改,而且可能包含敏感信息。
  • /vendor/etc:這個路徑是為供應商提供的配置文件,比如音頻驅動或服務的特定設置。供應商可以在這一層定制硬件特定的音頻配置,以適應他們的設備。這些配置文件通常隨硬件驅動一起更新,且不會隨著操作系統升級而改變。
  • /system/etc:這是系統級別的配置文件存放位置,包含了操作系統核心的音頻設置。這些配置文件是系統的一部分,可能會隨著 Android 系統的更新而改變。

??????? 所以,對于 Audio 的相關配置,一般位于?/vendor/etc 下。下面我們繼續看一下配置文件的加載。

3、Serializer.cpp

源碼位置:/frameworks/av/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp

deserializeAudioPolicyFile

status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config)
{PolicySerializer serializer;return serializer.deserialize(fileName, config);
}

??????? 這里調用了?PolicySerializer 的 deserialize() 方法。

deserialize

status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig *config)
{// 解析XML配置文件auto doc = make_xmlUnique(xmlParseFile(configFile));if (doc == nullptr) {ALOGE("%s: Could not parse %s document.", __func__, configFile);return BAD_VALUE;}// 獲取XML文檔的根節點xmlNodePtr root = xmlDocGetRootElement(doc.get());if (root == NULL) {ALOGE("%s: Could not parse %s document: empty.", __func__, configFile);return BAD_VALUE;}// 處理XML文檔中的XInclude指令if (xmlXIncludeProcess(doc.get()) < 0) {ALOGE("%s: libxml failed to resolve XIncludes on %s document.", __func__, configFile);}// 檢查根節點的名稱是否與"rootName = "audioPolicyConfiguration""的一致if (xmlStrcmp(root->name, reinterpret_cast<const xmlChar*>(rootName)))  {ALOGE("%s: No %s root element found in xml data %s.", __func__, rootName,reinterpret_cast<const char*>(root->name));return BAD_VALUE;}//  獲取根節點的版本屬性std::string version = getXmlAttribute(root, versionAttribute);if (version.empty()) {ALOGE("%s: No version found in root node %s", __func__, rootName);return BAD_VALUE;}if (version != mVersion) {ALOGE("%s: Version does not match; expect %s got %s", __func__, mVersion.c_str(),version.c_str());return BAD_VALUE;}// 反序列化子模塊ModuleTraits::Collection modules;status_t status = deserializeCollection<ModuleTraits>(root, &modules, config);if (status != NO_ERROR) {return status;}config->setHwModules(modules);// 全局配置,利用C++的函數模板語法調用到不同的deserializeGlobalConfigTraits::deserialize(root, config);// Surround configurationSurroundSoundTraits::deserialize(root, config);return android::OK;
}

??????? 這里主要調用?deserializeCollection() 方法解析文件的。?

template <class Trait>
status_t deserializeCollection(const xmlNode *cur, typename Trait::Collection *collection,typename Trait::PtrSerializingCtx serializingContext)
{// 用兩個for循環,遍歷當前xml節點的所有子節點for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {const xmlNode *child = NULL;if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::collectionTag))) {child = cur->xmlChildrenNode;} else if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {child = cur;}for (; child != NULL; child = child->next) {if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {// 找到對應模板的名字,然后再調用auto element = Trait::deserialize(child, serializingContext);if (element.isOk()) {status_t status = Trait::addElementToCollection(element, collection);if (status != NO_ERROR) {ALOGE("%s: could not add element to %s collection", __func__,Trait::collectionTag);return status;}} else {return BAD_VALUE;}}}if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(Trait::tag))) {return NO_ERROR;}}return NO_ERROR;
}

??????? 該函數是音頻系統中用于恢復音頻策略配置的一個重要步驟,尤其是在系統啟動或音頻服務重啟時。它通過調用序列化器類的方法來讀取外部存儲的配置信息,以便于正確配置音頻策略,保證系統能夠按照預定的規則處理音頻流。

??????? 這樣我們就將 audio_policy_configuration.xml 中的 Audio 相關信息保存到 AudioPolicyConfig 中了。

二、配置信息

1、audio_policy_configuration.xml

????????audio_policy_configuration.xml 文件是 Android 音頻框架中用于定義音頻策略配置的 XML 文件,該文件通常隨硬件驅動一起更新,且不會隨著操作系統升級而改變。所以該文件通常在 Android 源碼中無法看到,可以通過 adb shell 進入到車機的 /vendor/etc 目錄下進行查看

<audio-policy-config><devices><device type="speaker" /><device type="headphone" /><device type="earpiece" /></devices><use-cases><use-case type="voice_call" /><use-case type="ringtone" /><use-case type="alarm" /><use-case type="media" /></use-cases><routes><route use-case="voice_call"><output device="earpiece" /></route><route use-case="ringtone"><output device="speaker" /></route><route use-case="alarm"><output device="speaker" /></route><route use-case="media"><output device="headphone" optional="true" /><output device="speaker" /></route></routes><volume-ranges><range type="media"><min-value>0</min-value><max-value>15</max-value></range><range type="ring"><min-value>0</min-value><max-value>15</max-value></range></volume-ranges><!-- Additional configuration elements -->
</audio-policy-config>

??????? 這是一個簡化版的 audio_policy_configuration.xml 示例,它包含了各種音頻設備的配置信息,如設備類型、音量級別、路由策略等。解析這個文件的過程通常涉及以下幾個步驟:

解析設備(devices):識別音頻設備類型,如揚聲器(speaker)、耳機(headphone)和聽筒(earpiece)。
解析使用場景(use-cases):確定音頻的使用場景,如語音通話(voice_call)、鈴聲(ringtone)、鬧鐘(alarm)和媒體播放(media)。
解析路由(routes):根據使用場景將音頻路由到相應的輸出設備。例如,語音通話使用聽筒,鈴聲和鬧鐘使用揚聲器,媒體播放首選耳機,如果沒有耳機則使用揚聲器。
解析音量范圍(volume-ranges):設置不同類型的音量范圍,如媒體音量和鈴聲音量的最大值和最小值。
其他配置:文件可能還包含其他配置元素,如音頻效果、策略規則等。

????????在 Android 系統中,音頻服務(AudioService)會讀取并解析這個 XML 文件,然后根據配置信息來管理音頻路由、音量控制和其他音頻策略。?

2、AudioPolicyConfig

????????AudioPolicyConfig? 是 Android 音頻框架中一個關鍵的類,它封裝了音頻策略配置的細節。這個類通常包含了一系列的屬性和方法,用于描述音頻服務如何處理音頻流的路由、音量控制、混音策略等。

源碼位置:/frameworks/base/media/java/android/media/audiopolicy/AudioPolicyConfig.java

/*** @hide* AudioPolicy配置的內部存儲類*/
public class AudioPolicyConfig implements Parcelable {// AudioMix定義了一組音頻流的混合規則,包括路由、格式、匹配條件等。protected final ArrayList<AudioMix> mMixes;// 降音策略,用于控制當有更高優先級的音頻流開始播放時,當前音頻流降音。protected int mDuckingPolicy = AudioPolicy.FOCUS_POLICY_DUCKING_IN_APP;// 注冊ID,用于標識音頻策略配置的唯一性。private String mRegistrationId = null;// 記錄添加到配置中的音頻混合策略的數量。private int mMixCounter = 0;protected AudioPolicyConfig(AudioPolicyConfig conf) {mMixes = conf.mMixes;}AudioPolicyConfig(ArrayList<AudioMix> mixes) {mMixes = mixes;}/*** 添加一個AudioMix到配置中*/public void addMix(AudioMix mix) throws IllegalArgumentException {if (mix == null) {throw new IllegalArgumentException("Illegal null AudioMix argument");}mMixes.add(mix);}public ArrayList<AudioMix> getMixes() {return mMixes;}……@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(mMixes.size());for (AudioMix mix : mMixes) {// 寫入混合路由標志,這些標志指示如何處理音頻流的路由。dest.writeInt(mix.getRouteFlags());// 寫入回調標志,可能用于指定何時觸發回調事件。dest.writeInt(mix.mCallbackFlags);// 寫入設備的系統類型,如藍牙、USB或其他設備類型。dest.writeInt(mix.mDeviceSystemType);// 寫入設備的地址,如藍牙設備的MAC地址或USB設備的唯一標識。dest.writeString(mix.mDeviceAddress);// 寫入音頻格式的采樣率,如44100 Hz。dest.writeInt(mix.getFormat().getSampleRate());// 寫入音頻編碼,如PCM、AAC等dest.writeInt(mix.getFormat().getEncoding());// 寫入音頻通道掩碼,表示音頻流的聲道布局,如立體聲或環繞聲。dest.writeInt(mix.getFormat().getChannelMask());// 寫入是否允許特權播放捕獲,這涉及到音頻流的權限控制。dest.writeBoolean(mix.getRule().allowPrivilegedPlaybackCapture());// 寫入是否允許語音通信捕獲,可能與電話或VoIP應用相關dest.writeBoolean(mix.getRule().voiceCommunicationCaptureAllowed());// 寫入混合規則列表的大小,即AudioMixMatchCriterion的數量。final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();dest.writeInt(criteria.size());for (AudioMixMatchCriterion criterion : criteria) {criterion.writeToParcel(dest);}}}……
}

????????綜上所述,AudioPolicyConfig 類是 Android 音頻策略的核心組件之一,用于定義復雜的音頻混合邏輯和路由策略,支持跨進程傳輸,對于構建靈活、可擴展的音頻管理系統至關重要。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/21441.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21441.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21441.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

將下拉彈層渲染節點固定在觸發器的父元素中

將下拉彈層渲染節點固定在觸發器的父元素中 注意: 如果發現下拉菜單跟隨頁面滾動&#xff0c;或者需要在其他彈層中觸發 Select&#xff0c; 請嘗試使用 getPopupContainer{triggerNode > triggerNode.parentElement} 將下拉彈層渲染節點固定在觸發器的父元素中。

【MySQL】探索 MySQL 的 GROUP_CONCAT 函數

緣分讓我們相遇亂世以外 命運卻要我們危難中相愛 也許未來遙遠在光年之外 我愿守候未知里為你等待 我沒想到為了你我能瘋狂到 山崩海嘯沒有你根本不想逃 我的大腦為了你已經瘋狂到 脈搏心跳沒有你根本不重要 &#x1f3b5; 鄧紫棋《光年之外》 什么是 GRO…

遺傳算法與應用分析

遺傳算法的概念 簡單來說&#xff0c;遺傳算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;是一種模擬自然進化過程的優化算法。它通過模擬生物進化的遺傳機制&#xff0c;通過選擇、交叉和變異等操作&#xff0c;逐代優化搜索空間中的解。遺傳算法最初由約翰霍蘭…

【面試題-001】什么是面向對象?

文章目錄 什么是面向對象&#xff1f;與面向過程的區別&#xff1f;哪些語言是面向對象 哪些是面向過程&#xff1f; 什么是面向對象&#xff1f; 面向對象&#xff08;Object-oriented&#xff09;是一種程序設計范例&#xff0c;它通過將數據與對數據操作的函數&#xff08;…

V90 PN伺服驅動器附加報文750詳細使用介紹(算法分析)

1、V90PN伺服驅動器轉矩控制(750報文) V90 PN伺服驅動器轉矩控制(750報文)_v90pn轉矩控制-CSDN博客文章瀏覽閱讀3.4k次,點贊2次,收藏3次。主要介紹通過標準報文加附加報文 750 實現發送驅動報文的控制字、速度給定、轉矩限幅及附加轉矩給定的功能,首先就是V90在博途環境下…

算法學習筆記——對數器

對數器 對數器的實現&#xff1a; 你想要測的方法a&#xff08;最優解&#xff09;實現復雜度不好但是容易實現的方法b&#xff08;暴力解&#xff09;實現一個隨機樣本產生器&#xff08;長度也隨機、值也隨機&#xff09;把方法a和方法b跑相同的輸入樣本&#xff0c;看看得…

分享5款.NET開源免費的Redis客戶端組件庫

前言 今天大姚給大家分享5款.NET開源、免費的Redis客戶端組件庫&#xff0c;希望可以幫助到有需要的同學。 StackExchange.Redis StackExchange.Redis是一個基于.NET的高性能Redis客戶端&#xff0c;提供了完整的Redis數據庫功能支持&#xff0c;并且具有多節點支持、異步編…

總結2024/6/3

省流&#xff0c;藍橋杯國優&#xff0c;還是太菜了&#xff0c;聽說都是板子題但是還是寫不出來&#xff0c;靠暴力好歹沒有爆0&#xff0c;還是得多練&#xff0c;明年加油了

JWT 簽名用對稱加密還是非對稱加密?

一 概念梳理 對稱加密和非對稱加密是兩種基本的加密方法&#xff0c;它們在現代密碼學中扮演著核心角色&#xff0c;用于保護數據的安全和隱私。 1.1 對稱加密&#xff08;Symmetric Encryption&#xff09; 對稱加密是指加密和解密使用同一個密鑰的過程。這意味著發送方和接…

!力扣 108. 將有序數組轉換為二叉搜索樹

給你一個整數數組 nums &#xff0c;其中元素已經按升序排列&#xff0c;請你將其轉換為一棵 平衡二叉搜索樹。 示例 1&#xff1a; 輸入&#xff1a;nums [-10,-3,0,5,9] 輸出&#xff1a;[0,-3,9,-10,null,5] 解釋&#xff1a;[0,-10,5,null,-3,null,9] 也將被視為正確答案…

封裝了一個使用UICollectionViewLayout 實現的吸附居左banner圖

首先查看效果圖 實現的原理就是通過自定義UICollectionView layout&#xff0c;然后 設置減速速率是快速就可以達到吸附的效果 _collectionView.decelerationRate UIScrollViewDecelerationRateFast; 下面貼出所有代碼 這里是.h // // LBMiddleExpandLayout.h // Liubo…

文章解讀與仿真程序復現思路——電力系統自動化EI\CSCD\北大核心《具有源荷不平衡特性的配電網智能軟開關和儲能聯合規劃》

本專欄欄目提供文章與程序復現思路&#xff0c;具體已有的論文與論文源程序可翻閱本博主免費的專欄欄目《論文與完整程序》 論文與完整源程序_電網論文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 電網論文源程序-CSDN博客電網論文源…

CTF_RE學習

學了一個 map&#xff08;&#xff09;函數的使用 import base64rawData "e3nifIH9b_CndH" target list(map(ord, rawData)) # map 函數將 rawData 中的每個字符傳遞給 ord 函數。ord 函數返回給定字符的 Unicode 碼點 print(target) # 打印 map 對象的內存地址&…

汽車線束搭鐵與接地

一、搭鐵與接地的概念 首先在這里解釋一下“搭鐵”與“接地”的概念&#xff0c;不要混為一團&#xff01; 先說接地&#xff0c;大地是可導電的&#xff0c;其電位通常取為零。電力系統和電氣裝置的中性點、電氣設備的外露導電部分及裝置外導電部分通過導體與大地相連&#xf…

MySQL數據庫的約束

MySQL對于數據庫存儲的數據, 做出一些限制性要求, 就叫做數據庫的"約束". 在每一列的 列名, 類型 后面加上"約束". 一. not null (非空) 指定某列不能存儲null值. 二. unique (唯一) 保證這一列的每行必須有唯一值. 我們可以看到, 給 table 的 sn 列插…

【微服務】docker部署redis,一主二從三哨兵,讀寫分離

配置redis讀寫分離 3臺虛擬機 創建目錄用于掛載 mkdir -p /root/redis/{conf,data,logs} #master配置文件 bind 0.0.0.0 //任何ip都能訪問 port 6379 //redis端口號 logfile "/data/redis.log" //日志文件存放位置&#xff0c;啟動redis之前設置為空&#xff…

prometheus docker部署

1.安裝Docker sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors":["https://hub-mirror.c.163.com"] } EOF export DOWNLOAD_URL"https://hub-mirror.163.com/docker-ce" curl -fsSL https://ge…

TypeScript 中的聲明合并

1. 聲明合并的概念 聲明合并是指當 TypeScript 遇到多個同名的聲明時&#xff0c;會將它們合并為一個單一的聲明。這使得開發者可以分散地定義同一個實體的不同部分&#xff0c;最終將它們合并為一個整體。在進行聲明合并時&#xff0c;TypeScript 會根據不同類型的聲明進行不…

【LIN】STM32新能源汽車LIN通信實現過程

【LIN】STM32新能源汽車LIN通信實現過程 文章目錄 前言一、軟件二、接線圖三、硬件原理圖四、上位機五、PICO示波器串行解碼1.軟件中的LIN波特率設置-192002.PIC設置3.PIC串行解碼 六.引用總結 前言 【電機控制】直流有刷電機、無刷電機匯總——持續更新 使用工具&#xff1a;…

godot.bk

1.搜索godot國內鏡像&#xff0c;直接安裝&#xff0c;mono是csharp版本 2.直接解壓&#xff0c;50m&#xff0c;無需安裝&#xff0c;直接運行 3.godot里分為場景&#xff0c;節點 主場景用control場景&#xff0c;下面掛textureact放背景圖片&#xff0c;右鍵實例化子場景把…