【AVRCP】规范精讲[34]:控制端主动切播放器并播放,完整流程与避坑指

在车载蓝牙、TWS耳机这类产品的开发中,你肯定遇到过这种场景:用户在车机上切换了手机里的音乐App并点击播放,结果手机里原来的App还在响歌,车机也没正确控制新App,两边声音打架,体验直接拉胯。本文就来拆解控制端主动发起、切换到新播放器并播放的完整AVRCP交互流程,把协议里的细节、坑点和开发要点一次性讲透,彻底解决这类兼容性问题。


目录

一、什么是控制端主动切播放器并播放?

二、核心交互流程

三、关键协议逻辑与原文解析

四、核心知识点与避坑指南

五、测验


一、什么是控制端主动切播放器并播放?

直白说,就是控制端CT(比如车机、耳机)主动发起操作,先浏览目标端TG(比如手机)上的媒体播放器列表,再把寻址播放器切换到目标App,最后发送播放命令让新播放器开始工作。

这和之前讲的用户在TG本地切换播放器的场景完全相反------主动权完全在CT手里,整个流程的控制权都在车机上。

打个比方,就像你用万能遥控器,先扫了一遍电视上所有能控制的App,然后把遥控器对准了新的视频App,按下播放键,让它开始放视频。

二、核心交互流程

我们结合时序图,一步步拆解每一步的交互和背后的协议逻辑。

1. 初始状态: AVRCP 会话建立

CT和TG已经建立好AVRCP连接,基础控制通道正常,这是所有交互的前提。

2. 第一步:CT获取媒体播放器列表

CT发送GetFolderItems_Cmd命令,请求获取TG上ROOT目录下的媒体播放器列表。

TG回复GetFolderItemsRsp,返回所有可用的播放器信息,包括每个播放器的ID、状态、名称等。

伪代码示例:

cpp 复制代码
// 伪代码:获取媒体播放器列表
avrcp_pkt_t get_folder_pkt;
avrcp_build_get_folder_items(&get_folder_pkt, 0x00, 0, 0xFFFF); // ROOT目录,获取所有播放器
avrcp_send_pkt(session, &get_folder_pkt);

这一步是CT选目标的过程,必须拿到所有播放器列表,才能知道要切换到哪个App。

3. 第二步:浏览并选择目标播放器

CT根据返回的列表,让用户选择目标播放器,或者直接选择默认播放器。这一步在时序图里标注为<Browsing and song>,代表用户在车机上完成了选择操作。

4. 第三步:CT发送寻址播放器切换命令

CT发送SetAddressedPlayerCmd,携带目标播放器的ID,要求TG将寻址播放器切换到这个App。

TG回复SetAddressedPlayerRsp,确认切换成功,并更新内部状态,把控制目标切换到新播放器。

同时,TG会触发AddressedPlayerChangedNotification,把新的播放器ID同步给CT,让CT确认切换完成。

5. 第四步:CT注册寻址播放器变更通知

CT发送RegisterAddressedPlayerChangedNotification,注册后续的播放器变更事件,确保之后如果用户在手机上切了播放器,车机能收到通知。

6. 第五步:CT发送播放命令,启动新播放器

用户在车机上按下播放键,CT发送PlayCmd命令,TG收到后,将播放指令转发给新的寻址播放器。

TG回复PlayRsp,确认命令执行成功,新播放器开始播放。

这里有个关键细节:如果用户再按一次播放键,CT会再次发送 PlayCmd ,确保新播放器进入播放状态,这是为了适配部分App需要两次播放命令的 兼容性 问题。

三、关键协议逻辑与原文解析

整个流程里有两个容易被忽略的关键点,直接关系到产品的用户体验:

CT should not send STOP without user action

这句话是协议给CT开发的硬性约束:控制端不能在没有用户操作的情况下,主动向旧播放器发送STOP命令

很多开发同学会想当然地认为,切换播放器时,车机应该先把旧播放器的歌停掉,再切新的。但协议明确禁止了这种行为------如果用户没有明确操作停止旧播放器,CT不能主动发STOP,否则会导致手机上原来的App被强制停歌,用户体验非常差。旧播放器是否继续播放,完全由TG(手机)自己决定。

Device dependant if previous player continues playing

这句话说明,旧播放器的状态是设备相关的,不同手机的行为可能不一样:有的手机会自动暂停旧App的播放,有的则会继续播放。CT的职责只是切换寻址播放器,控制新播放器,不能干预旧播放器的状态。

四、核心知识点与避坑指南

1. 关键命令与事件梳理

|------------------------------------|-----------|---------|
| 命令/事件 | 作用 | 发送方 |
| GetFolderItems_Cmd | 获取媒体播放器列表 | CT |
| GetFolderItemsRsp | 返回可用播放器信息 | TG |
| SetAddressedPlayerCmd | 切换寻址播放器 | CT |
| AddressedPlayerChangedNotification | 同步寻址播放器变更 | TG |
| PlayCmd | 发送播放命令 | CT |

2. 开发中最容易踩的3个坑

  • 坑1:CT主动发送STOP命令给旧播放器

这是协议明确禁止的行为,会导致用户手机上原来的App被强制停歌,引发投诉。正确的做法是只切换寻址播放器,不干预旧播放器的状态。

  • 坑2:不注册AddressedPlayerChangedNotification

很多CT实现只切换了寻址播放器,没有注册变更通知,导致后续用户在手机上切播放器时,车机完全感知不到,控制失效。

  • 坑3:发送一次PlayCmd就不管了

部分App第一次收到PlayCmd可能不会响应,或者需要确认状态,协议流程里的两次PlayCmd是兼容性设计,建议在产品中保留,确保新播放器能稳定启动播放。

3. 为什么要严格遵循这个流程?

这个流程的核心是尊重用户操作的控制权:CT只能响应用户的主动操作,切换寻址播放器并控制新播放器,不能越权干预旧播放器的状态。一旦打破这个规则,就会出现用户手机里的App被莫名停歌、两边声音打架的问题。

五、测验

问:控制端主动切换寻址播放器时,是否可以主动向旧播放器发送STOP命令?为什么?(车载蓝牙开发面试真题)

答:

不可以。协议明确规定,CT不能在没有用户操作的情况下主动发送STOP命令给旧播放器,否则会强制停止用户手机上正在播放的App,破坏用户体验。旧播放器的状态由TG设备自行决定。

问:CT切换寻址播放器后,为什么需要注册AddressedPlayerChangedNotification?(蓝牙协议栈开发面试题)

答:

注册该通知后,当用户在TG端本地切换播放器时,CT能及时收到变更通知,更新控制目标,避免后续播放控制命令发送给无效的播放器ID。

问:控制端主动切换播放器并播放时,第一步必须做什么?(嵌入式蓝牙开发面试题)

答:

必须先通过GetFolderItems命令获取TG端的媒体播放器列表,确定目标播放器的ID,再发送SetAddressedPlayer命令切换寻址播放器。