最近做的车载蓝牙音乐开发,遇到很多问题,记录一下。也是到处东拼西凑的,勉强看看吧。
AVRCP:Audio/Video Remote Control Profile,音视频远端控制协议,所以该协议不但能控制蓝牙音乐,也可以控制视频流相应的功能。常见的使用到AVRCP控制功能的场景有如下几种:
AVRCP协议版本变化,版本之间都是向下兼容的关系:
所以如果两端设备的AVRCP协议都支持1.6及以上,则可实现通过蓝牙传输图片的功能。由于蓝牙传输数据量的限制,该功能也只是适用于音乐专辑封面照等小数据量的传输,而不适合大批量图片的传输。
至此,最新的v1.6版本的AVRCP协议结构及依赖关系如下:
类似于其他蓝牙协议,AVRCP协议也将设备两端划分为两种角色:
需要注意的是,通常情况下CT和TG都是成对出现在同一个piconet网中,但是同一个piconet网中可以存在多个CT设备,比如同一个手机(TG)可以同时连接多个耳机(CT)。常见的关系图如下:
从上面的概况图可知AVRCP协议的传输依赖于AVCTP,所以AVRCP协议的连接、交互实际上是AVCTP传输协议的连接、交互流程。
AVCTP的连接建立
用于AVCTP控制的L2CAP链路的连接建立可以由CT或TG启动,此L2CAP链路称为控制通道。如果设备双方都支持浏览功能(AVRCP版本都在v1.4及以上),则还可以在控制通道建立后再建立一条浏览通道,浏览通道在建立时应配置为使用L2CAP增强重传模式。同理通道的释放也可以由CT或TG启动,如果存在浏览通道则需要在控制通道断开前释放。
如果两端设备同时打开AVCTP通道建立连接,双方检测到连接冲突后两个通道都应关闭,每端设备应该等待随机时间(100ms <= time <= 1s),然后再尝试打开AVCTP通道。如果知道哪个设备是主设备,则该设备可以立即重新尝试连接。
实际使用过程中AVCTP的连接都会在AVDTP(A2DP协议依赖的传输协议)连接成功后由CT端设备主动发起连接,当然GT端设备可选择性的也发起主动连接。
AVRCP中基本AV/C命令
AV/C命令启动的前提是AVCTP的连接已建立,这样由用户触发或程序内部启动的AV/C流程才可正常运作,大致流程图如下:
OBEX的连接和断开
AVRCP(v1.6版本及以上)使用来自BIP协议通用成像图像特性中定义的功能来提供封面艺术图片,而BIP又依赖于OBEX。所以封面艺术OBEX连接时使用的目标标头为封面艺术专属的 UUID=7163DD54-4A7E-11E2-B47C-0050C2490048。
链路建立成功后,AVRCP的CT端设备具有BIP成像发起者的作用,而对应的AVRCP的GT端设备具有BIP成像应答器的作用。
AVRCP命令的类型:
1、AV/C命令,通过AVCTP的控制通道交互,存在如下两组命令:
2、浏览命令,通过AVCTP的浏览通道交互
3、封面艺术命令,通过OBEX建立的连接交互
AVRCP协议中特有的常见命令如下图所示:
因为AVRCP中的指令都是以CT端发起请求,TG端做出答复的形式进行的,那么我们就以CT为视角,学习AVRCP协议在安卓系统中的是如何实现的。
从上面的架构图可以看出AVRCP的架构类似于蓝牙的其他协议,但也有不同。不同之处在于应用层还通过安卓系统中的媒体浏览器服务 MediaBrowserService 与蓝牙服务进行通信,为何要多此一举呢?
查看安卓官方说明,安卓系统通过媒体浏览器服务已经为大家提供了一套完整的音乐控制解决方案,并进行了封装。所以音乐类应用通过媒体浏览器服务可以轻松实现音乐控制等功能。那对蓝牙音乐也不另外,从而安卓蓝牙对外提供的接口文件BluetoothAvrcpController中,从安卓N版本(API:24)及之后的版本是没有音乐控制的接口,而之前的版本通过 BluetoothAvrcpController.sendPassThroughCmd() 接口直接将控制指令下发到蓝牙服务层。
蓝牙音乐应用根据当前系统的安卓版本通过构建相应的 ComponentName 来初始化媒体浏览器服务的客户端,也即是 MediaBrowser 来连接媒体浏览器服务的服务端 MediaBrowserService,连接成功后应用获取到 MediaController 来控制音乐。
因为ComponentName指明了bind哪个服务,从而可以正确找到蓝牙服务中对应于媒体浏览器的服务。根据蓝牙服务的清单文件AndroidManifest.xml指定,应用构建相应的ComponentName,构建此变量需要提供包名package和类名class。
mComponentName = new ComponentName(package,class);
由于安卓版本迭代相关参数有所变化:
1、android-7(N版本) ~ android-9(P版本):
String package = "com.android.bluetooth";
String class = "com.android.bluetooth.a2dpsink.mbs.A2dpMediaBrowserService"
2、android-10(Q版本):
String package = "com.android.bluetooth";
String class = "com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService"
蓝牙音乐应用最终通过 MediaController.getTransportControls() 提供的音乐控制接口下发相应的指令,指令经过媒体浏览器服务转送到蓝牙服务中,通过蓝牙技术传输到远端设备执行响应的动作,最终达到控制蓝牙音乐的目的。
继续分析蓝牙的接口文件BluetoothAvrcpController,没有提供协议连接断开的接口,那AVRCP协议是如何连接的呢?因为AVRCP和A2DP协议通常都是一起使用的,A2DP连接成功后,sink端的协议栈会主动发起AVRCP的连接,具体可以查看android部分源码。
AVRCP连接
从上期协议分析篇知晓了AVRCP的连接也涉及到了两条通道的建立:控制通道 + 浏览通道(双方都支持浏览功能),那次协议的连接流程主要是建立两条L2CAP链路的过程,但第一步还是发起SDP服务,这是蓝牙连接中必不可少的一环。连接的时序简图如下:
蓝牙音乐应用只需要注册如下系统广播监听AVRCP的连接状态改变:
如果应用还关系浏览通道是否连接成功,还需注册如下系统广播来监听:
连接流程在HCI层的交互如下:
底层两条l2cap链路通道成功建立,应用层也已通过媒体浏览服务框架和蓝牙服务绑定成功,则蓝牙音乐应用就能随心所欲地控制远端蓝牙设备上的音乐了。