【Bluedroid】A2dp初始化流程源码分析
一、概述
Bluedroid是Android系统中用于蓝牙通信的底层协议栈,它支持多种蓝牙协议,包括A2DP(Advanced Audio Distribution Profile,高级音频分发协议)。A2DP主要用于通过蓝牙传输高质量音频,如立体声音乐。以下是Bluedroid中A2DP初始化的基本流程。
1.1. 启动BluetoothAdapter服务
- 调用enable方法:首先,系统会调用BluetoothAdapter的enable方法以启动蓝牙服务。如果Bluedroid尚未初始化,则会先进行初始化。
- 绑定服务:在BluetoothManagerService中,通过绑定服务的方式连接到BluetoothAdapterService和GattService等蓝牙相关服务。
1.2. 初始化BluetoothAdapterService
- 加载蓝牙库:BluetoothAdapterService在初始化过程中会加载蓝牙相关的库文件(如libbluetooth.so),这些库文件包含了实现蓝牙协议栈所需的代码。
- 创建接口:通过dlopen和dlsym等函数,从加载的库文件中获取蓝牙接口的指针,以便后续调用蓝牙相关的API。
1.3. 初始化A2DP Profile
- 启动核心服务:在BluetoothAdapterService中,会启动包括A2DP在内的核心蓝牙服务。这通常是通过发送消息给服务处理线程,并调用相应的服务启动函数来实现的。
- 初始化 A2dp 模块自身:这一步会创建和初始化 A2dp 相关的模块或对象。例如,可能会初始化一些用于管理 A2dp 连接状态、音频编解码器(codec)配置、数据传输缓冲等方面的内部数据结构和对象。
- 设置编解码器优先级和支持情况:A2dp 支持多种音频编解码器,如 SBC(Subband Coding)、AAC(Advanced Audio Coding)等。在初始化过程中,会根据设备的硬件能力和用户设置(如果有)来确定各种编解码器的优先级顺序。通常会将设备支持较好、音质和性能综合表现较好的编解码器设置为较高优先级。同时,也会确定设备对各种编解码器的支持情况,并记录下来,以便在后续连接和音频传输过程中能够根据实际情况选择合适的编解码器。
- 注册回调函数:为了能够在 A2dp 连接建立、音频传输过程中出现各种事件(如连接成功、连接断开、音频播放暂停等)时进行相应的处理,会注册一系列回调函数。这些回调函数通常会与上层应用(如音乐播放应用)或系统的其他相关部分进行交互,以便及时将 A2dp 相关的事件信息传递给需要的地方,并根据事件进行相应的操作。
- 创建状态机:A2DP服务内部会创建一个状态机来管理A2DP的连接状态和数据传输。这个状态机会根据接收到的命令和事件来更新A2DP的状态,并触发相应的操作。
1.4. 建立A2DP连接
- 搜索设备:在成功初始化A2DP Profile后,用户可以通过蓝牙设置界面搜索附近的蓝牙设备。
- 选择设备并连接:从搜索结果中选择要连接的蓝牙设备,并发起连接请求。A2DP服务会处理这个请求,并与选定的设备进行配对和连接。
- 建立数据传输通道:一旦连接成功,A2DP服务会建立数据传输通道,以便在设备和蓝牙音频设备之间传输音频数据。
1.5. 音频数据传输
- 配置音频参数:在建立数据传输通道之前,A2DP服务会配置音频参数,如采样率、比特率等。这些参数会影响音频数据的质量和传输速度。
- 开始数据传输:配置完成后,A2DP服务会开始传输音频数据。音频数据会通过之前建立的数据传输通道发送到蓝牙音频设备。
1.6. 断开A2DP连接
- 发起断开请求:当用户不再需要蓝牙音频连接时,可以通过蓝牙设置界面发起断开请求。
- 处理断开请求:A2DP服务会处理这个请求,并断开与蓝牙音频设备的连接。同时,它会释放与连接相关的资源,以便后续使用。
Bluedroid中A2DP的初始化流程涉及多个步骤和组件的协同工作。这些步骤包括启动BluetoothAdapter服务、初始化BluetoothAdapterService、初始化A2DP Profile、建立A2DP连接、音频数据传输以及断开A2DP连接等。通过这些步骤,Bluedroid能够支持高质量的蓝牙音频传输。
二、源码分析
从初始化蓝牙A2DP(Advanced Audio Distribution Profile)接口的本地(native)函数开始分析。initNative通过一系列严谨的步骤,包括资源清理、回调对象设置、编解码器配置准备以及接口初始化等操作,确保了蓝牙 A2DP 功能在本地环境下能够正确初始化并在初始化成功后处于可用状态,为后续的音频传输等相关操作奠定了基础。 说明:源码基于Android14分析。
initNative
packages/modules/Bluetooth/android/app/jni/com_android_bluetooth_a2dp.cpp
static void initNative(JNIEnv* env, jobject object,jint maxConnectedAudioDevices,jobjectArray codecConfigArray,jobjectArray codecOffloadingArray) {std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);const bt_interface_t* btInf = getBluetoothInterface();if (btInf == nullptr) {log::error("Bluetooth module is not loaded");return;}if (sBluetoothA2dpInterface != nullptr) {log::warn("Cleaning up A2DP Interface before initializing...");sBluetoothA2dpInterface->cleanup();sBluetoothA2dpInterface = nullptr;}if (mCallbacksObj != nullptr) {log::warn("Cleaning up A2DP callback object");env->DeleteGlobalRef(mCallbacksObj);mCallbacksObj = nullptr;}if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {log::error("Failed to allocate Global Ref for A2DP Callbacks");return;}android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(env->FindClass("android/bluetooth/BluetoothCodecConfig"));if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {log::error("Failed to allocate Global Ref for BluetoothCodecConfig class");return;}sBluetoothA2dpInterface =(btav_source_interface_t*)btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);if (sBluetoothA2dpInterface == nullptr) {log::error("Failed to get Bluetooth A2DP Interface");return;}std::vector<btav_a2dp_codec_config_t> codec_priorities =prepareCodecPreferences(env, object, codecConfigArray);std::vector<btav_a2dp_codec_config_t> codec_offloading =prepareCodecPreferences(env, object, codecOffloadingArray);bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, maxConnectedAudioDevices, codec_priorities,codec_offloading, &supported_codecs);if (status != BT_STATUS_SUCCESS) {log::error("Failed to initialize Bluetooth A2DP, status: {}",bt_status_text(status));sBluetoothA2dpInterface = nullptr;return;}
}
通过 bt_status_t status = sBluetoothA2dpInterface->init( &sBluetoothA2dpCallbacks, maxConnectedAudioDevices, codec_priorities, codec_offloading, &supported_codecs)
调用 A2DP 接口的初始化函数,并将相关参数传入,包括回调对象指针、最大连接音频设备数量、编解码器优先级配置信息、编解码器卸载配置信息以及一个用于存储支持的编解码器信息的指针(supported_codecs
相关文章:
【Bluedroid】A2dp初始化流程源码分析
一、概述 Bluedroid是Android系统中用于蓝牙通信的底层协议栈,它支持多种蓝牙协议,包括A2DP(Advanced Audio Distribution Profile,高级音频分发协议)。A2DP主要用于通过蓝牙传输高质量音频,如立体声音乐。以下是Bluedroid中A2DP初始化的基本流程。 1.1. 启动Bluetooth…...
Redis简介、数据结构、高性能读写、持久化机制、分布式架构
Redis 是一个开源的、基于内存的数据结构存储系统,它可以用作数据库、缓存和消息中间件等,以下是对 Redis 的详细介绍: 数据结构丰富 Redis 支持多种数据结构,如字符串(String)、哈希(Hash&am…...

鸿蒙自定义UI组件导出使用
上期讲解了在Entry入口写了一个系统的下拉列表组件,如果我们想要封装一个可供复用的组件供团队其他人使用,那么需要掌握一下自定义组件的写法: 1、自定义可导入组件 - export 声明模块 如果要定义一个在外部可使用的组件 , 需要再定义组件…...
python os.path.join 详解
os.path.join 是 Python 中 os 模块提供的一个函数,用于智能地连接一个或多个路径组件。它可以根据操作系统的不同,自动选择合适的路径分隔符(如 Windows 上的反斜杠 \ 或 Unix/Linux 上的正斜杠 /),从而生成正确的路径…...
JavaScript高效处理CSV文件的操作指南
前言 CSV(Comma-Separated Values)文件是一种广泛应用于数据存储和交换的格式,尤其在数据分析、数据迁移和系统集成等场景中有着重要作用。作为高级计算机工程师,本文将通过专业且通俗易懂的方式,介绍如何利用JavaScr…...

Go开发指南- Goroutine
目录: (1)Go开发指南-Hello World (2)Go开发指南-Gin与Web开发 (3)Go开发指南-Goroutine Goroutine 在java中我们要实现并发编程的时候,通常要自己维护一个线程池,并且需要去包装任务、调度任务和维护上下文切换。这个过程需要消耗大量的精…...

Dubbo 3.x源码(24)—Dubbo服务引用源码(7)接口级服务发现订阅refreshInterfaceInvoker
基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了Dubbo3.1版本的MigrationRuleHandler这个处理器,它用于通过动态更改规则来控制迁移行为。MigrationRuleListener的onrefer方法是Dubbo2.x 接口级服务发现与Dubbo3.x应用级服务发现…...
高级java每日一道面试题-2024年11月04日-Redis篇-Redis如何做内存优化?
如果有遗漏,评论区告诉我进行补充 面试官: Redis如何做内存优化? 我回答: 在Java高级面试中,关于Redis如何做内存优化的问题,可以从以下几个方面进行详细解答: 一、Redis内存优化概述 Redis内存优化主要是指通过一系列策略和技术&#…...

数据结构 -二叉搜索树
一.什么是二叉搜索树 树插入删除方便比线性数组 二.二叉搜索树的查找操作 尾递归可以用循环递归 三.二叉树的插入操作 35要挂在33上面必须记住33的位置 解决方法,要求递归函数返回一个 结点插到33的右子树 四.二叉搜索树的删除 要是删除的是叶子节点之间删除 只有一…...
Ubuntu配置阿里云docker apt源
一、配置阿里云docker apt源 Ubuntu 放弃了apt-key的GPG 密钥的管理方法,用户可以直接添加gpg密钥到/etc/apt/trusted.gpg.d/目录下。 同时添加删除apt source 直接在/etc/apt/sources.list.d/目录下操作即可。 1、删除旧的镜像源 #旧版操作方法 apt-key list # …...

【React】状态管理之Redux
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 状态管理之Redux引言1. Redux 的核心概念1.1 单一数据源(Single Sou…...

3195. 有趣的数-13年12月CCF计算机软件能力认证(组合数)
题目 思路 统计方案的时候先去分类,先放01,然后在考虑23对于第k类, 对于01的选择 对于所有的分类:本题我觉得要考虑的几个点就是:状态分类得到数学公式组合数的计算防越界处理 代码 计算组合数的代码模板࿱…...

基于 Python 的 Bilibili 评论分析与可视化
一、项目概述 本项目利用 Python 对 Bilibili (哔哩哔哩)平台上的视频评论数据进行爬取、清洗和分析,并通过可视化展示数据的主要特征。我们通过以下几个步骤实现了这一过程: 数据爬取:使用 Bilibili 提供的 API 获取…...

大语言模型理论基础
文章目录 前言大语言模型必需知识概述大语言模型目标模型上下文神经网络的神经元常见激活函数SigmoidTanhRelusoftmax 通用近似定理多层感知机(MLP)拟合最后 前言 你好,我是醉墨居士,我们接下来对大语言模型一探究竟,…...

【 LLM论文日更|检索增强:大型语言模型是强大的零样本检索器 】
论文:https://aclanthology.org/2024.findings-acl.943.pdf代码:GitHub - taoshen58/LameR机构:悉尼科技大学 & 微软 & 阿姆斯特丹大学 & 马里兰大学领域:retrieval & llm发表:ACL2024 研究背景 研究…...
【基于轻量型架构的WEB开发】课程 作业3 Spring框架
一. 单选题(共12题,48分) 1. (单选题)以下有关Spring框架优点的说法不正确的是( )。 A. Spring就大大降低了组件之间的耦合性。 B. Spring是一种侵入式框架 C. 在Spring中,可以直接通过Spring配置文件管理…...

14.最长公共前缀-力扣(LeetCode)
题目: 解题思路: 解决本题的关键点是确定扫描的方式,大体上有两种方式:横向扫描和纵向扫描。 1、横向扫描:首先比较第一个字符串和第二个字符串,记录二者的公共前缀,然后用当前公共前缀与下一个…...

客户案例|智能进化:通过大模型重塑企业智能客服体验
01 概 述 随着人工智能技术的快速发展,客户对服务体验的期待和需求不断升级。在此背景下,大模型技术的崛起,为智能客服领域带来了创造性的变革。 在上篇文章《在后LLM时代,关于新一代智能体的思考》中有提到,智能客服…...
Flink Job更新和恢复
Checkpoints 的主要目的是为意外失败的作业提供恢复机制。 Savepoints的设计更侧重于可移植性和操作灵活性,尤其是在 job 变更方面。Savepoint 的用例是针对计划中的、手动的运维。例如,可能是更新你的 Flink 版本,更改你的作业图等等。 fli…...
读多写少业务中,MySQL如何优化数据查询方案?
小熊学Java站点:https://www.javaxiaobear.cn 编程资料合集:https://pqgmzk7qbdv.feishu.cn/base/QXq2bY5OQaZiDksJfZMc30w5nNb?from=from_copylink 看一看当面试官提及“在读多写少的网络环境下,MySQL 如何优化数据查询方案”时,你要从哪些角度出发回答问题??? 案例…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...