車載藍牙音樂流程簡單分析

關鍵類:

/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java

/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java

一、音樂播放狀態

CPP中通過JNI接口將接從手機端接收來的播放狀態回調到AvrcpControllerService的onPlayStatusChanged方法,然后通過狀態機AvrcpControllerStateMachine處理。

// Called by JNI on changes of play statusprivate synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {if (DBG) {Log.d(TAG, "onPlayStatusChanged " + playStatus);}int playbackState = PlaybackStateCompat.STATE_NONE;switch (playStatus) {case JNI_PLAY_STATUS_STOPPED:playbackState = PlaybackStateCompat.STATE_STOPPED;break;case JNI_PLAY_STATUS_PLAYING:playbackState = PlaybackStateCompat.STATE_PLAYING;break;case JNI_PLAY_STATUS_PAUSED:playbackState = PlaybackStateCompat.STATE_PAUSED;break;case JNI_PLAY_STATUS_FWD_SEEK:playbackState = PlaybackStateCompat.STATE_FAST_FORWARDING;break;case JNI_PLAY_STATUS_REV_SEEK:playbackState = PlaybackStateCompat.STATE_REWINDING;break;default:playbackState = PlaybackStateCompat.STATE_NONE;}BluetoothDevice device = mAdapter.getRemoteDevice(address);AvrcpControllerStateMachine stateMachine = getStateMachine(device);if (stateMachine != null) {stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, playbackState);}}
AvrcpControllerStateMachine.java中Connected內部類的processMessage方法中,調BluetoothMediaBrowserService類的方法將播放狀態通知到UI應用,如果是播放狀態,要申請音頻焦點。
case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:mAddressedPlayer.setPlayStatus(msg.arg1);if (!isActive()) {sendMessage(MSG_AVRCP_PASSTHRU,AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);return true;}PlaybackStateCompat playbackState = mAddressedPlayer.getPlaybackState();// 將播放狀態通過mediasession通知給UIBluetoothMediaBrowserService.notifyChanged(playbackState);// 獲取當前的音頻焦點狀態int focusState = AudioManager.ERROR;A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();if (a2dpSinkService != null) {focusState = a2dpSinkService.getFocusState();}if (focusState == AudioManager.ERROR) {sendMessage(MSG_AVRCP_PASSTHRU,AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);return true;}if (playbackState.getState() == PlaybackStateCompat.STATE_PLAYING&& focusState == AudioManager.AUDIOFOCUS_NONE) {// 申請音頻焦點if (shouldRequestFocus()) {requestAudioFocus();} else {sendMessage(MSG_AVRCP_PASSTHRU,AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);}}return true;

BluetoothMediaBrowserService是MediaSession框架的一個服務,UI端通過MediaSession框架綁定該服務就可以監聽藍牙音頻的播放狀態,控制藍牙音頻的播放。

BluetoothMediaBrowserService的notifyChanged方法中通過MediaSession框架將播放狀態通知到UI端。

static synchronized void notifyChanged(PlaybackStateCompat playbackState) {Log.d(TAG, "notifyChanged PlaybackState" + playbackState);if (sBluetoothMediaBrowserService != null) {sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);} else {Log.w(TAG, "notifyChanged Unavailable");}}

二、音頻焦點申請

上面播放狀態處理的邏輯中會調到A2dpSinkService的requestAudioFocus方法

 /*** Request audio focus such that the designated device can stream audio*/public void requestAudioFocus(BluetoothDevice device, boolean request) {synchronized (mStreamHandlerLock) {if (mA2dpSinkStreamHandler == null) return;mA2dpSinkStreamHandler.requestAudioFocus(request);}}
A2dpSinkStreamHandler
private synchronized int requestAudioFocus() {if (DBG) Log.d(TAG, "requestAudioFocus()");// Bluetooth A2DP may carry Music, Audio Books, Navigation, or other sounds so mark content// type unknown.AudioAttributes streamAttributes =new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN).build();// Bluetooth ducking is handled at the native layer at the request of AudioManager.AudioFocusRequest focusRequest =new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).setAudioAttributes(streamAttributes).setOnAudioFocusChangeListener(mAudioFocusListener, this).build();int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest);// If the request is granted begin streaming immediately and schedule an upgrade.if (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {startFluorideStreaming();mAudioFocus = AudioManager.AUDIOFOCUS_GAIN;}return focusRequestStatus;}

三、播放控制

UI通過MediaSession框架綁定到BluetoothMediaBrowserService服務,BluetoothMediaBrowserService中的MediaSessionCompat對象是關鍵,MediaSessionCompat.CallBack是專門接收MediaSession客戶端的指令并處理的。AvrcpControllerStateMachine的setActive方法中,會將MediaSessionCompat.CallBack對象傳給BluetoothMediaBrowserService的MediaSessionCompat。

BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks);
MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() {@Overridepublic void onPlay() {logD("onPlay");requestAudioFocus();sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PLAY);}@Overridepublic void onPause() {logD("onPause");sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);}};
public boolean processMessage(Message msg) {logD(STATE_TAG + " processMessage " + msg.what);switch (msg.what) {case MSG_AVRCP_PASSTHRU:passThru(msg.arg1);return true;}
}

而后調AvrcpControllerService類的sendPassThroughCommandNative方法將指令通過JNI接口發給CPP層。

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

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

相關文章

Python中利用遺傳算法探索迷宮出路

更多資料獲取 📚 個人網站:ipengtao.com 當處理迷宮問題時,遺傳算法提供了一種創新的解決方案。本文將深入探討如何運用Python和遺傳算法來解決迷宮問題。迷宮問題是一個經典的尋路問題,尋找從起點到終點的最佳路徑。遺傳算法是一…

ActiveMQ斷線重連技巧,即通信高可用的配置

最近在做一個內部應用的時候,應用到了ActiveMQ作為服務之間消息傳遞,解耦服務之間的關聯,但是在應用的過程中遇到了連接斷線無法重連的問題,下面基于這個問題,深入了解一下ActiveMQ的一些相關原理和知識。 一、前置知…

springboot2 在Java項目中你們是如何配置時間格式響應給前端呢

在 Spring Boot 2 項目中配置時間格式,通常可以通過配置文件(application.properties 或 application.yml)或者通過 Java 代碼進行配置。以下是兩種常見的配置方式: 1. 通過配置文件配置時間格式: 在 application.pr…

mybaties plus插入數據,自動回顯 機制

結論:mybaties plus會將庫里數據自動回顯到 要插入的數據上 測試表格 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- 表結構 DROP TABLE IF EXISTS t_stu; CREATE TABLE t_stu (id int NOT NULL COMMENT id,name varchar(255) CHARACTER SET utf8mb4 COLLATE…

【PyTorch】計算設備

文章目錄 1. 介紹2. 查詢和使用 1. 介紹 CPU設備意味著所有物理CPU和內存, 這意味著PyTorch的計算將嘗試使用所有CPU核心。可以用以下方式表示: torch.device(cpu) GPU設備只代表一個GPU和相應的顯存。 torch.device(cuda)如果有多個GPU,我們…

Java解決矩陣對角線元素的和問題

Java解決矩陣對角線元素的和問題 01 題目 給你一個正方形矩陣 mat,請你返回矩陣對角線元素的和。 請你返回在矩陣主對角線上的元素和副對角線上且不在主對角線上元素的和。 示例 1: 輸入:mat [[1,2,3],[4,5,6],[7,8,9]] 輸出&#xff1a…

為什么流量對店鋪轉化率重要?亞馬遜、速賣通等跨境賣家通過自養號測評提升店鋪轉化率

亞馬遜、速賣通等電商平臺賣家非常清楚流量對店鋪轉化率的重要性,測評補單在跨境電商賣家中扮演著重要的角色,是一種必要的運營手段之一。在追求更好的產品曝光和更高的轉化率時,Listing的排名是關鍵因素之一。而在各個平臺的Listing中&#…

正確使用AFX_MANAGE_STATE宏管理MFC模塊狀態, AFX_MANAGE_STATE宏作用,真的很重要!!!

簡介: 在使用 MFC(Microsoft Foundation Classes)開發 DLL(動態鏈接庫)時,正確管理 MFC 模塊狀態是確保功能正常運行的關鍵。本文將深入探討使用 AFX_MANAGE_STATE 宏的重要性,以及在 DLL 中正確…

連接Redis報錯解決方案

連接Redis報錯&解決方案 問題描述:Could not connect to Redis at 127.0.0.1:6379: 由于目標計算機積極拒絕,無法連接。 問題原因:redis啟動方式不正確 解決方案: 在redis根目錄下打開命令行窗口,輸入命令redi…

聽GPT 講Rust源代碼--src/tools(12)

File: rust/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs 在Rust源代碼中,rust/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs文件的作用是定義和解析rust-analyzer的配置文件。該文件包含了各種配置項的數據結構和枚舉類型&#xf…

MQTT主題、通配符和最佳實踐

MQTT主題在MQTT生態系統非常重要,因為代理(broker)依賴主題確定哪個客戶端接收指定的主題。本文我們將聚集MQTT主題、MQTT通配符,詳細討論使用它們的最佳實踐,也會探究SYS主題,提供給代理(broke…

【npm | npm常用命令及鏡像設置】

npm常用命令及鏡像設置 概述常用命令對比本地安裝全局安裝--save (或 -S)--save-dev (或 -D) 鏡像設置設置鏡像方法切換回npm官方鏡像選擇鏡像源 主頁傳送門:📀 傳送 概述 npm致力于讓 JavaScript 開發變得…

iOS——UIPickerView選擇器

UIPickerView UIPickerView是 iOS 開發中常用的用戶界面組件之一,用于在垂直方向上顯示一個滾動的列表,用戶可以通過滾動選擇其中的一項。 UIPickerView的協議方法 UIPickerView和UItableView差不多,UIPickerView也要設置代理和數據源。UI…

fl studio2024試用版本如何漢化中文?

fl studio2024全稱Fruity Loops Studio2024,這款軟件也被人們親切的稱之為水果,它是一款功能強大的音樂創作編輯軟件,擁有全功能的錄音室,大混音盤以及先進的音樂制作工具,用戶通過使用該軟件,就可以輕松制…

git上傳流程

git安裝網址:https://git-scm.com 如果您要將本地文件夾上傳到名為"compiling"的GitHub倉庫,可以按照以下步驟進行操作: 1.安裝無腦下一步 2.cd到想上傳的文件夾的上一級目錄 2.初始化Git倉庫:git init 設置分支&a…

C++特殊類設計

1.設計不能被拷貝的類 解析:拷貝只會放生在兩個場景中 拷貝構造函數賦值運算符重載 因此想要讓一個類禁止拷貝, 就需讓該類不能調用“拷貝構造函數”以及“賦值運算符重載”,而C11提供的delete重載關鍵字可以讓這件事情變得更加簡單。 1.1.C9…

stl庫之list鏈表與例題

stl中的list是雙向鏈表&#xff0c;優點在于插入/刪除元素方便&#xff0c;缺點是隨機訪問元素時間長 所需頭文件&#xff1a;#include <list> 初始化 list<類型名> 變量名 定義一個int類型的變量a list<int> a; 在末尾插入元素 a.push_back(i); 在開…

LeetCode 每日一題 Day 8 || 簡單枚舉

2048. 下一個更大的數值平衡數 如果整數 x 滿足&#xff1a;對于每個數位 d &#xff0c;這個數位 恰好 在 x 中出現 d 次。那么整數 x 就是一個 數值平衡數 。 給你一個整數 n &#xff0c;請你返回 嚴格大于 n 的 最小數值平衡數 。 示例 1&#xff1a; 輸入&#xff1a;n …

Error: Cannot find module ‘@npmcli/config‘ 最新解決辦法

看了網上許多這個問題的小伙伴&#xff0c;都是降級node版本來解決的。但是降級并不是我想要的結果。 真正的解決辦法就是更新nvm&#xff0c;將你的nvm升級到最新版本&#xff0c;然后卸載掉npm報錯的node版本&#xff0c;重新安裝即可使用。 解決辦法&#xff1a;更新nvm nv…

2020年第九屆數學建模國際賽小美賽B題血氧飽和度的變異性解題全過程文檔及程序

2020年第九屆數學建模國際賽小美賽 B題 血氧飽和度的變異性 原題再現&#xff1a; 脈搏血氧飽和度是監測患者血氧飽和度的常規方法。在連續監測期間&#xff0c;我們希望能夠使用模型描述血氧飽和度的模式。 ??我們有36名受試者的數據&#xff0c;每個受試者以1 Hz的頻率連…