RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题。实现通常对不同类型的消息分配不同的优先级,当运载能力有限时,这会影响等待流传输的消息的次序。
RTMP协议是应用层协议,是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接,在Connection链接上会传输一些控制信息,如SetChunkSize,SetACKWindowSize。其中CreateStream命令会创建一个Stream链接,用于传输具体的音视频数据和控制这些信息传输的命令信息。RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,在接收端会根据chunk中包含的data的长度,message id和message的长度把chunk还原成完整的Message,从而实现信息的收发。
所有整数型属性以网络字节顺序传输,字节 0 代表第一个字节,零位是一个单词或字段最常用的有效位。字节序通常是大端排序。关于传输顺序的更多细节描述参考 IP 协议[RFC0791]。
除非另有规定,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中创建的每个块都有一个唯一 ID 对其进行关联,这个 ID 叫做 chunk stream ID (块流 ID)。这些块通过网络进行传输。传递时,每个块必须被完全发送才可以发送下一块。在接收端,这些块被根据块流 ID 被组装成消息。
分块允许上层协议将大的消息分解为更小的消息,例如,防止体积大的但优先级小的消息 (比如视频) 阻碍体积较小但优先级高的消息 (比如音频或者控制命令)。
分块也让我们能够使用较小开销发送小消息,因为块头包含包含在消息内部的信息压缩提示。
块的大小是可以配置的。它可以使用一个设置块大小的控制消息进行设置 。更大的块大小可以降低 CPU 开销,但在低带宽连接时因为它的大量的写入也会延迟其他内容的传递。更小的块不利于高比特率的流化。所以块的大小设置取决于具体情况。
每个块包含一个头和数据体。块头包含三个部分:
类型 0 : 11 个字节。这一类型必须用在块流的起始位置,和流 timestamp 重来的时候 (比如,重置)。
类型 1 : 7 个字节。不包含消息流 ID;这一块使用前一块一样的流 ID。可变长度消息的流 (例如,一些视频格式) 应该在第一块之后使用这一格式表示之后的每个新消息。
类型 2 : 3 个字节。既不包含流 ID 也不包含消息长度;这一块具有和前一块相同的流 ID 和消息长度。具有不变长度的消息 (例如,一些音频和数据格式) 应该在第一块之后使用这一格式表示之后的每个新消息。
类型 3 :没有消息头。流 ID、消息长度以及 timestamp delta 等字段都不存在;这种类型的块使用前面块一样的块流 ID。当单一一个消息被分割为多块时,除了第一块的其他块都应该使用这种类型。
RTMP 协议设计使用 RTMP 块流,可以使用其他任意传输协议对消息进行发送。服务器端和客户端通过网络发送 RTMP 消息来进行彼此通信。消息可以包含音频、视频、数据,或者其他消息。RTMP 消息有两部分:头和它的有效载荷。
消息头包含以下:
Timestamp(时间戳):对于一个类型 1 或者类型 2 的块,前一块的 timestamp 和当前块的 timestamp 的区别在这里发送。如果 delta 大于或者等于 16777215 (十六进制 0xFFFFFF),那么这一字段必须是为 16777215,表示具有扩展 timestamp 字段来对整个 32 位 delta 进行编码。否则的话,这一字段应该是为具体 delta。
Length (长度):对于一个类型 0 或者类型 1 的块,消息长度在这里进行发送。注意这通常不同于块的有效载荷的长度。块的有效载荷代表所有的除了最后一块的最大块大小,以及剩余的 (也可能是小消息的整个长度) 最后一块。
Message Type Id (消息类型):对于类型 0 或者类型 1 的块,消息的类型在这里发送。类型 ID 1 - 6 被保留用于协议控制消息。这些传播信息的消息由 RTMP 块流协议和上层协议共同处理。其他的所有类型 ID 可用于上层协议,它们被 RTMP 块流处理为不透明值。事实上,RTMP 块流中没有任何地方要把这些值当做类型使用;所有消息必须是同一类型,或者应用使用这一字段来区分同步跟踪,而不是类型。
Message Stream Id (消息流 ID):对于一个类型为 0 的块,保存消息流 ID。消息流 ID 以小端格式保存。所有同一个块流下的消息都来自同一个消息流。当可以将不同的消息流组合进同一个块流时,这种方法比头压缩的做法要好。但是,当一个消息流被关闭而其他的随后另一个是打开着的,就没有理由将现有块流以发送一个新的类型 0 的块进行复用了。message stream (消息流) ID 可以使任意值。合并到同一个块流的不同的消息流是根据各自的消息流 ID 进行分解。除此之外,对 RTMP 块流而言,这是一个不透明的值。
消息的另一个部分就是有效负载,这是这个消息所包含的实际内容。例如,它可以是一些音频样本或者压缩的视频数据。
例:
服务器端和客户端交换的不同消息类型包括用于发送音频数据的音频消息、用于发送视频数据的视频消息、用于发送任意用户数据的数据消息、共享对象消息以及命令消息。共享对象消息提供了一个通用的方法来管理多用户和一台服务器之间的分布式数据。命令消息在客户端和服务器端传输 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 文件的格式,用于反向查找。
使用统计消息具有以下性能优势:
客户端或者服务器端发送这一消息来通知对端用户控制事件。
支持以下用户控制事件类型:
事件 |
描述 |
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 那个。 |
RTMP 使用消息类型 ID 4 表示用户控制消息。这些消息包含 RTMP 流传输层所使用的信息。RTMP 块流协议使用 ID 为 1、2、3、5 和 6。
用户控制消息应该使用消息流 ID 0 (以被认为是控制流),并且以 RTMP 块流发送时以块流 ID 为 2。用户控制消息一旦被接收立马生效;它们的 timestamp 是被忽略的。
客户端或者服务器端发送这个消息来通知对端用户操作事件。这一消息携带有事件类型和事件数据。
消息数据的前两个字节用于指示事件类型。事件类型被事件数据紧随。事件数据字段的大小是可变的。但是,如果消息必须通过 RTMP 块流层传输时,最大块大小应该足够大以允许这些消息填充在一个单一块中。
RTMP 块流使用消息类型 ID 为 1、2、3、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 位):这一字段保存有目前接收到的字节数。
客户端或者服务器端发送这条消息来通知对端发送和应答之间的窗口大小。发送者在发送完窗口大小字节之后期待对端的确认。接收端在上次确认发送后接收到的指示数值后,或者会话建立之后尚未发送确认,必须发送一个确认。
客户端或者服务器端发送这一消息来限制其对端的输出带宽。对端接收到这一消息后,将通过限制这一消息中窗口大小指出的已发送但未被答复的数据的数量以限制其输出带宽。接收到这一消息的对端应该回复一个窗口确认大小消息,如果这个窗口大小不同于其发送给 (设置对端带宽) 发送者的最后一条消息。
限制类型取以下值之一:
客户端和服务器端交换 AMF 编码的命令。服务器端发送一个命令消息,这个命令消息由命令名、事务 ID 以及包含有相关参数的命令对象组成。例如,包含有 'app' 参数的连接命令,这个命令说明了客户端连接到的服务器端的应用名。接收者处理这一命令并回发一个同样事务 ID 的响应。回复字符串可以是 _result、_error 或者 一个方法名的任意一个,比如,verifyClient 或者 contactExternalServer。
命令字符串 _result 或者 _error 是响应信号。事务 ID 指示出响应所指向的命令。这和 AMAP 和其他一些协议的标签一样。命令字符串中的方法名表示发送者试图执行接收者一端的一个方法。
以下类的对象用于发送不同的命令:
NetConnection 代表上层的服务器端和客户端之间连接的一个对象。
NetStream 一个代表发送音频流、视频流和其他相关数据的通道的对象。当然,我们也会发送控制数据流的命令,诸如 play、pause 等等。
命令执行时消息流动如下:
服务器端到客户端的命令的结构如下:
NetConnection 管理着一个客户端应用和服务器端之间的双相连接。此外,它还提供远程方法的异步调用。
NetConnection 可以发送以下命令:
(a)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 属性值:
(b)call 方法
NetConnection 对象的 call 方法执行接收端远程方法的调用 (PRC)。被调用的 PRC 名字作为一个参数传给调用命令。
发送端发送给接收端的命令结构如下:
字段名 |
类型 |
描述 |
Procedure Name |
字符串 |
调用的远程方法的名字。 |
Transaction ID |
数字 |
如果期望回复我们要给一个事务 ID。否则我们传 0 值即可。 |
Command Object |
对象 |
如果存在一些命令信息要设置这个对象,否则置空。 |
Optional Arguments |
对象 |
任意要提供的可选参数。 |
回复的命令结构如下:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
命令的名字。 |
Transaction ID |
数字 |
响应所属的命令的 ID。 |
Command Object |
对象 |
如果存在一些命令信息要设置这个对象,否则置空。 |
Response |
对象 |
调用方法的回复。 |
(c)createStream 命令
客户端发送这一命令到服务器端以为消息连接创建一个逻辑通道。音频、视频和元数据使用 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" (字符串):关于这个消息人类可读描述。 |
(a)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。 |
(b)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] 有所描述。
(c)deleteStream 命令
当 NetStream 对象消亡时 NetStream 发送 deleteStream 命令。
客户端发送给服务器端的命令结构如下:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
命令名,设置为 "deleteStream"。 |
Transaction ID |
数字 |
事务 ID 设置为 0。 |
Command Object |
Null |
命令信息对象不存在,设为 null 类型。 |
Stream ID |
数字 |
服务器端消亡的流 ID。 |
服务器端不再发送任何回复。
(d)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 进行回复。
(e)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 进行回复。
(f)publish 命令
客户端发送给服务器端这一命令以发布一个已命名的流。使用这个名字,任意客户端都可以播放这个流,并接受发布的音频、视频以及数据消息。
客户端发送给服务器端的命令结构如下:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
命令名,设置为 "publish"。 |
Transaction ID |
数字 |
事务 ID 设置为 0。 |
Command Object |
Null |
命令信息对象不存在,设置为 null 类型。 |
Publishing Name |
字符串 |
发布的流的名字。 |
Publishing Type |
字符串 |
发布类型。可以设置为 "live"、"record" 或者 "append"。record:流被发布,数据被录制到一个新的文件。新文件被存储在服务器上包含服务应用目录的子路径。如果文件已存在,将重写。append:流被发布,数据被添加到一个文件。如果该文件没找着,将新建一个。live:直播数据只被发布,并不对其进行录制。 |
服务器端回复 onStatus 命令以标注发布的起始位置。
(g)seek 命令
客户端发送 seek 命令以查找一个多媒体文件或一个播放列表的偏移量 (以毫秒为单位)。
客户端发送到服务器端的命令结构如下:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
命令的名字,设为 "seek"。 |
Transaction ID |
数字 |
事务 ID 设为 0。 |
Command Object |
Null |
没有命令信息对象,设置为 null 类型。 |
milliSeconds |
数字 |
播放列表查找的毫秒数。 |
seek 命令执行成功时服务器会发送一个状态消息 NetStream.Seek.Notify。失败的话,服务器端返回一个 _error 消息。
(h)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 消息。
(1)在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接。播放一个RTMP协议的流媒体需要经过以下几个基本步骤:握手,建立网络连接,建立网络流,播放。服务器和客户端之间只能建立一个网络连接,但是基于该连接可以创建很多网络流。
(2)RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,在接受端会根据chunk中包含的data的长度,message id和message的长度把chunk还原成完整的Message,从而实现信息的收发。
要建立一个有效的RTMP Connection链接,首先要“握手”:客户端要向服务器发送C0,C1,C2(按序)三个chunk,服务器向客户端发送S0,S1,S2(按序)三个chunk,然后才能进行有效的信息传输。RTMP协议本身并没有规定这6个Message的具体传输顺序。
注:
另:
理论上来讲只要满足以上条件,如何安排6个Message的顺序都是可以的,但实际实现中为了在保证握手的身份验证功能的基础上尽量减少通信的次数,一般的发送顺序是这样的:
例:
(a)C0和S0的格式
C0 和 S0 包都是一个单一的八位字节,以一个单独的八位整型域进行处理:
版本号 (八位):在 C0 中,这一字段指示出客户端要求的 RTMP 版本号。在 S0 中,这一字段指示出服务器端选择的 RTMP 版本号。本文中规范的版本号为 3。0、1、2 三个值是由早期其他产品使用的,是废弃值;4 - 31 被保留为 RTMP 协议的未来实现版本使用;32 - 255 不允许使用 (以区分开 RTMP 和其他常以一个可打印字符开始的文本协议)。无法识别客户端所请求版本号的服务器应该以版本 3 响应,(收到响应的) 客户端可以选择降低到版本 3,或者放弃握手。
(b)C1和S1的格式
C1 和 S1 数据包的长度都是 1536 字节,包含以下字段:
(c)C2和S2的格式
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 以快速估算带宽和/或者连接延迟,但这不太可能是有多大用处。
客户端发送命令消息中的“连接”(connect)到服务器,请求与一个服务应用实例建立连接。
(a)RTMP Header:
StreamID:每个消息的唯一标识,划分成Chunk和还原Chunk为Message的时候都是根据这个ID来辨识是否是同一个消息的Chunk的,这里面为0说明这个消息是初始的0消息。
Chunk stream ID:message会拆分成多个chunk,同一个Chunk Stream ID必然属于同一个Message。
(b)RTMP Body
此connect包中是一个OSI五层模型,最后一个是RTMP协议发送了connect链接消息,查看内容包含推流地址名,但是可以观察到还没有发流名,地址是有app名。
服务端给客户端发送chunk大小。
服务器接收到连接命令消息后,发送确认窗口大小(Window Acknowledgement Size)协议消息到客户端,同时连接到连接命令中提到的应用程序。
客户端或者服务器端发送这一消息来限制其对端的输出带宽。对端接收到这一消息后,将通过限制这一消息中窗口大小指出的已发送但未被答复的数据的数量以限制其输出带宽。如果这个窗口大小不同于其发送给 (设置对端带宽) 发送者的最后一条消息,那么接收到这一消息的对端应该回复一个窗口确认大小消息。
服务端返回连接结果。
服务端向客户端发送的关于之前客户端向服务端请求的响应结果。
客户端向服务端发送窗口大小,请求创建流,最后检查请求的响应结果。
(2)服务端收到请求后向客户端发送_result(),对创建流的消息进行响应。此时NetStream创建完成。
(1)客户端发送命令消息中的播放(play)命令到服务端
(2)服务器收到播放命令后,发送设置大小(chunksize)协议消息。
(3)服务端发送用户控制消息中的“streambegin”,告知客户端流ID。
(4)播放命令成功的话,服务器发送命令消息中的“响应状态”NetStream.Play.reset & NetStream.Play.Start,告知客户端“播放”命令执行成功。
(5)服务端通过发送数据消息类型以发送元数据或者任何用户数据到对端。元数据包括数据 (音频,视频等等) 的详细信息,比如创建时间,时长,主题等等。
(6)在此之后服务器发送客户端要播放的音频和视频数据
其中视频type是(0x99),音频type是(0x88)。
推流从握手开始和前面步骤一直,和播放的区别在于netstream改为publish。
参考:
Adobe官方文档:https://www.adobe.com/devnet/rtmp.html
https://my.oschina.net/shishuo365/blog/745489