音视频分布传输协议(AVDTP)
本笔记为作者在学习蓝牙Host协议栈的一些心得体会,如有不对的地方,请包涵与谅解!
------------by wsoz
音视频分布传输协议(AVDTP)
下面就是蓝牙的重点音视频传输相关的协议了,AVDTP、A2DP、AVCTP、AVRCP都与音视频相关,再这个之前我们还是先去了解一下SCO/eSCO链路,便于我们后续理解。
音频链路
通话音频链路
SCO/eSCO链路是经典蓝牙里的同步语音链路 ,主要给通话音频用(不是媒体音频);ACL是异步链路是并列关系同一层级,ACL链路主要由L2CAP协议进行服务而SCO/eSCO则不由L2CAP进行服务。
音频链路路径
- Controller→HCI→Host→PCM/I2S→外部Codec音频解码器进行播放
- Controller→内部PCM/I2S→ 外部Codec音频解码器进行播放
首先第一条路径就是通过HCI传输到Host进行解析,中间会利用HCI进行传输,而第二条路径则直接通过Controller内部的PCM/I2S直接传输到外部音频解码器上进行播放即可。
相对于第一种由Host参与音频处理,在像蓝牙耳机、蓝牙音箱这类设备中通常采用第二条路径目的就是为了节省功耗。
同时要注意区分控制通道 和数据通道:
- 控制通道就是在建立 SCO/eSCO 连接的指令走的HCI命令,要通过HCI进行传输
- 数据通道就是音频数据的传输通道,就是上面的两条路径
媒体音频链路
媒体音频链路对应的就是音乐播放、视频声音播放等场景。它和通话音频不一样,媒体音频不走SCO/eSCO,而是走 ACL/L2CAP 以及上层媒体协议。
如果本机是 Source(音源端),发送路径大致可以理解为:
- 上层音频数据/编码帧(如 SBC) → A2DP/AVDTP → L2CAP → HCI → Controller → 空口发给对端
如果本机是 Sink(接收端),接收路径大致可以理解为:
- 对端空口数据 → Controller → HCI → L2CAP → AVDTP/A2DP → 解码播放(通过I2S进行外部播放)
对应的控制主要由 AVDTP 负责,用来做音频流的发现、协商、配置和状态控制。
协议介绍
AVDTP(Audio/Video Distribution Transport Protocol)是经典蓝牙里专门用于音视频分发的传输控制协议。
它本身不定义"播放/暂停"这类用户按键语义,也不直接规定具体业务界面,而是负责把音频流"怎么建起来、怎么协商、怎么控制状态"这件事做好。
你可以把 AVDTP 理解成:媒体音频链路的控制与管理层。
它主要做这些事:
- 发现对端可用的流端点(SEP)
- 交换并协商能力(比如支持哪些编解码能力)
- 配置流参数并建立媒体通道
- 控制流状态(打开、开始、暂停、关闭、释放)
- 异常时中止流(Abort)并回到可恢复状态
在分层上,AVDTP 位于 L2CAP 之上,负责媒体流的信令控制。
后续像 A2DP 这类媒体 Profile,会基于 AVDTP 来完成实际的音频分发流程。

AVDTP构成与服务
构成
AVDTP的组成由以下的几部分构成:

-
Signalling(信令)
控制面。负责发现端点、能力协商、配置、Open/Start/Suspend/Close/Abort 等命令交互
-
Adaptation Layer(适配层)
把上层编码后的音频帧适配到 AVDTP/L2CAP 传输格式(封包、解包、边界处理等),让数据能正确下发到传输层。
-
Stream Manager(流管理)
状态机与资源管理。维护每条流的状态(Idle/Configured/Open/Streaming 等),决定何时建流、停流、释放。
-
Recovery(恢复)
可选功能。用于丢包恢复相关能力(如恢复窗口等)协商与处理,提升链路较差时的稳健性。
服务
下面这里按照规范中的服务划分继续看:
-
Basic Service(Media Transport)
AVDTP 基本服务确保通过单个传输通道传输每个 session 的媒体包。
当 Basic Service 开启时,活跃的实体主要只有 Signalling 和 Stream Manager 。
这里才是真正传输媒体数据的基础服务,也就是最基础的媒体包传输机制。
这个服务是最基础的服务,同时也是必选服务。
-
Recovery Service(可选)
该服务是在媒体包传输过程中增加"恢复相关能力"的一种可选服务,本质上是在 Basic Service 的基础上增加抗丢包能力。
当该服务启用时,除了 Signalling 和 Stream Manager 之外,还会用到 Recovery 组件。
常见会协商这些参数:
Recovery TypeMRWS(Maximum Recovery Window Size)MNMP(Maximum Number of Media Packets in Parity Code)
-
Reporting Service(可选)
该服务打开时,会向本地应用提供到远程设备媒体流的统计信息,例如时间对齐 、包丢失情况等。
这些报告主要用于做媒体流同步,或者让应用根据当前链路情况去调整错误隐藏策略。
Reporting Service 可以配置为:
- 单向(从 SNK 到 SRC,或从 SRC 到 SNK)
- 双向
具体采用哪种方式,取决于应用需求。
同时应用还可以根据上下文去调整报告周期,以及控制该服务的激活和停用。
报告服务可以通过独立的传输通道,把 report packet 发送到远端设备。
-
Adaptation Service - Multiplexing (可选)
在 Multiplexing 模式下,多个 transport session 可以共享同一个公共的传输(L2CAP)通道。
这几个 transport session:
- 可以属于同一个流
- 也可以属于不同的流
此外,一个 L2CAP 数据包中还可以包含多个数据包,这些数据包可以来自相同 transport session,也可以来自不同 transport session。
正因为如此,每个封装头里都需要带上对应信息来标识媒体包、报告包或恢复包;其中头里的
TSID用于让 SNK 端正确地把数据路由到对应的 transport session。在流配置过程中,
INT会分配TSID和TCID并通知ACP。建立流程也不一定每次都新开一个 L2CAP 通道,而是可以通过引用已有
TCID的方式,把新的 transport session 映射到现有 L2CAP 通道上。 -
Adaptation Service - Robust Header Compression(可选)
健壮报头压缩是一种传输增强服务,用来减少媒体包和恢复包头部引入的额外开销。
这个机制是可选的,但如果要建立该能力,那么流两端必须使用相同配置。
该机制还允许使用反馈通道,这个反馈通道会在媒体流配置协商时一起确定。
传输和信令通道的建立
在参与 stream connection 的两个设备之间,最多可能需要 4 个 L2CAP 通道。
这一部分主要关注的就是:
- Signaling Channel 怎么建立
- Transport Channel 怎么建立
- 哪些服务可能共用通道
- 建立顺序和优先级是什么
后面继续看通道建立流程时,再结合图去理解会更直观。
AVDTP术语
下面对AVDTP中常用的术语进行介绍方便熟悉:
Stream :两个点对点设备之间的流媒体数据。
Source (SRC) and Sink (SNK) :SRC 是音视频的发送方 ,SNK 是音视频的接收方。
Initiator (INT) and Acceptor (ACP) :INT和ACP是通过谁发送命令谁就是INT,然后回复response的为ACP,是在动态切换的。
Application and Transport Service Capabilities :这个说的就是应用服务 和传输服务的功能。应用服务功能比如协商、配置音源设备的 codec,内容保护系统等;传输服务能力比如数据报文的分割和重组,数据包的防丢检测等等。
Services, Service Categories, and Service Parameters :AVDTP 的特性集合 。在启动时,INT会询问ACP的特性能力,并对需要的特性能力进行配置。具体就可以包括上面的 Application Capabilities 和 Transport Service Capabilities 的能力。
Media Packets, Recovery Packets, and Reporting Packets :流媒体数据包用来封装流媒体数据,方向从 SRC→SNK ;恢复数据包主要包含了恢复数据,方向 SRC→SNK ;报告数据包含的时 Qos 的报告包,方向是双向的。
Stream End Point (SEP) :流端点,提供一个协议中"可被发现 、可被配置 、可用于收发媒体流的音频端口/能力实体"。
Stream Context (SC) :流上下文,指在流设置过程中,两个对等设备达到一个公共的了解流的配置,包括选择的服务,参数,以及传输通道分配。简而言之就是流通道的公共配置上下文。
Stream Handle (SH):流句柄。在 SRC 和 SNK 建立了连接之后分配的一个独立的标识符,代表了上层对流的引用。
**Stream End Point Identifier (SEID):**流端点标识,对特定设备的跨设备引用,该引用用于信令事物。
**Stream End Point State:**流端点状态。
**Transport Session:**传输会话。在 A/V 传输层的内部,在配对的 AVDTP 实体之间,流可以分解为一个、两个或多个三个传输会话。
**Transport Session Identifier (TSID):**传输会话标识。代表对一个传输会话的引用。
**Transport Channel:**传输通道。传输通道指的是对 A/V 传输层下层承载程序的抽象,始终对应 L2CAP 的通道
**Transport Channel Identifier (TCID):**传输通道标识。代表对一个传输通道的引用。
整体概念框架
上面这些术语单独看都不难,但真正容易混的是:角色、端点、流、会话、通道分别在说哪一层东西。
可以先按下面这个顺序去理解:
第一层:角色
SRC / SNK解决的是"谁发音频,谁收音频"INT / ACP解决的是"这次命令事务里谁发命令,谁回响应"
也就是说:
SRC / SNK看的是媒体流方向INT / ACP看的是信令交互方向
这两个角色不是一回事。
第二层:端点
SEP是逻辑上的流端点- 一个设备上可以有一个或多个
SEP - 每个
SEP都会声明自己的能力,例如它是Source还是Sink,支持哪些 Service Capabilities
所以 SEP 可以理解成:一条媒体流最终要挂接到的逻辑能力端口。
第三层:流
Stream是 SRC 和 SNK 之间建立起来的一条逻辑媒体流- 它不是物理通道,而是一条已经协商好的媒体会话
SC表示这条流协商完成后的公共配置上下文SH是上层对这条流进行引用时使用的本地句柄
所以:
Stream更像是一条"业务流"SH只是这条流的"编号/引用"
第四层:传输
- 一条流在 AVDTP 内部还会进一步映射到一个或多个
Transport Session TSID用来标识某个Transport SessionTransport Channel则是更底层的承载抽象,它最终对应到L2CAP ChannelTCID用来标识某个Transport Channel
可以简单理解成:
Stream是逻辑业务流Transport Session是这条流在传输层里的具体会话Transport Channel是这些会话真正落到下面时所使用的传输通道
第五层:真正怎么串起来
一条典型媒体流大致是这样建立起来的:
- 设备之间先通过
Signaling Channel做发现和协商 INT发现对端的SEP,读取它的 capabilities- 双方对某个
SRC SEP和某个SNK SEP达成配置一致 - 形成这条流对应的
SC - 为这条流分配或映射
Transport Session / Transport Channel - 后续媒体包、恢复包、报告包就通过对应的 transport path 传输
如果只压成一句话去记,可以这样记:
SRC / SNK是角色SEP是端点Stream是逻辑流SH是流句柄Transport Session是流在传输层里的会话Transport Channel是最终承载这些会话的 L2CAP 通道
所以你后面再看图时,就不要把这些概念都看成"同一个东西":
SEP不是StreamStream不是ChannelChannel也不是Handle
它们只是从能力端点 -> 逻辑流 -> 传输会话 -> 承载通道这样一层层落下去的关系。
AVDTP封包及格式
AVDTP的封包主要有两种封包:一种是控制连接建立等的signaling包;一种是传输媒体流的media transport包。
AVDTP Signal 封包
该封包的主要用于专门传 AVDTP 的控制命令和响应,比如:Discover、Set Configuration、Open、Start。
AVDTP 的 Signaling message 根据是否分片,可以分为:
- Single Packet:单一封包,不分片
- Start Packet:分片起始封包
- Continue Packet:分片中间封包
- End Packet:分片结束封包
Single Packet 格式
当一条 signaling message 没有超过当前 L2CAP MTU 时,就可以直接用单一封包发送。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | Signal Identifier(6) |
+-----------+--------------------------+
Octet 2 ~ n:
+--------------------------------------+
| Signal Parameters |
+--------------------------------------+
Start Packet 格式
当 signaling message 过长时,需要分片发送。第一片就是 Start Packet。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+--------------------------------------+
| NOSP (Number Of Signal Packets) |
+--------------------------------------+
Octet 2:
+-----------+--------------------------+
| RFA(2) | Signal Identifier(6) |
+-----------+--------------------------+
Octet 3 ~ n:
+--------------------------------------+
| First Fragment of Signal Parameters |
+--------------------------------------+
Continue Packet 格式
Start Packet 后面的中间分片就是 Continue Packet。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1 ~ n:
+--------------------------------------+
| Continued Signal Parameters |
+--------------------------------------+
End Packet 格式
最后一片就是 End Packet,表示这一条 signaling message 的分片发送结束。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1 ~ n:
+--------------------------------------+
| Last Fragment of Signal Parameters |
+--------------------------------------+
头字段含义
-
Transaction Label
事务标签,用来标识一次 signaling 事务。
这个值由
INT发起命令时给出,ACP在返回对应 response 时要带回同样的值。 -
Packet Type
表示当前信令消息是否分片,以及当前这一片属于哪种类型:
00:Single Packet01:Start Packet10:Continue Packet11:End Packet
-
Message Type
表示当前 signaling packet 是命令还是响应,以及响应结果类型:
00:Command01:General Reject10:Response Accept11:Response Reject
-
RFA
保留字段(Reserved for Future Addition),当前一般置
0即可。 -
Signal Identifier
表示具体是哪一种 AVDTP 信令命令,例如
Discover、Get Capabilities、Set Configuration、Open、Start等。在 response 中,也会带回同样的
Signal Identifier。 -
NOSP
Number Of Signal Packets,表示当前这条分片 signaling message 一共会被拆成多少个 packet。接收端可以根据这个值做重组与完整性判断。
-
Signal Parameters
该信令命令/响应实际携带的参数内容,例如
SEID、capabilities、error code 等。
补充理解
Single Packet和Start Packet都带有Signal Identifier,因为接收端要先知道这是哪种命令Continue Packet和End Packet只负责继续承载剩余参数数据,因此不再重复带Signal Identifier- 一条被分片的 signaling message,在同一方向上要按顺序连续发送,不能和下一条普通 signaling message 交错发送
AVDTP media transport 封包
这里先看最常见的 Basic Service 下的媒体传输封包。
它和前面的 signaling 封包不一样,不是拿来传控制命令的,而是拿来真正承载媒体数据的。
从格式上看,这里的媒体头部是一个RTP 风格的头部,后面再跟真正的媒体负载。

基本格式
text
Octet 0:
+-------------+-----+-----+----------------+
| Version(2) | P(1)| X(1)| CSRC Count(4) |
+-------------+-----+-----+----------------+
Octet 1:
+-----------+-------------------------------+
| Marker(1) | Payload Type(7) |
+-----------+-------------------------------+
Octet 2 ~ 3:
+-------------------------------------------+
| Sequence Number |
+-------------------------------------------+
Octet 4 ~ 7:
+-------------------------------------------+
| Timestamp |
+-------------------------------------------+
Octet 8 ~ 11:
+-------------------------------------------+
| SSRC |
+-------------------------------------------+
Octet 12 ~ m:
+-------------------------------------------+
| CSRC List(可选) |
+-------------------------------------------+
Octet m+1 ~ n:
+-------------------------------------------+
| Media Payload |
+-------------------------------------------+
头字段含义
-
Version
版本字段。这里通常就是
2。 -
P(Padding)
填充标志。
如果置位,说明这个 packet 的末尾带有 padding 字节。常见音频场景里一般为
0。 -
X(Extension)
扩展头标志。
如果置位,说明在基本头后面还跟着扩展头。常见 A2DP 音频里一般也很少用,通常为
0。 -
CSRC Count
后面跟随的
CSRC个数。一般蓝牙音频里这个值通常是
0,也就是后面没有额外的CSRC List。 -
Marker
标记位。
这个位的具体语义通常由上层 profile / 应用去定义。实际学习 A2DP 时,这个位一般不是最核心的关注点。
-
Payload Type
负载类型。
用来标识当前媒体负载的类型。抓包里经常能看到一个固定值,但真正的 codec 类型并不是只靠它判断,更关键的还是前面协商好的 codec capability。
-
Sequence Number
序列号。
每发送一个媒体 packet 一般递增
1,接收端可以用它判断:- 包是否乱序
- 是否丢包
-
Timestamp
时间戳。
这个时间戳反映的是媒体采样时间基准 ,不是简单的"当前系统时间"。
接收端可以用它做同步、抖动缓冲和播放时序控制。
-
SSRC
Synchronization Source,同步源标识。用来标识这一条媒体流的发送源。
-
CSRC List
Contributing Source列表。用来表示有哪些贡献源参与了当前媒体内容。蓝牙音频里通常基本不会重点碰到,很多时候就是没有。
-
Media Payload
真正的媒体数据负载。
这里面放的就是编码后的音频内容,比如 SBC、AAC 之类的数据。
再往里面看时,还可能会有codec 自己的 payload header,这个就属于更上面的 A2DP/codec 具体定义了。
补充理解
AVDTP Signal 封包走的是信令通道 ,用来传Discover / Set Configuration / Open / Start这些控制命令AVDTP media transport 封包走的是传输通道,用来真正传媒体数据- media transport 封包没有 前面 signaling 里的
Transaction Label / Message Type / Signal Identifier Sequence Number是按媒体包递增,不是按里面的音频帧递增- 一个 media payload 里可以装一个或多个 codec frame,这取决于具体 codec 封装方式
- 如果底层因为 MTU/HCI 需要做分段,那是更下面链路层或 L2CAP/HCI 的事情,不是 signaling 那种
Start / Continue / End分片头
这里先把它记成一句话就行:
- Signal packet 管控制
- Media transport packet 管真正的音频数据
AVDTP Error Code
AVDTP 里的 Error Code 本质上就是:当对端拒绝某个 signaling 命令时,用来说明拒绝原因的错误码 。
它一般出现在 Response Reject 的参数里,用来告诉对端到底是:
- 封包格式错了
- SEID 不对
- capability 不合法
- 当前状态不允许执行该命令
这里要注意一个点:
General Reject主要是通用拒绝,不等同于下面这些普通Error Code- 后面我们学习具体 signaling 命令时,看到
Reject响应再去对应这些错误码就行
下面先把常见的 Error Code 记住:
| Error Code | 名称 | 含义 |
|---|---|---|
0x01 |
BAD_HEADER_FORMAT |
信令包头格式错误 |
0x11 |
BAD_LENGTH |
参数长度不对 |
0x12 |
BAD_SEID |
SEID 不合法 |
0x13 |
SEP_IN_USE |
该 SEP 已经在使用中 |
0x14 |
SEP_NOT_IN_USE |
该 SEP 当前并未处于使用中 |
0x17 |
BAD_SERV_CATEGORY |
Service Category 不合法 |
0x18 |
BAD_PAYLOAD_FORMAT |
payload 格式错误 |
0x19 |
NOT_SUPPORTED_COMMAND |
对端不支持该命令 |
0x1A |
INVALID_CAPABILITIES |
capability 内容不合法,常见于 Reconfigure |
0x22 |
BAD_RECOVERY_TYPE |
Recovery Type 不合法或未定义 |
0x23 |
BAD_MEDIA_TRANSPORT_FORMAT |
Media Transport 相关 capability 格式错误 |
0x25 |
BAD_RECOVERY_FORMAT |
Recovery capability 格式错误 |
0x26 |
BAD_ROHC_FORMAT |
Header Compression capability 格式错误 |
0x27 |
BAD_CP_FORMAT |
Content Protection capability 格式错误 |
0x28 |
BAD_MULTIPLEXING_FORMAT |
Multiplexing capability 格式错误 |
0x29 |
UNSUPPORTED_CONFIGURATION |
配置本身格式没错,但对端不支持 |
0x31 |
BAD_STATE |
当前状态下不能处理该命令 |
0x65 |
BAD_REPORT_FORMAT |
Reporting Service capability 格式错误 |
0x80 |
INVALID_SERVICE_CATEGORY |
无效的 service category |
0x81 |
INSUFFICIENT_RESOURCE |
资源不足,无法完成本次请求 |
如果只先记最重要的几个,优先记这几个就够了:
0x12 BAD_SEID0x13 SEP_IN_USE0x14 SEP_NOT_IN_USE0x29 UNSUPPORTED_CONFIGURATION0x31 BAD_STATE
因为后面在 Set Configuration / Open / Start / Suspend / Close 这些命令里,经常会碰到它们。
AVDTP Signal 常用命令
signal 命令主要是按照建立流程如下:Discover → Capabilities → Set Configuration → Open → Start → Suspend/Close/Abort 。

下面我们依次来进行学习。
AVDTP_DISCOVER
每个 AVDTP 端都会注册一个或者多个 SEP,通过 SEID(SEP ID) 来标示,这个命令就是获取对端的 SEP 信息,包括 SEID(SEP 的 ID),In Use(是否被使用),Media Type(Audio,Media,MultiMedia),TSEP(角色是 Sink 还是 Source)。
命令格式
AVDTP_DISCOVER 的 Signal Identifier = 0x01,该命令本身没有额外参数。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_DISCOVER(0x01) |
+-----------+--------------------------+
也就是说本质上就是:INT 发一个 Discover 命令,请 ACP 把当前有哪些 SEP 告诉我。
Response Accept 格式
如果 Discover 成功,对端会返回一个或多个 SEP 信息项,每个 SEP 占 2 字节。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_DISCOVER(0x01) |
+-----------+--------------------------+
Octet 2:
+--------------------------+----------+------+
| ACP SEID(6) | In Use(1)| RFA |
+--------------------------+----------+------+
Octet 3:
+----------------------+---------+---------+
| Media Type(4) | TSEP(1) | RFA(3) |
+----------------------+---------+---------+
Octet 4 ~ ...:
+-----------------------------------------+
| Other SEP Information (每个 SEP 2 字节) |
+-----------------------------------------+
返回参数说明
-
ACP SEID
对端 SEP 的编号。
-
In Use
表示该 SEP 当前是否已经在使用:
0:Not In Use1:In Use
-
Media Type
表示媒体类型,常见有:
0x0:Audio0x1:Video0x2:Multimedia
-
TSEP
表示该 SEP 的方向角色:
0:SRC1:SNK
Response Reject 格式
如果 Discover 失败,则返回 Reject,后面带 Error Code:
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_DISCOVER(0x01) |
+-----------+--------------------------+
Octet 2:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
补充理解
Discover只负责告诉你:对端有哪些 SEP- 它不会返回这个 SEP 具体支持哪些 codec
- 更详细的能力信息要靠后面的
Get Capabilities / Get All Capabilities
AVDTP_GET_ALL_CAPABILTIES
该命令会直接获取到SEP的整个CAPABILTIES集合,相对于AVDTP_GET_CAPABILTIES则只是获取到某个特定的CAPABILTIES,效率太低了已经被替代了。
补充说明
更直接一点理解就是:
AVDTP_GET_CAPABILITIES:查询该SEP的能力AVDTP_GET_ALL_CAPABILTIES:查询该SEP的完整能力集合
现在实际使用时一般更偏向直接用 GET_ALL_CAPABILITIES,因为它返回得更全。
尤其像一些后续扩展能力,例如 Delay Reporting,通常就更适合通过这个命令一起拿回来。
命令格式
AVDTP_GET_ALL_CAPABILTIES 的 Signal Identifier = 0x0C。
该命令需要指定要查询哪个对端 SEP ,所以请求里要带 ACP SEID。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_GET_ALL_CAPABILITIES(0x0C)|
+-----------+--------------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
也就是说:
INT先指定一个对端的SEP- 然后要求
ACP把这个SEP的完整能力集合返回回来
请求参数说明
-
ACP SEID
表示要查询的对端
SEP编号。也就是:我要查的是哪一个 SEP 的能力。
Response Accept 格式
如果成功,对端会返回这个 SEP 的一组 Service Capabilities。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_GET_ALL_CAPABILITIES(0x0C)|
+-----------+--------------------------------+
Octet 2 ~ ...:
+------------------------------------------------------+
| Service Capabilities |
+------------------------------------------------------+
Response Reject 格式
如果查询失败,则返回 Reject,后面带 Error Code:
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_GET_ALL_CAPABILITIES(0x0C)|
+-----------+--------------------------------+
Octet 2:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
补充理解
Discover是先拿到有哪些SEPGet All Capabilities才是继续查看某个SEP具体支持什么- 后面像
Set Configuration,本质上就是从这里返回的能力里选一套双方都支持的配置 - 如果你只是想快速理解流程,就把它记成:Discover 找端点,Get All Capabilities 查端点能力
Service Capabilities介绍
Service Capabilities 可以直接理解成:某个 SEP 对外声明的能力项集合 。
前面的 Discover 只是告诉我们"有哪些 SEP",而真正要知道这个 SEP 支持什么能力、能不能拿来建流、支持什么 codec、有没有内容保护/恢复/延迟上报 ,就要看这里的 Service Capabilities。
它一般会出现在这些命令里:
AVDTP_GET_CAPABILITIESAVDTP_GET_ALL_CAPABILITIESAVDTP_SET_CONFIGURATIONAVDTP_RECONFIGURE
你可以把它简单理解成:
- 查询阶段 :看对端这个
SEP有哪些能力 - 配置阶段:从这些能力里选出一套双方都接受的配置
基本格式
每一个 Service Capability 都是同样的基本结构:
text
Octet 0:
+--------------------------------------+
| Service Category |
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC |
+--------------------------------------+
Octet 2 ~ n:
+--------------------------------------+
| Service Capability Information |
+--------------------------------------+
字段说明
-
Service Category
表示当前这一项 capability 属于哪一类能力。
比如是
Media Transport、Media Codec、Content Protection还是Delay Reporting。 -
LOSC
Length Of Service Capabilities,表示后面这项 capability 参数区的长度 。也就是说接收端就是靠这个字段知道:这一项 capability 后面还有多少字节。
-
Service Capability Information
这一项 capability 的具体内容。
如果是
Media Codec,这里面放的就是 codec 类型和 codec 参数;如果是Content Protection,这里面放的就是保护方式相关内容。
常见的 Service Category
| Service Category | 名称 | 作用 |
|---|---|---|
0x01 |
Media Transport |
最基础的媒体传输能力,表示该 SEP 可用于基本媒体数据传输 |
0x02 |
Reporting |
支持报告服务,可用于上报统计/同步相关信息 |
0x03 |
Recovery |
支持恢复服务,用于增强抗丢包能力 |
0x04 |
Content Protection |
内容保护能力 |
0x05 |
Header Compression |
报头压缩能力 |
0x06 |
Multiplexing |
多路复用能力 |
0x07 |
Media Codec |
编解码能力,后面最重要的一项 |
0x08 |
Delay Reporting |
支持延迟上报 |
Media Transport Capabilities
这是最基础的一项 capability,没有额外参数。
text
Octet 0:
+--------------------------------------+
| Service Category = Media Transport |
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC = 0x00 |
+--------------------------------------+
也就是说:
LOSC = 0- 后面没有额外的 capability information
这里其实很好理解:
Service Category:声明这是Media TransportLOSC = 0:表示后面没有参数
也就是单纯告诉对端:我支持最基础的媒体传输能力。
Reporting Capabilities
这个格式和 Media Transport 很像,也没有额外参数。
text
Octet 0:
+--------------------------------------+
| Service Category = Reporting |
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC = 0x00 |
+--------------------------------------+
也就是只声明:该 SEP 支持 Reporting Service。
这里同样很简单:
Service Category:声明这是ReportingLOSC = 0:表示后面没有额外参数
也就是单纯说明:我支持 Reporting 功能。
Recovery Capabilities
Recovery 的参数区固定是 3 字节。
text
Octet 0:
+--------------------------------------+
| Service Category = Recovery |
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC = 0x03 |
+--------------------------------------+
Octet 2:
+--------------------------------------+
| Recovery Type |
+--------------------------------------+
Octet 3:
+--------------------------------------+
| MRWS |
+--------------------------------------+
Octet 4:
+--------------------------------------+
| MNMP |
+--------------------------------------+
其中:
Recovery Type:恢复类型,常见0x01 = RFC2733MRWS:Maximum Recovery Window SizeMNMP:Maximum Number of Media Packets in Parity Code
简单理解这 3 个参数:
Recovery Type:采用哪一种恢复方式MRWS:恢复窗口大小,可以理解成"一次恢复机制最多覆盖多大的窗口"MNMP:一次恢复编码里最多涉及多少个媒体包
Media Codec Capabilities
这个是后面最关键的一项,因为真正的 codec 信息就在这里。
text
Octet 0:
+--------------------------------------+
| Service Category = Media Codec |
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC |
+--------------------------------------+
Octet 2:
+----------------------+---------------+
| Media Type(4) | RFA(4) |
+----------------------+---------------+
Octet 3:
+--------------------------------------+
| Media Codec Type |
+--------------------------------------+
Octet 4 ~ n:
+--------------------------------------+
| Media Codec Specific Information |
+--------------------------------------+
其中:
Media Type:媒体类型,比如Audio / Video / MultimediaMedia Codec Type:具体 codec 类型Media Codec Specific Information:codec 自己的参数内容
这里你重点先看这几个:
Media Type:这是音频、视频还是多媒体Media Codec Type:到底是哪种 codec,例如后面常见的SBCMedia Codec Specific Information:这个 codec 的具体参数,比如采样率、声道、bitpool 这类内容
所以这块本质上就是:声明这个 SEP 支持什么编码格式,以及这个编码格式的参数范围。
Content Protection Capabilities
这个 capability 用来描述内容保护类型,以及可选的保护相关参数。
text
Octet 0:
+--------------------------------------+
| Service Category = Content Protection|
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC |
+--------------------------------------+
Octet 2:
+--------------------------------------+
| CP_TYPE_LSB |
+--------------------------------------+
Octet 3:
+--------------------------------------+
| CP_TYPE_MSB |
+--------------------------------------+
Octet 4 ~ n:
+--------------------------------------+
| CP Type Specific Value |
+--------------------------------------+
也就是说:
CP_TYPE是一个 16 bit 的内容保护类型- 后面还可以跟该保护类型自己定义的附加数据
- 所以这里通常可以理解成:
LOSC = 2 + N
简单理解:
CP_TYPE_LSB / CP_TYPE_MSB:这两个字节拼起来就是内容保护类型CP Type Specific Value:该保护方案自己的额外参数
也就是:先说明保护类型,再带这个保护类型对应的附加数据。
Header Compression Capabilities
这个 capability 的参数区是 1 字节标志位。
text
Octet 0:
+--------------------------------------+
| Service Category = Header Compression|
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC = 0x01 |
+--------------------------------------+
Octet 2:
+-----------+------------+----------+----------+
| BackCh(1) | Recovery(1)| Media(1) | RFA(5) |
+-----------+------------+----------+----------+
这里本质上就是:
BackCh:back channel 相关Recovery:是否用于 recovery packetMedia:是否用于 media packet
简单理解这 3 个 bit:
Media = 1:表示支持对媒体包做头压缩Recovery = 1:表示支持对恢复包做头压缩BackCh = 1:表示支持对回传通道相关内容做头压缩
剩下的 RFA(5) 就是保留位,先不用管。
Multiplexing Capabilities
这个 capability 的格式相对复杂一些,因为它要把 Transport Session 和 Transport Channel 的映射带出来。
text
Octet 0:
+--------------------------------------+
| Service Category = Multiplexing |
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC |
+--------------------------------------+
Octet 2:
+-----------+--------------------------+
| FRAG(1) | RFA(7) |
+-----------+--------------------------+
Octet 3:
+----------------------+---------------+
| TSID(Media)(5) | RFA(3) |
+----------------------+---------------+
Octet 4:
+----------------------+---------------+
| TCID(Media)(5) | RFA(3) |
+----------------------+---------------+
Octet 5 ~ 6(可选):
+----------------------+---------------+
| TSID/TCID(Reporting) | RFA |
+----------------------+---------------+
Octet 7 ~ 8(可选):
+----------------------+---------------+
| TSID/TCID(Recovery) | RFA |
+----------------------+---------------+
这里要注意:
LOSC是可变的- 如果只有
Media,那后面字段更短 - 如果还带
Reporting / Recovery,那对应的TSID / TCID也会继续跟在后面
其中:
FRAG:是否允许 Adaptation Layer FragmentationTSID:Transport Session IdentifierTCID:Transport Channel Identifier
这里简单理解:
FRAG:是否允许适配层分片TSID:某个传输会话的编号TCID:某个传输通道的编号
所以 Multiplexing 这块本质上是在说:
哪些 transport session 要映射到哪些 transport channel 上。
Delay Reporting Capabilities
这个 capability 也没有额外参数,格式和 Media Transport / Reporting 类似。
text
Octet 0:
+--------------------------------------+
| Service Category = Delay Reporting |
+--------------------------------------+
Octet 1:
+--------------------------------------+
| LOSC = 0x00 |
+--------------------------------------+
也就是只声明:该 SEP 支持 Delay Reporting。
这里也很好理解:
Service Category:声明这是Delay ReportingLOSC = 0:没有额外参数
也就是单纯说明:我支持延迟上报能力。
AVDTP_SET_CONFIGURATION
该命令就是在前面 Discover 和 Get All Capabilities 之后,真正去配置一条流 的关键命令。
也就是从已经查到的那些 Service Capabilities 里,选出一套双方都能接受的配置,告诉对端:后面这条流就按这套配置来建立。
这里要注意一点:
- 前面的
Get All Capabilities是"看对端支持什么" Set Configuration则是"我现在决定用哪一套"
所以这个命令是正式建流前最关键的一步。
命令格式
AVDTP_SET_CONFIGURATION 的 Signal Identifier = 0x03。
该命令需要同时带:
ACP SEID:对端要被配置的 SEPINT SEID:本地参与这条流的 SEPService Capabilities:本次选定的配置内容
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_SET_CONFIGURATION(0x03) |
+-----------+--------------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
Octet 3:
+--------------------------+------------------+
| INT SEID(6) | RFA(2) |
+--------------------------+------------------+
Octet 4 ~ n:
+--------------------------------------------+
| Service Capabilities |
+--------------------------------------------+
请求参数说明
-
ACP SEID
对端的
SEP编号。也就是:我要去配置对端哪一个 SEP。
-
INT SEID
本地的
SEP编号。也就是:本地是哪一个 SEP 要和对端这个 SEP 建流。
-
Service Capabilities
本次真正选中的配置内容。
这里带的已经不是"对端全部支持项",而是本次决定采用的那一套参数。
举个最直接的理解:
- 前面
Get All Capabilities返回的是"候选集合" - 这里
Set Configuration带的是"最终选项"
Response Accept 格式
如果对端接受这次配置,就返回 Response Accept,它本身没有额外参数。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_SET_CONFIGURATION(0x03) |
+-----------+--------------------------------+
也就是说:
对端同意后,这条流的配置就基本确定下来了。
Response Reject 格式
如果对端拒绝,则返回 Reject。
这个命令的 Reject 比前面普通的 Error Code 多一个字段:Service Category。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_SET_CONFIGURATION(0x03) |
+-----------+--------------------------------+
Octet 2:
+--------------------------------------+
| Service Category |
+--------------------------------------+
Octet 3:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
这里多带一个 Service Category 的原因很好理解:
- 因为
Set Configuration一次可能会带很多 capability - 对端如果拒绝,需要告诉你:到底是哪一项 capability 配错了
Reject 参数说明
-
Service Category
表示出错的是哪一类 capability。
-
ERROR_CODE
表示具体的失败原因。
补充理解
Set Configuration 这一步本质上就是:给一对 SEP 指定最终要使用的流配置 。
没有 Set Configuration,后面的流就还没有真正被配置完成。
AVDTP_GET_CONFIGURATION
这个命令就是用于读取某个已经配置过的 SEP 当前配置内容 。
也就是说,在 Set Configuration 成功之后,如果想再确认一下当前这条流到底配置成了什么样,就可以使用 Get Configuration。
所以它和前面的区别可以直接这样记:
Get All Capabilities:看这个 SEP 支持什么Set Configuration:设置这条流 决定用什么Get Configuration:读取这条流 当前实际用了什么
命令格式
AVDTP_GET_CONFIGURATION 的 Signal Identifier = 0x04。
这个命令需要指定要读取哪个对端 SEP 的当前配置 ,所以请求里要带 ACP SEID。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_GET_CONFIGURATION(0x04) |
+-----------+--------------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
请求参数说明
-
ACP SEID
对端的
SEP编号。也就是:我要读取对端哪一个 SEP 当前已经生效的配置。
Response Accept 格式
如果成功,对端会返回这个 SEP 当前已经设置好的 Service Capabilities。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_GET_CONFIGURATION(0x04) |
+-----------+--------------------------------+
Octet 2 ~ n:
+--------------------------------------------+
| Service Capabilities |
+--------------------------------------------+
这里返回的就不是"所有支持项",而是:
当前这条流已经选定并生效的那一组配置项。
返回参数说明
-
Service Capabilities
返回当前这个
SEP已经配置好的 capability 集合。常见里面会包含:
Media TransportMedia Codec- 以及其他当前实际启用的可选 capability
也就是说:
Get All Capabilities返回的是"可选范围"Get Configuration返回的是"当前结果"
Response Reject 格式
如果读取失败,则返回 Reject,后面带 Error Code:
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------------+
| RFA(2) | AVDTP_GET_CONFIGURATION(0x04) |
+-----------+--------------------------------+
Octet 2:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
补充理解
这个命令本质上更像一个"回读确认 "命令。
它不是用来协商能力的,也不是用来下发配置的,而是用来读取当前已经生效的配置结果。
所以你后面看到它时,可以直接这样理解:
Get All Capabilities:候选列表Set Configuration:下发选择Get Configuration:回读结果
在主流程里它没有 Discover / Set Configuration / Open / Start 那么核心,
但它的作用很清楚:确认当前这条流到底配置成了什么。
AVDTP_OPEN
这个命令就是在 Set Configuration 完成之后,用来打开这条流对应的传输通道 。
也就是说,到了这一步,双方已经不是"只是把参数谈好了",而是开始真正去把后面传媒体数据要用的 transport path 准备好。
这里要注意:
Set Configuration:只是把流配置定下来Open:把这条流真正打开Start:才是开始真正传媒体数据
所以 Open 和 Start 不是一回事。
命令格式
AVDTP_OPEN 的 Signal Identifier = 0x06。
这个命令只需要带一个 ACP SEID,表示要打开哪一个已经配置好的对端 SEP。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_OPEN(0x06) |
+-----------+--------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
请求参数说明
-
ACP SEID
对端已经配置好的
SEP编号。也就是:我要打开的是哪一条已经配置好的流端点。
Response Accept 格式
如果对端接受 Open,则返回 Response Accept,并且这个响应没有额外参数。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_OPEN(0x06) |
+-----------+--------------------------+
在最常见的 Basic Service 场景下,这一步之后就会把后续媒体传输要用的 L2CAP transport channel 建起来。
也就是说,流进入了"已经打开,但还没开始送媒体数据"的状态。
Response Reject 格式
如果对端拒绝 Open,则返回 Reject,后面只带 Error Code。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_OPEN(0x06) |
+-----------+--------------------------+
Octet 2:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
和 Set Configuration 不一样,这里 Reject 没有 Service Category。
因为到 Open 这一步时,前面的配置已经确定了,现在失败更多是:
- 这个
SEP当前状态不允许打开 - 或者这条流本身还没有正确配置好
补充理解
你可以直接把 Open 理解成:
把"已经协商好的流"切换到"可开始传输"的准备状态。
如果只压成一句话去记:
Open= 链路/传输通道准备好Start= 媒体数据真正开始发送
AVDTP_START
这个命令就是在 Open 之后,真正通知对端:这条流现在开始进入媒体传输状态 。
也就是说,从这一步开始,双方不再只是"链路准备好了",而是正式进入 Streaming 阶段。
所以这三个命令的关系可以直接这样记:
Set Configuration:把配置定下来Open:把传输通道准备好Start:开始真正传媒体数据
命令格式
AVDTP_START 的 Signal Identifier = 0x07。
这个命令请求里带的是一个或多个 ACP SEID,表示要启动哪些已经打开的流。
在最常见的场景里,一般就是只带 1 个 SEID。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_START(0x07) |
+-----------+--------------------------+
Octet 2 ~ n:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) | (可继续重复)
+--------------------------+------------------+
也就是说:
- 请求里没有单独的
NSEP字段 - 带了几个
SEID,就表示要启动几个流 - 实际常见抓包里往往只有一个
SEID
请求参数说明
-
ACP SEID
表示要启动的对端
SEP。这些
SEP必须已经:- 配置完成
- 已经
Open
否则通常就会被拒绝。
Response Accept 格式
如果对端接受 Start,则返回 Response Accept,它没有额外参数。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_START(0x07) |
+-----------+--------------------------+
这个响应一旦成功,就表示这条流已经进入可以收发媒体数据的状态。
后面就会真正开始走 AVDTP media transport packet。
Response Reject 格式
如果对端拒绝 Start,则返回 Reject。
这里会带:
- 出错的
ACP SEID ERROR_CODE
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_START(0x07) |
+-----------+--------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
Octet 3:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
这里多带一个 ACP SEID 很重要,因为 Start 可以一次启动多个流。
如果对端拒绝,它就要明确告诉你:到底是哪一个 SEID 启动失败了。
补充理解
Start 才是真正意义上的"开始送媒体数据"。
如果只压成一句话去记:
Open:把路打通Start:真正开始跑数据
AVDTP_SUSPEND
这个命令就是在流已经 Start 之后,临时把媒体流暂停下来 。
也就是说,媒体数据先不继续传了,但这条流本身并没有被彻底关掉。
所以它和 Close 的区别可以先这样记:
Suspend:暂停流,后面还可以再StartClose:关闭流,要把传输通道收掉
命令格式
AVDTP_SUSPEND 的 Signal Identifier = 0x09。
这个命令和 Start 很像,也可以带一个或多个 ACP SEID。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_SUSPEND(0x09) |
+-----------+--------------------------+
Octet 2 ~ n:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) | (可继续重复)
+--------------------------+------------------+
也就是说:
- 带几个
SEID,就表示要暂停几个流 - 最常见场景还是只带一个
SEID
请求参数说明
-
ACP SEID
表示要暂停的对端
SEP。这些
SEP一般都应该已经处于Streaming状态。
Response Accept 格式
如果对端接受 Suspend,则返回 Response Accept,它没有额外参数。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_SUSPEND(0x09) |
+-----------+--------------------------+
成功之后,可以直接理解成:
- 这条流从
Streaming回到Open - 媒体数据先停下来
- 但流本身还在
Response Reject 格式
如果对端拒绝 Suspend,则返回 Reject。
因为 Suspend 可以一次带多个 SEID,所以 Reject 里会带:
- 出错的
ACP SEID ERROR_CODE
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_SUSPEND(0x09) |
+-----------+--------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
Octet 3:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
补充理解
你可以把 Suspend 理解成:
媒体流暂停,但传输通道和流配置都还保留着。
所以后面如果再想恢复,通常就直接重新发:
Start
AVDTP_CLOSE
这个命令就是把这条已经打开的流关闭掉 。
和 Suspend 不一样,Close 不是"先停一下",而是要把这条流当前的传输连接收掉。
命令格式
AVDTP_CLOSE 的 Signal Identifier = 0x08。
这个命令只带一个 ACP SEID。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_CLOSE(0x08) |
+-----------+--------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
请求参数说明
-
ACP SEID
表示要关闭的对端
SEP。一般来说这个
SEP当前应该已经处于:Open- 或者
Streaming
Response Accept 格式
如果对端接受 Close,则返回 Response Accept,它没有额外参数。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_CLOSE(0x08) |
+-----------+--------------------------+
成功之后可以直接理解成:
- 这条流不再处于
Open / Streaming - transport channel 会被关闭
- 如果后面还想重新传,通常要重新走
Open -> Start
Response Reject 格式
如果对端拒绝 Close,则返回 Reject。
这里 Reject 只带 ERROR_CODE。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_CLOSE(0x08) |
+-----------+--------------------------+
Octet 2:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
补充理解
如果只看流程关系:
Suspend:从Streaming回到OpenClose:把Open / Streaming这条流真正关掉
所以 Close 的动作明显比 Suspend 更重。
AVDTP_ABORT
这个命令就是异常中止当前流 。
它不是正常的"暂停"或"关闭"流程,而更像是:当前这条流出问题了,直接强制终止,尽快回收状态。
所以它和 Close 的区别可以这样记:
Close:正常关闭Abort:异常中止
命令格式
AVDTP_ABORT 的 Signal Identifier = 0x0A。
这个命令同样只带一个 ACP SEID。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_ABORT(0x0A) |
+-----------+--------------------------+
Octet 2:
+--------------------------+------------------+
| ACP SEID(6) | RFA(2) |
+--------------------------+------------------+
请求参数说明
-
ACP SEID
表示要被中止的对端
SEP。
Response Accept 格式
如果对端接受 Abort,则返回 Response Accept,它没有额外参数。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_ABORT(0x0A) |
+-----------+--------------------------+
成功之后,可以理解成:
- 当前这条流被强制终止
- 状态机会尽快回到可重新开始的初始状态
Response Reject 格式
如果对端拒绝 Abort,则返回 Reject。
这里和 Close 一样,Reject 只带 ERROR_CODE。
text
Octet 0:
+----------------------+---------------+---------------+
| Transaction Label(4) | Packet Type(2)| Message Type(2)|
+----------------------+---------------+---------------+
Octet 1:
+-----------+--------------------------+
| RFA(2) | AVDTP_ABORT(0x0A) |
+-----------+--------------------------+
Octet 2:
+--------------------------------------+
| ERROR_CODE |
+--------------------------------------+
总结
AVDTP我的理解就是管理媒体音频流传输通道的协议,它主要负责的就是对于媒体流通道的发现、能力配置协商、连接断开控制等。
然后对于封包的话就是:1. 信令包就是该层的一个控制包,直接传递给L2CAP进行再一次包的构建。 2.媒体流包则是先要接收到上层的payload进行封装后再向L2CAP层进行发送完成再一次封包。
AVDTP控制流通道建立:
- 首先先进行SEP的发现
- 询问SEP的能力特性
- 综合本端支持的能力特性,进行配置SEP(之后可选择再次确认)
- 然后就进入到OPEN→START就可以开启流通道
- 最后如果结束就进行CLOSE(中间如果异常会出现SUSPEND或者ABORT)