Android蓝牙协议栈fluoride(八) - 音乐播放与控制(1)

概述

通常情况下音乐播放与控制这两个profile(即A2DP和AVRCP)都是同时存在的,A2DP分为Sink(SNK)和Source(SRC)两个角色,ACRVP分为Controller(CT)和Target(TG)两个角色。接下来的几篇博客将详细介绍这两个profile。
Sink和Source、CT和TG都是成对出现的。CT和TG可以同时存在在一个设备上,而Sink和Source则不能同时存在在一个设备上(不能同时工作)。角色关系如下:
在这里插入图片描述
在这里插入图片描述

A2DP profile底层依赖AVDTP协议,AVRCP底层依赖AVCTP协议,它们都基于L2CAP协议实现,SDP支持双方的服务发现。AVDTP协议实现了音频流的传输,A2DP进行角色管理以及编解码;AVRCP实现播放控制与信息同步,控制包括:播放、暂停、上一首、下一首、音量调节等,信息包括播放位置、播放状态、专辑信息、音量同步等。SIG的spec对协议栈定义如下:
在这里插入图片描述在这里插入图片描述

接口

flouride协议栈中,A2DP的接口定义如下:

// a2dp source的回调
typedef struct {size_t size;// 连接状态变化上报btav_connection_state_callback connection_state_cb;// 音频状态变化上报btav_audio_state_callback audio_state_cb;// 音频配置变化上报btav_audio_source_config_callback audio_config_cb;btav_mandatory_codec_preferred_callback mandatory_codec_preferred_cb;
} btav_source_callbacks_t;// a2dp source的api
typedef struct {size_t size;// 向协议栈注册回调bt_status_t (*init)(btav_source_callbacks_t* callbacks, int max_connected_audio_devices, const std::vector<btav_a2dp_codec_config_t>& codec_priorities, const std::vector<btav_a2dp_codec_config_t>& offloading_preference);// 连接指定设备bt_status_t (*connect)(const RawAddress& bd_addr);// 端口指定设备bt_status_t (*disconnect)(const RawAddress& bd_addr);// 设置指定的设备是否静音bt_status_t (*set_silence_device)(const RawAddress& bd_addr, bool silence);// 设置制定的设备为active,有播放时会将音频发送给该设备bt_status_t (*set_active_device)(const RawAddress& bd_addr);// 编码器配置bt_status_t (*config_codec)(const RawAddress& bd_addr, std::vector<btav_a2dp_codec_config_t> codec_preferences);void (*cleanup)(void);
} btav_source_interface_t;// a2dp sink的回调
typedef struct {size_t size;// 连接状态变化上报btav_connection_state_callback connection_state_cb;// audio状态变化上报btav_audio_state_callback audio_state_cb;// audio配置变化上报btav_audio_sink_config_callback audio_config_cb;
} btav_sink_callbacks_t;// a2dp sink的api
typedef struct {size_t size;// 向协议栈注册回调bt_status_t (*init)(btav_sink_callbacks_t* callbacks);// 连接指定的设备bt_status_t (*connect)(const RawAddress& bd_addr);// 断开指定设备bt_status_t (*disconnect)(const RawAddress& bd_addr);void (*cleanup)(void);// 设置音频播放焦点void (*set_audio_focus_state)(int focus_state);// 设置播放增益void (*set_audio_track_gain)(float gain);//将制定设备设置为active,即播放收到的该设备的数据bt_status_t (*set_active_device)(const RawAddress& bd_addr);
} btav_sink_interface_t;

可以看到,没有音频流相关的API,作为Sink时,解码后的数据直接写入到Audio模块,作为Source时,直接从Audio模块获取数据进行编码,蓝牙应用层不需要关注音频流的具体内容。

AVRCP中大部分接口都可以直接从函数名理解其作用,因此文章中没有详细列出,可参考源码进行理解。

AVRCP Controller的接口如下:

// ACRCP CT的回调
typedef struct {...
} btrc_ctrl_callbacks_t;//ACRCP TG的API
typedef struct {...
} btrc_ctrl_interface_t;

以前的android版本使用的AVRCP target的接口定义如下:

// AVRCP TG的回调
typedef struct {... 
} btrc_callbacks_t;// ACRCP TG的API
typedef struct {...
} btrc_interface_t;

旧版本TG的API渐渐的不满足AVRCP版本更新的需求,新版本API采用了面向对象的思想重新实现,接口定义如下:

// 媒体相关的回调
class MediaCallbacks {public:virtual void SendMediaUpdate(bool track_changed, bool play_state, bool queue) = 0;virtual void SendFolderUpdate(bool available_players, bool addressed_players, bool uids_changed) = 0;virtual void SendActiveDeviceChanged(const RawAddress& address) = 0;virtual ~MediaCallbacks() = default;
};// JNI给协议栈提供的媒体相关接口
class MediaInterface {public:// 将CT端的按键事件(AVRCP中的Passthrough命令)发送给应用层,其中包含播放/暂停等virtual void SendKeyEvent(uint8_t key, KeyState state) = 0;// 从应用层获取歌曲信息using SongInfoCallback = base::Callback<void(SongInfo)>;virtual void GetSongInfo(SongInfoCallback info_cb) = 0;// 从应用层获取播放状态using PlayStatusCallback = base::Callback<void(PlayStatus)>;virtual void GetPlayStatus(PlayStatusCallback status_cb) = 0;// 当前播放列表using NowPlayingCallback = base::Callback<void(std::string, std::vector<SongInfo>)>;virtual void GetNowPlayingList(NowPlayingCallback now_playing_cb) = 0;// 媒体播放器列表using MediaListCallback = base::Callback<void(uint16_t curr_player, std::vector<MediaPlayerInfo>)>;virtual void GetMediaPlayerList(MediaListCallback list_cb) = 0;// 获取目录中的列表using FolderItemsCallback = base::Callback<void(std::vector<ListItem>)>;virtual void GetFolderItems(uint16_t player_id, std::string media_id, FolderItemsCallback folder_cb) = 0;// 浏览播放器using SetBrowsedPlayerCallback = base::Callback<void(bool success, std::string root_id, uint32_t num_items)>;virtual void SetBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCallback browse_cb) = 0;// 播放指定itemvirtual void PlayItem(uint16_t player_id, bool now_playing, std::string media_id) = 0;// 设置active设备virtual void SetActiveDevice(const RawAddress& address) = 0;// 注册/注销媒体更新的回调virtual void RegisterUpdateCallback(MediaCallbacks* callback) = 0;virtual void UnregisterUpdateCallback(MediaCallbacks* callback) = 0;MediaInterface() = default;virtual ~MediaInterface() = default;
};// JNI给协议栈提供的音量相关接口
class VolumeInterface {public:using VolumeChangedCb = base::Callback<void(int8_t volume)>;// 不支持绝对音量时指示连接状态virtual void DeviceConnected(const RawAddress& bdaddr) = 0;// 支持绝对音量时指示连接状态,cb是协议栈提供给JNI调节音量的回调,调用cb时向CT发送ACRCP中的SET_ABSOLUTE_VOLUME命令virtual void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) = 0;// 指示断开状态virtual void DeviceDisconnected(const RawAddress& bdaddr) = 0;// 收到CT端的AVRC_EVT_VOLUME_CHANGE事件时调用,通知应用层音量变化virtual void SetVolume(int8_t volume) = 0;virtual ~VolumeInterface() = default;
};// 协议栈提供给JNI的接口
class ServiceInterface {public:// JNI注销接口给协议栈,mediaInterface不能为空,volumeInterface为空时不支持绝对音量virtual void Init(MediaInterface* mediaInterface, VolumeInterface* volumeInterface) = 0;// 注册/注销BIP服务,用于传输专辑封面信息virtual void RegisterBipServer(int psm) = 0;virtual void UnregisterBipServer() = 0;// 连接/断开指定设备virtual bool ConnectDevice(const RawAddress& bdaddr) = 0;virtual bool DisconnectDevice(const RawAddress& bdaddr) = 0;// 设置BIP客户端的状态virtual void SetBipClientStatus(const RawAddress& bdaddr, bool connected) = 0;virtual bool Cleanup() = 0;protected:virtual ~ServiceInterface() = default;
};

在之后的文章中不会专门描述旧接口相关的实现,主要集中在新的接口上,但旧接口和CT的接口是类似的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/222137.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智能优化算法应用:基于梯度算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于梯度算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于梯度算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.梯度算法4.实验参数设定5.算法结果6.参考文献7.MA…

vue中最重要的点,双向数据绑定是什么?

一、什么是双向绑定 我们先从单向绑定切入单向绑定非常简单&#xff0c;就是把Model绑定到View&#xff0c;当我们用JavaScript代码更新Model时&#xff0c;View就会自动更新双向绑定就很容易联想到了&#xff0c;在单向绑定的基础上&#xff0c;用户更新了View&#xff0c;Mo…

多维时序 | MATLAB实现BiTCN-Multihead-Attention多头注意力机制多变量时间序列预测

多维时序 | MATLAB实现BiTCN-Multihead-Attention多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现BiTCN-Multihead-Attention多头注意力机制多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | MATLAB实现BiTCN-Multihea…

vp与vs联合开发-串口通信

模拟串口通信 1.配置虚拟串口驱动 winform 实现串口通信 1.模拟串口和winform程序通信 2.模拟串口通信 控制拍照功能

ubuntu保存分辨率失效解决办法

在VM虚拟机中&#xff0c;遇到修改ubuntu分辨率后&#xff0c;重启后又重置的解决办法。 目前我的ubuntu版本是&#xff1a;ubuntu 18.04.6 版本。 1.首先&#xff0c;在你喜欢的目录建立一个.sh 脚本文件。 终端执行命令&#xff1a;sudo vim xrandr.sh 2.按 i 进入编辑状…

【数据结构】五、数组与广义表

目录 一、定义 二、计算数组元素地址 三、稀疏矩阵快速转置 稀疏矩阵的表示 稀疏矩阵快速转置 四、广义表 一、定义 我们所熟知的一维、二维数组的元素是原子类型。广义表中的元素除了原子类型还可以是另一个线性表。当然所有的数据元素仍然属于同一类型。 这里的数组可…

VSCode安装PYQT5

安装PYQT5 pip install PyQt5 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install PyQt5-tools -i https://pypi.tuna.tsinghua.edu.cn/simple 获得Python环境位置 查看函数库安装位置 pip show 函数库名 通过查询函数库&#xff0c;了解到python安装位置为 C:\User…

在 Kubernetes 上部署 Python 3.7、Chrome 和 Chromedriver(版本 114.0.5735.90)的完整指南

一、构建基础镜像 docker build -f /u01/isi/DockerFile . -t thinking_code.com/xhh/crawler_base_image:v1.0.2docker push thinking_code.com/xhh/crawler_base_image:v1.0.2 二、K8s运行Pod 三、DockerFile文件 # 基于镜像基础 FROM python:3.7# 设置代码文件夹工作目录…

泛微e-cology XmlRpcServlet文件读取漏洞复现

漏洞介绍 泛微新一代移动办公平台e-cology不仅组织提供了一体化的协同工作平台,将组织事务逐渐实现全程电子化,改变传统纸质文件、实体签章的方式。泛微OA E-Cology 平台XmRpcServlet接口处存在任意文件读取漏洞&#xff0c;攻击者可通过该漏洞读取系统重要文件 (如数据库配置…

python编程(1)之通用引脚GPIO使用

在之前的章节中&#xff0c;小编带领大家学习了&#xff1a;如何构建esp32的python开发环境-CSDN博客 今天小编带领大家开始学习python编程的第一节&#xff0c;通用引脚。esp32c3核心板是一个高度集成&#xff0c;功能丰富的模块&#xff0c;来看下他的功能分布&#xff1a; 我…

【小黑嵌入式系统第十一课】μC/OS-III程序设计基础(一)——任务设计、任务管理(创建基本状态内部任务)、任务调度、系统函数

上一课&#xff1a; 【小黑嵌入式系统第十课】μC/OS-III概况——实时操作系统的特点、基本概念&#xff08;内核&任务&中断&#xff09;、与硬件的关系&实现 文章目录 一、任务设计1.1 任务概述1.2 任务的类型1.2.1 单次执行类任务&#xff08;运行至完成型&#…

centos7安装开源日志系统graylog5.1.2

安装包链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1Zl5s7x1zMWpuKfaePy0gPg?pwd1eup 提取码&#xff1a;1eup 这里采用的shell脚本安装&#xff0c;脚本如下&#xff1a; 先使用命令产生2个参数代入到脚本中&#xff1a; 使用pwgen生成password_secret密码 …

Linux之进程(五)(进程控制)

目录 一、进程创建 1、fork函数创建进程 2、fork函数的返回值 3、fork常规用法 4、fork调用失败的原因 二、进程终止 1、进程终止的方式 2、进程退出码 3、进程的退出方法 三、进程等待 1、进程等待的必要性 2、wait函数 3、waitpid函数 四、进程程序替换 1、概念…

利用ffmpeg cv2取h265码流视频(转换图片灰屏问题解决)

利用海康威视相机拍出来的视频是H265格式的&#xff0c;相比于常规的H264编码&#xff0c;压缩率更高&#xff0c;但因此如果直接用正常取流方法读取&#xff0c;会出现无法读取的情况 1. 如图h265码流取出图片为灰屏 2 、解决灰屏问题 import subprocess import cv2# 将h265流…

100GPTS计划-AI学术AcademicRefiner

地址 https://chat.openai.com/g/g-LcMl7q6rk-academic-refiner https://poe.com/AcademicRefiner 测试 减少相似性 增加独特性 修改http://t.csdnimg.cn/jyHwo这篇文章微调 专注于人工智能、科技、金融和医学领域的学术论文改写&#xff0c;秉承严格的专业和学术标准。 …

使用opencv实现图像中几何图形检测

1 几何图形检测介绍 1.1 轮廓(contours) 什么是轮廓&#xff0c;简单说轮廓就是一些列点相连组成形状、它们拥有同样的颜色、轮廓发现在图像的对象分析、对象检测等方面是非常有用的工具&#xff0c;在OpenCV 中使用轮廓发现相关函数时候要求输入图像是二值图像&#xff0c;这…

华为安防监控摄像头

华为政企42 华为政企 目录 上一篇华为政企城市一张网研究报告下一篇华为全屋wifi6蜂鸟套装标准

【数据结构】二叉树的模拟实现

前言:前面我们学习了堆的模拟实现&#xff0c;今天我们来进一步学习二叉树&#xff0c;当然了内容肯定是越来越难的&#xff0c;各位我们一起努力&#xff01; &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:数据结构 &#x1f448; &…

大创项目推荐 深度学习 机器视觉 人脸识别系统 - opencv python

文章目录 0 前言1 机器学习-人脸识别过程人脸检测人脸对其人脸特征向量化人脸识别 2 深度学习-人脸识别过程人脸检测人脸识别Metric Larning 3 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 人脸识别系统 该项目…

LabVIEW开发自动驾驶的双目测距系统

LabVIEW开发自动驾驶的双目测距系统 随着车辆驾驶技术的不断发展&#xff0c;自动驾驶技术正日益成为现实。从L2级别的辅助驾驶技术到L3级别的受条件约束的自动驾驶技术&#xff0c;车辆安全性和智能化水平正在不断提升。在这个过程中&#xff0c;车辆主动安全预警系统发挥着关…