大家在做车载蓝牙、TWS耳机、多媒体终端的AVRCP开发时,一定会遇到播放器被异常卸载/退出/进程杀死 的场景。如果只是普通播放控制还好,一旦涉及多播放器管理、寻址播放器切换、通知事件清理,很容易出现控制端卡死、UI不刷新、命令报错等问题。本文就围绕未激活状态下播放器被移除这一高频异常场景,把整套协议交互、事件机制、状态清理逻辑一次性讲透。
目录
[一、什么是Player Removed when not Active](#一、什么是Player Removed when not Active)
一、什么是Player Removed when not Active
直白说就是:
控制端CT已经寻址到某台播放器(比如手机里的音乐App),但该播放器当前并没有在播放,处于闲置未激活状态;突然这个播放器被系统杀掉、被卸载、被手动退出,目标端 TG 必须按规范完成一整套善后流程,告诉CT播放器没了,同时清理所有旧状态。
这一段对应的是协议里典型的多播放器管理 异常退出 流程 ,也是车载、音箱、耳机项目里兼容性 与稳定性必过的用例。
二、核心交互流程
我们用最容易理解的时序逻辑,把CT与TG的每一步交互讲清楚。

1. 初始状态
CT与TG已建立AVRCP连接
CT通过SetAddressedPlayer指定了某台播放器(假设ID=2)
CT注册了TRACK_CHANGED、ADDRESSED_PLAYER_CHANGED等通知
播放器处于未激活状态(未播放、暂停、后台闲置)
2. 触发: TG 端播放器被移除
用户操作/系统行为 → 目标播放器进程消失 → TG内部感知到播放器已不可用
3. 协议交互全流程(按顺序)
(1)TG触发寻址播放器变更通知
向CT回复ADDRESSED_PLAYER_CHANGED携带已被移除的播放器ID
(2)TG批量拒绝所有旧播放器相关通知
把之前注册的TRACK_CHANGED等全部以REJECTED结束
(3)CT收到通知后,主动刷新可用播放器列表
发送GetFolderItems获取最新媒体播放器清单
(4)CT重新选择合法播放器,发送SetAddressedPlayer
寻址到新的有效播放器,恢复控制能力
三、协议原文深度解析
这段流程的英文描述非常精炼,我们逐句拆解含义,保证开发不踩坑。
Player Removed when not Active
Remove media player which is currently active (remove addressable player)
重点:
哪怕播放器未激活 ,只要它还是addressable player (可寻址、已被CT选中),它的移除都按"活动播放器移除"流程处理,必须通知、必须清理、必须拒绝旧通知。
CT establishes AVRCP session
SetAddressedPlayer(SubunitID=2, ...)
RegisterNotification(TRACK_CHANGED)
InterimResponse()
这一段对应代码里的寻址+注册通知,是所有蓝牙多媒体框架的标准开局:
cpp
// 伪代码:设置寻址播放器 + 注册轨道变更通知
avrcp_send_set_addressed_player(session, 0x0002);
avrcp_register_notification(session, EVENT_TRACK_CHANGED);
User removes player SUBUNITID=2
RegisterNotification(ADDRESSED_PLAYER_CHANGE,..)
InterimResponse(SubunitID=2)
关键点:
TG必须先回INTERIM,携带消失的播放器ID,告诉CT"我要变寻址了"。
RegisterNotificationResponse(ADDRESSED_PLAYER_CHANGED, SUBUNITID=2, Removed)
RegisterNotificationResponse(TRACK_CHANGED, REJECTED)
这是整个流程的灵魂:
-
寻址播放器变更事件正式返回,携带Removed状态
-
所有和旧播放器绑定的通知,全部以REJECTED关闭
防止CT继续等待无效事件,造成卡死或超时
CT gets folder items to check available players
GetFolderItem(ROOT,...)
GetFolderItemRsp(ListMediaPlayers)
CT不能猜哪个还活着,必须重新拉播放器列表,这是规范强约束。
CT sets new addressed player
SetAddressablePlayer(SubunitID=3, ...)
切换到新播放器,会话恢复正常。
四、关键知识点体系化总结
1. 两条核心通知(必须掌握)
|--------------------------------|---------------|--------------|
| 通知事件 | 作用 | 触发时机 |
| EVENT_ADDRESSED_PLAYER_CHANGED | 告知CT当前寻址播放器变了 | 播放器被移除、切换、新增 |
| EVENT_TRACK_CHANGED | 轨道变更 | 切歌、播放新文件 |
2. 异常处理三原则(协议硬性要求)
(1)任何寻址播放器消失,必须先发变更通知
(2)旧播放器的所有注册通知必须REJECTED
(3)CT必须重新拉取播放器列表,不能复用旧缓存
3. 为什么未激活也要走完整流程?
因为AVRCP的寻址状态与播放状态是分离的:
-
寻址:代表"我要控制谁"
-
播放:代表"媒体是否在运行"
哪怕播放器静音、暂停、后台,只要还是寻址目标,移除就必须按规范流程走,否则CT永远以为它还存在。
五、实战开发避坑指南
- 不要在TG里直接删播放器对象
必须先发送通知、拒绝事件,再销毁资源,否则CT收不到状态
- CT收到REJECTED通知不要重传
这代表资源已失效,重传只会增加错误日志
- 车载系统中强烈建议定时刷新播放器列表
适配各种App异常退出的情况
六、测验
题目:AVRCP中,当已寻址但未激活的播放器被移除时,TG第一步必须发送什么通知?(车载蓝牙工程师真题)
答案:
必须先发送EVENT_ADDRESSED_PLAYER_CHANGED通知,携带被移除的播放器ID,让CT感知寻址对象失效。
题目:播放器移除后,TG如何处理之前注册的TRACK_CHANGED等事件?(蓝牙协议栈开发面试)
答案:
TG必须将所有与旧播放器相关的未完成通知,以REJECTED状态回复给CT,终止这些事件等待,避免CT超时或卡死。
题目:CT在收到播放器移除通知后,为什么不能直接假设其他播放器可用,而必须调用GetFolderItems?(嵌入式蓝牙开发面试)
答案:
因为可用播放器列表可能随时变化,规范要求CT必须重新获取有效列表,保证寻址命令不会发给无效ID,避免命令报错与状态不同步。