1.RTMP chunk stream协议
- RTMP协议(Real Time Messaging Protocol)实时消息传送协议是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输 开发的私有协议,是被Flash用于对象,视频,音频的传输.该协议建立在TCP协议或者轮询HTTP协议之上.
它有三种变种:
a.工作在TCP之上的明文协议,使用端口1935;
b.RTMPT封装在HTTP请求之中,可穿防火墙;
c.RTMPS类似RTMPT,但使用的是HTTPS连接;
2) RTMP协议就像一个用来装数据包的容器,这些数据可以是AMF格式的数据,也可以是FLV中的视/音频数据.
3)一个单一的连接可以通过不同的通道传输多路网络流(NetStream).这些通道中的包都是按照固定大小的包传输的.
1.1握手(HandShake)
RTMP连接从握手开始,不像其他的一些协议,它的握手过程由三个定长带有头部信息的chunk包组成。客户端和服务器各向对方发送三个同样大小的chunk包。为便于理解,将这三个chunk包按如下定义:
1) 客户端发往服务器的三个chunk:C0、C1、C2 (Client ---> Server)
2) 服务器发往客户端的三个chunk:S0、S1、S2 (Server --->Client)
1.1.1 握手次序
Client端发送顺序:
1)首先发送C0、C1到Server端
2)发送C2之前 必须受到S1
3)发送任何其他数据之前必须受到S2
Server端发送顺序:
- 发送S0、S1之前必须受到C0,受到C1之后也可
- 发送S2之前必须受到C1
- 发送任何其他数据之前,必须受到C2
常规使用方法:
- Client发送C0+C1到Server端
- Server收到C1后,发送S0+S1+S2到Client端
- Client受到S1后,发送C2到Server端
1.1.2 C0、S0格式
Version : 8位
C0中该字段标示着client端请求的RTMP版本,S0中该字段标示着服务器选择的RTMP版本。目前RTMP使用的版本是0x03,0—2已被遗弃不用,4—31为将来的应用保留,32—255不允许使用。
如果Server未识别Client的请求版本,默认回复0x03,Client端自动降低到0x03,或者是终止HandShake过程。
1.1.3 C1、S1格式
C1、S1包长1536字节,包括如下字段:
- Time字段:4字节
本字段包含时间戳。该时间戳应该是发送这个数据块的端点的后续块的时间起始点。可以是0,或其他的任何值。为了同步多个流,端点可能发送其chunk stream的当前值。
- Zero字段:4字节
本字段必须是全零。(实际代码怎么是 FMS的版本号呢?如3.5.1.1)
- 随机数据:1528字节。
本字段可以包含任何值。因为每个端点必须用自己初始化的握手和对端初始化的握手来区分身份,所以这个数据应有充分的随机性。但是并不需要加密安全的随机值,或者动态值。
1.1.4 C2、S2格式
C2和S2消息有1536字节长。几乎等于是S1和C1的回复??。本消息由下列字段组成。
- Time: 4字节
本字段必须包含对等段发送的时间(对C2来说是S1,对S2来说是C1)
- Time2:4字节
本字段必须包含先前发送的并被对端读取的包的时间戳,即上一个握手包的时间戳
- Random echo
本字段必须包含对端发送的随机数据字段(对C2来说是S1,对S2来说是C1)
1.1.5握手示意图
状态 |
描述 |
未初始化 |
在这个状态中发送双方的版本。此时客户端和服务端都未初始化。客户端在C0包中发送版本号。如果服务端支持那个版本,则发送S0 和S1作为响应,否则,服务端采用适当的行为作为响应。在RTMP规范中应终止连接。 |
版本已发送 |
在未初始化状态之后客户端和服务端都进入版本已发送状态。客户端等待S1包,服务端等待C1包。在接收到所等待的包后客户端发送C2包,服务端发送S2包。进入发送确认状态。 |
确认发送 |
客户端和服务端依次等待S2和C2。 |
握手完成 |
客户端和服务端发送消息。 |
1.2 Chunking(分块)
握手成功之后,连接将复用一个或者多个chunk Stream。每一个chunk Stream承载一个message stream的一类消息。每一个被创建的chunk都被关联到唯一的Chunk Stream ID。所有的Chunk通过网络进行传输。传输过程中,在传输过程中,必须一个块发送完之后再发送下一个块。在接收端,根据chunk Stream ID将这些chunks重新组装成消息。
Chunking把高层协议的比较大的消息分成很多的小的消息,这样可以防止低优先级的大消息阻塞高优先级的小消息。
分块把原本应该消息中包含的信息压缩在块头中减少了小块消息发送的开销。
块大小是可配置的。
最大块是65535字节,最小块是128字节。块越大CPU使用率越低,但是也导致大的写入,在低带宽下产生其他内容的延迟。块大小对每个方向都保持独立。
1.2.1 块格式
每一个块都包括一个块头和数据。而头本身又分为三个部分。
注:
网上查找资料,BasicHeader和 chunk Msg Header共同组成了chunk header,即:
Header Length = Basic Header Len + Chunk Msg Header Len
一般消息由两部分组成:消息头和消息体(数据)
- 块基础头 (1到3 bytes 个人抓包,都是1 没见到其他值)
- 这个字节里面记录了包的类型和包的chunk Stream ID。
- 块类型决定编码的消息头的格式。前两个Bit和包头长度对应关系:
Bits Head Len
00 12 bytes (12 = 1 + 11)
01 8 bytes (8 = 1 +7)
10 4 bytes (4 = 1 + 3)
11 1 byte (1 = 1 + 0)
- Basic Header后六位是Chunk StreamID。
- 块消息头(0,3,7或11 bytes)
- 扩展时间戳 (0或4 bytes)
本字段必须在发送普通时间戳(普通时间戳是指块消息头中的时间戳)设置为0xffffff时发送,正常时间戳为其他值时都不应发送本值。当普通时间戳的值小于0xffffff时,本字段不用出现,而应当使用正常时间戳字段
1.2.1.1块基础头(chunk basic header)
- 块基本头编码chunk Stream ID 和 chunk type(在下图中用fmt表示)。块类型决定编码消息头的格式。块基本头字段可能是1,2或3个字节。这取决于块流ID。
- 一个实现应该用最少的数据来表示ID。
- 本协议支持65597种流,ID从3-65599。ID 0、1、2作为保留。0,表示ID的范围是64-319(第二个字节+64);1,表示ID范围是64-65599(第三个字节*256+第二个字节+64);2表示低层协议消息。没有其他的字节来表示流ID。3-63表示完整的流ID。3-63之间的值表示完整的流ID。没有其他的字节表示流ID。
- 0-5(从最低位开始)位表示块流ID,即 下图的 2—
- Chunk stream ID 2-63可以用1字节的字段表示
块流ID64-319可以用2-字节表示。ID计算是(第二个字节+64)
块流ID64-65599可以表示为3个字节。ID计算为第三个字节*255+第二个字节+64
- Cs id :6位
这个字段包含的是chunk Stream ID 表示从2-63范围。0和1用来表示2或者3字节版本。
- Fmt : 2 位
标示chunk msg header 所使用的版本,chunk stream header下一节讲解。
转块块格式格式
- Cs id-64:8-16位
本字段包含块流ID减去64 的值。例如365,应在cs id中表示1,而用这里的16位表示301。
- 块流ID在64-319 范围之内,可以用2个字节版本表示,也可以用3字节版本表示。
注:目前抓包,只看到1字节长度的Basic Header = fmt + cs id
1.2.1.2块消息头(chunk message header)
块消息头总共有四种格式,格式类型有fmt字段来选择。一个实现应该使用最紧致的方式来表示块消息头。
1.2.1.2.1 type0
0类型的块长度为11字节。在一个chunk stream的开始和时间戳返回的时候必须有这种块。
- Timestamp: 3 bytes
对于0类型的块。消息的绝对时间戳在这里发送。如果时间戳大于或等于16777215(16进制0x00ffffff),该值必须为16777215,并且扩展时间戳必须出现。否则该值就是整个的时间戳。
- Message length: 3 bytes
标示着此chunk所承载的数据的大小。
- Message type id: 1 byte
该字段是RTMP包里面所承载的数据的类型,占用1个字节。例如音频包的类型为8,视频包的类型为9。下面列出的是常用的数据类型:
- 0×01 Chunk Size changes the chunk size for packets
- 0×02 Unknown
- 0×03 Bytes Read send every x bytes read by both sides
- 0×04 Ping ping is a stream control message, has subtypes
- 0×05 Server BW the servers downstream bw
- 0×06 Client BW the clients upstream bw
- 0×07 Unknown
- 0×08 Audio Data packet containing audio
- 0×09 Video Data packet containing video data
- 0x0A-0x0E Unknown
- 0x0F FLEX_STREAM_SEND TYPE_FLEX_STREAM_SEND
- 0x10 FLEX_SHARED_OBJECT TYPE_FLEX_SHARED_OBJECT
- 0x11 FLEX_MESSAGE TYPE_FLEX_MESSAGE
- 0×12 Notify an invoke which does not expect a reply
- 0×13 Shared Object has subtypes
- 0×14 Invoke like remoting call, used for stream actions too.
- 0x16 StreamData 这是FMS3出来后新增的数据类型,这种类型数据中包含AudioData和VideoData
- Msg stream id : 4 bytes
占用RTMP包头的最后4个字节,是一个big-endian的int型数据。我们x86 计算机内存中数据存放都是小尾数模式:little-endian,而网络数据流一般都是大尾数模式:big-endian。msg StreamID是音视频流的唯一ID, 一路流如果既有音频包又有视频包,那么这路流音频包的msg StreamID和他视频包的msg StreamID相同,但chunk stream id不同。
chunk stream id和msg StreamID之间的计算公式:msg StreamID=(chunk stream id-4)/5+1 参考red5。如果这个封包既不是音频包,也不是视频包,那么他的msg StreamID=0.例如当音视频包chunk stream id 为2、3、4时msg StreamID都为1 当音视频包chunk stream id 为9的时候msg StreamID为2.(此公式从网上共享查到,未验证)
1.2.1.2.2 type1
类型1的块占7个字节长。Message stream ID不包含在本块中。块的Message stream ID与的块相同。具有可变大小消息的流(比如许多视频格式消息),在第一个消息之后的每个消息的第一个块应该使用这个格式(因为他们的message stream id相同)。
1.2.1.2.3 type2
类型2的块占3个字节。既不包含message stream ID也不包含message length、message type id。本块使用的流ID和消息长度与先前的块相同。具有固定大小消息的流,在第一个消息之后的每个消息的第一个块应该使用这个格式。
1.2.1.2.4 type3
类型3的块没有头。流ID,消息长度,时间戳都不出现。这种类型的块使用与先前块相同的数据。当一个消息被分成多个块,除了第一块以外,所有的块都应使用这种类型。由相同大小,流ID,和时间间隔的流在类型2的块之后应使用这种块。
如果第一个消息和第二个消息的时间增量与第一个消息的时间戳相同,那么0类型的块之后必须是3类型的块,不需要类型2的块来注册时间增量。如果类型3的块在类型0的块之后,那么类型3的时间戳增量与0类型的块的时间戳相同。具体实例
以下是对消息头字段的解释:
timestamp delta:3字节
对于类型1的块和类型2的块,本字段表示先前块的时间戳与当前块的时间戳的差值。如果增量大于等于1677215(16进制0x00ffffff),这个值必须是16777215 ,并且扩展时间戳必须出现。否则这个值就是整个的增量。
Message length:3字节
对于类型0或类型1的块本字段表示消息的长度。
注意:这个值通常与负载长度是不相同的(The chunk payload length isthe maximum chunk size for all but the last chunk, and the remainder (which may be the entire length, for small messages) for the last chunk.)
Message type ID:1字节
对于0类型和1类型的块,本字段标示发送消息类型。
Message stream ID:4 字节
对于0类型的块,本字段存储消息流ID。通常,在一个块流中的消息来自于同一个消息流。虽然,由于不同的消息可能复用到一个块流中而使头压缩无法有效实施。但是,如果一个消息流关闭而另一个消息流才打开,那么通过发送一个新的0类型的块重复使用一个存在的块流也不是不可以
1.2.1.3扩展时间戳( Extended timestamp)
只有当块消息头中的普通时间戳设置为0x00ffffff时,本字段才被传送。如果普通时间戳的值小于0x00ffffff,那么本字段一定不能出现。如果时间戳字段不出现本字段也一定不能出现。类型3的块一定不能含有本字段。本字段在chunk messageheader之后,chunk data之前。
1.2.2 实例
1.2.2.1 例1
例1展示一个简单的音频消息流。这个例子显示了信息的冗余。
|
Message Stream ID |
Message Type ID |
Time |
Length |
Msg # 1 |
12345 |
8 |
1000 |
32 |
Msg # 2 |
12345 |
8 |
1020 |
32 |
Msg # 3 |
12345 |
8 |
1040 |
32 |
Msg # 4 |
12345 |
8 |
1060 |
32 |
Figure 13 Sample Audio messages to be made into chunks
下表显示了这个流产生的块。从消息3 开始,数据传输开始优化。在消息3之后,每个消息只有一个字节的开销。
|
Chunk Stream ID |
ChunkType |
Header Data |
No.of Bytes AfterHeader |
Total No.of Bytes in theChunk |
Chunk#1 |
3 |
0 |
delta: 1000 length: 32 type: 8 stream ID:12345 (11bytes) |
32 |
44 |
Chunk#2 |
3 |
2 |
20 (3 bytes) |
32 |
36 |
Chunk#3 |
3 |
3 |
none(0bytes) |
32 |
33 |
Chunk#4 |
3 |
3 |
none(0bytes) |
32 |
33 |
Figure 14 Format of each of the chunks of audio messages above
1.2.2.2 例2
例2 演示一个消息由于太长,而被分割成128字节的块。
|
Message Stream ID |
Message TYpe ID |
Time |
Length |
Msg # 1 |
12346 |
9 (video) |
1000 |
307 |
Figure 15 Sample Message to be broken to chunks
下面是产生的块。
|
ChunkStreamID |
ChunkType |
HeaderData |
No. ofBytes afterHeader |
Total No. ofbytes in the chunk |
Chunk#1 |
4 |
0 |
delta: 1000 length: 307 type: 9 streamID:12346 (11bytes) |
128 |
140 |
Chunk#2 |
4 |
3 |
none (0bytes) |
128 |
129 |
Chunk#3 |
4 |
3 |
none (0bytes) |
51 |
52 |
Figure 16 Format of each of the broken chunk.
Chunk1的头部数据标示着整个消息msg的大小为307bytes。
总结:从上面两个例子我们可以看出,chunk type3可用于以下两种方式:
- 一个消息的延续
- 一个新消息的头部信息从现存状态数据直接读取
1.3 Protocol Control Message(协议控制消息)
RTMP块流支持一些协议控制消息。这些消息包含rtmp chunk stream proprotocol 需要的信息,并且不能传播到更高层的协议。当前有两种消息用于RTMP chunk stream。一个协议消息用于设置块大小(set the chunk size),另一个用于由于余下的块不可得而取消一个消息(abort a message)。
协议控制消息应该含有消message stream ID 0(称为控制流)和chunk stream ID 2,并且应有最高的发送优先级。
每个协议控制消息有一个固定大小的负载,并且作为一个独立的块发送。
1.3.1 设置块大小(set chunk size)
协议控制消息1,设置块大小,用于通知对端新的最大块大小。
块大小可以设置默认值,但是客户端或服务端可以改变和更新这个值。例如,假设一个客户端想发送131字节的音频数据,而块大小是128。那么,客户端可以向服务端发送一个协议消息通知对方块大小设置为131字节。然后,客户端就可以在一个块中发送音频数据。
最大块大小是65536字节。块大小在每个方向上保持独立。
1.3.2 取消消息(Abort Message)
本协议控制消息用于通知对等端,不用再等待接收块来完成消息,而可以丢弃以前通过块流接收到的消息了。这个协议消息的负载是对等端接收到的块流ID。一个应用程序在关闭的时候发送这个消息,以告诉对方不需要继续处理消息了.
1. RTMP
实时消息协议规定通过传输层传输的消息的格式。虽然RTMP被设计与rtmp chunk stream一起工作,但是仍然可以用任何其他的传输协议来发送消息。RTMP块流和RTMP一起适用于广泛的音视频应用。从点到点和点到多的实时直播,到vod服务,到交互式视频会议。
2.1 RTMP消息格式
服务端和客户端通过在网络上发送RTMP消息进行通讯。消息可能包含音频,视频,数据,或其他的消息。
RTMP消息头(Header)和负载两部分(Payload)。以下分别介绍。
2.1.1 消息头(Message Header)
消息头包括以下几个部分:
- 消息类型(Message Type):
一个字节字段用于表示消息类型。范围在1-7内的消息ID用于协议控制消息。
- 负载长度(length):
三个字节字段用于表示负载的字节数。设置为big-endian格式。
- 时间戳(timestamp):
四字节字段包含消息的时间戳。4个字节用big-endian方式打包。
- 消息流ID(Message Stream ID):
三字节字段标识消息流。这些字节设置为big-endian格式。
2.1.2 消息负载(Message Payload)
负载时消息中包含的真实数据。例如,它可以是音频样本或压缩的视频数据。
2.2 RTMP消息类型
服务端和客户端之间交换的消息包括发送音频数据的音频消息,发送视频数据的视频消息,发送用户数据的数据消息,共享对象消息和命令消息。共享对象消息,在多个客户端和服务端之间管理分布数据。
命令消息(command msg)在客户端和服务端之间承载AMF编码命令。客户端和服务端可以通过使用命令消息向对方请求远程过程调用.
2.2.1 命令消息(command message)
命令消息承载用AMF编码的客户端与服务端之间的命令。消息类型为20(0x14)的用AMF0编码,消息类型为17(0x11)的用AMF3编码。
这些消息用于在远端实现连接(connect),创建流(createStream),发布(publish),播放(play)和暂停(pause)等操作。状态(onstatus),结果(result)等命令消息用于通知发送者请求命令的状态。命令消息由命令名,传输ID,和包含相关参数的命令对象组成。客户端或服务端可以使用命令消息(RPC)向远端请求远程过程调用.
客户端和服务端交换AMF编码的命令。发送端发送命令消息。命令消息由命令名(command name),传输ID(transaction ID),和命令对象(command object)组成。命令对象由一系列的相关参数组成。例如,连接命令包含”app”参数,这个参数告知服务端,客户端要连接的应用名。接收端处理命令并且返回含有相同传输ID(transaction ID)的响应。响应字符串含有_result或_error或一个方法名,例如,一个verifyclient,或一个contactExternalServer。
一个含有_result或_error的命令字符串表示该命令是一响应命令。传输ID,指示出响应所参考未响应的命令????。这个相当于IMAP或其他协议中的标签。命令字符串中的方法名表示发送端想要在接收端运行一个方法。
下列的类对象用于发送各种命令:
- NetConnection-代表服务端和客户端之间连接的更高层的对象。
- NetStream-代表发送音频流,视频流和其他相关数据的通道的对象。我们也发送像播放,暂停等控制数据流动的命令。
2.2.1.1 NetConnection 命令
NetConnection管理服务端和客户端之间的双路连接。另外,它还支持异步远程方法调用。
下列的方法可以通过NetConnection发送:
- connect
- call
- close
- createStream
2.2.1.1.1 connect
客户端向服务端发送一个连接命令请求连接到一个服务应用实例。
客户端到服务端的命令结构如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名。设置为”connect” |
传输ID |
数字 |
总是设为1 |
命令对象 |
对象 |
含有名值对的命令信息对象 |
可选的用户变量 |
对象 |
任何可选信息 |
下面是在连接命令的命令对象中使用的名值对的描述:
属性 |
类型 |
描述 |
示例值 |
App |
字符串 |
客户端要连接到的服务应用名 |
Testapp |
Flashver |
字符串 |
Flash播放器版本。和应用文档中getversion()函数返回的字符串相同。 |
FMSc/1.0 |
SwfUrl |
字符串 |
发起连接的swf文件的url |
file://C:/FlvPlayer.swf |
TcUrl |
字符串 |
服务url。有下列的格式。protocol://servername:port/appName/appInstance |
rtmp://localhost::1935/testapp/instance1 |
fpad |
布尔值 |
是否使用代理 |
true or false |
audioCodecs |
数字 |
指示客户端支持的音频编解码器 |
SUPPORT_SND_MP3 |
videoCodecs |
数字 |
指示支持的视频编解码器 |
SUPPORT_VID_SORENSON |
pageUrl |
字符串 |
SWF文件被加载的页面的Url |
http ://somehost/sample.html |
objectEncoding |
数字 |
AMF编码方法 |
kAMF3 |
音频编解码器属性的值:
源码常量 |
使用 |
值 |
SUPPORT_SND_NONE |
原始音频数据,无压缩 |
0x0001 |
SUPPORT_SND_ADPCM |
ADPCM 压缩 |
0x0002 |
SUPPORT_SND_MP3 |
mp3 压缩 |
0x0004 |
SUPPORT_SND_INTEL |
没有使用 |
0x0008 |
SUPPORT_SND_UNUSED |
没有使用 |
0x0010 |
SUPPORT_SND_NELLY8 |
NellyMoser 8KHZ压缩 |
0x0020 |
SUPPORT_SND_NELLY |
NellyMose压缩(5,11,22和44KHZ) |
0x0040 |
SUPPORT_SND_G711A |
G711A音频压缩(只用于flash media server) |
0x0080 |
SUPPORT_SND_G711U |
G711U音频压缩(只用于flash media server) |
0x0100 |
SUPPORT_SND_NELLY16 |
NellyMoser 16KHZ压缩 |
0x0200 |
SUPPORT_SND_AAC |
AAC编解码 |
0x0400 |
SUPPORT_SND_SPEEX |
Speex音频 |
0x0800 |
SUPPORT_SND_ALL |
所有RTMP支持的音频格式 |
0x0fff |
视频编解码器属性值:
源码常量 |
使用 |
值 |
SUPPORT_VID_UNUSED |
废弃的值 |
0x0001 |
SUPPORT_VID_JPEG |
废弃的值 |
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 with alphachannel) |
On2 video with alphachannel |
0x0020 |
SUPPORT_VID_HOMEBREWV (screensharing v2) |
Screen sharing version 2 (Flash 8+) |
0x0040 |
SUPPORT_VID_H264 |
H264 视频 |
0x0080 |
SUPPORT_VID_ALL |
RTMP支持的所有视频编解码器 |
0x00ff |
视频函数属性值:
源码常量 |
使用 |
值 |
SUPPORT_VID_CLIENT_SEEK |
Indicates that the clientcan seek frame-accurateon the client |
1 |
对象编码属性值:
源码常量 |
使用 |
值 |
kAMF0 |
Flash 6 和以后版本支持的AMF0 对象编码 |
0 |
kAMF3 |
来自Flash 9(AS3)的AMF3编码 |
3 |
从服务端到客户端的命令结构:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
含有_result或_error,表示响应时结果还是错误。 |
传输ID |
数字 |
对于调用连接响应,传输ID是1 |
属性 |
对象 |
描述连接属性(例如fmsver)的名-值对 |
信息 |
对象 |
描述来自服务端的响应的名值对。’code’,‘level’, ‘description’是这些名字中的一小部分。. |
命令执行过程中的消息流是:
- 客户端发送连接命令到服务端,请求与一个服务应用实例建立连接。
- 接收到连接命令后,服务端发送协议控制消息”窗口确认大小”到客户端。服务端同时连接到连接命令中提到的应用(the server alse connects to the application metioned in the connect command)。
- 服务端发送”设置带宽”协议控制消息到客户端。
- 在处理完”设置带宽”消息后,客户端发送”窗口确认大小”消息到服务端。
- 服务端发送用户控制消息中的流开始消息到客户端。
- 服务端发送结果命令消息通知客户端连接状态。该命令指定传输ID(对于连接命令总是1)。同时还指定一些属性,例如,Flash media server 版本(字符串),能力(数字),以及其他的连接信息,例如,层(字符串),代码(字符串),描述(字符串),对象编码(数字)等。
2.2.1.1.2 call
NetConnection 对象的调用方法在接收端运行远程过程调用。远程方法的名作为调用命令的参数。
从发送端到接收端的命令结构如下:
字段名 |
类型 |
描述 |
过程名 |
字符串 |
所调用的远程过程的名字 |
传输ID |
数字 |
如果希望有响应则给出传输ID,否则,传递一个0值 |
命令对象 |
对象 |
如果存在任何命令信息,则设置此对象。否则,设置为空类型。 |
可选变量 |
对象 |
所提供的任何可选变量 |
响应的命令结构:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名 |
传输ID |
数字 |
响应所属的命令的ID |
命令结构 |
对象 |
如果存在任何命令信息,则设置此对象。否则,设置为空类型。 |
响应 |
对象 |
来自所调用的方法的响应 |
2.2.1.1.3 createStream
客户端发送本命令到服务端创建一个消息通讯的逻辑通道。音频,视频和元数据的发布是由创建流命令建立的流通道承载的。
NetConnection本身是默认的流通道,具有流ID 0 。协议和一少部分命令消息,包括创建流,就使用默认的通讯通道。
客户端到命令端的命令结构:
字段 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设置为 ”createstream” |
传输ID |
数字 |
命令的传输ID |
命令对象 |
对象 |
如果存在任何命令信息,则设置此对象。否则,设置为空类型 |
从服务端到客户端的命令结构:
字段 |
类型 |
描述 |
命令名 |
字符串 |
含有_result或_error;表示响应是结果或是错误 |
传输ID |
数字 |
响应所属的命令的ID |
命令对象 |
对象 |
如果存在任何命令信息,则设置此对象。否则,设置为空类型 |
流ID |
数字 |
返回值或者是一个流ID,或者是一个错误信息对象 |
2.2.1.2 NetStream 命令
NetStream定义,基于连接客户端和服务端的NetConnection对象的,可以使音频流,视频流和数据消息传输的通道。对于多数据流,一个NetConnection对象可以支持多个NetStreams。
下列的命令可以在netstream上发送:
- play
- play2
- deleteStream
- closeStream
- receiveAudio
- receiveVideo
- publish
- seek
- pause
2.2.1.2.1 play
客户端发送本命令到服务端播放一个流。使用本命令多次也可以创建一个播放列表。如果想创建一个可以在不同的直播流或录制流间切换的动态播放列表,可以多次使用播放命令,并且将重设设为假。相反,如果想立即播放一个流。清楚队列中正在等待的其它流,将重设设为真。
从客户端到服务端的命令结构入下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设为”play” |
传输ID |
数字 |
设置为0 |
命令对象 |
NULL |
命令信息不存在,设为NULL 类型 |
流名 |
字符串 |
要播放的流名。对于播放一个FLV文件,则流名不带文件后缀(例如,"sample”)。对于回放MP3或ID3标签,必须在流名前加MP3(例如:”MP3:Sample”)。对于播放H264/AAC 文件,必须在流名前加MP4前缀,并且指定扩展名,例如播放sample.m4v,则指定”mp4:sample.m4v”。 |
开始 |
数字 |
一个指定开始时间的可选参数。默认值是-2,意味着用户首先尝试播放流名中指定的直播流。如果流名字段中指定的直播流不存在,则播放同名的录制流。如果本字段设置为-1,则只播放流名字段中指定的直播流。如果,本字段为0,或正值,则在本字段指定的时间,播放流名字段中指定的录制流。如果指定的录制流不存在,则播放播放列表中的下一项。 |
时长 |
数字 |
指定播放时长的可选字段。默认值是-1。-1 意味着播放一个直播流,直到没有数据可以活到,或者播放一个录制流知道结束。如果本字段位0,则播放一个录制流中从start字段中指定的时间开始的单个帧。假设,start字段中指定的是大于或等于0的值。如果本字段为正值,则以本字段中的值为时间周期播放直播流。之后,则以时长字段中指定的时间播放录制流?。(如果一个流在时长字段中指定的时间之前结束,则回放结束。)。如果传递一个非-1 的负值,则把值当作-1。 |
Reset |
布尔值 |
指定是否刷新先前的播放列表的BOOL值或数值。 |
从服务端到客户端的命令结构:
字段 |
类型 |
描述 |
命令名 |
字符串 |
命令名。如果播放成功,则命令名设为onStatus。 |
描述 |
字符串 |
如果播放命令成功,则客户端从服务端接收NetStream.Play.Start状态响应消息。如果指定的流没有找到,则接收到NetStream.Play.StreamNotFound 响应状态消息。 |
执行命令的消息流:
- 客户端从服务端接收到流创建成功消息,发送“Play”命令到服务端。
- 接收到播放命令后,服务端发送协议消息"Set Chunk size”。
- 服务端发送另一个协议消息”User control Msg”,并且在消息中指定事件”stream is recorded”和流ID。消息承载的头2个字,为事件类型,后4个字节为流ID。
- 服务端发送事件”streambegin”的协议消息(用户控制),告知客户端流ID。
- 如果客户端发送的播放命令成功的话,服务端发送响应状态命令消息NetStream.Play.Start&NetStream.Play.reset。只有当客户端发送的播放命令设置了reset命令的条件下,服务端才发送NetStream.Play.reset消息。如果要发送的流没有找的话,服务端发送NetStream.Play.StreamNotFound消息。
在此之后服务端发送客户端要播放的音频和视频数据。
2.2.1.2.2 play2
和播放命令不同,播放2命令可以切换到不同的码率,而不用改变已经播放的内
容的时间线。服务端对播放2命令可以请求的多个码率维护多个文件。
从客户端到服务端的命令结构如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设置为 “play2” |
传输ID |
数字 |
传输ID设置为2 |
开始时间 |
对象 |
存储数字值的AMF编码对象。本字段的值,指定以秒位单位的流的开始位置。如果本字段传递0,则流从当前时间开始播放。 |
旧流名 |
对象 |
存储字符串值的AMF编码对象。该值是一个含有流值参数和旧流名的字符串 |
流名 |
对象 |
存储字符串值的AMF编码对象。存储要播放的流的名字。 |
时长 |
对象 |
存储数字值的AMF编码对象。该值指定要播放的流的总时长。 |
转换 |
对象 |
存储AMF编码对象的字符串值。该值定义播放列表转换模式(switch或swap)。Switch:通过切换流的单码率版本实现多码率流。SWAP:用streamname字段的值替换oldstreamname中的值,并且存储剩余的播放列表队列。然而在这种情况下,服务端并不对流的内容作任何假设,而只是把它们当作不同的流内容。因此,只在流的两段切换,或者从不切换。 |
命令的消息流如下所示:
2.2.1.2.3 deleteStream
当NetStream对象销毁的时候发送删除流命令。
从客户端到服务端的命令结构如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设置为”deleteStream” |
传输ID |
数字 |
传输ID设置为0 |
命令对象 |
空 |
命令信息对象不存在。设为空类型。 |
流ID |
数字 |
要销毁的流的ID |
服务端不返回任何响应。
2.2.1.2.4 receiveAudio
NetStream对象发送接收音频消息通知服务端发送还是不发送音频到客户端。
从客户端到服务端的命令结构如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设为”receiveAudio” |
传输ID |
数字 |
传输ID设置为0 |
命令对象 |
空 |
命令信息对象不存在。设为空类型。 |
布尔标志 |
布尔值 |
真假表示是否接收音频流 |
服务端不返回响应.
2.2.1.2.5 receiveVideo
NetStream对象发送receiveVideo消息通知服务端是否发送视频到客户端。
从客户端到服务端的命令结构如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设为”receiveVideo” |
传输ID |
数字 |
传输ID设置为0 |
命令对象 |
空 |
命令信息对象不存在。设为空类型。 |
布尔标志 |
布尔值 |
真假表示是否接收视频流 |
服务端不返回任何响应。
2.2.1.2.6 publish
客户端发送一个发布命令,发布一个命名流到服务端。使用这个名字,任何客户端可以播放该流并且接收音频,视频,和数据消息。
从客户端到服务端的命令如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设置为”seek” |
传输ID |
数字 |
传输ID为0 |
命令对象 |
空 |
命令信息对象不存在。设为空类型。 |
发布名 |
字符串 |
流发布的名字 |
发布类型 |
字符串 |
发布类型。 设置为”live”,”record”或”append”。 “record”:流被发布,并且数据被录制到一个新的文件。文件被存储到服务端的服务应用的目录的一个子目录下。如果文件已经存在则重写文件。 “append”:流被发布并且附加到一个文件之后。如果没有发现文件则创建一个文件。 “live”:发布直播数据而不录制到文件。 |
服务端用响应状态命令响应表示一个发布的开始
2.2.1.2.7 seek
客户端发送搜寻命令在一个媒体文件中或播放列表中搜寻偏移。
从客户端到服务端的命令结构如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设置为”seek” |
传输ID |
数字 |
传输ID设置为0 |
命令对象 |
空 |
命令信息对象不存在。设为空类型 |
毫秒 |
数字 |
在播放列表中搜寻的毫秒数 |
如果搜寻成功服务端发送一个NetStream.Seek.Notify状态消息。如果失败,则返回含有_error的消息。
2.2.1.2.8 pause
客户端发送暂停命令告诉服务端暂停或开始一个命令。
从客户端到服务端的命令结构如下:
字段名 |
类型 |
描述 |
命令名 |
字符串 |
命令名,设置为”pause” |
传输ID |
数字 |
本命令没有传输ID。设为0 |
命令对象 |
空 |
|
暂停/取消暂停标志 |
布尔 |
真或假,指示暂停还是恢复播放 |
毫秒 |
数字 |
流暂停或恢复播放的毫秒数。当回放恢复的时候,服务端将只发送含有大于该值得时间戳。 |
当流暂停的时候,服务端发送一个NetStream.Pause.Notify状态消息。当恢复播放的时候发送NetStream.Unpause.Notify消息。如果失败,则返回_error消息。
2.2.2 数据消息(Data Message)
客户端或服务端通过本消息向对方发送元数据(MetaData)和用户数据(user data)。元数据包括数据的创建时间、时长、主题等细节。消息类型为18(0x12)的用AMF0编码,消息类型为15的用AMF3编码。
2.2.3 共享对象消息(shared object message)
共享对象是跨多个客户端,实例同步的FLASH对象(名值对的集合)。消息类型kMsgContainer=19用AMF0编码,kMsgContainerEx=16用AMF3编码,这两个消息用于共享对象事件。每个消息可包含多个事件。
事件 |
描述 |
Use (=1) |
客户端发送这个事件通知服务端创建一个命名的共享对象。 |
Release (=2) |
当客户端删除共享对象时,发送这个事件通知服务端。 |
Requeset change (=3) |
当请求改变一个共享对象的某个命名参数的关联的值时,客户端发送本消息。 |
Change (=4) |
当某个命名参数的关联值被改变时,服务端发送本事件给所有的客户端。 |
Success (=5) |
当接受请求改变事件后,服务端向发请求的客户端响应本消息。 |
SendMessage (=6) |
客户端向服务端发送本事件广播一个消息。接收到本事件后服务端向包括发送本事件在内的所有客户端广播一个消息。 |
Status (=7) |
针对一个错误状态,服务端向客户端发送本事件。 |
Clear (=8) |
服务端向客户端发送本事件,清除一个共享对象。本事件也作为客户端在连接时发送use事件的响应。 |
Remove (=9 ) |
服务端发送本事件使客户端删除一个插槽。 |
Request Remove (=10) |
客户端删除一个插槽时向服务端发送本事件。 |
Use Success(=11) |
当连接成功时服务端向客户端发送本事件。 |
2.2.4 音频消息(Audio message)
客户端或服务端发送本消息用于发送音频数据。消息类型0x08 ,保留为音频消息。
2.2.5 视频消息(Video message)
客户端或服务端使用本消息向对方发送视频数据。消息类型值9,保留为视频消息。视频消息比较大,会造成其他消息的延迟。为了避免这种情况,这种消息被关联为最低优先级。
2.2.6 聚合消息(Aggregate Message)
聚合消息是含有一个消息列表的一种消息。消息类型值22(0x16),保留用于聚合消息。
后端包含前面消息的包含头在内的大小。这个设置匹配flv文件格式,用于后向搜索。
使用聚合消息的好处:
块流在每个块中最多发送一个块。因此增加块的大小并且使用聚合消息可以减少块数。
2.2.7 用户控制消息(User Control Message)
客户端或服务端发送本消息通知对方用户控制事件。关于本消息的格式,参考 RTMP Protocol Msg用户控制消息。(用户控制消息是协议控制消息的一类,消息类型ID位4)
支持下列类型的用户控制事件:
事件 |
描述 |
Stream Begin(=0) |
服务端向客户端发送本事件通知对方一个流开始起作用可以用于通讯。在默认情况下,服务端在成功地从客户端接收连接命令之后发送本事件,事件ID为0。事件数据是表示开始起作用的流的ID。 |
Stream EOF(=1) |
服务端向客户端发送本事件通知客户端,数据回放完成。如果没有发行额外的命令,就不再发送数据。客户端丢弃从流中接收的消息。4字节的事件数据表示,回放结束的流的ID。 |
StreamDry(=2) |
服务端向客户端发送本事件通知客户端,流中没有更多的数据。如果服务端在一定周期内没有探测到更多的数据,就可以通知客户端流枯竭。4字节的事件数据表示枯竭流的ID。 |
SetBufferLength(=3) |
客户端向服务端发送本事件,告知对方自己存储一个流的数据的缓存的长度(毫秒单位)。当服务端开始处理一个流得时候发送本事件。事件数据的头四个字节表示流ID,后4个字节表示缓存长度(毫秒单位)。 |
StreamIsRecorded(=4) |
服务端发送本事件通知客户端,该流是一个录制流。4字节的事件数据表示录制流的ID。 |
PingRequest(=6) |
服务端通过本事件测试客户端是否可达。事件数据是4个字节的事件戳。代表服务调用本命令的本地时间。客户端在接收到kMsgPingRequest之后返回 kMsgPingResponse事件。 |
PingResponse(=7) |
客户端向服务端发送本消息响应ping请求。事件数据是接收kMsgPingRequest请求的时间。 |
2.2.8 协议控制消息(Protocol Control Message)
RTMP保留消息类型ID 在1-7之内的消息为协议控制消息。这些消息包含RTMP块流协议和RTMP协议本身要使用的信息。ID为1和2用于RTMP块流协议。ID在3-6之内用于RTMP本身。ID 7的消息用于边缘服务与源服务器。
协议控制消息必须有消息流ID 0和块流ID 2,并且有最高的发送优先级。
每个协议控制消息都有固定大小的负载。
2.2.8.1设置块大小(Set chunk size) 0x01
协议控制消息1,设置块大小,用于通知对等端使用新的最大块大小。
块大小的值被承载为4字节大小的负载。块大小有默认的值,但是如果发送者希望改变这个值,则用本消息通知对等端。例如,一个客户端想要发送131字节的数据,而块大小是默认的128字节。那么,客户端发送的消息要分成两个块发送。客户端可以选择改变块大小为131字节,这样消息就不用被分割为两个块。客户端必须向服务端发送本消息来通知对方块大小改为131字节。
最大块大小为65536字节。服务端向客户端通讯的块大小与客户端向服务端通讯的块大小互相独立。
块大小:32位
本字段承载新的块大小。新的块大小用于之后的这个块流得所有块
2.2.8.2取消消息(Abort Message) 0x02
协议控制消息2是取消消息,用于通知正在等待接收块以完成消息的对等端,丢弃一个块流中已经接收的部分并且取消对该消息的处理。对等端把这个消息的负载当作要丢弃的消息的块流ID。当发送者已经发送了一个消息的一部分,但是希望告诉接收者消息的余下部分不再发送时,发送本消息。
块流ID:32位
本字段承载要丢弃的消息所在的块流ID。
2.2.8.3确认(Acknowledgement) 0x03
当客户端或服务端接受的字节数等于窗口大小的时候,发送本消息来通知对方。窗口大小(windows size)即 发送端在未接受到来自接受端确认的情况下发送的最大字节数。服务器在应用连接后发送window size给客户机,此消息指明了序号 seq num(它标志着到目前为止已接受的字节数)。
2.2.8.4用户控制消息(User control message) 0x04
客户端或服务端发送本消息通知对方用户的控制事件。本消息承载事件类型和事件数据。
消息数据的头两个字节用于标识事件类型。事件类型之后是事件数据。事件数据字段是可变长的。具体 事件类型和事件数据
2.2.8.5确认窗口大小(window acknowledge size) 0x05
客户端或服务端发送本消息来通知对方发送确认消息的窗口大小。例如,服务端希望每当发送的字节数等于窗口大小时从客户端收到确认。服务端在成功处理了客户端的连接请求后向客户端更新窗口大小。
2.2.8.6 设置对等端带宽(Set Peer BandWidth) 0x06
客户端或服务端发送本消息更新对等端的输出带宽。输出带宽值与窗口大小值相同。如果对等端在本消息中收到的值与窗口大小不相同,则发回确认窗口大小消息。
发送者可以在限制类型字段把消息标记为硬(0),软(1),或者动态(2)。如果是硬限制对等端必须按提供的带宽发送数据。如果是软限制,对等端可以灵活决定带宽,发送端可以限制带宽?。如果是动态限制,带宽既可以是硬限制也可以是软限制
2.2.9 消息类型值
0x01 Chunk Size changes the chunk size for packets (Set Chunk Size) RTMP包体中的数据内容
0×02 Unknown (Abort Message)
0×03 Bytes Read send every x bytes read by both sides(Acknowledgement)
0×04 Ping ping is a stream control message, has subtypes (User Control Message)
0×05 Server BW the servers downstream bw (window acknowledgement size 2012-03-16)
0×06 Client BW the clients upstream bw (Set Peer BandWidth 03-16)
0×07 Unknown
0×08 Audio Data packet containing audio
0×09 Video Data packet containing video data
0x0A-0x0E Unknown
0x0F FLEX_STREAM_SEND TYPE_FLEX_STREAM_SEND
0x10 FLEX_SHARED_OBJECT TYPE_FLEX_SHARED_OBJECT
0x11 FLEX_MESSAGE TYPE_FLEX_MESSAGE
0×12 Notify an invoke which does not expect a reply
0×13 Shared Object has subtypes
0×14 Invoke like remoting call, used for stream actions too. (AMF0 0x14)
0×16 StreamData 这是FMS3出来后新增的数据类型,这种类型数据中包含AudioData和VideoData
2.2.10 数据交互实例
下面有一些示例解释RTMP的消息交换 (其中的交互过程中 并不是每一步都是按照固定流程,某一些消息的发送前后顺序有一定的随机性,但大体流程是相同的)。
2.2.10.1 发布录制视频
下面的例子演示一个发布者如何发布一个流并且传输视频到服务端。其他的客户端可以使用这些发布的流并且播放视频
2.2.10.2广播一个共享对象消息
下面的例子演示共享对象创建和变化期间消息的交换。同时也演示了共享对象消息的广播。
2.2.10.3从录制流发布元数据
下面的例子演示发布元数据的消息交换。
2. AMF
AMF是Adobe独家开发出来的通信协议,它采用二进制压缩,序列化、反序列化、传输数据,从而为Flash 播放器与Flash Remoting网关通信提供了一种轻量级的、高效能的通信方式。(AMF数据格式只是使用,个人感觉不比深究).
AMF目前有两种版本,AMF0和AMF3,他们在数据类型的定义上有细微不同。官方文档
Type |
Byte code |
Notes |
Number |
0×00 |
|
Boolean |
0×01 |
|
String |
0×02 |
|
Object |
0×03 |
|
MovieClip |
0×04 |
Not available in Remoting |
Null |
0×05 |
|
Undefined |
0×06 |
|
Reference |
0×07 |
|
MixedArray |
0×08 |
|
EndOfObject |
0×09 |
See Object |
Array |
0x0a |
|
Date |
0x0b |
|
LongString |
0x0c |
|
Unsupported |
0x0d |
|
Recordset |
0x0e |
Remoting, server-to-client only |
XML |
0x0f |
|
TypedObject (Class instance) |
0×10 |
|
AMF3 data |
0×11 |
Sent by Flash player 9+ |
对应的枚举就是:
public enum DataType
{
Number = 0x00, // 0
Boolean = 0x01, // 1
String = 0x02, // 2
UntypedObject = 0x03, // 3
MovieClip = 0x04, // 4
Null = 0x05, // 5
Undefined = 0x06, // 6
ReferencedObject = 0x07, // 7
MixedArray = 0x08, // 8
End = 0x09, // 9
Array = 0x10, // 10
Date = 0x11, // 11
LongString = 0x12, // 12
TypeAsObject = 0x13, // 13
Recordset = 0x14, // 14
Xml = 0x15, // 15
TypedObject = 0x16, // 16
AMF3data = 0x17 // 17
}
Number这里指的是double类型,数据用8字节表示,比如十六进制00 40 10 00 00 00 00 00 00就表示的是一个double数4.0,
// 这里的顺序是和amf文件中的顺序正好相反,不要忘记了
Boolean数据使用1字节表示,和C语言差不多,使用00表示false,使用01表示true。比如十六进制01 01就表示true。
String相当于所占用的空间有1个类型标识字节和2个表示字符串UTF8长度的字节加上字符串UTF8格式的内容组成。比如十六进制03 00 08 73 68 61 6E 67 67 75 61表示的就是字符串,该字符串长8字节,字符串内容为73 68 61 6E 67 67 75 61,对应的就是“shanggua”
Object内容由UTF8字符串作为Key,其他AMF类型作为Value,该对象由3个字节:00 00 09来表示结束。
MovieClip Not available in Remoting
Null 就是空对象,该对象只占用一个字节,那就是Null对象标识0x05。
Undefined 也是只占用一个字节0x06.
ReferencedObject 不清楚(可能是类型定义什么的)
MixedArray 相当于Hashtable,与3不同的是该对象定义了Hashtable的大小。读取该对象的C#代码是:
End 查看Object
Array该对象首先使用32位整数定义了ArralyList的长度,然后是密集的跟着ArrayList中的对象,读取该对象使用如下函数:
Date 对应.net中的DateTime数据类型,Date在类型标识符0x0B后使用double来表示从1970/1/1到表示的时间所经过的毫秒数,然后再跟一个ushort的16位无符号整数表示时区。
LongString对应的也是string类型,不过和2对应的String不同的是这里使用32位整数来表示字符串的UTF8长度,而String使用的是16位。
TypeAsObject
Recordset
XML是使用类型标识符0x0F后直接跟LongString类型的字符串表示。
TypedObject
AMF3data