【AVRCP】规范精讲[31]:未激活播放器被移除,整套信令流程与异常处理全解

大家在做车载蓝牙、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)

这是整个流程的灵魂

  1. 寻址播放器变更事件正式返回,携带Removed状态

  2. 所有和旧播放器绑定的通知,全部以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永远以为它还存在。

五、实战开发避坑指南

  1. 不要在TG里直接删播放器对象

必须先发送通知、拒绝事件,再销毁资源,否则CT收不到状态

  1. CT收到REJECTED通知不要重传

这代表资源已失效,重传只会增加错误日志

  1. 车载系统中强烈建议定时刷新播放器列表

适配各种App异常退出的情况


六、测验

题目:AVRCP中,当已寻址但未激活的播放器被移除时,TG第一步必须发送什么通知?(车载蓝牙工程师真题)

答案:

必须先发送EVENT_ADDRESSED_PLAYER_CHANGED通知,携带被移除的播放器ID,让CT感知寻址对象失效。

题目:播放器移除后,TG如何处理之前注册的TRACK_CHANGED等事件?(蓝牙协议栈开发面试)

答案:

TG必须将所有与旧播放器相关的未完成通知,以REJECTED状态回复给CT,终止这些事件等待,避免CT超时或卡死。

题目:CT在收到播放器移除通知后,为什么不能直接假设其他播放器可用,而必须调用GetFolderItems?(嵌入式蓝牙开发面试)

答案:

因为可用播放器列表可能随时变化,规范要求CT必须重新获取有效列表,保证寻址命令不会发给无效ID,避免命令报错与状态不同步。