车载蓝牙音乐流程简单分析

关键类:

/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.mzph.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的频率连…