说明:这是RTMP协议官方文档 rtmp_specification_1.0.pdf 的一个翻译,其中肯定有很多错误。官方文档地址:https://wwwimages2.adobe.com/content/dam/acom/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf。
如果官方地方打不开,这里提供了一个下载地址rtmp_specification_1.0.pdf。
1. 简介
Adobe 公司的 RTMP (Real Time Messaging Protocol) 是一个基于TCP[RFC0793]应用层协议,意图实现在通信端之间传递带有时间信息的视频、音频和数据消息流。
2. 贡献
Rajesh Mallipeddi,Adobe Systems 原成员,起草了本文档原始规范,并提供大部分的原始内容。
Mohit Srivastava,Adobe Systems 成员,促成了本规范的开发。
3. 定义
- Payload:包含于一个数据包中的数据,例如音频和视频数据。
- Packet: 由一个固定头和有效载荷数据构成数据包。
- Port:端口。
- Transport address: 传输地址,如一个IP地址。
- Message stream: 消息流,通信中消息流通的一个逻辑通道。
- Message stream ID: 消息ID,每个消息有一个关联的 ID,使用 ID 可以识别出流通中的消息流。
- Chunk: 块,一段消息。消息在网络发送之前被拆分成很多小的chunk。
- Chunk stream: 块流。通信中允许块流向一个特定方向的逻辑通道。块流可以从客户端流向服务器,也可以从服务器流向客户端。
- Chunk stream ID: 每个块有一个关联的 ID,使用 ID 可以识别出流通中的块流。
- Multiplexing: 合成,将独立的音频/视频数据合成为一个连续的音频/视频流的加工,这样可以同时发送几个视频和音频。
- DeMultiplexing: 差分或者分解,Multiplexing 的逆向处理,将交叉的音频和视频数据还原成原始音频和视频数据的格式。
- Remote Procedure Call: RPC远程调用。
- Metadata:元数据,关于数据的一个描述。一个电影的 metadata 包括电影标题、持续时间、创建时间等等。
- Application Instance: 应用实例,服务器上应用的实例,客户端可以连接这个实例并发送连接请求。
- Action Message Format: AMF,动作消息格式协议。一个用于序列化 ActionScript 对象图的紧凑的二进制格式,类似于json这样的自描述数据结构,只不过AMF是二进制的,json是文本格式的。AMF 有两个版本:AMF0 [AMF0] 和 AMF3 [AMF3]。
4. 字节序、对齐和时间格式
所有整数型以网络字节序(大端字节序 -- MSB -- 高位在前)传输,字节 0 代表第一个字节。
RTMP 中的 Timestamps 以一个整数形式给出,表示一个未指明的时间点,典型地,每个流会以一个为 0 的 timestamp 起始,但这不是必须的,只要双端能够就时间点达成一致。注意这意味着任意不同流 (尤其是来自不同主机的) 的同步需要 RTMP 之外的机制。
因为 timestamp 的长度为 32 位,每隔 49 天 17 小时 2 分钟和 47.296 秒就要重来一次。因为允许流连续传输,有可能要多年,RTMP 应用在处理 timestamp 时应该使用序列码算法 [RFC1982],并且能够处理无限循环。例如,一个应用假定所有相邻的 timestamp 都在 2^31 - 1 毫秒之内,因此 10000 在 4000000000 之后,而 3000000000 在 4000000000 之前。
timestamp 也可以使用无符整数定义,相对于前面的 timestamp。timestamp 的长度可能会是 24 位或者 32 位。
5. RTMP 块流
本节介绍实时消息传输协议的块流 (RTMP 块流)。 它为上层多媒体流协议提供合并和打包的服务。
5.1 消息格式
为了支持依赖上层协议的多路复用,消息格式可以被拆分成chunks。消息格式应该包含以下创建chunk必须的字段。
- Timestamp:消息的 timestamp。这个字段可以传输为四个字节。
- Length: 消息有效负载的长度。如果不能省略消息头,Length 包含消息头的长度。Length在chunk头中占用3个字节。
- Type Id: 一部分 Type ID 的值保留给协议控制消息使用,这些控制信息的消息由RTMP块流协议和高级协议共同处理。所有其他类型的id都可以被更高级别的协议使用,并被RTMP Chunk Stream视为不透明值。事实上,RTMP块流中没有任何东西要求这些值被用作类型;所有(非协议)消息可以是相同类型的,或者应用程序可以使用此字段来区分同步跟踪而不是类型。该字段占用块头1个字节。
- Message Stream ID: Message Stream ID 可以是任意值。被复用到同一块流上的不同消息流根据其Message Stream Id 被解复用。除此之外,就RTMP Chunk Stream而言,这是一个不透明的值。该字段在小端字节格式的块头中占用4个字节。
5.2 握手
一个 RTMP 连接以握手开始。RTMP 的握手不同于其他协议;RTMP 握手由三个固定长度的块组成,而不是像其他协议一样的带有报头的可变长度的块。
客户端 (发起连接请求的终端) 和服务端各自发送相同的三块。便于演示,当发送自客户端时这些块被指定为 C0、C1 和 C2;当发送自服务端时这些块分别被指定为 S0、S1 和 S2。
5.2.1 握手顺序
- 握手以客户端发送 C0 和 C1 块开始。
- 客户端必须等待接收到 S1 才能发送 C2。
- 客户端必须等待接收到 S2 才能发送任何其他数据。
- 服务端必须等待接收到 C0 才能发送 S0 和 S1,也可以等待接收到 C1 再发送 S0 和 S1。服务端必须等待接收到 C1 才能发送 S2。服务端必须等待接收到 C2 才能发送任何其他数据。
5.2.1 C0 和 S0 的格式
C0 和 S0 包都是一个字节。
0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ | version | +-+-+-+-+-+-+-+-+
以下是 C0/S0 包中的字段:
版本号 (8bit):在 C0 中,这一字段指示出客户端要求的 RTMP 版本号。在 S0 中,这一字段指示出服务端选择的 RTMP 版本号。本文档中规范的版本号为 3。0、1、2 三个值是由早期其他产品使用的,是废弃值;4 - 31 被保留为 RTMP 协议的未来实现版本使用;32 - 255 不允许使用 (以区分开 RTMP 和其他常以一个可打印字符开始的文本协议)。无法识别客户端所请求版本号的服务器应该以版本 3 响应,(收到响应的) 客户端可以选择降低到版本 3,或者放弃握手。
5.2.3 C1 和 S1 的格式
C1 和 S1 数据包的长度都是 1536 字节,包含以下字段:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | zero (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random bytes | | (cont) | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Time (4字节): 这个字段包含一个 timestamp,用于本终端发送的所有后续块的时间起点。这个值可以是 0,或者一些任意值。要同步多个块流,终端可以发送其他块流当前的 timestamp 的值。
- Zero (4字节):这个字段必须都是 0。
- Random data (1528 字节):这个字段可以包含任意值。终端需要区分出响应来自它发起的握手还是对端发起的握手,这个数据应该发送一些足够随机的数。这个不需要对随机数进行加密保护,也不需要动态值。
5.2.4 C2 和 S2 的格式
C2 和 S2 数据包长度都是 1536 字节,基本就是 S1 和 C1 的副本 (分别),包含有以下字段:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time2 (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random echo | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | random echo | | (cont) | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- Time (4个字节):这个字段必须包含终端在 S1 (给 C2) 或者 C1 (给 S2) 发的 timestamp。
- Time2 (4个字节):这个字段必须包含终端先前发出数据包 (s1 或者 c1) timestamp。
- Random echo (1528 个字节):这个字段必须包含终端发的 S1 (给 C2) 或者 S2 (给 C1) 的随机数。两端都可以一起使用 time 和 time2 字段再加当前 timestamp 以快速估算带宽和/或者连接延迟,但这不太可能有多大用处。
5.2.5 握手示意图
+-------------+ +-------------+ | Client | TCP/IP Network | Server | +-------------+ | +-------------+ | | | Uninitialized | Uninitialized | C0 | | |------------------->| C0 | | |-------------------->| | C1 | | |------------------->| S0 | | |<--------------------| | | S1 | Version sent |<--------------------| | S0 | | |<-------------------| | | S1 | | |<-------------------| Version sent | | C1 | | |-------------------->| | C2 | | |------------------->| S2 | | |<--------------------| Ack sent | Ack Sent | S2 | | |<-------------------| | | | C2 | | |-------------------->| Handshake Done | Handshake Done | | | Pictorial Representation of Handshake
- Uninitialized:协议的版本号在这个阶段被发送。客户端和服务端都是 uninitialized (未初始化) 状态。之后客户端在数据包 C0 中将协议版本号发出。如果服务端支持这个版本,它将在回应中发送 S0 和 S1。如果不支持,服务端会适当的行为进行响应。在 RTMP 协议中,这个行为就是终止连接。
- Version Sent:在未初始化状态之后,客户端和服务端都进入 Version Sent (版本已发送) 状态。客户端会等待接收数据包 S1 而服务端在等待 C1。一旦拿到期待的包,客户端会发送数据包 C2 而服务端发送数据包 S2。(客户端和服务端各自的)状态随即变为 Ack Sent (确认已发送)。
- Ack Sent (确认已发送):客户端和服务端分别等待 S2 和 C2。
- Handshake Done (握手结束):客户端和服务端可以开始交换消息了。
5.3 分块(chunking)
握手之后,连接复用一个或多个块流。每个块流携带一种消息类型,每个块创建时都会关联一个唯一ID,叫块流ID。在传输时,必须在下一个数据块之前完整地发送每个数据块。在接收端,数据块根据数据块流ID组装成消息。
5.3.1 块(chunk)格式
每个块包含一个头和数据体。块头包含三个部分:
+--------------+----------------+--------------------+--------------+ | Basic Header | Message Header | Extended Timestamp | Chunk Data | +--------------+----------------+--------------------+--------------+ | | |<------------------- Chunk Header ----------------->|
- Basic Header (基本头,1 到 3 个字节): 这个字段对块流 ID 和块类型进行编码。块类型决定了消息头的编码格式。块流 ID 是一个可变长度的字段,长度完全取决于块流 ID 的值。
- Message Header (消息头,0,3,7,或 11 个字节):这一字段对正在发送的消息 (不管是整个消息,还是只是一小部分) 信息进行编码,其长度由块头(Chunk Header)中携带的块类型决定。
- Extended Timestamp (扩展时间戳,0 或 4 字节):这一字段是否出现取决于块消息头中的 timestamp 或者 timestamp delta 字段。更多信息参考 5.3.1.3 节。
- Chunk Data (块数据,可变长度):块的有效payload,最长是配置的块最大值。
5.3.1.1 基本头(Chunk Basic Header)
基本头编码了块流ID和块类型,基本头长度可能是 1,2 或 3 个字节,取决于块流 ID(chunk stream ID)。
块流ID取值范围3-65599,所以RTMP协议最多支持 65597 个流。ID 0、1、2 被保留。0 表示基本头长二字节,ID 范围 64 - 319 (第二个字节 + 64)。1 表示基本头长三字节,ID 范围为 64 - 65599 ((第三个字节) * 256 + 第二个字节 + 64)。3 - 63 范围内的值表示整个流 ID,基本头长一个字节。带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。
基本头第一个字节的 0 - 5 bit (最低有效) 代表块流 ID,第一字节的 6 - 7 bit表示fmt。
块流 ID 2 - 63 基本头长度 1 个字节
0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |fmt| cs id | +-+-+-+-+-+-+-+-+ Chunk basic header 1
块流 ID 64 - 319 基本头长度 2 个字节,ID 计算为 (第2个字节 + 64)
0 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |fmt| 0 | cs id - 64 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Chunk basic header 2
块流 ID 64 - 65599 基本头长度 3 个字节,ID 计算为 ((第三个字节) * 256 + (第二个字节) + 64)
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |fmt| 1 | cs id - 64 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Chunk basic header 3
通过判断基本头第一字节的流ID(cs id),就可以知道基本头的长度,0 -> 2字节, 1 -> 3字节, 其他 -> 1字节。
- fmt (2bit): 表示 4 种消息头,下一小节讲。
5.3.1.2 消息头(Chunk Message Header)
消息头有四种不同的格式,由基本头中的 "fmt" 字段进行选择。
5.3.1.2.1 type 0
类型 0 的块头长11个字节。这一类型必须用在块流的起始位置,和流 timestamp 重来的时候 (比如,重置)。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp |message length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message length (cont) |message type id| msg stream id | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message stream id (cont) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Chunk Message Header - Type 0
- timestamp (3个字节):对于 type-0 块,当前消息的绝对 timestamp 在这里发送。如果 timestamp 大于或者等于 16777215 (16进制 0xFFFFFF),这一字段必须是 16777215,表明有扩展 timestamp 字段来补充完整的 32 位 timestamp。否则的话,这一字段必须是整个的 timestamp。
5.3.1.2.2 type 1
类型 1 的块头长度为7字节。不包含消息流ID(message stream id):这个块和前一个块的流ID相同。带有可变大小消息的流(例如,许多视频格式)应该在每个新消息的第一个数据块之后使用这种格式。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp delta |message length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message length (cont) |message type id| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Chunk Message Header - Type 1
5.3.1.2.3 type 2
类型 2 块头长3字节。流ID和消息长度都不包括在内;该块具有与前一个块相同的流ID和消息长度。带有固定大小消息(例如,一些音频和数据格式)的流应该在每个消息的第一个数据块之后使用这种格式。
0 1 2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp delta | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Chunk Message Header - Type 2
5.3.1.2.4 type 3
类型 3 块没有消息头。流ID、消息长度和时间戳增量字段不存在,从前面的块中获取相同的chunk Stream ID值。当单个消息被分割成块时,除第一个消息块外的所有消息块都应该使用这种类型。如果第一个消息和第二个消息之间的增量与第一个消息的时间戳相同,那么类型3的数据块可以立即跟随类型0的数据块,因为不需要类型2的数据块注册该增量。如果类型 3 块跟在类型 0块后面,那么这个类型 3块的时间戳增量与类型 0块的时间戳增量相同。
5.3.1.2.5 通用头字段
块消息头中每个字段的描述
- timestamp delta (3 bytes):对于类型1或类型2块,是前一个块的时间戳与当前块的时间戳之间的差值。如果增量大于或等于16777215(16进制0xFFFFFF),该字段必须为16777215,表明存在扩展时间戳字段来编码完整的32位增量。否则,这个域应该是实际的差值。
- message length (3 bytes): 对于类型0或类型1块,是消息的长度。请注意,这通常与块有效负载的长度不同。块有效载荷长度是除了最后一个块以外的所有块的最大块大小,以及最后一个块的剩余部分(对于小消息,可能是整个长度)。
- message type id (1 byte): 对于类型0或类型1的块,表示消息类型ID。
- message stream id (4 bytes):对于类型0块,存储消息流ID。消息流ID以little-endian格式存储。通常,同一块流中的所有消息都来自同一消息流。虽然可以将不同的消息流多路复用到相同的块流中,但这破坏了报头压缩的好处。但是,如果一个消息流被关闭,而另一个消息流随后被打开,那么没有理由不能通过发送一个新的类型0块来重用现有的块流。
5.3.1.3 扩展时间戳(Extended Timestamp)
Extended Timestamp字段用于编码大于16777215 (0xFFFFFF)的时间戳或时间戳增量;也就是说,时间戳或时间戳增量不适合类型0、1或2块的24位字段。此字段编码完整的32位时间戳或时间戳增量。该字段的存在是通过将Type 0块的时间戳字段或Type 1或Type 2块的时间戳增量字段设置为16777215 (0xFFFFFF)来表示的。当同一块流ID的最近的Type 0、1或2块表示存在扩展的时间戳字段时,该字段出现在Type 3块中。
5.4 协议控制消息
RTMP 块流使用消息类型 ID 的 1、2、3、5 和 6 用于协议控制消息。这些协议控制消息的块流ID是 2,消息流ID是 0。协议控制消息一旦被接收到就立即生效;协议控制消息的 timestamp 被忽略。
5.4.1 设置块大小(1)
协议控制消息 1,设置块大小,通知对端一个新的最大块大小。
默认的最大块大小是为 128 字节,但是客户端或者服务器可以改变这个大小,并使用这一消息对对端进行更新。
最大块大小最大值最少为 128 字节,最少值 1 字节。客户端服务端维护各自的最大块大小。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| chunk size (31 bits) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Payload for the ‘Set Chunk Size’ protocol message
- 0 : 这位必须是0.
- chunk size (块大小,31 位): 这字段保存新的最大块大小值,以字节为单位。有效值为 1 到 2147483647 (0x7FFFFFFF),但是所有大于 16777215 (0xFFFFFF) 的大小值是等价的,因为没有一个块比一整个消息大,并且没有一个消息大于 16777215 字节。
5.4.2 终止消息(2)
协议控制消息 2,终止消息,通知对端终止发送流消息,已发送的作废。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | chunk stream id (32 bits) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Payload for the ‘Abort Message’ protocol message
- chunk stream ID (块流 ID,32 位):被终止消息的块流 ID。
5.4.3 确认 (3)
客户端或者服务器在接收到等同于窗口大小的字节之后必须要发送给对端一个确认。窗口大小是指发送者在没有收到接收者确认之前发送的最大数量的字节。这个消息定义了序列号,也就是目前接收到的字节数。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sequence number (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Payload for the ‘Acknowledgement’ protocol message
- sequence number (序列号,32 位):目前接收到的字节数。
5.4.5 窗口确认大小 (5)
客户端或者服务器端发送这条消息来通知对端发送和应答之间的窗口大小。发送者在发送完窗口大小字节之后期待对端的确认。接收端在上次确认发送后接收到的指示数值后,或者会话建立之后尚未发送确认,必须发送一个确认 (5.4.3 小节)。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgement Window size (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Payload for the ‘Window Acknowledgement Size’ protocol message
5.4.5 设置对端带宽 (6)
客户端或者服务器端发送这一消息来限制其对端的输出带宽。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgement Window size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Limit Type | +-+-+-+-+-+-+-+-+ Payload for the ‘Set Peer Bandwidth’ protocol message
- 0 - Hard:对端应该限制其输出带宽到窗口大小。
- 1 - Soft: 对端应该限制其输出带宽到该消息中指定的窗口或已经生效的窗口,两者中取最小值。
- 2 - Dynamic: 如果前面的限制类型为Hard,则将此消息视为标记为Hard,否则忽略此消息。
6 RTMP 消息格式
这节定义了使用底层传输层 (如 RTMP 块流协议) 传输的 RTMP 消息的格式。
6.1 RTMP 消息格式
服务器端和客户端通过网络发送 RTMP 消息来进行通信。消息可以包含音频、视频、数据,或者其他消息。
RTMP 消息有两部分:头和它的有效载荷。
6.1.1 消息头
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Message Type | Payload length | | (1 byte) | (3 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Timestamp | | (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Stream ID | | (3 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Message Header
- Message Type (消息类型):一个字节。类型 ID 1 - 6 被保留用于协议控制消息。
- Length (长度):三个字节, 表示有效负载的字节数。以大端格式保存。
- Timestamp:四个字节, 包含了当前消息的 timestamp。以大端格式保存。
- Message Stream Id (消息流 ID):三个字节, 指示出当前消息的流。以大端格式保存。
6.1.2 消息有效载荷
消息的另一个部分就是有效负载,这是消息所包含的实际内容。例如,可以是音频样本或者视频数据。
6.2 用户控制消息 (4)
RTMP 使用消息类型 ID 4 表示用户控制消息。这些消息包含 RTMP 流传输层所使用的信息。RTMP 块流协议使用 ID 为 1、2、3、5 和 6 (5.4 节介绍)。
用户控制消息应该使用消息流 ID 0,并且以 RTMP 块流发送时块流 ID 为 2。用户控制消息一旦被接收立马生效;它们的 timestamp 是被忽略的。
客户端或者服务器端发送这个消息来通知对端用户操作事件,消息携带有事件类型和事件数据。
+------------------------------+------------------------- | Event Type (16 bits) | Event Data +------------------------------+------------------------- Payload for the ‘User Control’ protocol message
前 2 字节定义了事件类型,之后是事件数据,事件数据大小是可变的。
7 RTMP 命令消息
这一节描述了服务器端和客户端间通信交换的各种消息类型和命令类型。
服务器端和客户端交换的不同消息类型包括: 用于发送音频数据的音频消息,用于发送视频数据的视频消息,用于发送任意用户数据的数据消息、共享对象消息以及命令消息。共享对象消息提供了一个通用的方法来管理多用户和一台服务器之间的分布式数据。命令消息使用 AMF 编码传输,命令消息可以实现远程过程调用(RPC)。
7.1 消息的类型
消息的类型有: 音频消息,视频消息,命令消息,共享对象消息,数据消息,以及用户控制消息。
7.1.1 命令消息 (20, 17)
命令消息使用 AMF 方式编码后传输。消息类型20 为 AMF0,17 为 AMF3。这些消息可以进行一些操作,比如,连接,创建流,发布,播放,对端暂停。一个命令消息由命令名、事务 ID 和相关参数的命令对象组成。
7.1.2 数据消息 (18, 15)
数据消息携带元数据或者用户数据。元数据是数据(音频,视频)的详细信息,比如创建时间,时长,主题等等。18表示AMF0编码,15表示AMF3编码。
7.1.3 共享对象消息 (19, 16)
共享对象其实是一个 Flash 对象 (一个键值对的集合),这个对象在多个不同客户端、应用实例中保持同步。消息类型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
事件 | 描述 |
---|---|
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) | 服务器端发送给客户端这一事件表示连接成功。 |
7.1.4 音频消息 (8)
消息类型 8 为音频消息。
7.1.5 视频消息 (9)
消息类型 9 为视频消息。
7.1.6 聚合消息 (22)
消息类型 22 用于聚合消息。聚合消息是一个包含一系列RTMP 子消息的消息单一消息,子消息描述在6.1小节。
+---------+-------------------------+ | 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。
7.1.7 用户控制消息事件
客户端或者服务器端发送这一消息来通知对端用户控制事件。消息格式参考 6.2 节。
支持的用户控制事件类型:
事件 | 描述 |
---|---|
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 那个。 |
7.2 命令类型
客户端和服务器端交换 AMF 编码的命令。服务器端发送一个命令消息,这个命令消息由命令名、事务 ID 以及包含有相关参数的命令对象组成。例如,连接命令包含有 'app' 参数,这个命令说明了客户端连接到的服务器端的应用名'app'。接收者处理这一命令并回发一个同样事务 ID 的响应。回复字符串可以是 _result、_error 或者 一个方法名的任意一个,比如,verifyClient 或者 contactExternalServer。
命令字符串 _result 或者 _error 是响应信号。事务 ID 指示出响应的是哪一个命令。这和 IMAP 和其他一些协议的标签一样。命令字符串中的方法名表示发送者尝试运行一个在接收者上的方法。
以下类的对象用于发送不同的命令:
- NetConnection: 代表上层的服务器端和客户端之间连接的一个对象。
- NetStream:一个代表发送音频流、视频流和其他相关数据的通道的对象。当然,我们也会发送控制数据流的命令,诸如 play、pause 等等。
7.2.1 NetConnection 命令
NetConnection 管理着一个客户端应用和服务器端之间的双相连接。此外,它还提供远程方法的异步调用。NetConnection 可以发送以下命令:
- connect
- call
- close
- createStream
7.2.1.1 connect
客户端发送 connect 命令到服务器端来请求连接到一个服务器应用的实例。
客户端发送到服务器端的 connect 命令结构如下
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令的名字。设置给 "connect"。 |
Transaction ID | 数字 | 总是设置为 1。 |
Command Object | 对象 | 具有键值对的命令信息对象。 |
Optional User Arguments | 对象 | 任意可选信息。 |
以下是为 connect 命令中使用的名值对对象的描述
属性 | 类型 | 描述 | 示例 |
---|---|---|---|
app | 字符串 | 客户端连接到的服务器端应用的名字。 | testapp |
flashver | 字符串 | Flash Player 版本号。 | FMSc/1.0 |
swfUrl | 字符串 | 进行当前连接的 SWF 文件源地址。 | file://C:/FlvPlayer.swf |
tcUrl | 字符串 | 服务器 URL。具有以下格式:protocol://servername:port/appName/appInstance | rtmp://localhost:1935/testapp/instance1 |
fpad | 布尔 | 如果使用了代理就是 true。 | true |
audioCodecs | 数字 | 表明客户端所支持的音频编码。定义见下表 | SUPPORT_SND_MP3 |
videoCodecs | 数字 | 表明支持的视频编码。定义见下表 | SUPPORT_VID_SORENSON |
videoFunction | 数字 | 表明所支持的特殊视频方法。 | SUPPORT_VID_CLIENT_SEEK |
pageUrl | 字符串 | SWF 文件所加载的网页 URL。 | http://somehost/sample.html |
objectEncoding | 数字 | AMF 编码方法。 | AMF3 |
audioCodecs 属性:
Flag values for the audioCodecs property: +----------------------+----------------------------+--------------+ | Codec Flag | Usage | Value | +----------------------+----------------------------+--------------+ | SUPPORT_SND_NONE | Raw sound, no compression | 0x0001 | +----------------------+----------------------------+--------------+ | SUPPORT_SND_ADPCM | ADPCM compression | 0x0002 | +----------------------+----------------------------+--------------+ | SUPPORT_SND_MP3 | mp3 compression | 0x0004 | +----------------------+----------------------------+--------------+ | SUPPORT_SND_INTEL | Not used | 0x0008 | +----------------------+----------------------------+--------------+ | SUPPORT_SND_UNUSED | Not used | 0x0010 | +----------------------+----------------------------+--------------+ | SUPPORT_SND_NELLY8 | NellyMoser at 8-kHz | 0x0020 | | | compression | | +----------------------+----------------------------+--------------+ | SUPPORT_SND_NELLY | NellyMoser compression | 0x0040 | | | (5, 11, 22, and 44 kHz) | | +----------------------+----------------------------+--------------+ | SUPPORT_SND_G711A | G711A sound compression | 0x0080 | | | (Flash Media Server only) | | +----------------------+----------------------------+--------------+ | SUPPORT_SND_G711U | G711U sound compression | 0x0100 | | | (Flash Media Server only) | | +----------------------+----------------------------+--------------+ | SUPPORT_SND_NELLY16 | NellyMouser at 16-kHz | 0x0200 | | | compression | | +----------------------+----------------------------+--------------+ | SUPPORT_SND_AAC | Advanced audio coding | 0x0400 | | | (AAC) codec | | +----------------------+----------------------------+--------------+ | SUPPORT_SND_SPEEX | Speex Audio | 0x0800 | +----------------------+----------------------------+--------------+ | SUPPORT_SND_ALL | All RTMP-supported audio | 0x0FFF | | | codecs | | +----------------------+----------------------------+--------------+
videoCodecs 属性:
Flag values for the videoCodecs Property: +----------------------+----------------------------+--------------+ | Codec Flag | Usage | Value | +----------------------+----------------------------+--------------+ | SUPPORT_VID_UNUSED | Obsolete value | 0x0001 | +----------------------+----------------------------+--------------+ | SUPPORT_VID_JPEG | Obsolete value | 0x0002 | +----------------------+----------------------------+--------------+ | SUPPORT_VID_SORENSON | Sorenson Flash video | 0x0004 | +----------------------+----------------------------+--------------+ | SUPPORT_VID_HOMEBREW | V1 screen sharing | 0x0008 | +----------------------+----------------------------+--------------+ | SUPPORT_VID_VP6 (On2)| On2 video (Flash 8+) | 0x0010 | +----------------------+----------------------------+--------------+ | SUPPORT_VID_VP6ALPHA | On2 video with alpha | 0x0020 | | (On2 with alpha | channel | | | channel) | | | +----------------------+----------------------------+--------------+ | SUPPORT_VID_HOMEBREWV| Screen sharing version 2 | 0x0040 | | (screensharing v2) | (Flash 8+) | | +----------------------+----------------------------+--------------+ | SUPPORT_VID_H264 | H264 video | 0x0080 | +----------------------+----------------------------+--------------+ | SUPPORT_VID_ALL | All RTMP-supported video | 0x00FF | | | codecs | | +----------------------+----------------------------+--------------+
videoFunction 属性:
Flag values for the videoFunction property: +----------------------+----------------------------+--------------+ | Function Flag | Usage | Value | +----------------------+----------------------------+--------------+ | SUPPORT_VID_CLIENT | Indicates that the client | 1 | | _SEEK | can perform frame-accurate | | | | seeks. | | +----------------------+----------------------------+--------------+
encoding 属性:
Values for the object encoding property: +----------------------+----------------------------+--------------+ | Encoding Type | Usage | Value | +----------------------+----------------------------+--------------+ | AMF0 | AMF0 object encoding | 0 | | | supported by Flash 6 and | | | | later | | +----------------------+----------------------------+--------------+ | AMF3 | AMF3 encoding from | 3 | | | Flash 9 (AS3) | | +----------------------+----------------------------+--------------+
服务器端到客户端的命令的结构如下:
+--------------+----------+----------------------------------------+ | Field Name | Type | Description | +--------------+----------+----------------------------------------+ | Command Name | String | _result or _error; indicates whether | | | | the response is result or error. | +--------------+----------+----------------------------------------+ | Transaction | Number | Transaction ID is 1 for connect | | ID | | responses | | | | | +--------------+----------+----------------------------------------+ | Properties | Object | Name-value pairs that describe the | | | | properties(fmsver etc.) of the | | | | connection. | +--------------+----------+----------------------------------------+ | Information | Object | Name-value pairs that describe the | | | | response from|the server. ’code’, | | | | ’level’, ’description’ are names of few| | | | among such information. | +--------------+----------+----------------------------------------+
命令执行时消息流动如下:
+--------------+ +-------------+ | Client | | | Server | +------+-------+ | +------+------+ | Handshaking done | | | | | | | | | | | | | |----------- Command Message(connect) ------->| | | |<------- Window Acknowledgement Size --------| | | |<----------- Set Peer Bandwidth -------------| | | |-------- Window Acknowledgement Size ------->| | | |<------ User Control Message(StreamBegin) ---| | | |<------------ Command Message ---------------| | (_result- connect response) | | | Message flow in the connect command
- 客户端发送 connect 命令到服务器端以请求对服务器端应用实例的连接。
- 收到 connect 命令后,服务器端发送协议消息 '窗口确认大小' 到客户端。服务器端也会连接到 connect 命令中提到的应用。
- 服务器端发送协议消息 '设置对端带宽' 到客户端。
- 在处理完协议消息 '设置对端带宽' 之后客户端发送协议消息 '窗口确认大小' 到服务器端。
- 服务器端发送另一个用户控制消息 (StreamBegin) 类型的协议消息到客户端。
- 服务器端发送结果命令消息告知客户端连接状态 (success/fail)。这一命令定义了事务 ID (常常为 connect 命令设置为 1)。这一消息也定义了一些属性,比如 FMS 服务器版本 (字符串)。此外,它还定义了其他连接关联到的信息,比如 level (字符串)、code (字符串)、description (字符串)、objectencoding (数字) 等等。
7.2.1.2 call
NetConnection对象的call方法在接收端运行远程过程调用(RPC)。调用的RPC名称作为参数传递给调用命令。
命令结构如下:
+--------------+----------+----------------------------------------+ | Field Name | Type | Description | +--------------+----------+----------------------------------------+ | Procedure | String | Name of the remote procedure that is | | Name | | called. | +--------------+----------+----------------------------------------+ | Transaction | Number | If a response is expected we give a | | ID | | transaction Id. Else we pass a value of| | | | 0 | +--------------+----------+----------------------------------------+ | Command | Object | If there exists any command info this | | Object | | is set, else this is set to null type. | +--------------+----------+----------------------------------------+ | Optional | Object | Any optional arguments to be provided | | Arguments | | | +--------------+----------+----------------------------------------+
回复的命令结构如下:
+--------------+----------+----------------------------------------+ | Field Name | Type | Description | +--------------+----------+----------------------------------------+ | Command Name | String | Name of the command. | | | | | +--------------+----------+----------------------------------------+ | Transaction | Number | ID of the command, to which the | | ID | | response belongs. | +--------------+----------+----------------------------------------+ | Command | Object | If there exists any command info this | | Object | | is set, else this is set to null type. | +--------------+----------+----------------------------------------+ | Response | Object | Response from the method that was | | | | called. | +------------------------------------------------------------------+
7.2.1.3 createStream
客户端发送这一命令到服务器端为消息连接创建一个逻辑通道。音频、视频和元数据使用 createStream 命令创建的流通道传输。NetConnection 是默认的通信通道,它的流 ID 为 0。协议和一些命令消息,包括 createStream,使用默认的通信通道。
客户端发送命令结构如下:
+--------------+----------+----------------------------------------+ | Field Name | Type | Description | +--------------+----------+----------------------------------------+ | Command Name | String | Name of the command. Set to | | | | "createStream". | +--------------+----------+----------------------------------------+ | Transaction | Number | Transaction ID of the command. | | ID | | | +--------------+----------+----------------------------------------+ | Command | Object | If there exists any command info this | | Object | | is set, else this is set to null type. | +--------------+----------+----------------------------------------+
服务端响应结构如下:
+--------------+----------+----------------------------------------+ | Field Name | Type | Description | +--------------+----------+----------------------------------------+ | Command Name | String | _result or _error; indicates whether | | | | the response is result or error. | +--------------+----------+----------------------------------------+ | Transaction | Number | ID of the command that response belongs| | ID | | to. | +--------------+----------+----------------------------------------+ | Command | Object | If there exists any command info this | | Object | | is set, else this is set to null type. | +--------------+----------+----------------------------------------+ | Stream ID | Number | The return value is either a stream ID | | | | or an error information object. | +--------------+----------+----------------------------------------+
7.2.2 NetStream 命令
NetStream 定义了传输通道,通过这个通道,音频流、视频流以及数据消息流可以通过 NetConnection 传输。一个NetConnection对象可以支持多个NetStreams用于多个数据流。
客户端可以通过NetStream向服务器发送如下命令:
- play
- play2
- deleteStream
- closeStream
- receiveAudio
- receiveVideo
- publish
- seek
- pause
服务器端使用 "onStatus" 命令向客户端发送 NetStream 状态:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名 "onStatus"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | onStatus 消息没有命令对象。 |
Info Object | 对象 | 一个 AMF 对象至少要有以下三个属性。"level" (字符串):这一消息的等级,"warning"、"status"、"error" 中的某个值;"code" (字符串):消息码,例如 "NetStream.Play.Start";"description" (字符串):关于这个消息人可读描述。 |
7.2.2.1 play
客户端发送此命令到服务器端播放流。也可以多次使用此命令创建一个播放列表。
如果你想要创建一个动态的播放列表,然后在列表上切换直播和录播,可以多次调用 play 方法,并在每次调用时 reset 设置为 false。相反的,如果你想要立即播放指定流,将其他等待播放的流清空,应该将 reset 设置为 true。
客户端发送到服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | "play" |
Transaction ID | 数字 | 0 |
Command Object | Null | 命令信息不存在。设为 null 类型。 |
Stream Name | 字符串 | 要播放流的名字。要播放视频 (FLV) 文件,使用没有文件扩展名的名字对流名进行定义 (例如,"sample")。要重播 MP3 或者 ID3,你必须在流名前加上 mp3:例如,"mp3:sample"。要播放 H.264/AAC 文件,你必须在流名前加上 mp4:并指定文件扩展名。例如,要播放 sample.m4v 文件,定义 "mp4:sample.m4v"。 |
Start | 数字 | 一个可选的参数,以秒为单位定义开始时间。默认值为 -2,表示用户首先尝试播放流名字段中定义的直播流。如果那个名字的直播流没有找到,它将播放同名的录制流。如果没有那个名字的录制流,客户端将等待一个新的那个名字的直播流,并当其有效时进行播放。如果你在 Start 字段中传递 -1,那么就只播放流名中定义的那个名字的直播流。如果你在 Start 字段中传递 0 或一个正整数,那么将从 Start 字段定义的时间开始播放流名中定义的那个录制流。如果没有找到录制流,那么将播放播放列表中的下一项。 |
Duration | 数字 | 一个可选的参数,以秒为单位定义了回放的持续时间。默认值为 -1,-1 值意味着一个直播流会一直播放到它不再可用或者一个录制流一直播放到结束。如果你传递 0 值,它将只播放单一一帧,因为播放时间已经在录制流的开始的 Start 指定了。假定 Start 的值大于或者等于 0,你传递一个正数,将播放 Duration 字段定义的一段直播流。(如果流在 Duration 字段定义的时间段内结束,那么流结束时回放结束)。如果你在 Duration 字段中传递一个小于 -1 的值,它将按照 -1 处理。 |
Reset | 布尔 | 一个可选的布尔值或者数字,是否对以前的播放列表进行清空。 |
+-------------+ +----------+ | Play Client | | | Server | +------+------+ | +-----+----+ | Handshaking and Application | | connect done | | | | | | | | | | | | | ---+---- |------Command Message(createStream) ----->| Create| | | Stream| | | ---+---- |<---------- Command Message --------------| | (_result- createStream response) | | | ---+---- |------ Command Message (play) ----------->| | | | | |<------------- SetChunkSize --------------| | | | | |<---- User Control (StreamIsRecorded) ----| Play | | | | |<---- UserControl (StreamBegin) ----------| | | | | |<--Command Message(onStatus-play reset) --| | | | | |<--Command Message(onStatus-play start) --| | | | | |<-------------Audio Message---------------| | | | | |<-------------Video Message---------------| | | | | | Keep receiving audio and video stream till finishes Message flow in the play command
命令执行时的消息流是:
- 当客户端从服务端接收到 createStream 命令的结果是为 success 时,发送 play 命令。
- 接收到 play 命令,服务器端发送一个协议消息来设置块大小。
- 服务器端发送另一个协议消息 (用户控制),这个消息中定义了 'StreamIsRecorded' 事件和流 ID。消息在前两个字节中保存事件类型,在后四个字节中保存流 ID。
- 服务器端发送另一个协议消息 (用户控制),这一消息包含 'StreamBegin' 事件,来指示发送给客户端的流的起点。
- 如果客户端发送的 play 命令成功(正确的),服务器端发送一个 onStatus 命令消息 NetStream.Play.Start & NetStream.Play.Reset。只有当客户端发送的 play 命令设置了 reset 时服务器端才会发送 NetStream.Play.Reset。如果要播放的流没有找到,服务器端发送 onStatus 消息 NetStream.Play.StreamNotFound。
之后,服务器端发送视频和音频数据,客户端对其进行播放。
7.2.2.2 play2
不同于 play 命令的是,play2 可以在不改变播放时间轴的情况下切换到不同的比特率。服务端为客户端可以在 play2 中请求所有支持的码率维护了不同的(音视频)文件。
客户端发送给服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名,设置为 "play2"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息不存在,设置为 null 类型。 |
Parameters | 对象 | 一个 AMF 编码的对象,该对象的属性是为公开的 flash.net.NetStreamPlayOptions ActionScript 对象所描述的属性。 |
NetStreamPlayOptions 对象的公开属性在 ActionScript 3 语言指南中 [AS3] 有所描述。
命令执行时的消息流动如下图所示:
+-------------+ +----------+ | Play Client | | | Server | +------+------+ | +-----+----+ | Handshaking and Application | | connect done | | | | | | | | | | | | | ---+---- |------Command Message(createStream) ----->| Create| | | Stream| | | ---+---- |<---------- Command Message --------------| | (_result- createStream response) | | | ---+---- |------ Command Message (play) ----------->| | | | | |<------------- SetChunkSize --------------| | | | | |<---- User Control (StreamIsRecorded) ----| Play | | | | |<---- UserControl (StreamBegin) ----------| | | | | |<--Command Message(onStatus-play reset) --| | | | | |<--Command Message(onStatus-play start) --| | | | | |<-------------Audio Message---------------| | | | | |<-------------Video Message---------------| | | | | | | ---+---- |--------- Command Message(play2) -------->| | | | | |<-------- Audio Message (new rate) -------| Play2 | | | | |<-------- Video Message (new rate) -------| | | | | | | | | | Keep receiving audio and video stream till finishes | Message flow in the play2 command
7.2.2.3 deleteStream
当NetStream对象被销毁时,NetStream发送deleteStream命令。
客户端发送给服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名,设置为 "deleteStream"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设为 null 类型。 |
Stream ID | 数字 | 服务端消亡的流 ID。 |
此命令服务端无响应。
7.2.2.4 receiveAudio
NetStream 通过发送 receiveAudio 消息来通知服务端是否发送音频到客户端。
客户端发送给服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名,设置为 "receiveAudio"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设为 null 类型。 |
Bool Flag | 布尔 | true 或者 false 以表明是否接受音频。 |
如果发送来的 receiveAudio 命令布尔字段被设为 false 时服务器端不发送任何回复。如果这一标识被设为 true,服务器端以状态消息 NetStream.Seek.Notify 和 NetStream.Play.Start 进行回复。
7.2.2.5 receiveVideo
NetStream 通过发送 receiveVideo 消息来通知服务端是否发送视频到客户端。
客户端发送给服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名,设置为 "receiveVideo"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设为 null 类型。 |
Bool Flag | 布尔 | true 或者 false 以表明是否接受视频。 |
如果发送来的 receiveVideo 命令布尔字段被设为 false 时服务器端不发送任何回复。如果这一标识被设为 true,服务器端以状态消息 NetStream.Seek.Notify 和 NetStream.Play.Start 进行回复。
7.2.2.6 publish
客户端发送给服务器端这一命令以发布一个有名字的流。使用这个名字,任意客户端都可以播放这个流,并接收发布的音频、视频以及数据消息。
客户端发送给服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名,设置为 "publish"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设为 null 类型。 |
Publishing Name | 字符串 | 发布的流的名字。 |
Publishing Type | 字符串 | 发布类型。可以设置为 "live"、"record" 或者 "append"。record:流被发布,数据被录制到一个新的文件。新文件被存储在服务器上包含服务应用目录的子路径。如果文件已存在,将重写。append:流被发布,数据被添加到一个文件。如果该文件没找着,将新建一个。live:直播数据只被发布,并不对其进行录制。 |
服务器端回复 onStatus 命令以标注发布的起始位置。
7.2.2.7 seek
客户端发送seek命令来移动媒体文件或播放列表中的偏移量(以毫秒为单位)。
客户端发送给服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名,设置为 "seek"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设为 null 类型。 |
milliSeconds | 数字 | 播放列表移动的毫秒数。 |
seek 命令执行成功时服务器会发送一个状态消息 NetStream.Seek.Notify。失败的话,服务器端返回一个 _error 消息。
7.2.2.8 pause
客户端发送 pause 命令以告知服务器端是暂停还是开始播放。
客户端发送给服务端的命令结构如下:
字段 | 类型 | 描述 |
---|---|---|
Command Name | 字符串 | 命令名,设置为 "pause"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设为 null 类型。 |
Pause/Unpause Flag | 布尔 | true 或者 false,来指示暂停或者恢复播放。 |
milliSeconds | 数字 | 流暂停或者恢复播放所在的时间毫秒数。这个是客户端暂停的当前流时间。当回放恢复时,服务端发送消息的 timestamp 比这个值要大。 |
当流暂停时,服务器端发送一个状态消息 NetStream.Pause.Notify。NetStream.Unpause.Notify 只有针对没有暂停的流进行发放。失败的话,服务器端返回一个 _error 消息。
8. 参考文献
- [RFC0791] Postel, J., "Internet Protocol", STD 5, RFC 791, September 1981.
- [RFC0793] Postel, J., "Transmission Control Protocol", STD 7,RFC 793, September 1981.
- [RFC1982] Elz, R. and R. Bush, "Serial Number Arithmetic", RFC 1982, August 1996.
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
- [AS3] Adobe Systems, Inc., "ActionScript 3.0 Reference for the Adobe Flash Platform", 2011, http://www.adobe.com/devnet/actionscript/documentation.html.
- [AMF0] Adobe Systems, Inc., "Action Message Format -- AMF 0", December 2007, http://opensource.adobe.com/wiki/download/attachments/1114283/amf0_spec_121207.pdf.
- [AMF3] Adobe Systems, Inc., "Action Message Format -- AMF 3", May 2008, http://opensource.adobe.com/wiki/download/attachments/1114283/amf3_spec_05_05_08.pdf.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!