目录
一、Notification PDUs 概述
二、GetPlayStatus:同步查询播放状态
2.1 命令功能与应用场景
2.2 请求格式(CT → TG)
2.3 响应格式(TG → CT)
2.4 注意事项
2.5 协议实现示例(伪代码)
三、RegisterNotification:异步事件订阅
3.1 命令概述
3.2 命令格式
3.3 响应格式
①EVENT_PLAYBACK_STATUS_CHANGED(通知播放状态的改变)
②EVENT_TRACK_CHANGED(通知媒体轨道的改变)
③EVENT_TRACK_REACHED_END / START
④EVENT_PLAYBACK_POS_CHANGED(通知播放位置的改变)
⑤EVENT_BATT_STATUS_CHANGED
⑦EVENT_SYSTEM_STATUS_CHANGED
3.4 协议实现的 5 大陷阱
四、GetPlayStatus和RegisterNotification的协同策略
五、总结
AVRCP(音频/视频远程控制协议)中的 Notification PDUs(通知 PDU) 是实现设备间状态同步的核心机制,允许控制器(Controller, CT)主动获取或订阅目标设备(Target, TG)的实时状态变化,无论是同步还是异步更新。本文将深入解析其工作原理、关键命令及实际应用场景。
一、Notification PDUs 概述
-
作用:通知 PDU(Protocol Data Unit)用于在目标设备(TG)状态改变时,为控制设备(CT)提供同步和异步更新。
-
应用场景:当控制终端(CT,Control Terminal)想知道媒体轨道的当前状态或其变化情况,以便在控制器显示屏上显示新的媒体信息。CT 可以选择查询播放状态,或者向 TG 注册以接收播放状态通知。当 CT 注册了特定状态变化的通知后,TG 在状态改变时会发送通知 PDU。
-
实现方式:通知 PDU 分为两种类型:
-
同步查询:CT 主动向 TG 请求当前状态。
-
异步订阅:CT 注册特定事件,TG 在状态变化时自动推送通知。
-
这种机制适用于媒体播放状态更新(如播放/暂停)、电池状态变化等场景,确保 CT 界面信息与 TG 实际状态一致。
二、GetPlayStatus:同步查询播放状态
2.1 命令功能与应用场景
作用:CT 通过此命令实时获取 TG 的播放状态、曲目时长和播放进度。 典型场景:
-
用户手动点击播放器界面的“刷新”按钮
-
界面初始化时加载当前播放信息
-
处理用户操作(如暂停/播放)后验证状态
2.2 请求格式(CT → TG)
-
无参数:命令本身不携带任何附加数据
-
传输效率:命令包仅需 3 字节(包括 AVRCP 头部)
HCI LOG:
2.3 响应格式(TG → CT)
响应参数详解:
①曲目时长(SongLength)
字段 | 值范围 | 特殊值 | 处理逻辑 |
SongLength | 0x00000000 ~ 0xFFFFFFFF | 0xFFFFFFFF | 显示"未知时长",禁用进度条拖拽功能 |
示例:
-
0x000EA600
→ 转换为十进制 60,000 毫秒(1分钟) -
0xFFFFFFFF
→ TG 不支持时长反馈
②播放位置(SongPosition)
字段 | 值范围 | 特殊值 | 处理逻辑 |
SongPosition | 0x00000000 ~ 0xFFFFFFFF | 0xFFFFFFFF | 显示"未知位置",停止进度条自动更新 |
动态行为:
-
播放时每秒变化约 1000 次(1秒=1000ms)
-
快进/快退时数值跳跃变化(需结合 PlayStatus 状态处理)
③播放状态(PlayStatus)
状态码 | 含义 | 典型触发场景 | 界面表现建议 |
0x00 | STOPPED | 用户点击停止/播放完成 | 显示停止图标,进度条归零 |
0x01 | PLAYING | 用户点击播放/恢复播放 | 显示播放图标,进度条持续更新 |
0x02 | PAUSED | 用户主动暂停 | 显示暂停图标,进度条静止 |
0x03 | FWD_SEEK | 用户长按快进键 | 显示快进图标,进度条快速前进 |
0x04 | REV_SEEK | 用户长按后退键 | 显示后退图标,进度条快速回退 |
0xFF | ERROR | 设备异常/媒体加载失败 | 显示错误提示,重置播放控件 |
HCI LOG:
2.4 注意事项
①字节序处理:所有 4 字节参数需按 大端序(Big-Endian) 解析:
song_length = int.from_bytes(data[0:4], byteorder='big')
②特殊值 0xFFFFFFFF
if (song_length == 0xFFFFFFFF) {
show_label("时长未知");
disable_seekbar();
}
③状态同步策略
-
高频查询限制:建议最小间隔 ≥500ms,避免频繁请求导致设备过载
-
结合异步通知:与
RegisterNotification(EVENT_PLAYBACK_POS_CHANGED)
配合使用
④错误恢复机制
function handlePlayStatus(response) {
if (response.playStatus === 0xFF) {
retryCount++;
if (retryCount < 3) {
setTimeout(fetchPlayStatus, 1000);
} else {
showErrorMessage("设备连接异常");
}
}
}
2.5 协议实现示例(伪代码)
#include <stdio.h>
#include <stdint.h>
// 定义 GetPlayStatus 命令结构体
typedef struct {
// 该命令无参数
} GetPlayStatusCommand;
// 定义 GetPlayStatus 响应结构体
typedef struct {
uint32_t songLength;
uint32_t songPosition;
uint8_t playStatus;
} GetPlayStatusResponse;
// 模拟 CT 发送 GetPlayStatus 命令
void sendGetPlayStatusCommand() {
GetPlayStatusCommand command;
// 这里模拟发送命令到 TG,实际应用中可能通过蓝牙协议栈发送
printf("CT 发送 GetPlayStatus 命令\n");
// 假设这里将命令发送到某个函数进行处理
// send_command_to_tg(&command);
}
// 模拟 TG 接收 GetPlayStatus 命令并返回响应
GetPlayStatusResponse receiveGetPlayStatusCommand() {
GetPlayStatusResponse response;
// 模拟获取播放状态信息
// 假设 TG 支持获取 SongLength 和 SongPosition
response.songLength = 300000; // 假设歌曲时长为 300000 毫秒
response.songPosition = 150000; // 假设当前播放位置为 150000 毫秒
response.playStatus = 0x01; // 假设当前播放状态为 PLAYING
// 如果 TG 不支持 SongLength 和 SongPosition
// response.songLength = 0xFFFFFFFF;
// response.songPosition = 0xFFFFFFFF;
printf("TG 接收 GetPlayStatus 命令并返回响应\n");
return response;
}
// 模拟 CT 接收 GetPlayStatus 响应
void receiveGetPlayStatusResponse(GetPlayStatusResponse response) {
printf("CT 接收 GetPlayStatus 响应\n");
printf("歌曲总时长: %u 毫秒\n", response.songLength);
printf("当前播放位置: %u 毫秒\n", response.songPosition);
switch (response.playStatus) {
case 0x00:
printf("播放状态: STOPPED\n");
break;
case 0x01:
printf("播放状态: PLAYING\n");
break;
case 0x02:
printf("播放状态: PAUSED\n");
break;
case 0x03:
printf("播放状态: FWD_SEEK\n");
break;
case 0x04:
printf("播放状态: REV_SEEK\n");
break;
case 0xFF:
printf("播放状态: ERROR\n");
break;
default:
printf("未知播放状态\n");
}
}
int main() {
// CT 发送 GetPlayStatus 命令
sendGetPlayStatusCommand();
// TG 接收命令并返回响应
GetPlayStatusResponse response = receiveGetPlayStatusCommand();
// CT 接收响应
receiveGetPlayStatusResponse(response);
return 0;
}
三、RegisterNotification:异步事件订阅
3.1 命令概述
目的:用于在目标设备(TG)上注册,以便基于特定事件异步接收通知。
双重响应机制:
-
INTERIM 响应(初始响应):接收到命令后,应在TMTP(200ms)时间内返回INTERIM响应(当前状态)。
-
CHANGED 响应(后续响应):异步推送状态变化
-
错误处理:可能返回 REJECTED(0x0A)/NOT_IMPLEMENTED(0x08)
核心机制图解: