做者:逸殊 审核:泰一git
简介
RTMP 在可靠流式传输(TCP)的基础上提供了双向的消息多路复用服务,在通信双方之间传输与时间相关的并行流数据,如音频,视频和数据消息。协议实现方一般为不一样的消息类型指定不一样的优先级,这样在网络带宽受限时能改变底层传输顺序。算法
定义
- 负载:包中所承载的数据。例如音频或视频数据。
- 包:一个数据包由固定头部和所承载的数据组成。一些底层协议可能须要定义数据包的封装格式。
- 端口:在一个计算机中用于区分不一样目标的抽象定义。在 TCP/IP 协议中用一个小的正整数来表示端口。OSI 传输层的传输选择器就至关于端口。
- 传输地址:标识一个传输终端的网络地址和端口的组合,例如 IP 地址和 TCP 端口的组合。
- 消息流:容许消息传播的逻辑通道。
- 消息流 ID:每一个消息都会有一个对应的 ID,用于标识其所在的消息流。
- 块:消息的一个片断。消息在传输以前会被分割成更小的片断,由于每一块都很小,以致于能够给不一样的块指定各自的优先级,经过这种方式保证多个流中数据能够按照时间戳的顺序传输。
- 块流:块向某一肯定方向传播的逻辑通道。能够是客户端到服务端,也能够是服务端到客户端。
- 块流 ID:每一个块都会有一个对应的 ID,用于标识其所在的块流。
- 复用:将独立的音频 / 视频数据整合为统一的音视频流,可使多个音视频流同步传输。
- 复用分离:复用的逆向过程。将合并的音视频数据分离为原始的音频和视频数据。
- 远程过程调用:客户端或服务端调用另外一端的功能。
- 元数据:媒体数据的描述信息。
- 应用实例:服务器上能够和 Client 创建链接的应用。
- 动做消息格式:一个可用于序列化 ActionScript 对象图的紧凑的二进制格式。
- 字节序:字节的顺序,即多字节类型的数据在内存中的存放顺序。TCP/IP 各层协议将字节序定义为大端字节序,所以 TCP/IP 协议中使用的字节序一般称之为网络字节序。
- 大字节序:高位字节排放在内存的低地址,低位字节排放在内存的高地址。
- 小字节序:低位字节排放在内存的低地址,高位字节排放在内存的高地址。
字节序,校准,时间格式
全部整数都是以网络字节序来表示的。除非另行说明,本文中的全部数字都是十进制数。 在没有特殊说明的状况下,RTMP 中的数据都是字节对齐的。若是有填充的话,填充字节应该用 0。 RTMP 中的时间戳是用一个整数来表示的,表明相对于一个起始时间的毫秒数。一般每一个流的时间戳都从 0 开始,但这不是必须的,只要通信双方使用统一的起始时间就能够了。要注意的是,跨流的时间同步(不一样主机之间)须要额外的机制来实现。 因为时间戳的长度只有 32 位,因此只能在 50 天内循环(49 天 17 小时 2 分钟 47.296 秒)。而流是能够不断运行的,可能多年才会结束。因此 RTMP 应用在处理时间戳是应该使用连续的数字算法,而且应该支持回环处理。例如:一个应用能够假设全部相邻的时间戳间隔不超过 2^31-1 毫秒,在此基础上,10000 在 4000000000 以后,3000000000 在 4000000000 以前。 时间戳增量也是以毫秒为单位的无符号整数。时间戳增量可能会是 24 位长度也多是 32 位长度。缓存
RTMP 块流
块流为上层流媒体协议提供复用和分包的功能。RTMP 块流是为配合 RTMP 协议而设计,但它可使用在任何发送消息流的协议中。每一个消息包含时间戳和负载类型信息。RTMP 块流和 RTMP 协议组合能够适用于多种音视频应用,从一对一或一对多直播到视频会议都能很好的知足。 当使用可靠传输协议(如 TCP)时,RTMP 块流为全部消息提供了可靠的跨流端对端按时间戳顺序发送的机制。RTMP 块流不提供优先级控制,可是能够由上层协议提供这样的优先级。例如:当某个客户端网络比较慢时,可能会选择抛弃一些视频消息来保证声音消息可以及时接收。 RTMP 块流除自身内置的协议控制消息外,还为上层协议提供了用户控制消息的机制。安全
消息格式
消息格式由上层协议定义,消息能够被分红多个块以支持多路复用。消息应该包含分块功能所需的全部字段,具体内容以下:服务器
- 时间戳(4-byte):消息的时间戳。
- 长度(3-byte):消息有效负载的长度,若是消息头不能被省略,则消息头的长度也应该包含在长度中。
- 类型 ID(1-byte):消息类型 ID。一些类型 ID 是为协议控制消息保留的,这些消息所表示的信息同时供 RTMP 块流协议和上层协议使用。全部其余类型 ID 都用于上层协议,RTMP 块流对这些 ID 作不透明处理。实际上,RTMP 块流不须要用这些值来区分类型,全部消息均可以是相同的类型,应用也能够用本字段来区分同步轨道而不是区分类型。
- 消息流 ID(4-byte):消息流 ID 能够是任意值。被复合到同一个块流的消息流,依据消息流 ID 进行分离。另外,就相关的块流而言,这个值是不透明的。这个字段使用小字节序。
握手
RTMP 链接以握手开始,它的握手过程可能和其余协议不一样,这里的握手由 3 个固定大小的块组成,而不是可变大小的块加上固定大小的头。网络
握手流程
握手由客户端发送 C0 和 C1 块开始。 客户端必须等接收到 S1 以后才能够发送 C2。客户端必须等接收到 S2 以后才能够发送其余数据。 服务器必须等接收到 C0 以后才能够发送 S0 和 S1,也可能接收到 C1 以后发送。服务器必须等接收到 C1 以后才能够发送 S2。服务器必须等接收到 C2 以后才能够发送其余数据。app
C0 和 S0 格式
C0 和 S0 是单独的一个字节,能够当作一个 8bit 的整数字段来对待。 异步
如下是 C0 和 S0 包的字段解释:ide
- 版本号(8 位): 在 C0 包中,该字段表示客户端请求的 RTMP 版本。在 S0 中,该字段表示服务器选择的 RTMP 版本。本规范所定义的版本是 3。可选值中,0-2 是早期版本所用的,已被丢弃,4-31 保留在将来使用,32-255 不容许使用(为了区分其余以某一可见字符开始的文本协议)。若是服务器不能识别客户端请求的版本,应该返回 3,客户端可能选择降级到版本 3,也可能放弃握手。
C1 和 S1 格式
C1 和 S1 包固定为 1536 字节,包含如下字段: 性能
- 时间戳(4 字节):该字段承载一个时间戳,该时间戳应该做为发送端点全部后续块的时间戳起始时间。能够是 0,也能够是其余的任意值。为了同步多个块流,端点可能会发送其余块流的当前时间戳。
- 零值(4 字节):该字段全部值都必须为 0。
- 随机数据(1528 字节):该字段能够是任意值。经过这个字段来区分本身和链接的另外一方,因此此数据应该有充分的随机性,可是不必使用加密安全的随机值或动态值。
C2 和 S2 格式
C2 和 S2 包的长度也为 1536 字节,基本上是 S1 和 C1 的回传,包含如下字段:
- 时间戳(4 字节):该字段必须包含对端发来的时间戳(对 C2 来讲是 S1, 对 S2 来讲是 C1)。
- 时间 2(4 字节):该字段必须包含先前发送的并被对端读取的包的时间戳。(对 C2 来讲是 C1,对 S2 来讲是 S1)。
- 随机数据回显(1528 字节):该字段必须包含对端发送过来的随机数据字段值(对 C2 来讲是 S1, 对 S2 来讲是 C1)。任何一端均可以用时间戳和时间戳 2 两个字段值和当前时间戳来快速的估算带宽和延迟,但这样可能并不实用。
握手流程示意图
上图提到的状态的解释以下:
- Uninitialized:未初始化状态。在该阶段发送协议版本。客户端在 C0 包中发送 RTMP 协议版本,若是服务器支持此版本,服务器将在响应中发送 S0 和 S1。若是不支持,服务器采用适当的行为做为响应,在 RTMP 规范中是终止链接。
- Version Send:版本已发送状态。在未初始化状态以后客户端和服务端都进入版本已发送状态。客户端等待接收 S1 包,服务端等待接收 C1 包。收到所等待的包后,客户端发送 C2 包,服务端发送 S2 包。以后状态进入发送确认状态。
- Ack Send:客户端和服务端等待接收 S2 和 C2 包,收到后进入握手完成状态。
- Handshake Done:握手完成, 客户端和服务端开始交换消息。
分块
握手完成后,一个或多个块流可能会复用同一个链接,每一个块流承载来自同一个消息流的同一类消息。每一个块都有一个惟一的块流 ID,这些块经过网络进行传输。在传输过程当中,必须一个块发送完毕以后再发送下一个块。在接收端,将全部块根据块中的块流 ID 组装成消息。 分块将上层协议的大消息分割成小的消息,保证大的低优先级消息(好比视频)不阻塞小的高优先级消息(好比音频或控制消息)。 分块还能下降消息发送的开销,它在块头中包含了压缩的本来须要在消息中所包含的信息。 块大小是可配置的,这个能够经过一个设置块大小控制消息进行设定修改。越大的块 CPU 使用率越低,可是在低带宽的状况下,大的写入会阻塞其余内容的写入。而小一些的块不适合高比特率的流。
块格式
每一个块由块头和数据组成,块头包含 3 部分:基本头、消息头和扩展时间戳。
- 基本头 (1-3 字节):块流 ID 和块类型,块类型决定了以后消息头的编码格式。基本头的长度取决于块流 ID,当块流 ID 越大时所须要的字节数越多。
- 消息头 (0,3,7 或 11 字节):所发送消息的描述信息。该部分的长度取决于基本头中指定的块类型。
- 扩展时间戳 (0 或 4 字节):该部分只有在某些特殊状况下才会使用,是否使用取决于时间戳或时间戳增量是否超出了块消息头中相应字段的描述范围。
- 块数据 (变长):块承载的有效数据,最大长度为配置的块大小。
基本头
基本头包含块流 ID 和块类型(在下图中用 fmt 字段表示),块类型决定了消息头的编码格式,基本头长度多是 1,2 或 3 字节,这取决于块流 ID 的长度。 协议实现方应该用可以用最短表示法来表示块流 ID。 RTMP 最多支持 65597 个流,ID 在 3-65599 范围内,0,1,2 为保留值。若是 2~7 位表明的值为 0 表示块基本头占 2 个字节,而且块流 ID 范围在 64-319 之间(第二个字节 + 64),若是 2~7 位表明的值为 1 表示块基本头占 3 个字节,而且 ID 范围在 64-65599 之间(第三个字节 * 256 + 第二个字节 + 64),当 ID 在 3-63 之间时直接使用 2~7 位的值来表示流 ID。 2-63 范围内的块流 ID 用 1 个字节来编码:
64-319 范围内的块流 ID 用 2 个字节来编码,块流 ID 为计算所得,公式为:第二个字节值 + 64:
64-65599 范围内的块流 ID 用 3 个字节来编码,块流 ID 为计算所得,公式为:第三个字节值 * 255 + 第二个字节值 + 64
上述图中各个部分的含义以下:
- cs id (6 位):该字段表示完整的块流 ID,取值在 2-63 之间。0,1 两个值是保留值,用来表示基本头是 2 字节仍是 3 字节长度。
- fmt:该字段代表了消息头使用的格式。
- cs id - 64 (8 位或 16 位):该字段表示块流 ID,取值在 64-63399 之间。
64-319 范围内的块流 ID 能够用 2 字节来表示,也能够用 3 字节表示。
消息头
消息头共有 4 种不一样的格式,根据基本头中的 "fmt" 字段值来肯定。协议实现方应该用最紧凑的格式来表示块消息头。
类型 0
0 类型的块消息头占 11 个字节长度,该类型必须用在一个块流的开头,和每当块流时间戳回退的时候(例如视频回退的操做)。
- timestamp (3 字节):对于 0 类型的消息块,消息的绝对时间戳在这里发送。 若是时间戳大于或等于 16777215 (0xFFFFFF),改字段值必须为 16777215,而且必须设置扩展时间戳来共同编码 32 位的时间戳。不然该字段就是完整的时间戳。
- message length (3 字节): 消息长度,类型 0 和类型 1 的块包含此字段,表示消息的长度。要注意的是,一般消息长度与块长度并不相同。块长度除了最后一个块以外,都与块最大长度相同。
- message type id (3 字节): 消息类型 id,类型 0 和类型 1 的块包含此字段,表示消息的类型。
- message stream id (4 字节): 消息流 ID,类型 0 的块包含此字段,表示消息流 ID。消息流 ID 以小字节序存储。一般,相同块流中的消息属于用一个消息流。虽然,不一样的消息流复用相同的块流会致使消息头没法有效压缩,可是当一个消息流已关闭,准备打开另一个消息流时,就能够经过发送一个新的 0 类型块来实现复用。
类型 1
1 类型的块消息头占用 7 个字节长度,不包含消息流 ID,该块沿用上一个消息的消息流 ID。对于传输大小可变消息的流(如多数视频格式),在发送第一个消息以后的每一个消息都应该使用该类型格式。
- timestamp delta (3 字节): 时间戳增量。类型 1 和类型 2 的块包含此字段,表示前一个块的 timestamp 字段和当前块 timestamp 间的差值。 若是时间戳增量大于或等于 16777215 (0xFFFFFF),该字段必须为 16777215,而且必须设置扩展时间戳,来共同表示 32 位的时间戳增量,不然该字段值就是实际的时间戳增量。
类型 2
2 类型的块消息头占用 3 个字节长度,不包含消息流 ID 和消息长度,沿用上一个块的消息流 ID 和消息长度。对于传输固定大小消息的流(如音频和数据格式),在发送第一个消息以后的每个消息都应该使用该类型格式。
类型 3
3 类型的块没有消息头,消息流 ID、消息长度和时间戳增量,该类型的块使用和上一个块相同的头数据。当一个消息被分割成块时,除了第一个块,其余块都应该使用该类型。由相同大小、消息流 ID 和时间间隔的消息组成的流,在类型 2 的块以后全部块都应该使用该类型格式。若是第一个消息和第二消息之间的时间增量与第一个消息的时间戳相同,则 0 类型的块以后能够立刻发送 3 类型的块,而没必要使用 2 类型的块来注册时间增量。若是类型 3 的块跟在类型 0 的块后面,那么 3 类型块的时间戳增量与 0 类型块的时间戳相同。
扩展时间戳
扩展时间戳用来辅助编码超过 16777215 (0xFFFFFF) 的时间戳或时间戳增量,也就说消息头没法用 24 位数字来表示时间戳或时间戳增量时,既 0 类型块的时间戳字段或 1,2 类型的时间戳增量字段值为 16777215 (0xFFFFFF)。当最近的属于相同块流 ID 的 0 类型块、1 类型块或 2 类型块有此字段时有此字段时,3 类型块也应该有此字段。
示例
示例 1
这是一个简单的音频流消息,这是示例示范了信息冗余。
下图展现该消息流以块流形式发送。从 3 类型块开始了数据传输优化,以后的块只附加了一个字节。
示例 2
该示例展现了一个超过 128 字节长度的消息,消息被分割成了数个块。
下图是被分割成的块:
第一个块的头信息指明了消息总大小为 307 字节。 注意这两个示例,3 类型块能够在两种状况下使用。第一种状况是消息拆分红多个块,另外一种状况是新消息复用上一个消息的全部头部内容。
协议控制消息
RTMP 块流用消息类型 1,2,3,5 和 6 来做为协议控制消息,这些消息包含 RTMP 块流协议所须要的信息。 这些协议控制消息必须用 0 做为消息流 ID (控制流 ID),并在 ID 为 2 的块流中发送。协议控制消息收到后当即生效,它们的时间戳信息是被忽略的。
设置块大小
协议控制消息类型 1:设置块大小,用于通知另外一端新的最大块大小。 最大块大小默认为 128 字节,客户端或服务端能够修改此值,并用该消息通知另外一端。例如,假设一个客户端想要发送 131 字节的音频数据,而最大块大小为 128。在这种状况下,客户端能够向服务端发送该消息,通知它最大块大小被设置为了 131 字节。这样客户端只用一个块就能够发送这些音频数据。 最大块大小不能小于 1 字节,一般应该不低于 128 字节。每一个方向上的最大块大小是独立的。
- 0 (1 位): 该位必须为 0.
- chunk size (31 位): 该字段以字节形式保存新的最大块大小,该值将用于后续的全部块的发送,直到收到新的通知。该值可取值范围为 1-2147483647 (0x7FFFFFFF),可是全部大于 1677215 (0xFFFFFF) 的值都是视做是 16777215,由于任何块不可能比消息大,而消息长度不能大于 16777215 字节。
终止消息
协议控制消息类型 2:终止消息,通知正在等待消息后续块的另外一端,能够丢弃指定块流接收到的数据,块流 ID 为该消息的载荷。应用可能在关闭的时候发送该消息,用来代表后面的消息没有必要继续处理了。
- chunk stream id (32 字节): 指定消息的块流 ID。
确认消息
客户端或服务器在收到数据总长和窗口大小相等时,经过它回复确认消息。在链接创建完成后,消息的发送方会通知接收方一个窗口的大小(指定一个长度),若是接收方收到指定长度的数据后没有发送回复消息,发送方就不会再发送任何内容了。
- sequence number (32 字节): 到当前时刻为止接收到的字节总数。
确认窗口大小
客户端或服务端发送该消息来通知对端发送确认消息所使用的视窗大小,并等待接收端发送确认消息。接收端在接收到视窗大小后必须发送确认消息。
设置对方传输带宽
客户端或服务端发送该消息来限制对端的输出带宽。接收端收到消息后,能够直接使用消息中指定的窗口大小,而不须要等待收到确认消息以后。若是视窗大小与上一个视窗大小不一样,则该消息的接收端应该向该消息的发送端发送新的窗口大小消息。这个消息和上一个消息都是调整窗口大小的,不一样的地方是,这个消息是接收者请求发送者,让它调整窗口大小,而上一个消息是发送者主动设置了窗口大小,通知数据接收者。
Limit Type(限制类型)有如下值:
- 0 - Hard: 应该将输出带宽限制为指定视窗大小。
- 1 - Soft: 应该将输出带宽限制为指定视窗大小和当前视窗大小中较小的值。
- 2 - Dynamic: 若是上一个消息的限制类型为 Hard,则该消息一样为 Hard,不然抛弃该消息。
RTMP 消息格式
虽然 RTMP 被设计成使用 RTMP 块流传输,可是它也可使用其余传输协议来发送消息,在这种状况下 RTMP 消息的格式以下所示。值得一提的是,RTMP 块流协议和 RTMP 协议配合时,很是适合音视频应用,包括单播、一对多实时直播、视频点播和视频会议等。
格式
服务端和客户端经过在网络上发送 RTMP 消息实现之间的交互,消息包括音频、视频、数据等。 RTMP 消息包含两部分,消息头和有效负载。
RTMP 消息头
消息头包含如下信息:
- Message Type: 消息类型,占用 1 个字节。1-6 的消息类型 ID 是为协议控制消息保留的。
- Length: 有效负载的字节数,占用 3 个字节。该字段是用大端序表示的。
- Timestamp: 时间戳,占用 4 个字节,用大端序表示。
- Message Stream Id: 消息流 ID,标识消息所使用的流,用大端序表示。
消息有效负载
消息的另外一部分就是有效负载,也是消息包含的实际数据,能够是音频样本或者压缩的视频数据。
用户控制消息
RTMP 协议将消息类型 4 做为用户控制消息 ID,这些消息包含 RTMP 流所需的必要信息。消息类型 1,2,3,5 和 6 由 RTMP 块流协议使用。 用户控制消息应该使用 ID 为 0 的消息流(控制流),而且经过 RTMP 块流传输时使用 ID 为 2 的块流。用户控制消息收到后当即生效,它们的时间戳信息会被忽略。 客户端或服务端经过发送该消息告知对方用户控制事件。该消息携带事件类型和事件数据两部分。
开头的 2 个字节用于指定事件类型,紧跟着是事件数据。事件数据字段长度可变,可是若是用 RTMP 块流传输,则消息总长度不能超过最大块大小,以使消息可使用一个单独的块进行传输。
RTMP 指令消息
各类类型的消息在客户端和服务端之间进行交换,包括用于发送音频数据的音频消息,用于发送视频数据的视频消息,用于发送任意用户数据的数据消息,共享对象消息和指令消息等。共享对象消息的主要用途是管理客户端和相同服务器的共享数据。指令消息发送的是客户端与服务端之间的 AMF 编码指令,客户端或服务端也能够经过指令消息来实现远程过程调用(RPC)。
消息类型
客户端和服务端经过在网络上发送消息来实现交互,消息能够是任意类型,包括音频消息、视频消息、指令消息、共享对象消息、数据消息和用户控制消息等。
指令消息
指令消息在客户端和服务端之间传递 AMF 编码的指令,消息类型 20 表明 AMF0 编码,消息类型 17 表明 AMF3 编码。发送这些消息来完成链接、建立流、发布、播放、暂停等操做。像状态、结果这样的指令消息,用于通知发送方请求的指令状态。一条指令消息由指令名、事务 ID 和包含相关参数的指令对象组成。客户端或服务端还能够经过指令消息来实现远程过程调用 (RPC)。
数据消息
客户端或服务端经过该消息来发送元数据或其余用户数据。元数据包括数据 (音频、视频) 的建立时间、时长、主题等详细信息。消息类型 18 表明 AMF0 编码,消息类型 15 表明 AMF3 编码。
共享对象消息
共享对象是在多个客户端之间同步的 Flash 对象 (键值对集合)。消息类型 19 表明 AMF0 编码,消息类型 16 表明 AMF3 编码。每一个消息均可以包含多个事件。
支持如下事件类型:
- 建立(1):客户端向服务端发送,请求建立指定名称的共享对象。
- 释放(2):客户端通知服务端,共享对象已在本地删除。
- 请求更新(3):客户端请求修改共享对象的属性值。
- 更新(4):通知服务端向除本身外的其余客户端发送共享数据消息,通知它们有属性的值发生了变化。
- 成功(5):“请求更新” 事件被接受后,服务端向发送请求的客户端回复此事件。
- 发送消息(6):客户端向服务端发送此事件,来广播一个消息。服务端收到此事件后向全部客户端广播一条消息,包括请求方客户端。
- 状态(7):服务端发送此事件来通知客户端错误信息。
- 清除(8):服务端向客户端发送此事件,通知客户端清除一个共享对象。服务端在回复客户端的 “建立” 事件时也会发送此事件。
- 移除(9):服务端发送此事件,使客户端删除一个插槽。
- 请求移除(10):客户端删除一个插槽时发送此事件。
- 建立成功(11):当链接成功时服务端向客户端发送此事件。
音频消息
客户端或服务端经过发送此消息来发送音频数据给对方,消息类型 8 是为音频消息预留的。
视频消息
客户端或服务端经过发送此消息来发送视频数据给对方,消息类型 9 是为视频消息预留的。
组合消息
组合消息,是一个消息包含多个子 RTMP 消息,子消息符合 RTMP 消息格式。消息类型 22 用于组合消息。
组合消息的消息流 ID 会覆盖其中子消息的消息流 ID。 组合消息的时间戳和其中第一个子消息的时间戳的差值,是用来将全部子消息的时间戳重整为流时间的位移量。位移量会加到每个子消息的时间戳上来换算出正常的流时间。第一个子消息的时间戳应该与组合消息的时间戳相同,因此位移量应该为 0。 Back Pointer (反向指针) 包含前一个消息的长度(包括消息头),这样符合 flv 文件格式,可用于进行后退操做。 使用组合消息有如下好处:
- 块流协议中,一个块最多只能发送一个消息,这样就使用组合消息,加大块大小,从而下降发送的块数量。
- 子消息在内存中连续存放,这样系统调用网络发送数据的性能更高。
用户控制消息事件
客户端或服务器经过该消息发送用户控制事件。
用户控制消息支持如下事件:
- 流开始(0):服务端发送该事件,用来通知客户端一个流已经能够用来通信了。默认状况下,该事件是在收到客户端链接指令并成功处理后发送的第一个事件。事件的数据使用 4 个字节来表示可用的流的 ID。
- 流结束(1):服务端发送该事件,用来通知客户端其在流中请求的数据已经结束了。若是没有额外的指令,将不会再发送任何数据,而客户端会丢弃以后从该流接收到的消息。事件数据使用 4 个字节来表示回放完成的流的 ID。
- 流枯竭(2):服务端发送该事件,用来通知客户端流中已经没有更多的数据了。若是服务端在必定时间后没有探测到更多数据,它就能够通知全部订阅该流的客户端,流已经枯竭。事件数据用 4 个字节来表示枯竭的流的 ID。
- 设置缓冲区大小(3):客户端发送该事件,用来告知服务端用来缓存流中数据的缓冲区大小 (单位毫秒)。该事件在服务端开始处理流数据以前发送。事件数据中,前 4 个字节用来表示流 ID,以后的 4 个字节用来表示缓冲区大小(单位毫秒)。
- 流已录制(4):服务端发送该事件,用来通知客户端指定流是一个录制流。事件数据用 4 个字节表示录制流的 ID。
- ping 请求(5):服务端发送该事件,用来探测客户端是否处于可达状态。事件数据是一个 4 字节的时间戳,表示服务端分发该事件时的服务器本地时间。客户端收到后用 ping 响应回复服务端。
- ping 响应(6):客户端用该事件回复服务端的 ping 请求,事件数据为收到的 ping 请求中携带的 4 字节的时间戳。
指令类型
客户端和服务器交换 AMF 编码的指令。发送端发送一条指令消息,其中包含了指令名称、处理 ID、以及含有相关参数的指令对象。例如,链接指令消息包含了’app' 参数,以告知服务器客户端但愿链接的目标程序。接收端处理这条指令并回复含有一样处理 ID 的响应。回复的字符串可能为_result、_error 或方法名。如 verifyClient 或 contactExternalServer. _result 或_error 的指令字符表明一条响应,处理 ID 则代表回复是针对哪条指令的,这在 IMAP 或其余协议中是彻底相同的。指令字符串中的方法名代表发送端但愿运行接收端上的一个方法。 指令消息可分为以下两类:
- NetConnection:一个服务器和客户端之间链接的高层表现对象。
- NetStream:一个音频流、视频流及其余相关数据传输流,咱们会发送如播放、暂停等指令来控制数据流动。
NetConnection 指令
NetConnection 管理着一个客户端程序和服务器之间的双向链接,除此以外,它还提供了对异步远程方法调用的支持。 下列指令可经过 NetConnection 进行发送:
- Connect
- Call
- Close
- CreateStream
Connect
客户端发送 connect 指令至服务器端以请求链接至某一服务器程序实例。 指令结构以下:
Connect 指令中会用到的键值对:
音频编码:
视频编码:
视频功能:
对象编码:
由服务器发送至客户端的指令结构以下:
指令执行流程:
指令执行的消息流以下:
- 客户端发送 connect 指令至服务器以请求链接至服务器端程序实例。
- 在收到链接指令后,服务器端发送协议消息 'Window Acknowledgement Size' 给客户端。同时,服务器端还会链接 connect 指令中提到的应用。
- 服务器端发送协议消息‘Set Peer Bandwidth’至客户端。
- 客户端成功处理‘Set Peer Bandwidth’后发送协议消息‘Window Acknowledgement Size' 给服务器端。
- 服务器端发送用户控制消息(StreamBegin)协议消息给客户端。
- 服务器端发送指令消息以通知客户端链接状态(success/fail)。该指令中含有处理 ID (与 1 中收到相同),该消息同时还制定了部分属性,如 Flash Media Server 版本(string)。除此以外,它还指定了链接响应相关的信息如 level (string),code (string),description (string),object-encoding (number) 等。
Call
NetConnection 对象的 call 方法用于远程调用接收端上的程序。须要远程调用的程序名称经过一个参数传递给 call 指令。 发送指令结构以下:
响应指令结构以下:
CreateStream
客户端发送该指令至服务器端以建立一条用于传递消息的逻辑通道,从而能够利用已建立的流通道发布音频、视频和元数据。 NetConnection 是默认的通信通道,流 ID 为 0。协议和一些指令消息,包括 createStream,使用默认通信通道。 客户端发出的指令结构以下:
服务器发出的指令结构以下:
NetStream 指令
基于 NetConnection 的客户端至服务器间链接,NetStream 定义了一条能够传递音频流、视频流以及消息流的通道。NetConnection 对象支持多个 NetStreams 以传输多个数据流。 客户端可在 NetStream 中发送下列指令至服务器:
- Play
- Play2
- DeleteStream
- CloseStream
- ReceiveAudio
- ReceiveVideo
- Publish
- Seek
- Pause
服务器端经过 “onStatus" 将 NetStream 的状态更新至客户端:
Play
客户端发送该指令值以播放一个流。屡次调用该指令也可建立一个播放清单。 若是你但愿建立一个在不一样 live 或 recorded 流间切换的动态播放清单,须要屡次调用 play 并传递 false 以免每次 reset。相反地,若是你但愿当即播放某一指定流,传递 true 以清除等待播放队列中的全部其余流。 客户端发送的指令结构以下:
流程图以下:
指令执行期间的消息流以下:
- 客户端在接收到来自服务器的 createStream 指令的成功结果后发送 play 指令。
- 在接收到 play 指令后,服务器发送协议数据来设置块大小。
- 服务器发送一些另一个协议数据 (用户控制),在这个消息里包含事件 “StreamIsRecord” 和流 ID。这个消息的前 2 个字节是事件类型随后的 4 字节是流 ID。
- 服务器向客户端发送另一个协议消息 (用户控制),这个消息指示了 “StreamBegin” 事件,表示流开始了。
- 若是客户端向服务器发送的 play 指令成功执行了,服务器会发送 onStatus 指令消息包含 NetStream.Play.Start 或 NetStream.Play.Reset。仅当客户端发送的 play 指令中的设置了 reset 标志 NetStream.Play.Reset 才会被发送。若是播放的流不存在,服务器会在发送 onStatus 消息中包含 NetStream.Play.StreamNotFound。随后,服务器就发送客户端播放的音频和视频数据。
Play2
不一样于 play 指令,play2 能够切换码率而不改变播放内容的时间轴。服务器为客户端能够在 play2 中请求的全部支持的码率维护多个字段。 客户端发送的指令结构以下:
该指令的消息流程以下图:
DeleteStream
当 NetStream 对象将要被销毁时,它发送该 deleteStream 指令。 客户端发送的指令结构以下:
服务器不须要发送任何应答。
ReceiveAudio
NetStream 发送 ReceiveAudio 消息通知服务器是否发送或不发送音频到客户端。 客户端发送的指令结构以下:
若是 receiveAudio 指令发送带有 flase 的 bool flag,服务器不发送任何响应。若是这个标志被设置为 true,服务器应答 NetStream.Seek.Notify 和 NetStream.Play.Start 的状态消息。
ReceiveVideo
NetStream 发送 ReceiveVideo 消息通知服务器是否发送或不发送视频到客户端。 客户端发送的指令结构以下:
若是 receiveVideo 指令发送带有 flase 的 bool flag,服务器不发送任何响应。若是这个标志被设置为 true,服务器应答 NetStream.Seek.Notify 和 NetStream.Play.Start 的状态消息。
Publish
客户端发送 publish 指令将已命名的流发布到服务器上。使用这个名称,任何客户端均可以播放此流,并接收已发布的音频、视频和数据消息。 客户端发送的指令结构以下:
服务器应答 onStatus 指令,以标记发布的开始。
Seek
客户端发送 seek 指令以定位媒体文件内或者播放列表的某个位置(以毫秒为单位)。 客户端发送的指令结构以下:
当定位成功,服务器发送 NetStream.Seek.Notify 的状态消息。失败的时候,它返回一个_error 的消息。
Pause
客户端发送 pause 指令以告诉服务器暂停或者开始播放。 客户端发送的指令结构以下:
当流被暂停,服务器发送一个 NetStream.Pause.Notify 的状态消息。当一个流变成未暂停状态,NetStream.Unpause.Notify 被发送。失败的时候,它返回一个_error 的消息。
消息交换例子
这里是一些样例,以解释使用 RTMP 的消息交换。
发布视频
这个例子说明了一个发布者如何发布一个流并将视频流推到服务器上。其余客户端能够订阅这个已发布的流,并播放视频。
广播一个共享对象消息
这个例子说明了在建立和更改共享对象时所交换的消息。它也说明了共享对象消息广播的过程。
发布媒体流元数据
这个例子描述了发布元数据的消息交换。
参考内容
[1] RTMP 规范 [2] RTMP 协议规范翻译工做 [3] RTMP 协议规范 1.0 中文版
「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。