本文是为截至发稿时止最新 Adobe 官方公布的 RTMP 规范。本文包含 RTMP 规范的所有内容。是第一个比较全面的 RTMP 规范的中译本。因为成文时间仓促,加上做者知识面所限,翻译错误之处在所不免,恳请各位朋友热心指出,能够直接在博客后面留言,先行谢过。html
rtmp_specification_1.0.pdf 官方下载地址: http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf.算法
请随时关注官方文档更新: http://www.adobe.com/cn/devnet/rtmp.html。缓存
如下内容来自 rtmp_specification_1.0.pdf。服务器
Adobe 公司的实时消息传输协议 (RTMP) 经过一个可靠地流传输提供了一个双向多通道消息服务,好比 TCP [RFC0793],意图在通讯端之间传递带有时间信息的视频、音频和数据消息流。实现一般对不一样类型的消息分配不一样的优先级,当运载能力有限时,这会影响等待流传输的消息的次序。本文档将对实时流传输协议 (Real Time Messaging Protocol) 的语法和操做进行描述。网络
本文档中出现的关键字,"MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"NOT RECOMMENDED"、"MAY" 、"OPTIONAL",都将在 [RFC2119] 中进行解释。并发
Rajesh Mallipeddi,Adobe Systems 原成员,起草了本文档原始规范,并提供大部分的原始内容。app
Mohit Srivastava,Adobe Systems 成员,促成了本规范的开发。dom
Payload (有效载荷):包含于一个数据包中的数据,例如音频采样或者压缩的视频数据。payload 的格式和解释,超出了本文档的范围。异步
Packet (数据包):一个数据包由一个固定头和有效载荷数据构成。一些个底层协议可能会要求对数据包定义封装。分布式
Port (端口):"传输协议用以区分开指定一台主机的不一样目的地的一个抽象。TCP/IP 使用小的正整数对端口进行标识。" OSI 传输层使用的运输选择器 (TSEL) 至关于端口。
Transport address (传输地址):用以识别传输层端点的网络地址和端口的组合,例如一个 IP 地址和一个 TCP 端口。数据包由一个源传输地址传送到一个目的传输地址。
Message stream (消息流):通讯中消息流通的一个逻辑通道。
Message stream ID (消息流 ID):每一个消息有一个关联的 ID,使用 ID 能够识别出流通中的消息流。
Chunk (块):消息的一段。消息在网络发送以前被拆分红不少小的部分。块能够确保端到端交付全部消息有序 timestamp,即便有不少不一样的流。
Chunk stream (块流):通讯中容许块流向一个特定方向的逻辑通道。块流能够从客户端流向服务器,也能够从服务器流向客户端。
Chunk stream ID (块流 ID):每一个块有一个关联的 ID,使用 ID 能够识别出流通中的块流。
Multiplexing (合成):将独立的音频/视频数据合成为一个连续的音频/视频流的加工,这样能够同时发送几个视频和音频。
DeMultiplexing (分解):Multiplexing 的逆向处理,将交叉的音频和视频数据还原成原始音频和视频数据的格式。
Remote Procedure Call (RPC 远程方法调用):容许客户端或服务器调用对端的一个子程序或者程序的请求。
Metadata (元数据):关于数据的一个描述。一个电影的 metadata 包括电影标题、持续时间、建立时间等等。
Application Instance (应用实例):服务器上应用的实例,客户端能够链接这个实例并发送链接请求。
Action Message Format (AMF 动做消息格式协议):一个用于序列化 ActionScript 对象图的紧凑的二进制格式。AMF 有两个版本:AMF 0 [AMF0] 和 AMF 3 [AMF3]。
全部整数型属性以网络字节顺序传输,字节 0 表明第一个字节,零位是一个单词或字段最经常使用的有效位。字节序一般是大端排序。关于传输顺序的更多细节描述参考 IP 协议[RFC0791]。除非另外注明,本文档中的数值常量都是十进制的 (以 10 为基础)。
除非另有规定,RTMP 中的全部数据都是字节对准的;例如,一个十六位的属性可能会在一个奇字节偏移上。填充后,填充字节应该有零值。
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 位。
本节介绍实时消息传输协议的块流 (RTMP 块流)。 它为上层多媒体流协议提供合并和打包的服务。
当设计 RTMP 块流使用实时消息传输协议时,它能够处理任何发送消息流的协议。每一个消息包含 timestamp 和 payload 类型标识。RTMP 块流和 RTMP 一块儿适合各类音频-视频应用,从一对一和一对多直播到点播服务,到互动会议应用。
当使用可靠传输协议时,好比 TCP [RFC0793],RTMP 块流可以对于多流提供全部消息可靠的 timestamp 有序端对端传输。RTMP 块流并不提供任何优先权或相似形式的控制,可是能够被上层协议用来提供这种优先级。例如,一个直播视频服务器可能会基于发送时间或者每一个消息的确认时间丢弃一个传输缓慢的客户端的视频消息以确保及时获取其音频消息。
RTMP 块流包括其自身的带内协议控制信息,而且提供机制为上层协议植入用户控制消息。
能够被分割为块以支持组合的消息的格式取决于上层协议。消息格式必须包含如下建立块所需的字段。
Timestamp:消息的 timestamp。这个字段能够传输四个字节。
Length:消息的有效负载长度。若是不能省略掉消息头,那它也被包括进这个长度。这个字段占用了块头的三个字节。
Type Id:一些类型 ID 保留给协议控制消息使用。这些传播信息的消息由 RTMP 块流协议和上层协议共同处理。其余的全部类型 ID 可用于上层协议,它们被 RTMP 块流处理为不透明值。事实上,RTMP 块流中没有任何地方要把这些值当作类型使用;全部消息必须是同一类型,或者应用使用这一字段来区分同步跟踪,而不是类型。这一字段占用了块头的一个字节。
Message Stream ID:message stream (消息流) ID 可使任意值。合并到同一个块流的不一样的消息流是根据各自的消息流 ID 进行分解。除此以外,对 RTMP 块流而言,这是一个不透明的值。这个字段以小端格式占用了块头的四个字节。
一个 RTMP 链接以握手开始。RTMP 的握手不一样于其余协议;RTMP 握手由三个固定长度的块组成,而不是像其余协议同样的带有报头的可变长度的块。
客户端 (发起链接请求的终端) 和服务器端各自发送相同的三块。便于演示,当发送自客户端时这些块被指定为 C0、C1 和 C2;当发送自服务器端时这些块分别被指定为 S0、S1 和 S2。
握手以客户端发送 C0 和 C1 块开始。
客户端必须等待接收到 S1 才能发送 C2。
客户端必须等待接收到 S2 才能发送任何其余数据。
服务器端必须等待接收到 C0 才能发送 S0 和 S1,也能够等待接收到 C1 再发送 S0 和 S1。服务器端必须等待接收到 C1 才能发送 S2。服务器端必须等待接收到 C2 才能发送任何其余数据。
C0 和 S0 包都是一个单一的八位字节,以一个单独的八位整型域进行处理:
如下是 C0/S0 包中的字段:
版本号 (八位):在 C0 中,这一字段指示出客户端要求的 RTMP 版本号。在 S0 中,这一字段指示出服务器端选择的 RTMP 版本号。本文档中规范的版本号为 3。0、一、2 三个值是由早期其余产品使用的,是废弃值;4 - 31 被保留为 RTMP 协议的将来实现版本使用;32 - 255 不容许使用 (以区分开 RTMP 和其余常以一个可打印字符开始的文本协议)。没法识别客户端所请求版本号的服务器应该以版本 3 响应,(收到响应的) 客户端能够选择下降到版本 3,或者放弃握手。
C1 和 S1 数据包的长度都是 1536 字节,包含如下字段:
Time (四个字节):这个字段包含一个 timestamp,用于本终端发送的全部后续块的时间起点。这个值能够是 0,或者一些任意值。要同步多个块流,终端能够发送其余块流当前的 timestamp 的值。
Zero (四个字节):这个字段必须都是 0。
Random data (1528 个字节):这个字段能够包含任意值。终端须要区分出响应来自它发起的握手仍是对端发起的握手,这个数据应该发送一些足够随机的数。这个不须要对随机数进行加密保护,也不须要动态值。
C2 和 S2 数据包长度都是 1536 字节,基本就是 S1 和 C1 的副本 (分别),包含有如下字段:
Time (四个字节):这个字段必须包含终端在 S1 (给 C2) 或者 C1 (给 S2) 发的 timestamp。
Time2 (四个字节):这个字段必须包含终端先前发出数据包 (s1 或者 c1) timestamp。
Random echo (1528 个字节):这个字段必须包含终端发的 S1 (给 C2) 或者 S2 (给 C1) 的随机数。两端均可以一块儿使用 time 和 time2 字段再加当前 timestamp 以快速估算带宽和/或者链接延迟,但这不太多是有多大用处。
下面描述了握手示意图中提到的状态:
Uninitialized (未初始化):协议的版本号在这个阶段被发送。客户端和服务器都是 uninitialized (未初始化) 状态。以后客户端在数据包 C0 中将协议版本号发出。若是服务器支持这个版本,它将在回应中发送 S0 和 S1。若是不支持呢,服务器会才去适当的行为进行响应。在 RTMP 协议中,这个行为就是终止链接。
Version Sent (版本已发送):在未初始化状态以后,客户端和服务器都进入 Version Sent (版本已发送) 状态。客户端会等待接收数据包 S1 而服务器在等待 C1。一旦拿到期待的包,客户端会发送数据包 C2 而服务器发送数据包 S2。(客户端和服务器各自的)状态随即变为 Ack Sent (确认已发送)。
Ack Sent (确认已发送):客户端和服务器分别等待 S2 和 C2。
Handshake Done (握手结束):客户端和服务器能够开始交换消息了。
握手以后,链接开始对一个或多个块流进行合并。建立的每一个块都有一个惟一 ID 对其进行关联,这个 ID 叫作 chunk stream ID (块流 ID)。这些块经过网络进行传输。传递时,每一个块必须被彻底发送才能够发送下一块。在接收端,这些块被根据块流 ID 被组装成消息。
分块容许上层协议将大的消息分解为更小的消息,例如,防止体积大的但优先级小的消息 (好比视频) 阻碍体积较小但优先级高的消息 (好比音频或者控制命令)。
分块也让咱们可以使用较小开销发送小消息,由于块头包含包含在消息内部的信息压缩提示。
块的大小是能够配置的。它可使用一个设置块大小的控制消息进行设置 (参考 5.4.1)。更大的块大小能够下降 CPU 开销,但在低带宽链接时由于它的大量的写入也会延迟其余内容的传递。更小的块不利于高比特率的流化。因此块的大小设置取决于具体状况。
每一个块包含一个头和数据体。块头包含三个部分:
Basic Header (基本头,1 到 3 个字节):这个字段对块流 ID 和块类型进行编码。块类型决定了消息头的编码格式。(这一字段的) 长度彻底取决于块流 ID,由于块流 ID 是一个可变长度的字段。
Message Header (消息头,0,3,7,或者 11 个字节):这一字段对正在发送的消息 (无论是整个消息,仍是只是一小部分) 的信息进行编码。这一字段的长度可使用块头中定义的块类型进行决定。
Extended Timestamp (扩展 timestamp,0 或 4 字节):这一字段是否出现取决于块消息头中的 timestamp 或者 timestamp delta 字段。更多信息参考 5.3.1.3 节。
Chunk Data (有效大小):当前块的有效负载,至关于定义的最大块大小。
块基本头对块流 ID 和块类型 (由下图中的 fmt 字段表示) 进行编码。块基本头字段可能会有 1,2 或者 3 个字节,取决于块流 ID。
一个 (RTMP) 实现应该使用可以容纳这个 ID 的最小的容量进行表示。
RTMP 协议最多支持 65597 个流,流 ID 范围 3 - 65599。ID 0、一、2 被保留。0 值表示二字节形式,而且 ID 范围 64 - 319 (第二个字节 + 64)。1 值表示三字节形式,而且 ID 范围为 64 - 65599 ((第三个字节) * 256 + 第二个字节 + 64)。3 - 63 范围内的值表示整个流 ID。带有 2 值的块流 ID 被保留,用于下层协议控制消息和命令。
块基本头中的 0 - 5 位 (最低有效) 表明块流 ID。
块流 ID 2 - 63 能够编进这一字段的一字节版本中。
块流 ID 64 - 319 能够以二字节的形式编码在头中。ID 计算为 (第二个字节 + 64):
块流 ID 64 - 65599 能够编码在这个字段的三字节版本中。ID 计算为 ((第三个字节) * 256 + (第二个字节) + 64)。
cs id (六位):这一字段包含有块流 ID,值的范围是 2 - 63。值 0 和 1 用于指示这一字段是 2- 或者 3- 字节版本。
fmt (两个字节):这一字段指示 'chunk message header' 使用的四种格式之一。没中块类型的 'chunk message header' 会在下一小节解释。
cs id - 64 (8 或者 16 位):这一字段包含了块流 ID 减掉 64 后的值。例如,ID 365 在 cs id 中会以一个 1 进行表示,和这里的一个 16 位 的 301 (cs id - 64)。
块流 ID 64 - 319 可使用 2-byte 或者 3-byte 的形式在头中表示。
块消息头又四种不一样的格式,由块基本头中的 "fmt" 字段进行选择。
一个 (RTMP) 实现应该为每一个块消息头使用最紧凑的表示。
类型 0 块头的长度是 11 个字节。这一类型必须用在块流的起始位置,和流 timestamp 重来的时候 (好比,重置)。
timestamp (三个字节):对于 type-0 块,当前消息的绝对 timestamp 在这里发送。若是 timestamp 大于或者等于 16777215 (十六进制 0xFFFFFF),这一字段必须是 16777215,代表有扩展 timestamp 字段来补充完整的 32 位 timestamp。不然的话,这一字段必须是整个的 timestamp。
类型 1 块头长为 7 个字节。不包含消息流 ID;这一块使用前一块同样的流 ID。可变长度消息的流 (例如,一些视频格式) 应该在第一块以后使用这一格式表示以后的每一个新消息。
类型 2 块头长度为 3 个字节。既不包含流 ID 也不包含消息长度;这一块具备和前一块相同的流 ID 和消息长度。具备不变长度的消息 (例如,一些音频和数据格式) 应该在第一块以后使用这一格式表示以后的每一个新消息。
类型 3 的块没有消息头。流 ID、消息长度以及 timestamp delta 等字段都不存在;这种类型的块使用前面块同样的块流 ID。当单一一个消息被分割为多块时,除了第一块的其余块都应该使用这种类型。参考例 2 (5.3.2.2 小节)。组成流的消息具备一样的大小,流 ID 和时间间隔应该在类型 2 以后的全部块都使用这一类型。参考例 1 (5.3.2.1 小节)。若是第一个消息和第二个消息之间的 delta 和第一个消息的 timestamp 同样的话,那么在类型 0 的块以后要紧跟一个类型 3 的块,由于无需再来一个类型 2 的块来注册 delta 了。若是一个类型 3 的块跟着一个类型 0 的块,那么这个类型 3 块的 timestamp delta 和类型 0 块的 timestamp 是同样的。
块消息头中各字段的描述以下:
timestamp delta (三个字节):对于一个类型 1 或者类型 2 的块,前一块的 timestamp 和当前块的 timestamp 的区别在这里发送。若是 delta 大于或者等于 16777215 (十六进制 0xFFFFFF),那么这一字段必须是为 16777215,表示具备扩展 timestamp 字段来对整个 32 位 delta 进行编码。不然的话,这一字段应该是为具体 delta。
message length (三个字节):对于一个类型 0 或者类型 1 的块,消息长度在这里进行发送。注意这一般不一样于块的有效载荷的长度。块的有效载荷表明全部的除了最后一块的最大块大小,以及剩余的 (也多是小消息的整个长度) 最后一块。
message type id (消息类型 id,一个字节):对于类型 0 或者类型 1 的块,消息的类型在这里发送。
message stream id (四个字节):对于一个类型为 0 的块,保存消息流 ID。消息流 ID 以小端格式保存。全部同一个块流下的消息都来自同一个消息流。当能够将不一样的消息流组合进同一个块流时,这种方法比头压缩的作法要好。可是,当一个消息流被关闭而其余的随后另外一个是打开着的,就没有理由将现有块流以发送一个新的类型 0 的块进行复用了。
扩展 timestamp 字段用于对大于 16777215 (0xFFFFFF) 的 timestamp 或者 timestamp delta 进行编码;也就是,对于不适合于在 24 位的类型 0、1 和 2 的块里的 timestamp 和 timestamp delta 编码。这一字段包含了整个 32 位的 timestamp 或者 timestamp delta 编码。能够经过设置类型 0 块的 timestamp 字段、类型 1 或者 2 块的 timestamp delta 字段 16777215 (0xFFFFFF) 来启用这一字段。当最近的具备同一块流的类型 0、1 或 2 块指示扩展 timestamp 字段出现时,这一字段才会在类型为 3 的块中出现。
这个例子演示了一个简单地音频消息流。这个例子演示了信息的冗余。
下一个表格演示了这个流所产生的块。从消息 3 起,数据传输获得了最佳化利用。每条消息的开销在这一点以后都只有一个字节。
这一例子阐述了一条消息太大,没法装在一个 128 字节的块里,被分割为若干块。
这是传输的块:
块 1 的数据头说明了整个消息长度是为 307 个字节。由以上俩例子能够得知,块类型 3 能够被用于两种不一样的方式。第一种是用于定义一条消息的配置。第二种是定义一个能够从现有状态数据中派生出来的新消息的起点。
RTMP 块流使用消息类型 ID 为 一、二、三、5 和 6 用于协议控制消息。这些消息包含有 RTMP 块流协议所须要的信息。
这些协议控制消息必须使用消息流 ID 0 (做为已知控制流) 并以流 ID 为 2 的块发送。协议控制消息一旦被接收到就当即生效;协议控制消息的 timestamp 被忽略。
协议控制消息 1,设置块大小,以通知对端一个新的最大块大小。
默认的最大块大小是为 128 字节,可是客户端或者服务器能够改变这个大小,并使用这一消息对对端进行更新。例如,假定一个客户端想要发送一个 131 字节的音频数据,当前块大小是默认的 128。在这种状况下,客户端能够发送这种消息到服务器以通知它块大小如今是 131 字节了。这样客户端就能够在单一块中发送整个音频数据了。
最大块大小设置的话最少为 128 字节,包含内容最少要一个字节。最大块大小由每一个方面 (服务器或者客户端) 自行维护。
0:这个位必须为 0。
chunk size (块大小,31 位):这一字段保存新的最大块大小值,以字节为单位,这将用于以后发送者发送的块,直到有更多 (关于最大块大小的) 通知。有效值为 1 到 2147483647 (0x7FFFFFFF,1 和 2147483647 均可取); 可是全部大于 16777215 (0xFFFFFF) 的大小值是等价的,由于没有一个块比一整个消息大,而且没有一个消息大于 16777215 字节。
协议控制消息 2,终止消息,用于通知对端,若是对端在等待去完成一个消息的块的话,而后抛弃一个块流中已接受到的部分消息。对端接收到块流 ID 做为当前协议消息的有效负载。一些程序可能会在关闭的时候使用这个消息以指示不须要进一步对这个消息的处理了。
chunk stream ID (块流 ID,32 位):这一字段保存块流 ID,该流的当前消息会被丢弃。
客户端或者服务器在接收到等同于窗口大小的字节以后必需要发送给对端一个确认。窗口大小是指发送者在没有收到接收者确认以前发送的最大数量的字节。这个消息定义了序列号,也就是目前接收到的字节数。
sequence number (序列号,32 位):这一字段保存有目前接收到的字节数。
客户端或者服务器端发送这条消息来通知对端发送和应答之间的窗口大小。发送者在发送完窗口大小字节以后期待对端的确认。接收端在上次确认发送后接收到的指示数值后,或者会话创建以后还没有发送确认,必须发送一个确认 (5.4.3 小节)。
客户端或者服务器端发送这一消息来限制其对端的输出带宽。对端接收到这一消息后,将经过限制这一消息中窗口大小指出的已发送但未被答复的数据的数量以限制其输出带宽。接收到这一消息的对端应该回复一个窗口确认大小消息,若是这个窗口大小不一样于其发送给 (设置对端带宽) 发送者的最后一条消息。
限制类型取如下值之一:
0 - Hard:对端应该限制其输出带宽到指示的窗口大小。
1 - Soft:对端应该限制其输出带宽到知识的窗口大小,或者已经有限制在其做用的话就取二者之间的较小值。
2 - Dynamic:若是先前的限制类型为 Hard,处理这个消息就好像它被标记为 Hard,不然的话忽略这个消息。
这一节定义了使用下层传输层 (好比 RTMP 块流协议) 传输的 RTMP 消息的格式。
RTMP 协议设计使用 RTMP 块流,可使用其余任意传输协议对消息进行发送。RTMP 块流和 RTMP 一块儿适用于多种音频 - 视频应用,从一对一和一对多直播到点播服务,再到互动会议应用。
服务器端和客户端经过网络发送 RTMP 消息来进行彼此通讯。消息能够包含音频、视频、数据,或者其余消息。
RTMP 消息有两部分:头和它的有效载荷。
消息头包含如下:
Message Type (消息类型):一个字节的字段来表示消息类型。类型 ID 1 - 6 被保留用于协议控制消息。
Length (长度):三个字节的字段来表示有效负载的字节数。以大端格式保存。
Timestamp:四个字节的字段包含了当前消息的 timestamp。四个字节也以大端格式保存。
Message Stream Id (消息流 ID):三个字节的字段以指示出当前消息的流。这三个字节以大端格式保存。
消息的另外一个部分就是有效负载,这是这个消息所包含的实际内容。例如,它能够是一些音频样本或者压缩的视频数据。有效载荷格式和解释不在本文档范围以内。
RTMP 使用消息类型 ID 4 表示用户控制消息。这些消息包含 RTMP 流传输层所使用的信息。RTMP 块流协议使用 ID 为 一、二、三、5 和 6 (5.4 节介绍)。
用户控制消息应该使用消息流 ID 0 (以被认为是控制流),而且以 RTMP 块流发送时以块流 ID 为 2。用户控制消息一旦被接收立马生效;它们的 timestamp 是被忽略的。
客户端或者服务器端发送这个消息来通知对端用户操做事件。这一消息携带有事件类型和事件数据。
消息数据的前两个字节用于指示事件类型。事件类型被事件数据紧随。事件数据字段的大小是可变的。可是,若是消息必须经过 RTMP 块流层传输时,最大块大小 (5.4.1 节) 应该足够大以容许这些消息填充在一个单一块中。
事件类型和事件数据格式将在 7.1.7 小节列出。
这一节描述了在服务器端和客户端彼此通讯交换的消息和命令的不一样的类型。
服务器端和客户端交换的不一样消息类型包括用于发送音频数据的音频消息、用于发送视频数据的视频消息、用于发送任意用户数据的数据消息、共享对象消息以及命令消息。共享对象消息提供了一个通用的方法来管理多用户和一台服务器之间的分布式数据。命令消息在客户端和服务器端传输 AMF 编码的命令。客户端或者服务器端能够经过使用命令消息和对端通讯的流请求远程方法调用 (RPC)。
服务器端和客户端经过在网络中发送消息来进行彼此通讯。消息能够是任何类型,包含音频消息,视频消息,命令消息,共享对象消息,数据消息,以及用户控制消息。
命令消息在客户端和服务器端传递 AMF 编码的命令。这些消息被分配以消息类型值为 20 以进行 AMF0 编码,消息类型值为 17 以进行 AMF3 编码。这些消息发送以进行一些操做,好比,链接,建立流,发布,播放,对端暂停。命令消息,像 onstatus、result 等等,用于通知发送者请求的命令的状态。一个命令消息由命令名、事务 ID 和包含相关参数的命令对象组成。一个客户端或者一个服务器端能够经过和对端通讯的流使用这些命令消息请求远程调用 (RPC)。
客户端或者服务器端经过发送这些消息以发送元数据或者任何用户数据到对端。元数据包括数据 (音频,视频等等) 的详细信息,好比建立时间,时长,主题等等。这些消息被分配以消息类型为 18 以进行 AMF0 编码和消息类型 15 以进行 AMF3 编码。
所谓共享对象实际上是一个 Flash 对象 (一个名值对的集合),这个对象在多个不一样客户端、应用实例中保持同步。消息类型 19 用于 AMF0 编码、16 用于 AMF3 编码都被为共享对象事件保留。每一个消息能够包含有不一样事件。
支持如下事件类型:
事件 | 描述 |
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) | 服务器端发送给客户端这一事件表示链接成功。 |
客户端或者服务器端发送这一消息以发送音频数据到对端。消息类型 8 为音频消息保留。
客户端或者服务器发送这一消息以发送视频数据到对端。消息类型 9 为视频消息保留。
统计消息是一个单一的包含一系列的使用 6.1 节描述的 RTMP 子消息的消息。消息类型 22 用于统计消息。
统计消息的消息流 ID 覆盖了统计中子消息的消息流 ID。
统计消息里的 timestamp 和第一个子消息的 timestamp 的不一样点在于子消息的 timestamp 被相对流时间标调整了偏移。每一个子消息的 timestamp 被加入偏移以达到一个统一流时间。第一个子消息的 timestamp 应该和统计消息的 timestamp 同样,因此这个偏移量应该为 0。
反向指针包含有前一个消息的大小 (包含前一个消息的头)。这样子匹配了 FLV 文件的格式,用于反向查找。
使用统计消息具备如下性能优点:
客户端或者服务器端发送这一消息来通知对端用户控制事件。关于这个的消息格式参考 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 那个。 |
客户端和服务器端交换 AMF 编码的命令。服务器端发送一个命令消息,这个命令消息由命令名、事务 ID 以及包含有相关参数的命令对象组成。例如,包含有 'app' 参数的链接命令,这个命令说明了客户端链接到的服务器端的应用名。接收者处理这一命令并回发一个一样事务 ID 的响应。回复字符串能够是 _result、_error 或者 一个方法名的任意一个,好比,verifyClient 或者 contactExternalServer。
命令字符串 _result 或者 _error 是响应信号。事务 ID 指示出响应所指向的命令。这和 AMAP 和其余一些协议的标签同样。命令字符串中的方法名表示发送者试图执行接收者一端的一个方法。
如下类的对象用于发送不一样的命令:
NetConnection 表明上层的服务器端和客户端之间链接的一个对象。
NetStream 一个表明发送音频流、视频流和其余相关数据的通道的对象。固然,咱们也会发送控制数据流的命令,诸如 play、pause 等等。
NetConnection 管理着一个客户端应用和服务器端之间的双相链接。此外,它还提供远程方法的异步调用。
NetConnection 能够发送如下命令:
客户端发送 connect 命令到服务器端来请求链接到一个服务器应用的实例。
由客户端发送到服务器端的 connect 命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令的名字。设置给 "connect"。 |
Transaction ID | 数字 | 老是设置为 1。 |
Command Object | 对象 | 具备名值对的命令信息对象。 |
Optional User Arguments | 对象 | 任意可选信息。 |
如下是为 connect 命令中使用的名值对对象的描述。
属性 | 类型 | 描述 | 范例 |
app | 字符串 | 客户端链接到的服务器端应用的名字。 | testapp |
flashver | 字符串 | Flash Player 版本号。和ApplicationScript getversion() 方法返回的是同一个字符串。 | FMSc/1.0 |
swfUrl | 字符串 | 进行当前链接的 SWF 文件源地址。 | file://C:/FlvPlayer.swf |
tcUrl | 字符串 | 服务器 URL。具备如下格式:protocol://servername:port/appName/appInstance | rtmp://localhost:1935/testapp/instance1 |
fpad | 布尔 | 若是使用了代理就是 true。 | true 或者 false。 |
audioCodecs | 数字 | 代表客户端所支持的音频编码。 | SUPPORT_SND_MP3 |
videoCodecs | 数字 | 代表支持的视频编码。 | SUPPORT_VID_SORENSON |
videoFunction | 数字 | 代表所支持的特殊视频方法。 | SUPPORT_VID_CLIENT_SEEK |
pageUrl | 字符串 | SWF 文件所加载的网页 URL。 | http://somehost/sample.html |
objectEncoding | 数字 | AMF 编码方法。 | AMF3 |
audioCodecs 属性的标识值:
videoCodecs 属性的标识值:
videoFunction 属性的标识值:
encoding 属性值:
服务器端到客户端的命令的结构以下:
命令执行时消息流动以下:
1. 客户端发送 connect 命令到服务器端以请求对服务器端应用实例的链接。
2. 收到 connect 命令后,服务器端发送协议消息 '窗口确认大小' 到客户端。服务器端也会链接到 connect 命令中提到的应用。
3. 服务器端发送协议消息 '设置对端带宽' 到客户端。
4. 在处理完协议消息 '设置对端带宽' 以后客户端发送协议消息 '窗口确认大小' 到服务器端。
5. 服务器端发送另外一个用户控制消息 (StreamBegin) 类型的协议消息到客户端。
6. 服务器端发送结果命令消息告知客户端链接状态 (success/fail)。这一命令定义了事务 ID (经常为 connect 命令设置为 1)。这一消息也定义了一些属性,好比 FMS 服务器版本 (字符串)。此外,它还定义了其余链接关联到的信息,好比 level (字符串)、code (字符串)、description (字符串)、objectencoding (数字) 等等。
7.2.1.2. call 方法
NetConnection 对象的 call 方法执行接收端远程方法的调用 (PRC)。被调用的 PRC 名字做为一个参数传给调用命令。
发送端发送给接收端的命令结构以下:
字段名 | 类型 | 描述 |
Procedure Name | 字符串 | 调用的远程方法的名字。 |
Transaction ID | 数字 | 若是指望回复咱们要给一个事务 ID。不然咱们传 0 值便可。 |
Command Object | 对象 | 若是存在一些命令信息要设置这个对象,不然置空。 |
Optional Arguments | 对象 | 任意要提供的可选参数。 |
回复的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令的名字。 |
Transaction ID | 数字 | 响应所属的命令的 ID。 |
Command Object | 对象 | 若是存在一些命令信息要设置这个对象,不然置空。 |
Response | 对象 | 调用方法的回复。 |
客户端发送这一命令到服务器端觉得消息链接建立一个逻辑通道。音频、视频和元数据使用 createStream 命令建立的流通道传输。
NetConnection 是默认的通讯通道,流 ID 为 0。协议和一些命令消息,包括 createStream,使用默认的通讯通道。
客户端发送给服务器端的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令名。设置给 "createStream"。 |
Transaction ID | 数字 | 命令的事务 ID。 |
Command Object | 对象 | 若是存在一些命令信息要设置这个对象,不然置空。 |
服务器端发送给客户端的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | _result 或者 _error;代表回复是一个结果仍是错误。 |
Transaction ID | 数字 | 响应所属的命令的 ID。 |
Command Object | 对象 | 若是存在一些命令信息要设置这个对象,不然置空。 |
Stream ID | 数字 | 返回值要么是一个流 ID 要么是一个错误信息对象。 |
NetStream 定义了传输通道,经过这个通道,音频流、视频流以及数据消息流能够经过链接客户端到服务端的 NetConnection 传输。
如下命令能够由客户端使用 NetStream 往服务器端发送:
服务器端使用 "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 方法,并在每次调用时传递重置为 false。相反的,若是你想要当即播放指定流,将其余等待播放的流清空,并为重置设为 true。
客户端发送到服务器端的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令名。设为 "play"。 |
Transaction ID | 数字 | 事务 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 字段定义的时间段内结束,那么流结束时回放结束)。若是你在 Duration 字段中传递一个 -1 之外的负数的话,它将把你给的值当作 -1 处理。 |
Reset | 布尔 | 一个可选的布尔值或者数字定义了是否对之前的播放列表进行 flush。 |
命令执行时的消息流动是为:
1. 当客户端从服务器端接收到 createStream 命令的结果是为 success 时,发送 play 命令。
2. 一旦接收到 play 命令,服务器端发送一个协议消息来设置块大小。
3. 服务器端发送另外一个协议消息 (用户控制),这个消息中定义了 'StreamIsRecorded' 事件和流 ID。消息在前两个字节中保存事件类型,在后四个字节中保存流 ID。
4. 服务器端发送另外一个协议消息 (用户控制),这一消息包含 'StreamBegin' 事件,来指示发送给客户端的流的起点。
5. 若是客户端发送的 play 命令成功,服务器端发送一个 onStatus 命令消息 NetStream.Play.Start & NetStream.Play.Reset。只有当客户端发送的 play 命令设置了 reset 时服务器端才会发送 NetStream.Play.Reset。若是要播放的流没有找到,服务器端发送 onStatus 消息 NetStream.Play.StreamNotFound。
以后,服务器端发送视频和音频数据,客户端对其进行播放。
不一样于 play 命令的是,play2 能够在不改变播放内容时间轴的状况下切换到不一样的比特率。服务器端为客户端能够在 play2 中请求全部支持的码率维护了不一样的字段。
客户端发送给服务器端的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令名,设置为 "play2"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息不存在,设置为 null 类型。 |
Parameters | 对象 | 一个 AMF 编码的对象,该对象的属性是为公开的 flash.net.NetStreamPlayOptions ActionScript 对象所描述的属性。 |
NetStreamPlayOptions 对象的公开属性在 ActionScript 3 语言指南中 [AS3] 有所描述。
命令执行时的消息流动以下图所示:
当 NetStream 对象消亡时 NetStream 发送 deleteStream 命令。
客户端发送给服务器端的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令名,设置为 "deleteStream"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设为 null 类型。 |
Stream ID | 数字 | 服务器端消亡的流 ID。 |
服务器端再也不发送任何回复。
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 进行回复。
客户端发送给服务器端这一命令以发布一个已命名的流。使用这个名字,任意客户端均可以播放这个流,并接受发布的音频、视频以及数据消息。
客户端发送给服务器端的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令名,设置为 "publish"。 |
Transaction ID | 数字 | 事务 ID 设置为 0。 |
Command Object | Null | 命令信息对象不存在,设置为 null 类型。 |
Publishing Name | 字符串 | 发布的流的名字。 |
Publishing Type | 字符串 | 发布类型。能够设置为 "live"、"record" 或者 "append"。record:流被发布,数据被录制到一个新的文件。新文件被存储在服务器上包含服务应用目录的子路径。若是文件已存在,将重写。append:流被发布,数据被添加到一个文件。若是该文件没找着,将新建一个。live:直播数据只被发布,并不对其进行录制。 |
服务器端回复 onStatus 命令以标注发布的起始位置。
客户端发送 seek 命令以查找一个多媒体文件或一个播放列表的偏移量 (以毫秒为单位)。
客户端发送到服务器端的命令结构以下:
字段名 | 类型 | 描述 |
Command Name | 字符串 | 命令的名字,设为 "seek"。 |
Transaction ID | 数字 | 事务 ID 设为 0。 |
Command Object | Null | 没有命令信息对象,设置为 null 类型。 |
milliSeconds | 数字 | 播放列表查找的毫秒数。 |
seek 命令执行成功时服务器会发送一个状态消息 NetStream.Seek.Notify。失败的话,服务器端返回一个 _error 消息。
客户端发送 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 消息。
这里有几个解释使用 RTMP 交换消息的例子。
这个例子说明了一个客户端是如何可以发布一个直播流而后传递视频流到服务器的。而后其余客户端能够对发布的流进行订阅并播放视频。
这个例子说明了在一个共享对象的建立和改变期间交换消息的变化。它也说明了共享对象消息广播的处理过程。
这个例子描述了用于发布元数据的消息交换。
[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>.