【AVRCP】规范精讲[41]:AV/C指令帧解析,蓝牙遥控交互的底层语言
很多做蓝牙音频开发的工程师都会有这样的困惑:为什么车机发一个播放指令,手机有时能响应有时没反应?为什么解析出来的指令帧总是对不上协议描述?为什么不同品牌设备交互时会出现指令不兼容?
这些问题的根源,几乎都出在AV/C指令帧这一层。
AV/C是AVRCP真正的指令承载层,相当于蓝牙音频控制的底层语言。我们平时用到的播放、暂停、上一曲、下一曲、音量调节、获取歌曲信息等所有操作,最终都会被封装成标准的AV/C指令帧在蓝牙链路上传输。可以说,不懂AV/C帧结构,就不可能真正吃透AVRCP。
目录
五、AV/C与AVRCP的关系:Vendor Dependent 扩展
AV/C全称是Audio/Video Digital Interface Command Set,最早由1394TA定义,原本用于高速音视频设备互联,后来被蓝牙SIG引入,成为AVRCP的指令层标准。它定义了一套统一的指令帧格式、交互规则、状态机与响应机制,让不同厂商的控制器与目标设备可以无缝对话。
本文彻底吃透AV/C指令帧与响应帧的完整结构、字段含义、交互规则,结合代码示例与实战场景,把这个蓝牙音频开发里最基础也最重要的知识点讲透。
一、AV/C在AVRCP体系中的位置
在正式讲解帧结构之前,我们先理清AV/C在整个蓝牙音频协议栈里的位置,建立全局认知。
AVRCP整体分为三层:
-
应用层:对外提供播放控制、音量、元数据获取、媒体浏览等功能
-
指令层:即AV/C,负责把应用操作转换成标准指令帧
-
传输层:即AVCTP,负责把AV/C帧通过蓝牙L2CAP通道可靠传输
简单比喻:
AVRCP应用层是用户说的话(播放、暂停)
AV/C是统一的文字编码
AVCTP是快递通道
所有控制指令必须先编码成AV/C帧,才能在蓝牙上传输。没有AV/C,不同厂商的设备就像说不同语言的人,无法互相理解。
二、AV/C指令帧完整结构
AV/C的交互由两种帧构成:
Command Frame:控制器CT发送给目标TG
Response Frame:目标TG回复给控制器CT
两者结构高度相似,只有少数字段不同。

2.1 指令帧总结构(单字节分段讲解)
一个标准的AV/C帧固定以8字节为基础长度,扩展指令可携带后续数据。
|
字节 |
位段分布(7~0) |
字段名称 |
核心作用 |
|
0 |
CType(4bit) + Reserved(4bit) |
CType |
指令类型/操作类型 |
|
1 |
Subunit_Type(5bit) + Subunit_ID(3bit) |
子单元信息 |
指明操作哪个功能模块 |
|
2 |
Opcode(8bit) |
操作码 |
具体指令动作 |
|
3~7 |
Operand[0~4] |
操作数 |
指令参数 |
2.2 逐字段深度精讲
1)字节0:CType 指令类型
CType占高4位,低4位保留为0。它决定这一帧是做什么操作。
协议原文:
CType defines the action of the command frame.
常见取值:
-
0x0:CONTROL 执行控制(播放、暂停、设置)
-
0x1:STATUS 查询状态(获取信息)
-
0x3:NOTIFY 注册通知(监听变化)
在响应帧里,CType字段变成Response字段,含义完全不同:
-
0x8:ACCEPTED 已接受
-
0x9:STABLE 状态稳定返回
-
0xA:REJECTED 拒绝执行
-
0xC:INTERIM 临时回复
-
0xD:CHANGED 状态变更推送
这是开发中最容易混淆的地方:同一位置,指令帧是CType,响应帧是Response。
2)字节1:Subunit_Type & Subunit_ID
这一字段把设备内部拆分成多个子功能单元,类似电脑里的不同硬件设备。
-
Subunit_Type(高5位):子单元类型
-
Subunit_ID(低3位):子单元编号,多实例时区分
AVRCP里99%的指令都走Panel子单元。
-
Panel子单元类型值:0x09
-
Subunit_ID固定为0x00
所以在AVRCP里,字节1几乎总是0x90。
其他常见类型(了解即可):
-
0x01:Monitor 显示器
-
0x02:Audio 音频
-
0x03:Tape 磁带
-
0x04:Disc 光盘
-
0x05:Tuner 调谐器
3)字节2:Opcode 操作码
Opcode是指令的动作编号,决定具体要做什么。
AVRCP高频Opcode:
-
0x30:UNIT INFO 获取设备信息
-
0x31:SUBUNIT INFO 获取子单元信息
-
0x7C:PASS THROUGH 穿透指令(播放/暂停/上下曲)
-
0x00:VENDOR DEPENDENT 厂商自定义(AVRCP扩展指令全用这个)
4)字节3~7:Operand 操作数
跟随Opcode携带参数,比如按键状态、音量值、厂商ID、PDU数据等。
三、AV/C响应帧结构
响应帧结构和指令帧几乎一致,只有字节0的含义变了。
3.1 响应帧字节0:Response 字段
|
值 |
含义 |
典型场景 |
|
0x8 |
NOT IMPLEMENTED |
不支持该指令 |
|
0x9 |
ACCEPTED |
已接受执行 |
|
0xA |
REJECTED |
拒绝执行 |
|
0xC |
STABLE |
查询完成返回 |
|
0xD |
INTERIM |
临时回复 |
|
0xE |
CHANGED |
状态变化通知 |
3.2 图示:AV/C Response Frame

四、AV/C核心交互规则(决定设备是否能正常通信)
只懂帧结构不够,必须遵守AV/C事务规则,否则指令会丢失、乱序、无响应。
规则1:一对一事务模型
一个Command Frame必须对应一个Response Frame,不能一对多、多对一。
规则2:事务标签匹配
AVCTP会给每个AV/C帧分配Transaction Label,响应必须带回相同Label。
规则3:NOTIFY 特殊机制
NOTIFY类型指令会触发两步回复:
-
先回复 INTERIM(当前状态)
-
状态变化时再回复 CHANGED
这是歌曲切换、音量变化、播放状态同步的基础。
规则4:控制时序约束
目标设备TG必须在规定时间内回复:
-
CONTROL 类:200ms 内回复
-
STATUS / NOTIFY 类:1000ms 内回复
超时未回复,控制器会判定指令丢失。
五、AV/C与AVRCP的关系:Vendor Dependent 扩展
AV/C本身只定义基础指令,AVRCP的高级功能(元数据、音量、浏览)都是通过Vendor Dependent扩展实现的。
机制:
-
Opcode固定填 0x00
-
Operand[0~2] 填蓝牙SIG分配的厂商ID 00 19 58
-
后面跟着AVRCP自定义的PDU数据
这就是AVRCP能在AV/C基础上实现复杂功能的秘密。
六、实战代码示例:构造播放暂停指令帧
下面给出一段可直接使用的C语言示例,构造一个PASS THROUGH播放指令帧。
// AV/C PASS THROUGH 播放指令帧
uint8_t avc_play_cmd[] = {
0x00, // Byte0: CType = CONTROL(0x0<<4)
0x90, // Byte1: Panel Subunit(0x9<<4) | ID 0
0x7C, // Byte2: Opcode = PASS THROUGH
0x44, // Byte3: Operation ID = PLAY
0x00, // Byte4: State = Press
0x00, // Byte5: 保留
0x00, // Byte6: 保留
0x00 // Byte7: 保留
};
// 释放按钮帧(必须发送,否则设备会一直认为按住)
uint8_t avc_play_release_cmd[] = {
0x00,
0x90,
0x7C,
0x44,
0x80, // State = Release
0x00,
0x00,
0x00
};
七、开发高频踩坑总结
-
混淆CType与Response:指令帧用CONTROL,响应帧用ACCEPTED/STABLE
-
少发Release帧:按键必须发送Press+Release,否则设备状态卡死
-
Subunit填错:AVRCP必须用0x90,填其他类型会直接不响应
-
Vendor ID错误:扩展指令必须带00 19 58,否则被当作其他厂商指令丢弃
-
超时不处理:NOTIFY指令超时要重注册,否则收不到更新
八、测验
题目:请说明AV/C指令帧中CType与Response字段的区别,并列出三种常见Response值及其含义(车载蓝牙开发-吉利2024)
答案
CType出现在指令帧,用于表示操作类型如CONTROL STATUS NOTIFY。Response出现在响应帧,用于表示指令处理结果。常见值包括ACCEPTED表示指令已接受执行,REJECTED表示指令被拒绝,STABLE表示查询状态完成返回。
题目:为什么AVRCP的高级指令要用Vendor Dependent方式封装?其固定Opcode与厂商ID分别是什么
答案
因为AV/C标准指令无法满足元数据获取音量控制媒体浏览等扩展需求,因此用厂商自定义方式扩展。固定Opcode为0x00,厂商ID为蓝牙SIG分配的00 19 58。
题目:AV/C交互中,NOTIFY类型指令的响应流程是什么?和普通CONTROL指令有何不同(TWS耳机开发-三星2023)
答案
NOTIFY指令会先回复INTERIM临时响应携带当前状态,当监听的状态发生变化时再回复CHANGED响应。普通CONTROL指令只回复一次ACCEPTED或REJECTED,不会持续监听状态变化。
更多推荐


所有评论(0)