【AVRCP】规范精讲[25]: 大数据包拆分传输的完整流程与实战

在蓝牙音频开发中,相信很多人都遇到过这样的问题:手机连接车载蓝牙后,播放一首名字很长的歌曲,车机屏幕上只显示了前几个字;或者获取专辑信息时,总是少了一部分内容。这背后其实是AVRCP协议的续传机制在起作用。很多开发者对这个机制一知半解,导致出现各种奇怪的兼容性问题。本文就来深入拆解AVRCP中的续传流程,包括RequestContinuingResponse和AbortContinuingResponse的工作原理、规范细节和实战中的坑点。


目录

一、为什么需要续传机制?

[二、核心字段:Packet Type的四种状态](#二、核心字段:Packet Type的四种状态)

三、RequestContinuingResponse完整流程拆解

四、AbortContinuingResponse:随时中止的能力

五、实战中的常见坑点与解决方案

六、代码示例:CT端续传处理逻辑

七、测验


一、为什么需要续传机制?

要理解续传机制,首先得从AVRCP的底层协议栈说起。AVRCP基于AV/C数字接口命令集,而AV/C协议有一个硬性规定:每个AV/C帧的最大长度只能是512字节。这个限制是历史遗留问题,在AV/C协议设计之初,512字节已经足够传输大部分控制命令和简单的元数据。

但随着蓝牙音频的发展,我们需要传输的元数据越来越多,比如完整的歌曲名、艺术家名、专辑名、歌词、专辑封面信息等,这些数据很容易就超过了512字节的限制。为了解决这个问题,AVRCP引入了续传机制,允许将一个大的响应拆分成多个AV/C帧进行传输。

这里需要特别注意一个关键点:

Note that Continuation is required due to the limit of 512 octets per AV/C frame. Continuation is therefore only necessary for AV/C commands.

这句话明确告诉我们,续传机制只适用于控制通道上的AV/C命令。而浏览通道上的命令因为不使用AV/C帧封装,直接通过AVCTP传输,所以不需要续传机制,它们的大小限制由L2CAP的MTU决定,通常可以达到几千字节。

二、核心字段:Packet Type的四种状态

在AVRCP特定的AV/C PDU格式中,有一个至关重要的字段叫做Packet Type,它占2位,用来标识数据包在整个续传过程中的状态。这个字段是整个续传机制的核心,所有的传输逻辑都围绕它展开。

Packet Type字段有四种可能的取值:

  • 00 非分片消息:整个响应数据可以在一个AV/C帧中传输完成,不需要续传

  • 01 开始包:这是分片响应的第一个包,后面还有更多的数据需要传输

  • 10 继续包:这是分片响应的中间包,前面已经有数据,后面还有更多数据

  • 11 结束包:这是分片响应的最后一个包,整个传输到此完成

规范对发送方和接收方的行为有严格的规定:

  1. 发送方不能交错发送不同PDU的分片。一旦发送了某个PDU的开始包,就必须连续发送该PDU的所有后续分片,直到传输完成或者被接收方中止。

  2. 如果接收方收到一个新的开始包或非分片包,而之前还有未完成的分片传输,那么之前的分片传输会被自动视为已中止。

  3. 所有属于同一个PDU的分片响应,必须使用和原始请求相同的PDU ID,这样接收方才能正确地将它们关联起来。

三、RequestContinuingResponse完整流程拆解

下面我们通过一个典型的场景来完整拆解续传流程。假设CT(控制器,比如车机)向TG(目标设备,比如手机)发送GetElementAttributes命令,请求当前播放歌曲的所有元数据。由于歌曲的元数据非常丰富,总大小达到了1200字节,超过了512字节的限制,这时候就需要触发续传机制。

整个流程可以分为以下几个步骤:

  1. CT发送原始请求:CT向TG发送GetElementAttributes命令(PDU ID 0x20),请求所有元数据属性。

  2. TG 返回开始包:TG收到请求后,计算响应数据大小,发现超过了512字节。于是将数据拆分成三个部分,先返回第一个部分,Packet Type设置为01(开始包)。

  3. CT请求继续传输:CT收到开始包后,识别出Packet Type为01,知道还有后续数据。于是发送RequestContinuingResponse命令(PDU ID 0x40),参数是原始请求的PDU ID 0x20。

  4. TG 返回继续包:TG收到续传请求后,返回第二部分数据,Packet Type设置为10(继续包)。

  5. CT再次请求继续传输:CT收到继续包后,识别出Packet Type为10,知道还有最后一部分数据。于是再次发送RequestContinuingResponse命令。

  6. TG 返回结束包:TG收到续传请求后,返回最后一部分数据,Packet Type设置为11(结束包)。

  7. CT 拼接数据:CT收到结束包后,将三个包的数据拼接在一起,得到完整的元数据响应,整个传输过程完成。

这个流程可以用一句话总结:TG 分片发送,CT主动拉取,直到收到结束包。这种设计的好处是CT可以控制传输的节奏,在不需要的时候随时中止传输。

四、AbortContinuingResponse:随时中止的能力

在实际使用中,CT可能在续传过程中不再需要后续的数据。比如用户在元数据传输过程中切换了歌曲,这时候之前的元数据请求就没有意义了。为了避免浪费带宽和资源,AVRCP提供了AbortContinuingResponse命令,允许CT随时中止正在进行的续传。

AbortContinuingResponse的使用非常简单:

  1. CT在收到任何一个分片包(开始包或继续包)之后,都可以发送AbortContinuingResponse命令(PDU ID 0x41),参数是要中止的PDU ID。

  2. TG收到中止命令后,立即停止发送后续的分片,返回一个空的响应。

  3. CT收到响应后,丢弃已经收到的部分数据,整个传输过程中止。

这个机制在实际开发中非常重要,尤其是在用户操作频繁的场景下,可以大大提高系统的响应速度和资源利用率。

五、实战中的常见坑点与解决方案

1. 续传与L2CAP分片的区别

很多开发者容易混淆AVRCP的续传机制和L2CAP的分片机制,其实它们是完全不同层面的东西:

  • AVRCP 续传:是应用层的分片机制,解决的是AV/C帧512字节的限制,只适用于控制通道的AV/C命令。

  • L2CAP分片:是链路层的分片机制,解决的是基带数据包最大长度的限制,适用于所有蓝牙数据传输。

在实际传输中,一个AV/C帧可能会被L2CAP进一步拆分成多个基带数据包进行传输。这两层分片是独立工作的,互不影响。

2. 超时处理策略

规范规定,TG必须在TMTP(1000毫秒)时间内响应任何AV/C命令,包括续传请求。如果CT在发送RequestContinuingResponse后,1000毫秒内没有收到响应,应该如何处理?

规范没有明确规定,但行业内的通用做法是:

  • 重试1-2次续传请求

  • 如果重试仍然失败,中止整个请求,返回超时错误

  • 不要无限重试,以免造成资源泄漏

3. 错误处理

如果在续传过程中发生错误,TG应该返回一个REJECTED响应,包含相应的错误代码。CT收到错误响应后,应该立即中止续传,不要继续发送续传请求。

常见的错误代码包括:

  • 0x00 无效命令

  • 0x01 无效参数

  • 0x03 内部错误

六、代码示例:CT端续传处理逻辑

下面是一个简化的CT端续传处理逻辑的伪代码示例,展示了如何判断Packet Type,如何发送续传请求,以及如何拼接数据:

复制代码
// 处理AV/C响应
void handle_avc_response(uint8_t pdu_id, uint8_t packet_type, uint8_t* data, uint16_t len) {
    static uint8_t* complete_data = NULL;
    static uint16_t complete_len = 0;

    switch (packet_type) {
        case 0x00: // 非分片消息
            // 直接处理完整数据
            process_complete_response(pdu_id, data, len);
            break;

        case 0x01: // 开始包
            // 分配内存,保存第一部分数据
            complete_data = malloc(len);
            memcpy(complete_data, data, len);
            complete_len = len;
            // 发送续传请求
            send_request_continuing_response(pdu_id);
            break;

        case 0x10: // 继续包
            // 重新分配内存,拼接数据
            complete_data = realloc(complete_data, complete_len + len);
            memcpy(complete_data + complete_len, data, len);
            complete_len += len;
            // 发送续传请求
            send_request_continuing_response(pdu_id);
            break;

        case 0x11: // 结束包
            // 拼接最后一部分数据
            complete_data = realloc(complete_data, complete_len + len);
            memcpy(complete_data + complete_len, data, len);
            complete_len += len;
            // 处理完整数据
            process_complete_response(pdu_id, complete_data, complete_len);
            // 释放内存
            free(complete_data);
            complete_data = NULL;
            complete_len = 0;
            break;
    }
}

// 发送RequestContinuingResponse命令
void send_request_continuing_response(uint8_t target_pdu_id) {
    uint8_t cmd[10];
    // 填充AV/C命令头
    cmd[0] = 0x00; // ctype: CONTROL
    cmd[1] = 0x90; // subunit_type: PANEL, subunit_id: 0
    cmd[2] = 0x00; // opcode: VENDOR DEPENDENT
    // 填充Bluetooth SIG公司ID
    cmd[3] = 0x00;
    cmd[4] = 0x19;
    cmd[5] = 0x58;
    // 填充AVRCP PDU头
    cmd[6] = 0x40; // PDU ID: RequestContinuingResponse
    cmd[7] = 0x00; // Reserved, Packet Type: 00
    cmd[8] = 0x00; // Parameter Length: 1
    cmd[9] = 0x01;
    cmd[10] = target_pdu_id; // 目标PDU ID
    // 发送命令
    avctp_send_command(cmd, sizeof(cmd));
}

七、测验

题目:为什么AVRCP需要RequestContinuingResponse机制?它和L2CAP的分片有什么区别?

答案

AVRCP需要续传机制是因为它基于AV/C协议,而AV/C协议规定每个AV/C帧的最大长度只能是512字节。当需要传输的元数据(如长歌曲名、专辑信息)超过这个限制时,就需要将数据拆分成多个AV/C帧进行传输。

它和L2CAP分片的区别在于:

  1. 层面不同:AVRCP续传是应用层的分片机制,L2CAP分片是链路层的分片机制。

  2. 适用范围不同:AVRCP续传只适用于控制通道的AV/C命令,L2CAP分片适用于所有蓝牙数据传输。

  3. 触发条件不同:AVRCP续传由512字节的AV/C帧限制触发,L2CAP分片由基带数据包最大长度限制触发。

  4. 控制方式不同:AVRCP续传是CT主动拉取的模式,L2CAP分片是自动完成的,对上层透明。

题目:请描述RequestContinuingResponse的完整工作流程,并说明Packet Type字段的各个取值含义。

答案

完整工作流程:

  1. CT发送原始AV/C命令给TG。

  2. TG发现响应数据超过512字节,将数据拆分成多个部分,先返回Packet Type为01的开始包。

  3. CT收到开始包后,发送RequestContinuingResponse命令,请求后续数据。

  4. TG返回Packet Type为10的继续包,包含下一部分数据。

  5. 步骤3和4重复,直到TG返回Packet Type为11的结束包。

  6. CT将所有分片数据拼接在一起,得到完整的响应。

Packet Type字段的取值含义:

  • 00:非分片消息,整个响应在一个AV/C帧中完成。

  • 01:开始包,分片响应的第一个包,后面还有更多数据。

  • 10:继续包,分片响应的中间包,前后都有数据。

  • 11:结束包,分片响应的最后一个包,传输完成。

题目:在AVRCP中,哪些命令需要使用续传机制?浏览通道的命令为什么不需要?

答案

只有控制通道上的AV/C命令需要使用续传机制,包括:

  • GetElementAttributes(获取当前播放媒体的元数据)

  • GetFolderItems(获取文件夹内容)

  • GetItemAttributes(获取特定媒体项的属性)

  • 其他可能返回大量数据的AV/C命令

浏览通道的命令不需要续传机制,原因是:

浏览通道的命令不使用AV/C帧封装,直接通过AVCTP协议传输。AVCTP协议没有512字节的帧限制,它的大小限制由L2CAP的MTU决定,通常可以达到几千字节,足够传输大部分浏览数据。因此浏览通道不需要应用层的续传机制。


相关推荐
lauo2 小时前
互动影游的Token经济革命:ibbot手机如何成为AI互动娱乐的生产节点
人工智能·智能手机·娱乐
wulechun2 小时前
深入解析微博数据挖掘与社会情绪分析实战项目:基于Python全栈技术构建舆情监控与情感计算系统的完整指南
智能手机
君为先-bey2 小时前
CineMaster: 3D感知电影级视频生成框架文献深度阅读分析
3d·音视频·扩散模型
weixin_419936922 小时前
Grok Imagine 双模型登陆 MetaChat:Arena 榜首图生视频超越seedance2.0 + 旗舰级图像编辑
音视频·grok
南山有乔木7893 小时前
怎么把音频ncm/kgg/m4a格式转换成mp3?手机App和电脑软件都能用的教程
智能手机·音视频
VOOHU-沃虎3 小时前
PoE供电在专业音频设备中的应用:从电源变压器到音频隔离的完整方案
音视频
Deitymoon3 小时前
RV1136——获取VENC的H264码流
音视频
音视频牛哥3 小时前
SmartMediaKit 还是云服务厂商?企业级音视频系统选型背后的技术逻辑
音视频·大牛直播sdk·低延迟rtsp播放器·轻量级rtsp服务器·rtmp同屏推流·smartmediakit·低延迟rtmp播放器
李二。13 小时前
鸿蒙原生ArkTS-鸿蒙6.0新特性-动态模糊视频背景登录页
华为·音视频·harmonyos