在介绍 RTMP 的分块与块包装中已经介绍过块(Chunk
)的格式,消息(Message
)的格式也是被封装其中。消息的格式如下:
+--------------+----------------+--------------------+-----------------+
| Basic Header | Message Header | Extended Timestamp | Message Payload |
+--------------+----------------+--------------------+-----------------+
| | |
|<------------------- Chunk Header ----------------->|<---Chunk Data-->|
-
消息头(
Message Header
):存在于Chunk Header
中,用于描述消息的类型与大小等。-
Message Type
(消息类型):一个字节的字段,简写msg tye
,用于区分不同消息格式。 -
Message Length
(消息长度):三个字节的字段,表示消息的长度,即整个块(chunk
)的字节长度。 -
Timestamp
(时间戳):原本是4个字节表示的,但在块中封装时候根据需要使用三个字节或额外的四个字节表示,并需要根据Chunk type
判断该字段是时间戳还是时间差。 -
Message Stream ID
(消息流 ID):三个字节的字段,表示当前信息处于的数据流,这三个字节以大端格式保存。
-
-
消息数据(
Message Payload
):又称消息(有效)载荷,存在于Chunk Data
中,是其的主要构成元素,该数据的具体格式由其消息类型确定。
客户端和服务端通过网络使用 RTMP 块流协议发送 RTMP 消息来进行通信,该消息可以是任何类型,包含音频消息、视频消息、命令消息、共享对象消息、数据数据以及用户控制消息。本篇将主要介绍不同消息类型下不同的消息数据格式。
协议控制消息(1,2,3,5,6)
协议控制消息(Protocol Control Messages
)用于客户端或服务端对协议相关属性(比如块大小、窗口大小和流带宽)进行配置,其消息类型为1
、2
、3
、5
和6
。在两端进行音频、视频或信息数据传输前,两端都需要使用协议控制消息来保证RTMP 块流协议所需的基本信息统一。
注意:协议控制信息必须使用消息流ID 0(作为已知控制流)并以块流 ID (
CSID
)为 2 的块发送。协议控制信息一旦被接收到就立即生效,解析协议控制消息时忽略timestamp
字段。
配置块大小(1)
协议控制消息 1,设置块大小(Set Chunk Size
),客户端和服务端发送此消息来修改当前最大的块大小,默认最大的块大小为 128 字节。
例如,假设客户端想要发送的音频数据大小为131 字节,而块大小为 128 字节。在这种情况下,客户端可以通知服务器新的块大小为 131 字节,然后就可以使用一个块来发送完整的音频数据了。下面是其消息数据的格式:
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0| chunk size (31 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Set Chunk Size’ protocol message
-
0
:当前比特位必须为零。 -
chunk size
(块大小):本字段保存了新的最大块大小,以字节为单位,发送端之后将使用此值作为最大的块大小。有效值为1
~2147483647
(0x7FFFFFFF
),由于块中支持的消息最大长度为16777215
(0xFFFFFF
),而一个块最多只能携带一条消息,因此本字段的实际有效值为1
~16777215
(0xFFFFFF
)。(消息长度是指整个块的大小)
注:最大的块大小至少为 128 字节,且块至少携带 1 个字节的内容。最大块大小由每个方面 (服务端或者客户端) 自行维护。
中断消息(2)
协议控制消息 2,中断消息(Abort Message
),客户端和服务端发送此消息通知对端当前某个块流ID正在传输的消息没有必要再处理了。若已接收块的部分信息,则可以直接丢弃此部分信息。下面是其消息数据的格式:
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk stream id (32 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Abort Message’ protocol message
-
chunk stream id
(块流 ID):本字段包含一个CSID
,用于标识需要中断传输的块流。
应答(3)
窗口大小(即应答窗口大小):指发送方在接收到接收方的任何应答前,可以发送的最大数据量,以字节为单位。
- 理解:
- 发送方告诉接收方我在接收到应答之前最多能发出的数据量
- 发送方不管接收方是否接受到消息都一直发送,直至达到自己设置的应答窗口大小
- 接收方处理了接收的前X的数据会发送一个应答消息,告诉发送方我接收到X个数据
- 发送方就能继续发送消息,限制数量就是 X 个数据
协议控制消息 3,应答(Acknowledgement
),客户端和服务端必须在接收完与窗口大小相等的字节数据量后发送一个应答消息给对端。下面是其消息数据的格式:
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sequence number (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Acknowledgement’ protocol message
-
sequence number
(序列号):本字段包含了截止目前接收到的数据总和,以字节为单位。
应答窗口大小(5)
协议控制信息 5,应答窗口大小(Window Acknowledgement Size
),客户端和服务端发送这个消息来通知对端应答窗口的大小。下面是其消息数据的格式:
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgement Window size (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Window Acknowledgement Size’ protocol message
-
Acknowledgement Window size
(应答的窗口大小):本字段包含应答窗口的大小,即在接收方应答之前能发送的最大数据量,由 4 个字节组成。
发送方在发送了等于窗口大小的数据之后,会等待接收对方的应答消息,在发送端接收到应答之前会停止发送数据。
配置流带宽(6)
协议控制信息 6,配置流带宽(Set Peer Bandwidth
),客户端和服务端发送此消息来说明对方的输出带宽限制。接收方以此来限制自己的输出带宽,即限制未被应答的消息数据大小。接收到此消息的一方,如果窗口大小与上次发送的不一致,应该回复应答窗口大小的消息。下面是其消息数据的格式:
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgement Window size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Limit Type |
+-+-+-+-+-+-+-+-+
Payload for the ‘Set Peer Bandwidth’ protocol message
-
Acknowledgement Window size
:与上述一致。 -
Limit Type
(限制类型):用 1 个字节表示,其取值意义如下:-
0
(硬限制):对端应该限制输出带宽为指定的窗口大小。 -
1
(软限制):对端应该限制输出带宽为指定的窗口大小,或者已经有限制在其作用的话就取两者之间的较小值。 -
2
(动态限制):如果上一次为硬限制,此消息被视为硬限制,否则忽略此消息。
-
用户控制信息(4)
用户控制事件消息(User Control Message Events
)的消息类型为 4,包含 RTMP 流传输层所使用的信息,客户端或服务端发送这个消息来通知对端操作事件。下面是其消息数据的格式:
+------------------------------+------------------------
| Event Type (16 bits) | Event Data
+------------------------------+-------------------------
Payload for the ‘User Control’ protocol message
-
Event Type
(事件类型):本字段用 2 个字节表示,具体类型如下表格所示 -
Event Data
(事件数据):本字段的大小是可变的,具体由事件类型决定,并且完整大小要符合当前块大小,该数据不可进行分块操作。
用户控制信息和协议控制信息一样需要使用消息流ID 0(作为已知控制流)并以块流 ID (
CSID
)为 2 的块发送,并且在解析的时候因其需即刻生效所以忽略Timestamp
。
事件类型与数据对照表
事件类型 | 事件数据 | 用途 | 备注 |
---|---|---|---|
Stream Begin (=0) | 4 字节,表示已就绪的流 ID | 服务端通知客户端指定流已经准备就绪可以用来通信 | 默认情况下,服务端会在成功接收到客户端的应用连接命令之后发送该事件 |
Stream EOF (=1) | 4字节,表示已经回放结束的流ID | 服务端通知客户端请求的回放数据已经结束 | 服务端发送该事件后,在发送额外的命令之前不再发送任何数据,随即客户端需要丢弃接收这个流的信息 |
StreamDry (=2) | 4字节,表示已没新数据的流ID | 服务端通知客户端当前流中已没有数据 | 当服务端在一段时间内没有检测到任何消息,它可以通知相关客户端当前流已经没数据了 |
SetBuffer Length (=3) | 4字节,表示以毫秒为单位的缓存长度 | 客户端通知服务端用于缓存流中任何数据的缓存大小(以毫秒为单位) | 一般情况下,服务端在开始处理流之前发送该事件 |
StreamIs Recorded (=4) | 4字节,表示录制流的流 ID | 服务端通知客户端当前流为一个录制流 | 暂无 |
PingRequest (=6) | 4字节,表示发送这一命令时服务端本地时间 | 服务端发送这一事件用于测试是否能够连通客户通 | 暂无 |
PingResponse (=7) | 4字节,表示应答的Ping 是时间,该内容就是对应的PingRequset 的时间戳数据 |
客户端对服务端PingRequset 请求的回复 |
暂无 |
一般情况下都是服务端发送用户控制消息?
命令消息(17,20)
命令消息(Command Message
)用于在客户端和服务端传递 AMF 编码的命令。消息类型为 20 表示进行 AMF0 编码,消息类型为 17 表示进行 AMF3 编码。
RTMP 通过发送命令消息命令对端进行特定操作,比如connect
、createStream
、publish
、play
、pause
。
命令消息大致分为两种,一种是用于发送者向接受者发送命令,这种命令信息由命令名(Command Name
)、事务ID(Transaction ID
)和相关参数的命令对象组成(Command Object
和命令自身所需其他参数)。另一种是用于通知发送者请求的命令状态,比如onstatus
、result
和error
等等。
因此,通过命令消息,客户端或服务端可以通过与对端通信的流进行远程方法调用(RPC)。
数据消息(15,18)
数据消息(Data Message
)用于客户端或服务端通过数据消息来发送元数据或任何用户数据到对端。消息类型为18 用于 AMF0 编码,消息类型为 15 用于 AMF3 编码。
元数据:包括数据(音频,视频等等)的详细信息,比如创建时间、时长和主题等等。
共享对象消息(16,19)
共享对象:一个 Flash 对象,即一个键值对(
name-value pairs
)的集合。这个对象在多个不同客户端、应用实例中由服务端保持同步。
共享对象消息(Shared Object Message
),用于客户端和服务端同步共享对象的参数值。消息类型 19 用于 AMF0 编码,消息类型 16 用于 AMF3 编码。下面是共享消息的格式:
+------+------+-------+-----+-----+------+-----+ +-----+------+-----+
|Header|Shared|Current|Flags|Event|Event |Event|.|Event|Event |Event|
| |Object|Version| |Type |data |data |.|Type |data |data |
| |Name | | | |length| |.| |length| |
+------+------+-------+-----+-----+------+-----+ +-----+------+-----+
| |
|<- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >|
| AMF Shared Object Message body |
The shared object message format
每个共享对象消息可以包含不同的事件,其支持的事件类型如下:
事件(Event ) |
描述(Description ) |
---|---|
Use (=1) | 客户端发送此事件通知服务端一个已命名的共享对象已创建 |
Release (=2) | 客户端发送此事件通知服务器客户端已将该共享对象删除,因此后续服务端不需继续维护该客户端的共享对象 |
Request Change (=3) | 客户端发送此事件向服务端请求共享对象的参数值变化 |
Change (=4) | 服务端发送此事件通知所有除自己以外的客户端共享对象的参数值变化 |
Success (=5) | 服务端发送此事件来响应客户端发送的Request Change 请求,表示已接收到该请求。(服务端通过比对后发现需要同步时,会发送Change 事件进行通知) |
SendMessage (=6) | 客户端发送此事件到服务端广播一条消息。服务端接收到此事件后,将会给所有客户端广播这消息,包括消息的发起者。 |
Status (=7) | 服务端发送此事件通知客户端异常情况 |
Clear (=8) | 服务端发送此事件请求清理客户端中的一个共享对象。服务端也会发送此事件作为客户端在连接时发出的Use 事件的响应。 |
Remove (=9) | 服务端发送此事件请求客户端删除一个slot
|
Request Remove (=10) | 客户端发送此事件请求客户端删除一个slot
|
Use Success (=11) | 服务端发送此事件通知客户端连接成功 |
slot:???????
音频消息(8)
音频消息(Audio Message
),用于客户端或者服务端通过音频消息发送音频数据到对端,其消息类型为 8。
视频消息(9)
视频消息(Video Message
),用于客户端或者服务端通过视频消息发送视频数据到对端,其消息类型为 9。
聚集消息(22)
聚集消息(Aggregate Message
)是一个包含一系列 RTMP 消息的消息,用于连续发送消息,其消息类型为 22。其结构如下所示:
+---------+-------------------------+
| Header | Aggregate Message body |
+---------+-------------------------+
The Aggregate Message format
信息数据主体构造如下:
+--------+-------+---------+--------+-------+---------+ - - - -
|Header 0|Message|Back |Header 1|Message|Back |
| |Data 0 |Pointer 0| |Data 1 |Pointer 1|
+--------+-------+---------+--------+-------+---------+ - - - -
The Aggregate Message body format
-
聚集消息的消息流ID覆盖此消息内子消息流的ID。
-
聚集消息的
timestamps
与第一个子消息的timestamps
差别在于子消息的timestamps
被相对流时标调整了偏移。每个子消息的timestamps
都被加入偏移以达到一个统一流时间。第一个子消息的timestamps
和聚集消息的timestamps
一样,因此整个偏移量应该为 0。 -
Back pointer
包含了前一个的消息(包含前一个消息的头)的大小。集合消息包含此字段,一是为了适配 FLV 文件格式,二是为了回放定位。
集合消息的优势:
- 块流在一个块内至多可以携带一条完整的消息。使用集合消息之后,不仅可以增加块大小,同时还减少了发送的块数量;
- 聚集消息的子消息可以连续的存储在内存中。当系统调用网络发送数据时更高效。
网友评论