【AVRCP】规范精讲[36]:车机远程加歌进播放队列?AVRCP Add to Queue 全流程拆解

做过车载蓝牙、智能音箱AVRCP开发的同学,大概率都遇到过加歌到播放队列这个需求。用户在车机上选了一首歌,想加到手机音乐App的播放列表里,结果要么加不进去,要么加完车机列表不刷新,体验直接拉胯。本文就把AVRCP里两种"浏览并添加到队列"的交互流程讲透,从CT主动加歌到TG本地加歌,把协议逻辑、时序细节和避坑要点一次性梳理清楚,彻底解决播放队列同步的兼容性问题。


目录

[一、什么是AVRCP Add to Queue?](#一、什么是AVRCP Add to Queue?)

二、核心交互流程

[2.1 模式一:CT主动发起加歌(车机主动加歌)](#2.1 模式一:CT主动发起加歌(车机主动加歌))

[2.2 模式二:TG本地加歌(手机端主动加歌)](#2.2 模式二:TG本地加歌(手机端主动加歌))

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

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

五、测验


一、什么是AVRCP Add to Queue?

简单说,就是用户通过控制端CT(比如车机)或目标端TG(比如手机)的操作,将一首歌曲添加到当前播放器的播放队列中,并且让控制端能同步更新队列显示,后续播放也能正常同步状态。

这个场景分为两种典型交互模式:

  1. CT主动发起加歌:用户在车机上选歌,主动发送AddItemToNowPlaying命令,添加到手机的播放队列。

  2. TG 本地加歌:用户在手机App里把歌加到队列,车机被动接收通知并刷新列表。

打个比方,就像你用电视遥控器,直接把新的视频加到播放列表里;或者别人在电视上把视频加进列表,遥控器能自动看到更新后的列表。

二、核心交互流程

我们结合时序图,分别拆解两种场景的交互逻辑,搞懂每一步的作用和协议要求。

2.1 模式一:CT主动发起加歌(车机主动加歌)

这是车机场景下最常见的交互流程,用户在车机上浏览歌曲,直接发送加歌命令到手机。

1. 初始状态:正在播放中

TG端的播放器正在播放歌曲,CT和TG的AVRCP会话正常,播放队列处于激活状态。

2. CT浏览并选择歌曲

用户在车机上浏览手机的媒体库,选择要添加到队列的歌曲,这一步是CT端的用户交互。

3. CT 发送AddItemToNowPlaying命令

CT发送AddItemToNowPlaying命令,携带目标歌曲的UID和Scope参数,请求TG将这首歌添加到当前播放队列。

伪代码示例:

cpp 复制代码
// 伪代码:添加歌曲到播放队列
avrcp_pkt_t add_queue_pkt;
uint8_t scope = SCOPE_NOW_PLAYING; // 添加到当前播放队列
uint64_t song_uid = 0x00012345;   // 目标歌曲的UID
avrcp_build_add_item_to_now_playing(&add_queue_pkt, song_uid, scope);
avrcp_send_pkt(session, &add_queue_pkt);

4. TG 回复NowPlayingContentChanged通知

TG收到加歌请求后,将歌曲添加到播放队列,并主动发送NowPlayingContentChanged通知,告知CT播放队列的内容已经变更。

5. CT 注册NowPlayingContentChanged通知

CT发送Register命令,注册队列内容变更通知,确保后续队列变化能被及时感知。

TG回复Interim响应,确认通知注册成功。

6. CT拉取更新后的队列列表

CT发送GetFolderItems命令,获取NowPlaying目录下的播放队列列表,拿到最新的队列内容,更新车机上的播放队列UI。

2.2 模式二:TG本地加歌(手机端主动加歌)

这是用户在手机App里操作的场景,车机需要被动接收通知并刷新队列。

1. 初始状态:正在播放中

播放器正在播放,CT和TG会话正常。

2. 用户在 TG 本地添加歌曲到队列

用户在手机App里手动将歌曲添加到播放队列,这一步完全在TG端完成,CT没有主动发送命令。

3. TG 主动发送NowPlayingContentChanged通知

TG感知到播放队列变更,主动发送NowPlayingContentChanged通知给CT,告知队列内容已更新。

4. CT拉取更新后的队列列表

CT收到通知后,发送GetFolderItems命令,获取NowPlaying目录下的最新队列列表,更新车机UI。

5. CT注册NowPlayingContentChanged通知

CT发送Register命令,注册队列变更通知,确保后续变化能被感知。

TG回复Interim响应,确认注册成功。

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

整个流程里,有几个容易被忽略的关键点,直接影响队列同步的兼容性:

Current Player is playing from the queue

这句话点明了场景的前提:播放器正在播放队列中的歌曲,所以队列的任何变更都需要同步给CT,否则车机显示的队列和手机实际播放的队列会不一致。

Updated Queue is shown

这句话是对CT的要求:收到队列变更通知后,必须主动拉取最新的队列列表并更新UI,不能只依赖旧缓存,否则用户在车机上看到的队列永远是旧的。

TG shall complete outstanding notifications

协议隐含的要求是,当队列变更时,TG必须先发送NowPlayingContentChanged通知,再处理后续的播放状态通知,确保CT先感知到队列变化,再同步播放状态,避免时序混乱。

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

1. 关键命令与事件梳理

|----------------------------------------------|------------|---------|
| 命令/事件 | 作用 | 发送方 |
| AddItemToNowPlaying(UID, Scope) | 添加歌曲到播放队列 | CT |
| NowPlayingContentChangedNotification | 播放队列内容变更通知 | TG |
| GetFolderItems(NowPlaying) | 获取当前播放队列列表 | CT |
| RegisterNowPlayingContentChangedNotification | 注册队列变更通知 | CT |

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

  • 坑1:CT发送AddItemToNowPlaying后,不主动拉取队列列表

很多CT实现只发送了加歌命令,没有调用GetFolderItems获取更新后的队列,导致车机上的队列显示还是旧的,用户以为加歌失败了。

  • 坑2:TG本地加歌后,不主动发送NowPlayingContentChanged通知

这是最常见的问题,用户在手机上加了歌,车机完全没反应,队列不更新,用户体验极差。协议明确要求TG在队列变更时主动发送通知。

  • 坑3:CT没有注册NowPlayingContentChanged通知,无法感知本地加歌

很多CT实现只处理了主动加歌的场景,没有注册队列变更通知,导致用户在手机上加歌时,车机完全收不到信号,无法同步队列。

3. 为什么两种场景的时序有差异?

因为主动权不同:

  • CT主动加歌时,是先发送命令,再收到通知,最后拉取列表。

  • TG本地加歌时,是先收到通知,再拉取列表,最后注册通知。

时序差异的核心是"谁先触发队列变更",但最终都要完成"通知+拉取列表"的流程,才能保证队列同步。

五、测验

问:用户在车机上发送AddItemToNowPlaying命令加歌后,为什么车机的播放队列不更新?(车载蓝牙开发面试真题)

答:

大概率是CT没有调用GetFolderItems命令获取更新后的NowPlaying队列列表,只依赖了旧缓存。收到TG的NowPlayingContentChanged通知后,必须主动拉取列表才能更新UI。

问:用户在手机App里添加歌曲到队列时,车机如何感知到队列变化?(蓝牙协议栈开发面试题)

答:

TG必须主动发送NowPlayingContentChanged通知给CT,CT收到通知后,调用GetFolderItems获取最新队列列表,才能更新车机上的队列显示。

问:CT注册NowPlayingContentChanged通知的作用是什么?(嵌入式蓝牙开发面试题)

答:

注册该通知后,无论是CT主动加歌还是TG本地加歌,CT都能及时收到队列变更的信号,触发列表拉取和UI更新,保证车机和手机的播放队列状态同步。