RTMP协议解析

类似于TCP的提供可靠的双向多通道消息服务,多通道指的是控制消息,音视频消息等共享一个连接。实现通常对不同类型的消息分配不同的优先级,当运载能力有限时,这会影响等待流传输的消息的次序。

Message stream (消息流):通信中消息流通的一个逻辑通道。

Message stream ID (消息流 ID):每个消息有一个关联的 ID,使用 ID 可以识别出流通中的消息流。

Chunk (块):消息的一段。消息在网络发送之前被拆分成很多小的部分。块可以确保端到端交付所有消息有序 timestamp,即使有很多不同的流。

Chunk stream (块流):通信中允许块流向一个特定方向的逻辑通道。块流可以从客户端流向服务器,也可以从服务器流向客户端。

Chunk stream ID (块流 ID):每个块有一个关联的 ID,使用 ID 可以识别出流通中的块流

交互过程

  • Uninitialized (未初始化):协议的版本号在这个阶段被发送。客户端和服务器都是 uninitialized (未初始化) 状态。之后客户端在数据包 C0 中将协议版本号发出。如果服务器支持这个版本,它将在回应中发送 S0 和 S1。如果不支持呢,服务器会才去适当的行为进行响应。在 RTMP 协议中,这个行为就是终止连接。
  • Version Sent (版本已发送):在未初始化状态之后,客户端和服务器都进入 Version Sent (版本已发送) 状态。客户端会等待接收数据包 S1 而服务器在等待 C1。一旦拿到期待的包,客户端会发送数据包 C2 而服务器发送数据包 S2。(客户端和服务器各自的)状态随即变为 Ack Sent (确认已发送)。
  • Ack Sent (确认已发送):客户端和服务器分别等待 S2 和 C2。
  • Handshake Done (握手结束):客户端和服务器可以开始交换消息了

CS系列握手包格式:RTMP 协议规范(中文版) - 仲达超 - 博客园 (cnblogs.com)

(这篇博客一些地方有误,建议看官方英文版就很好理解了)

分块

握手之后,连接开始对一个或多个块流进行合并。创建的每个块都有一个唯一 ID 对其进行关联,这个 ID 叫做 chunk stream ID (块流 ID)。这些块通过网络进行传输。传递时,每个块必须被完全发送才可以发送下一块。在接收端,这些块被根据块流 ID 被组装成消息。分块允许上层协议将大的消息分解为更小的消息,例如,防止体积大的但优先级小的消息 (比如视频) 阻碍体积较小但优先级高的消息 (比如音频或者控制命令)。分块也让我们能够使用较小开销发送小消息,因为块头包含包含在消息内部的信息压缩提示。块的大小是可以配置的。它可以使用一个设置块大小的控制消息进行设置 。更大的块大小可以降低 CPU 开销,但在低带宽连接时因为它的大量的写入也会延迟其他内容的传递。更小的块不利于高比特率的流化。所以块的大小设置取决于具体情况

块格式

每个块包含三部分:

Basic Header (基本头,1 到 3 个字节):这个字段对块流 ID 和块类型进行编码。块类型决定了消息头的编码格式。(这一字段的) 长度完全取决于块流 ID,因为块流 ID 是一个可变长度的字段。

Message Header (消息头,0,3,7,或者 11 个字节):这一字段对正在发送的消息 (不管是整个消息,还是只是一小部分) 的信息进行编码。这一字段的长度可以使用块头中定义的块类型进行决定。

Extended Timestamp (扩展 timestamp,0 或 4 字节):这一字段是否出现取决于块消息头中的 timestamp 或者 timestamp delta 字段。

Chunk Data (有效大小):当前块的有效负载,相当于定义的最大块大小

Basic Header

RTMP 协议最多支持 65597 个流,流 ID 范围 3 - 65599。ID 0、1、2 被保留。0 值表示二字节形式,并且 ID 范围 64 - 319 (第二个字节 + 64)。1 值表示三字节形式,并且 ID 范围为 64 - 65599 ((第三个字节) * 256 + 第二个字节 + 64)。3 - 63 范围内的值表示整个流 ID。带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。fmt (两个bit):这一字段指示 'chunk message header' 使用的四种格式之一。

一字节形式

块基本头中的 0 - 5 位 (最低有效) 代表块流 ID

块流 ID 2 - 63 可以编进这一字段的一字节版本

二字节形式

块流 ID 64 - 319 可以以二字节的形式编码在头中

ID 计算为 (第二个字节 + 64):

三字节形式

块流 ID 64 - 65599 可以编码在这个字段的三字节版本中

ID 计算为 ((第三个字节) * 256 + (第二个字节) + 64)

   Chunk Message Header

   结构类型由basic header的fmt字段决定。变长就是为了当前块和前一块头部信息相同时,节省资源。

类型0

长11字节

timestamp (三个字节):大于或者等于 16777215 

表明有extended time stamp拓展。

类型1

长7字节

不包含消息流 ID;这一块使用前一块一样的流 ID。可变长度消息的流 (例如,一些视频格式) 应该在第一块之后使用这一格式表示之后的每个新消息

类型2

长3字节

既不包含流 ID 也不包含消息长度;这一块具有和前一块相同的流 ID 和消息长度。具有不变长度的消息 (例如,一些音频和数据格式) 应该在第一块之后使用这一格式表示之后的每个新消息。

类型3

没有消息头

timestamp delta 、消息长度以及 流 ID等字段都不存在;这种类型的块使用前面块一样的事件戳、最大长度、块流 ID。当单一一个消息被分割为多块时,除了第一块的其他块都应该使用这种类型

RTMP消息格式

使用下层传输层 (比如 RTM 块流协议) 传输的 RTMP 消息的格式。RTMP 协议设计使用 RTM 块流,可以使用其他任意传输协议对消息进行发送。RTM 块流和 RTMP 一起适用于多种音频 - 视频应用,从一对一和一对多直播到点播服务,再到互动会议应用。Message type 1~7为控制消息,8音频,9视频

Message Type (消息类型):一个字节的字段来表示消息类型。类型 ID 1 - 6 被保留用于协议控制消息。

Length (长度):三个字节的字段来表示有效负载的字节数。以大端格式保存。

Timestamp:四个字节的字段包含了当前消息的 timestamp。四个字节也以大端格式保存。

Message Stream Id (消息流 ID):三个字节的字段以指示出当前消息的流。这三个字节以大端格式保存。

协议控制消息

RTMP使用 Message Type1~7用于协议控制消息。这些消息包含有 RTM 块流协议所需要的信息。Message Type 1 2用于RTM块流协议,3~6用于RTMP协议本身,7用于边缘服务器和源服务器之间。  其中MUST控制消息Stream ID为0(被称为控制流),并且RMP中的 chunk stream Id 为2则发送的优先级更高。每个协议控制消息类型具有固定大小的有效载荷。

 设置块类型 (1)

设置块大小,以通知对端一个新的最大块大小。

默认的最大块大小是为 128 字节,但是客户端或者服务器可以改变这个大小,并使用这一消息对对端进行更新。例如,假定一个客户端想要发送一个 131 字节的音频数据,当前块大小是默认的 128。在这种情况下,客户端可以发送这种消息到服务器以通知它块大小现在是 131 字节了。这样客户端就可以在单一块中发送整个音频数据了。

最大块大小设置的话最少为 128 字节,包含内容最少要一个字节。最大块大小由每个方面 (服务器或者客户端) 自行维护。

 

终止消息(2)

终止消息,用于通知对端,如果对端在等待去完成一个消息的块的话,然后抛弃一个块流中已接受到的部分消息。对端接收到块流 ID 作为当前协议消息的有效负载。一些程序可能会在关闭的时候使用这个消息以指示不需要进一步对这个消息的处理了。

chunk stream ID (块流 ID,32 位):这一字段保存块流 ID,该流的当前消息会被丢弃。

 

 确认 (3)

客户端或者服务器在接收到等同于窗口大小的字节之后必须要发送给对端一个确认。窗口大小是指发送者在没有收到接收者确认之前发送的最大数量的字节。这个消息定义了序列号,也就是目前接收到的字节数

sequence number (序列号,32 位):这一字段保存有目前接收到的字节数。

 

窗口确认大小 (5)

客户端或者服务器端发送这条消息来通知对端发送和应答之间的窗口大小。发送者在发送完窗口大小字节之后期待对端的确认。接收端在上次确认发送后接收到的指示数值后,或者会话建立之后尚未发送确认,必须发送一个确认

 

设置对端带宽 (6

客户端或者服务器端发送这一消息来限制其对端的输出带宽。对端接收到这一消息后,将通过限制这一消息中窗口大小指出的已发送但未被答复的数据的数量以限制其输出带宽。接收到这一消息的对端应该回复一个窗口确认大小消息,如果这个窗口大小不同于其发送给 (设置对端带宽) 发送者的最后一条消息。

限制类型取以下值之一:

0 - Hard:对端应该限制其输出带宽到指示的窗口大小。

1 - Soft:对端应该限制其输出带宽到知识的窗口大小,或者已经有限制在其作用的话就取两者之间的较小值。

2 - Dynamic:如果先前的限制类型为 Hard,处理这个消息就好像它被标记为 Hard,否则的话忽略这个消息。

 

用户控制消息 (4)

RTMP 应用层消息类型 ID 4 表示用户控制消息。这些消息包含 RTM块流传输层所使用的信息

 

RTMP命令消息

消息类型:音频(8)、视频(9)、命令消息(17,10)、共享对象(16,19)、数据消息(15,18)、控制消息(1~7),统计消息(22)

命令消息

 20 以进行 AMF0 编码,消息类型值为 17 以进行 AMF3 编码。

这些消息发送以进行一些操作,比如,连接,创建流,发布,播放,对端暂停。命令消息,像 onstatus、result 等等,用于通知发送者请求的命令的状态。一个命令消息由命令名、事务 ID 和包含相关参数的命令对象组成。一个客户端或者一个服务器端可以通过和对端通信的流使用这些命令消息请求远程调用 (RPC)。

数据消息

消息类型为 18 以进行 AMF0 编码和消息类型 15 以进行 AMF3 编码。客户端或者服务器端通过发送这些消息以发送元数据或者任何用户数据到对端。元数据包括数据 (音频,视频等等) 的详细信息,比如创建时间,时长,主题等等。

共享对象消息

所谓共享对象其实是一个 Flash 对象 (一个名值对的集合),这个对象在多个不同客户端、应用实例中保持同步。消息类型 19 用于 AMF0 编码、16 用于 AMF3 编码都被为共享对象事件保留。每个消息可以包含有不同事件。

音频消息

客户端或者服务器端发送这一消息以发送音频数据到对端。消息类型 8 为音频消息保留。

视频消息

客户端或者服务器发送这一消息以发送视频数据到对端。消息类型 9 为视频消息保留。

统计消息

用户控制消息

客户端或者服务器端发送这一消息来通知对端用户控制事件

共享对象消息支持以下类型:

事件

描述

Use(=1)

客户端发送这一事件以通知服务器端一个已命名的共享对象已创建。

Release(=2)

当共享对象在客户端被删除时客户端发送这一事件到服务器端。

Request Change (=3)

客户端发送给服务器端这一事件以请求共享对象的已命名的参数所关联到的值的改变。

Change (=4)

服务器端发送这一事件已通知发起这一请求之外的所有客户端,一个已命名参数的值的改变。

Success (=5)

如果请求被接受,服务器端发送这一事件给请求的客户端,以作为 RequestChange 事件的响应。

SendMessage (=6)

客户端发送这一事件到服务器端以广播一条消息。一旦接收到这一事件,服务器端将会给所有的客户端广播这一消息,包括这一消息的发起者。

Status (=7)

服务器端发送这一事件以通知客户端异常情况。

Clear (=8)

服务器端发送这一消息到客户端以清理一个共享对象。服务器端也会对客户端发送的 Use 事件使用这一事件进行响应。

Remove (=9)

服务器端发送这一事件有客户端删除一个 slot。

Request Remove (=10)

客户端发送这一事件有客户端删除一个 slot。

Use Success (=11)

服务器端发送给客户端这一事件表示连接成功

用户控制消息支持以下事件类型:

事件

描述

Stream Begin (=0)

服务器发送这个事件来通知客户端一个流已就绪并可以用来通信。默认情况下,这一事件在成功接收到客户端的应用连接命令之后以 ID 0 发送。这一事件数据为 4 字节,代表了已就绪流的流 ID。

Stream EOF (=1)

服务器端发送这一事件来通知客户端请求的流的回放数据已经结束。在发送额外的命令之前不再发送任何数据。客户端将丢弃接收到的这个流的消息。这一事件数据为 4 字节,代表了回放已结束的流的流 ID。

StreamDry (=2)

服务器端发送这一事件来通知客户端当前流中已没有数据。当服务器端在一段时间内没有检测到任何消息,它可以通知相关客户端当前流已经没数据了。这一事件数据为 4 字节,代表了已没数据的流的流 ID。

SetBuffer Length (=3)

客户端发送这一事件来通知服务器端用于缓存流中任何数据的缓存大小 (以毫秒为单位)。这一事件在服务器端开始处理流之前就发送。这一事件数据的前 4 个字节代表了流 ID 后 4 个字节代表了以毫秒为单位的缓存的长度。

StreamIs Recorded (=4)

服务器端发送这一事件来通知客户端当前流是一个录制流。这一事件数据为 4 字节,代表了录制流的流 ID。

PingRequest (=6)

服务器端发送这一事件用于测试是否能够送达客户端。时间数据是为一个 4 字节的 timestamp,代表了服务器端发送这一命令时的服务器本地时间。客户端在接收到这一消息后会立即发送 PingResponse 回复。

PingResponse (=7)

客户端作为对 ping 请求的回复发送这一事件到服务器端。这一事件数据是为一个 4 字节的 timestamp,就是接收自 PingRequest 那个。

常用通信命令:

 NetConnection 命令

NetConnection 管理着一个客户端应用和服务器端之间的双相连接。此外,它还提供远程方法的异步调用。

NetConnection 可以发送以下命令:

  • Connect: 客户端发送 connect 命令到服务器端来请求连接到一个服务器应用的 实例。
  • call :执行接收端远程方法的调用 (PRC)。被调用的 PRC 名字作为一个参数传给调用命令
  • Close:关闭连接
  • createStream:客户端发送这一命令到服务器端以为消息连接创建一个逻辑通道。音频、视频和元数据使用 createStream 命令创建的流通道传输。

NetStream 命令

NetStream 定义了传输通道,通过这个通道,音频流、视频流以及数据消息流可以通过连接客户端到服务端的 NetConnection 传输。

以下命令可以由客户端使用 NetStream 往服务器端发送:

 

服务器端使用 "onStatus" 命令向客户端发送 NetStream 状态:

 

  • play
  • play2
  • deleteStream
  • closeStream
  • receiveAudio
  • receiveVideo
  • publish
  • seek
  • Pause

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YanWenCheng_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值