本篇blog继续以结合日志的形式来分析AVRCP,以手机连接上耳机后,通过耳机控制音乐播放的暂停、播放来分析AVRCP的过程。

1. AVRCP

音频/视频远程控制配置文件 (AVRCP) 定义了所需的功能和程序,以确保在音频/视频分发场景中具有音频/视频控制功能的蓝牙设备之间的互操作性。

在此配置文件中,控制器将检测到的用户操作转换为 A/V 控制信号,然后将其传输到远程蓝牙设备。传统红外遥控器可用的功能可以在此配置文件中实现。除此之外,配置文件使用蓝牙特定的扩展来支持与要在蓝牙设备之间传输的内容相关的元数据的传输。提供浏览功能以允许遥控器导航媒体并控制远程目标设备上的特定媒体播放器。本配置文件中描述的遥控器专为 A/V 控制而设计。其他使用蓝牙无线技术的远程控制解决方案可应用于包括 A/V 设备在内的通用蓝牙设备。

请注意,AVRCP不处理音频/视频流。支持此配置文件的设备还可以通过实现高级音频分发配置文件和/或视频分发配置文件来支持音频/视频流。

如图所示,AVRCP依赖于GAP,对于封面艺术(Cover Art)功能,AVRCP使用基本成像配置文件(Basic Imaging Profile)中的通用成像图像提取(Generic Imaging Image Pull)功能。

android avd开启不了 android avrcp_android avd开启不了

1.1 Profile overview

Baseband、LMP 和 L2CAP 是 OSI 第 1 层和第 2 层蓝牙协议。 AVCTP 和 BIP 定义了为控制 A/V 设备而要交换的程序和消息。 SDP 是蓝牙服务发现协议。 OBEX 是 IrOBEX 的蓝牙适配,它是 BIP 的底层传输协议,BIP 是提供功能以交换与媒体相关的图像的实体。 BIP 在图 2.1 的括号中描述,以表明一些 BIP 功能被重用或重新定义用于 AVRCP,但 BIP 不用作独立配置文件。 AV/C 是负责基于 AV/C 命令的设备控制信令的实体。应用程序是 AVRCP 实体,交换本规范中定义的控制浏览命令。

android avd开启不了 android avrcp_配置文件_02

1.1.1 Configuration and roles

为符合AVRCP的设备定义了以下角色:

控制器(Controller,CT), 是通过向目标发送命令帧启动事务的设备。 CT 的示例是个人计算机、PDA、移动电话、遥控器或 AV 设备(例如车载系统、耳机、播放器/录音机、计时器、调谐器、监视器等)。

目标(Target,TG),是接收命令帧并相应生成响应帧的设备。 TG 的示例是音频播放器/记录器、视频播放器/记录器、电视、调谐器、放大器或耳机。

android avd开启不了 android avrcp_配置文件_03

此配置文件通过将 A/V 功能分为四类来确保互操作性:

  • player/recorder
  • monitor/amplifier
  • tuner
  • menu

1.1.2 Scenarios

1.1.2.1 Remote control from separate controller

在下图 2.3 所示的配置中,遥控器(Remote Controller)是事务的 CT。来自遥控器的命令帧发送到便携式光盘播放器(Portable Disc Player)作为 TG。音频流从便携式光盘播放器发送到耳机。耳机只接收音频流,不参与遥控器和便携式光盘播放器之间的事务。当用户希望控制便携式光盘播放器时,由遥控器触发事务。

android avd开启不了 android avrcp_字段_04

1.1.2.2 Remote control from car audio system

下图配置中,车载音响系统为CT,手机为TG。用户通过汽车界面浏览手机上的可用媒体。然后,用户可以执行触发从电话检索媒体元数据的动作,并执行其他控制操作。

android avd开启不了 android avrcp_android avd开启不了_05

1.1.2.3 Remote control and audio stream between two devices

在下图 2.5 所示的配置中,耳机是CT,便携式光盘播放器是 TG。当用户希望控制便携式光盘播放器时,由耳机附带的遥控器触发事务。

android avd开启不了 android avrcp_android avd开启不了_06

1.1.2.4 Mutual remote control within a piconet

在下图 2.6 所示的配置中,耳机和便携式光盘播放器都可以作为遥控器工作。例如,便携式光盘播放器如果控制成为 TG 的耳机的音量,就变成了 CT。另一方面,当耳机向便携式光盘发送开始播放或停止播放的命令作为 TG 时,耳机成为 CT。

android avd开启不了 android avrcp_android avd开启不了_07

1.1.2.5 Remote controller with LCD 

在下图 2.7 所示的配置中,带有 LCD 遥控器的耳机是 CT。它通过向媒体播放器(即 TG)发送命令来接收媒体元数据和浏览信息。遥控器可以有一个 LCD 来将接收到的数据呈现给用户。

android avd开启不了 android avrcp_字段_08

1.1.2.6 Car Kit with full display

在下图 2.8 所示的配置中,全屏显示的 carkit 是 CT 类别 1。它通过向 TG 移动设备发送命令来接收浏览信息。 CT 使用封面艺术功能来检索与浏览或播放的媒体项目相关的封面艺术。

android avd开启不了 android avrcp_控制通道_09

1.2 Control interoperability requirements

在此配置文件中,协议信号通过在通信设备中启动过程和通过交换消息来交换。信令图使用图 1.2 的约定:信令约定。

android avd开启不了 android avrcp_android avd开启不了_10

1.2.1 Procedures

1.2.1.1 AVCTP connection establishment/release

用于 AVCTP 控制的 L2CAP 连接建立可以由 CT 或 TG 发起。如果两个设备都支持浏览通道,则可以在控制通道之后建立浏览通道。控制通道应始终在浏览通道之前建立。

浏览通道应配置为使用 L2CAP 增强重传模式。

在 AVCTP 实体之间只能建立一个用于控制的 L2CAP 连接和一个用于浏览的 L2CAP 连接(如果两个设备都支持)。如果连接已经存在,CT/TG 不应发起连接请求。

android avd开启不了 android avrcp_android avd开启不了_11

android avd开启不了 android avrcp_控制通道_12

android avd开启不了 android avrcp_android avd开启不了_13

 AVCTP 的 L2CAP 连接释放可由 CT 或 TG 发起。如果存在浏览通道,则应在控制通道之前释放。如果不再需要浏览通道,可以在不释放控制通道的情况下释放浏览通道。如果浏览通道已经被释放,只要控制通道仍然存在,可以在需要时重新建立。 

android avd开启不了 android avrcp_控制通道_14

1.2.1.2 AV/C command

当用户产生内部或事件时,如果此时尚未建立连接,CT 将发起连接建立。一旦建立连接,它就可以发送一个 AV/C 命令。

android avd开启不了 android avrcp_android avd开启不了_15

下面是可能的命令:

android avd开启不了 android avrcp_android avd开启不了_16

1.2.2 Protocol concepts

(1)AV/C commands

AVRCP 中存在两组 AV/C 命令。 PASSTHROUGH 命令集、UNIT 和 SUBUNIT INFO 命令在 AV/C 规范中定义。还存在一组命令,以下称为 AVRCP 特定 AV/C 命令,定义为蓝牙 SIG 供应商相关扩展。这些命令使用 AV/C Vendor Dependent Opcode 和 Vendor Unique PASSTHROUGH 操作 id。它们通过 AVCTP 控制通道发送。

(2)Browsing commands

浏览命令集使用 AVCTP 浏览通道。直接使用 AVCTP,不经过 AV/C 层。 AVCTP 分片不应应用于浏览通道。这意味着 AVRCP 实体可以从 AVCTP 浏览通道 MTU 确定对等实体可以接受多少数据。然后,发送者可以采取必要的措施来限制发送的数据量,同时保留用户体验。例如,当向 TG 发送搜索命令时,CT 应限制用户可以输入的搜索字符串长度,以使搜索命令适合 L2CAP MTU。

(3)Cover Art commands

AVRCP 中使用的封面艺术命令被 BIP 重用或覆盖。 BIP 通用成像图像拉取功能用于提供允许 CT(作为成像启动器)检索与 TG(作为成像响应器)上的媒体相关联的图像的功能。这些命令通过 OBEX 发送。

2. AVCTP

AVCTP(Audio/Video Control Transport Protocol )描述了用于交换消息以控制音频和/或视频设备的传输机制。

AVCTP 用于传输为通过点对点连接控制远程 A/V 设备而交换的命令/响应消息。 AVCTP 没有描述用于控制 A/V 设备的命令和/或响应帧的格式和编码;命令和/或响应格式和编码规则由相关的控制配置文件指定。

android avd开启不了 android avrcp_配置文件_17

 此协议可以与其他 A/V 协议共存于同一设备中,并且应该能够共享一个公共 ACL 链接。

android avd开启不了 android avrcp_android_18

 控制器通过向作为该事务目标的远程设备发送命令来启动事务。一个完整的 AVCTP 事务由一个包含发给目标的命令的消息和零个或多个包含目标返回给控制器的响应的消息组成。

图 3.1 显示了一个基本事务的示例,其中目标在收到命令消息后执行内部操作,然后返回响应。

android avd开启不了 android avrcp_android_19

2.1 AVCTP Message Format

AVCTP 消息在一个或多个 AVCTP 数据包中传输。

包头格式取决于消息是否分片。

2.1.1 Non-Fragmented AVCTP Message

下表描述了封装在单个 L2CAP 数据包中的 AVCTP 消息的通用格式。

android avd开启不了 android avrcp_字段_20

 Transaction label:由应用程序提供;

Packet_type :设置为零 (00) 以指示命令/响应消息在单个 L2CAP 数据包中传输;

C/R:指示消息是传送命令帧 (0) 还是响应帧 (1),由应用程序提供;

IPID:在响应消息中设置,以指示在同一事务的命令消息中接收到无效的配置文件标识符;否则该位设置为零。在命令消息中,该位设置为零。

Profile Identifier (PID):指示命令/响应帧是根据所识别的配置文件定义的规则进行编码的。该值应与蓝牙分配编号文档中为此配置文件定义的服务类别的 16 位 UUID 相同。

图 6.1 描述了在单个 L2CAP 数据包中传输符合 MTU 要求的命令/响应消息的封装过程。

android avd开启不了 android avrcp_字段_21

2.1.2 Fragmented AVCTP Message

以下部分描述了用于传输无法放入单个 L2CAP 数据包的 AVCTP 命令/响应消息的 AVCTP 数据包格式。

在发送 AVCTP 实体中应发生分段,以将过大的消息信息划分为小于或等于 L2CAP 协商的 MTU 限制的 AVCTP 数据包,然后再将它们发送到 L2CAP 层。

分段消息在序列的每个数据包中由特定的数据包类型代码标记。分片消息包中使用了三种可能的类型:

  • start packet——序列中的第一个packet;
  • continue packet——介于start packet与end packet之间的packet;
  • end packet——序列中的最后一个packet;

在接收端,AVCTP 实体通过识别属于同一事务的数据包并检查每个数据包的类型来重组完整的消息。

L2CAP 通道保证单个 AVCTP 数据包的完整性按正确顺序传送而不是整个消息的完整性

由于通道可能不可靠,因此序列中的一些 AVCTP 数据包可能会丢失。

start packet提供了重组完整消息所需的 AVCTP 数据包数量,因此 AVCTP 接收实体应实施一致性检查并丢弃任何不完整的消息。

无论它是否在不同的 PID 或事务标签上.不允许在同一个 AVCTP 连接上交错不同的数据包片段。

分片消息的接收方可以基于起始包长度和第一包提供的包信息元素的数量来分配用于消息重组的资源:

  • 封装分段 AVCTP 消息的start和continue数据包的 L2CAP 数据包的有效负载应具有相同的长度。
  • 封装 AVCTP 消息的end包的 L2CAP 包的有效负载不应大于start或continue包的长度;

下面3个表描述了Start、Continue和End包的格式:

android avd开启不了 android avrcp_配置文件_22

android avd开启不了 android avrcp_控制通道_23

android avd开启不了 android avrcp_控制通道_24

Transaction label:由应用程序提供,并由序列的每个数据包中的消息发送者复制。它在接收端用于识别属于同一消息的数据包。

Packet_ type:将 AVCTP 数据包限定为开始 (01)、继续 (10) 或结束 (11) 数据包。

C/R:指示消息是传送命令帧 (0) 还是响应帧 (1)。该字段由应用程序提供,存在于消息的每个数据包中。

 IPID:在响应消息中设置,以指示在同一事务的命令消息中收到无效的 Profile Identifier;否则该位设置为零。在命令消息中,该位设置为零。该字段仅存在于消息的开始数据包中。

Number of AVCTP Packets:存在于每个起始数据包中,以指示属于同一消息的 AVCTP 数据包的总数。由于起始包也算在内,所以这个值总是大于1。

RFA:取代了Continue或End数据包中的 IPID 字段。它是为将来添加而保留的,应设置为0。

Profile Identifier (PID) :表示消息信息部分是根据所标识的配置文件定义的规则进行编码的。该值应与Assigned Numbers文档中为此配置文件定义的服务类别的 16 位 UUID 相同。该字段仅存在于消息的开始数据包中。

android avd开启不了 android avrcp_android_25

3. 日志分析

以一台手机连接蓝牙耳机听歌为例,通过蓝牙耳机控制音乐的开始、暂停。日志链接:AndroidBluetoothA2DP&&AVRCPBugreport-Android文档类资源-CSDN下载

3.1 SDP流程

连接耳机后,首先是通过SDP发现Service的过程。

android avd开启不了 android avrcp_android avd开启不了_26

 Frame#844由Slave发起,查询是否支持AVRCP的TG以及AVCTP。

android avd开启不了 android avrcp_配置文件_27

 Frame#845是Master发给Slave的response,PSM0x0017表示AVCTP,AVCTP版本为1.4,AVRCP版本为1.6,支持Catagory:player/recorder。

android avd开启不了 android avrcp_控制通道_28

android avd开启不了 android avrcp_android_29

Frame#958是Master发给Slave的对于AVRCP的查询。

android avd开启不了 android avrcp_android avd开启不了_30

 Frame#971是Slave发给Master的response,表示支持AVRCP,并且支持AVRCP的CT。支持AVCTP,版本为1.4。支持AVRCP,版本为1.6,且支持Catagory:Player/Recorder和Monitor/Amplifier。支持AVRCP的TG。

这边我们可以看到最后面的PSM处被截断了,详细的内容可以参考BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 3, Part B 4.3 PARTIAL RESPONSES AND CONTINUATION STATE。

android avd开启不了 android avrcp_配置文件_31

在客户端收到部分响应和伴随的Continuation状态参数后,它可以重新发出原始请求(使用新的事务 ID),并将Continuation状态包含在新请求中,向服务器指示原始响应的剩余部分是想要的。

Frame#974重新发出原始请求,使用新的事务ID 0x0001,并包含了Continuation状态。

android avd开启不了 android avrcp_android avd开启不了_32

 Frame#988,提示了数据是重构的,下图中之前传输过的数据被我折叠了,仅展示之前被分割开的。最后的Bytes for continuation length: 0表示完整的一帧。

android avd开启不了 android avrcp_字段_33

3.2 AVRCP流程

下图贴了AVRCP的部分流程,逐帧去分析。

android avd开启不了 android avrcp_字段_34

在上面1.2.1.2部分,我们知道了AV/C Comand的流程了,这里我们具体看发出来的frame。

AV/C Command可以阅读

3.2.1 AV/C Command & response

根据文档AV/C Digital Interface Command Set General Specification Version 4.1的第7章,这部分讲了AV/C Frame的详细信息。

在AVRCP展示的帧中,Ctype可以看到三种类型(已标红),其实有下面几种:

  • CONTROL
  • STATUS
  • SPECIFIC INQUIRY
  • NOTIFY
  • GENERAL INQUIRY

(1)CONTROL commands

控制命令由控制器发送到另一个设备,即目标,以指示目标执行操作。目标中的 AV/C 单元或子单元可能是命令的接收者,由命令帧中的 subunit_type 和 subunit_ID 字段确定。其余字段 opcode 和operand[n] 指定命令。下图显示了 CONROL 命令的命令和响应事务。

android avd开启不了 android avrcp_配置文件_35

NOT IMPLEMENTED:目标没有执行由 ck 列中标记的opcode 和operand[n] 指定的 CONTROL 命令,或者该命令的地址是目标未执行的子单元。目标的状态不会被修改。

INTERIM:如果执行了由 ck 列中标记的 opcode 和operand[n] 指定的 CONTROL 命令,但目标无法在 100 毫秒内响应 ACCEPTED 或 REJECTED,则它应返回指示 INTERIM 的响应帧。在 INTERIM 的第一个响应之后,目标不应为此命令发送任何额外的 INTERIM 响应。除非随后的总线复位导致 AV/C 事务被中止,否则目标最终将返回一个响应代码为 ACCEPTED 或 REJECTED 的响应帧。

其他的response都很好理解了。

 (2)STATUS commands

STATUS 命令由控制器发送到设备,以请求设备在命令上下文中的当前状态。 STATUS 命令可以发送到 AV/C 单元或子单元。 STATUS 命令不应改变目标的状态。

android avd开启不了 android avrcp_控制通道_36

 IN TRANSITION:目标执行由 ck 列中标记的opcode and operand[n]指定的 STATUS 命令,但目标状态处于转换中,可能是因为已确认的命令或手动操作。在未指定的未来时间,后续的STATUS命令可能会导致返回 STABLE 状态。注意,IN TRANSITION 响应帧应包括目标正在转换到的预期状态

STABLE:目标执行由 ck 列中标记的 opcode 和operand[n] 指定的状态命令,请求的信息在 AV/C 响应帧中的 opcode 和operand[n] 值中报告。

(3)NOTIFY commands

希望接收设备状态未来变化通知的控制器可以使用 NOTIFY 命令。对 NOTIFY 命令的响应应指示目标的当前状态,然后在未来某个不确定的时间指示目标的更改状态。下图展示了 NOTIFY 命令的命令和响应机制

android avd开启不了 android avrcp_字段_37

如下图为AV/C command frame格式:

android avd开启不了 android avrcp_android_38

如下图为 AV/C response frame格式:

android avd开启不了 android avrcp_字段_39

Frame#918为CT发出的AV/C Command:

android avd开启不了 android avrcp_字段_40

Command Type: STATUS用于查询TG的状态,对应的Code为0x01。

android avd开启不了 android avrcp_android avd开启不了_41

android avd开启不了 android avrcp_android_42

Subunit Type和Subunit ID字段的描述如下面三表:

android avd开启不了 android avrcp_android_43

Subunit Type占5位,Subunit ID占3位,合起来二进制表达就是01001000,即0x48。

android avd开启不了 android avrcp_配置文件_44

Operation code: VENDOR-DEPENDENT,其值就为0x00。

android avd开启不了 android avrcp_配置文件_45

android avd开启不了 android avrcp_控制通道_46

  对于VENDOR-DEPENDENT command,其帧格式如下:

android avd开启不了 android avrcp_android_47

 对于AVRCP,company_ID被写死为[0x001958],详见AVRCP 4.3.1。

android avd开启不了 android avrcp_控制通道_48

  

AVRCP 中使用了两种 PDU 格式。在控制通道上,所有命令和响应都是 AVRCP 6.3.1 节中定义的 AV/C 通用 PDU 或 AVRCP 特定 AV/C PDU。在浏览通道上,所有命令和响应都是AVRCP 6.3.2 节中定义的 AVRCP 特定浏览 PDU。

供应商相关命令/响应帧中的供应商相关数据字段应为 AVRCP 特定 AV/C PDU。每个 PDU 由一个 PDU 标识符、所有参数的长度(不包括参数长度字段)和 PDU 特定的参数组成。

android avd开启不了 android avrcp_android avd开启不了_49

AVRCP表 4.5 包含 AVRCP 特定命令。命令分为两组 - 命令类型为 AV/C CType的AV/C VENDOR DEPENDENT 命令和浏览命令。 AV/C 命令应在 AVCTP 控制信道上发送。浏览命令应在 AVCTP 浏览通道上发送

android avd开启不了 android avrcp_字段_50

android avd开启不了 android avrcp_android avd开启不了_51

Packet type: Non-Fragmented表示消息不分片,即0x00。若分片,对应的Start、Continue和End Packet的packet type分别为0x01,0x10,0x11。

android avd开启不了 android avrcp_控制通道_52

Capability ID允许的值如下:

android avd开启不了 android avrcp_android avd开启不了_53

android avd开启不了 android avrcp_配置文件_54

Frame#921为AV/C Response。如之前所述AV/C Command应在控制通道上发送,相应的response也应在控制通道上回复。回复的EventID是针对Command中的Capability ID进行的回复。在AVRCP 附录H中也有相应的说明。

android avd开启不了 android avrcp_控制通道_55

android avd开启不了 android avrcp_android_56

android avd开启不了 android avrcp_字段_57

CT知道TG的Events Supported后,就发出Notify Commands,这样TG相应的状态变化时会通知CT。

Frame#926 927 1597是一轮Notify Command的过程。

android avd开启不了 android avrcp_配置文件_58

Frame#926是CT(本轮CT为Slave,即耳机)发出Notify Command,Transaction Label: 2用于标识本次的事务,区分后续的CHANGED Response。Operands中的Event ID字段,表示本次注册的是EVENT_TRACK _CHANGED的通知。

android avd开启不了 android avrcp_android avd开启不了_59

 Frame#927是TG返回的一个INTERIM response,表示未来某个时刻会再给你返回一个REJECT或者CHANGED response。

android avd开启不了 android avrcp_字段_60

Frame#1597是TG发给CT的CHANGED response,表示本轮跟踪的状态已经发生改变。

android avd开启不了 android avrcp_配置文件_61

另一个跟踪的事件EVENT PLAYBACK STATUS CHANGED,TG在Frame#1598发出了CHANGED response,Current Play Status切换到PLAYING。

android avd开启不了 android avrcp_android avd开启不了_62

上面两个CHANGED response发出来是因为我在手机的音乐播放器上按了播放音乐的开始按键。

CT(本轮CT为Slave,即耳机)知道TG当前状态处于PLAYING就发出了新的STATUS Command——Get Element Attribute,获取歌曲的信息。

android avd开启不了 android avrcp_字段_63

TG在List of Attributes and their values中回复了详细信息。

android avd开启不了 android avrcp_字段_64

 3.2.2 PASS THROUGH command

android avd开启不了 android avrcp_字段_65

PASS THROUGH 命令用于将正确的用户操作传达给目标(对用户透明)。无论目标的状态如何,都可以传输 PASS THROUGH 命令。 PASS THROUGH 控制命令的格式如下,注意msb与lsb之间是8位大小。

android avd开启不了 android avrcp_字段_66

state_flag 字段表示“按下或释放按钮”的用户操作,作为遥控器操作。当按下按钮时,该字段的值应为0;释放时,该值应为 1。当用户按下然后释放按钮时,控制器通常会发送此命令两次,即按下和释放。具有按下值的命令在目标发送回命令响应后两秒内有效。当希望命令保持有效时,控制器应继续在 operation_id 字段中发送具有相同操作 id 值的按下值。如果目标在两秒内没有收到按下的命令,或者目标收到带有另一个操作id的按下命令,则目标认为释放的命令已发送但未接收。在这些情况下,当目标在超时或接收到新按下的命令后收到这个释放的命令时,目标将忽略释放的命令。

Frame#1958为CT(本轮CT为Slave,即耳机)发出按下按钮命令,State flag: Button Pushed值为0,Operation id为Play,对应0x44。

android avd开启不了 android avrcp_android avd开启不了_67

android avd开启不了 android avrcp_字段_68

android avd开启不了 android avrcp_配置文件_69

 另一个Frame#1960为CT(本轮CT为Slave,即耳机)发出释放按钮命令,State flag: Button Released,值为1,和Operation id合起来就是0xC4。

android avd开启不了 android avrcp_android avd开启不了_70

其他的,例如Browse Command,本文没有去抓取相关日志,感兴趣的可以自己去抓取研究。