音视频控制传输协议(AVCTP)
本笔记为作者在学习蓝牙Host协议栈的一些心得体会,如有不对的地方,请包涵与谅解!
------------by wsoz
音视频控制传输协议(AVCTP)
AVCTP 是蓝牙音视频 控制里的"传输/封装层协议",负责规定控制 command 和 response 的总体格式与交换机制;具体"控制内容"不是它自己定义的,而是由它承载的上层协议实现,例如 AVRCP。
协议特点
- AVCTP采用面向连接的点对点的L2CAP通道
- AVCTP支持连接两端同时具备controller和target功能(双向控制)
- 两个设备之间可以存在多个AVCTP连接,每个AVCTP连接都对应一条独立的L2CAP通道;在同一条ACL链路上,同一个PSM只允许存在一个AVCTP连接
- 每个
AVCTP packet都必须封装在单个L2CAP packet中传输 Transaction label的作用域仅限于各自的L2CAP channel,不同L2CAP channel上即使transaction label相同,也分别属于不同的message,不能跨channel关联
总结 :AVCTP 是一个建立在面向连接 的 L2CAP 通道之上的、支持双端对称控制 的音视频控制传输协议,并且以单通道为边界来承载和区分各自独立的控制消息。
架构如下:

AVCTP的PSM
在 L2CAP 中,PSM(Protocol/Service Multiplexer) 可以理解为上层服务的"入口号",建立 L2CAP channel 时需要先知道对方监听的是哪个 PSM。
对于 AVCTP 来说,常见会涉及两个 PSM:
| PSM | 名称 | 用途 |
|---|---|---|
0x0017 |
AVCTP |
控制通道,用于传输普通的音视频控制命令/响应 |
0x001B |
AVCTP_Browsing |
浏览通道,用于传输媒体浏览相关命令 |
这两个 PSM 的分工可以这样理解:
0x0017是基础的控制入口,常见的播放、暂停、上一曲、下一曲这类遥控控制,通常都走这条通道0x001B是扩展的浏览入口,主要给AVRCP Browsing使用,比如浏览媒体播放器、文件夹、歌曲列表等Browsing channel不是所有场景都必须建立,只有双方都支持浏览功能时才需要- 一般要先建立
0x0017对应的控制通道,再根据功能需要决定是否建立0x001B对应的浏览通道
也就是说,AVCTP 之所以会看到两个 PSM,不是因为一个连接内部必须同时混用两个入口,而是因为它把普通控制 和媒体浏览拆成了两类独立业务通道。
注意:
- 这里说的
0x0017和0x001B是 L2CAP 的 PSM 值 - 其中
0x0017同时也出现在协议标识里表示AVCTP - 但
0x001B在这里表示的是AVCTP_Browsing这个 PSM ,不要直接和别的编号空间里的UUID含义混为一谈
AVCTP封包格式
AVCTP message 在传输时会被封装成一个或多个 AVCTP packet。每个 AVCTP packet 又必须完整放进一个 L2CAP packet 中,因此 AVCTP 自己不带长度字段 ,而是依赖 L2CAP 的长度来界定一个包的边界。
从封包角度看,AVCTP 主要有两种情况:
- 未分包消息 :整个
message可以直接放进一个L2CAP packet - 分包消息 :整个
message太大,必须拆成多个AVCTP packet
未分包的AVCTP格式
当一条 AVCTP message 的长度没有超过当前 L2CAP MTU 时,就可以直接按单包格式发送:
text
Octet 0 : | Transaction Label(4bit) | Packet Type(2bit=00) | C/R(1bit) | IPID(1bit) |
Octet 1 : | PID[15:8] |
Octet 2 : | PID[7:0] |
Octet 3~ : AVCTP Message Information
也就是说,未分包格式的固定头部是 3 字节:
- 第1字节:
Transaction Label + Packet Type + C/R + IPID - 第2~3字节:
PID - 后面才是真正的上层控制数据
分包的AVCTP格式
当一条 AVCTP message 太长,无法一次装进单个 L2CAP packet 时,AVCTP 会把它拆成多个 AVCTP packet。这时 Packet Type 不再是 00,而会分为三种:
01:Start Packet10:Continue Packet11:End Packet
Start Packet
text
Octet 0 : | Transaction Label(4bit) | Packet Type(2bit=01) | C/R(1bit) | IPID(1bit) |
Octet 1 : | Number of AVCTP Packets |
Octet 2 : | PID[15:8] |
Octet 3 : | PID[7:0] |
Octet 4~ : AVCTP Message Information 的第一段
Start Packet 比单包格式多了一个 Number of AVCTP Packets 字段,用来告诉接收端:这条完整消息总共会分成几个 AVCTP packet。
Continue Packet
text
Octet 0 : | Transaction Label(4bit) | Packet Type(2bit=10) | C/R(1bit) | RFA(1bit) |
Octet 1~ : AVCTP Message Information 的中间部分
End Packet
text
Octet 0 : | Transaction Label(4bit) | Packet Type(2bit=11) | C/R(1bit) | RFA(1bit) |
Octet 1~ : AVCTP Message Information 的最后一段
可以看到:
Start Packet头部固定是 4 字节Continue Packet和End Packet头部固定都只有 1 字节- 在分包场景 下,
PID和IPID只出现在Start Packet
字段的含义
| 字段 | 含义 |
|---|---|
Transaction Label |
事务标签,占4bit,用来标识一条命令/响应事务;同一条被分包的消息中,各个分片都要带相同的标签 |
Packet Type |
包类型,占2bit,00=单包,01=起始包,10=中间包,11=结束包 |
C/R |
Command/Response 标志,占1bit,0 表示命令,1 表示响应 |
IPID |
Invalid Profile Identifier 标志,占1bit,仅在单包或起始包中有意义;在响应里置1表示收到的 PID 无效 |
RFA |
Reserved For Future Additions,保留位,在 Continue/End Packet 中替代 IPID,当前应置0 |
PID |
Profile Identifier,2字节,表示当前这条消息属于哪个上层Profile |
Number of AVCTP Packets |
仅出现在 Start Packet 中,表示这条完整消息一共包含多少个 AVCTP packet |
Message Information |
真正的上层命令/响应内容,具体格式由 PID 所指向的上层Profile定义,例如 AVRCP |
这里尤其要注意:AVCTP 只定义了外层封装头 ,至于 Message Information 里面怎么编码,不是 AVCTP 自己规定的,而是交给对应的上层协议去定义。
如何对数据段进行分析
按下面顺序拆:
- 先看
Packet Type,判断这是单包还是分包中的某一片 - 再看
C/R,判断它是命令还是响应 - 再看
Transaction Label,判断它属于哪一笔事务 - 再看
PID,判断这条AVCTP承载的是哪个上层Profile - 最后再去解析
Message Information里的真正业务内容
注意:这里先说的是 AVCTP 的通用封包格式 ,在 AVRCP Browsing 场景时,虽然底层仍然使用 AVCTP 相关通道,但浏览命令本身还有自己的PDU格式约束,并且浏览通道不使用 AVCTP 分包。
我的理解:AVCTP 这一层的主要作用,就是在 L2CAP 之上给上层控制协议提供一个统一的传输封装和交换机制,就是提供一个数据承载的框架。