用途 | 大小(Byte) | 含义 |
Head_Type | 1 | 包头 |
TIMER | 3 | 时间戳 |
AMFSize | 3 | 数据大小 |
AMFType | 1 | 数据类型 |
StreamID | 4 | 流ID |
Head_Type占用RTMP包的第一个字节,这个字节里面记录了包的类型和包的ChannelID。Head_Type字节的前两个Bits决定了包头的长度。
Bits | Header Length |
00 | 12 bytes |
01 | 8 bytes |
10 | 4 bytes |
11 | 1 byte |
一个视频流的第一个包必须是12bytes,也就是00,要告诉对方这个流的信息。Head_Type的后面6个Bits记录着ChannelID,其为一下内容:
02 | Ping 和ByteRead通道 |
03 | Invoke通道 我们的connect() publish()和自字写的NetConnection.Call() 数据都是在这个通道的 |
04 | Audio和Vidio通道 |
05 06 07 | 服务器保留,经观察FMS2用这些Channel也用来发送音频或视频数据 |
比如传一个视频流,第一个块的head type就是00 00 00 04(0x04)。实测fms用4或者5在传影视频。官方文档上面说head_type可能会不止一个byte,但实际情况用一个byte也够了。
TiMMER - 时间戳
音视频的播放同步是由时间戳来控制的,单位是毫秒。如果时间戳大于或等于16777215(16进制0x00ffffff),该值必须为16777215,并且扩展时间戳必须出现,4bytes的扩展时间戳出现在包头之后包体之前。这个时间戳一般去flv文件里面每个tag里面的时间戳。
AMFSize - 数据大小
AMFSize占三个字节,这个长度是AMF长度,其实就是本次数据的长度。
AMFType - 数据类型
AMFType是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. |
0×16 | StreamData | 这是FMS3出来后新增的数据类型,这种类型数据中包含AudioData和VideoData |
StreamID - 流ID
占用RTMP包头的最后4个字节,是一个big-endian的int型数据,每个消息所关联的ID,用于区分其所在的消息流。
前面说包头有几种长度,第一个长度是12bytes,包含了全部的头信息,第一个数据流也就是流的开始必须是这个长度。
第二种8bytes的包,没有了streamID,发这种包,对方就默认此streamID和上次相同,一个视频数据在第一个流之后都可以使这种格式,比如一个1M的视频,第一次发128bytes用12bytes的包,之后每次发的数据都应该用8bytes的包。当然如果每次都用12bytes也没有问题。
第三种为4bytes,只有head_type和时间戳,缺少的对方认为与之前的一样,实际应用中很难出现。
第四中就只有head_type一个byte。如果一次数据超过了长度(默认是128bytes),就要分块,第一个块是12bytes或者8bytes的,之后的块就是1byte。因为分的N块,每一块的信息都是一样的,所以只需要告诉对方此次包的长度就行了。
例子:
例如有一个RTMP封包的数据03 00 00 00 00 01 02 14 00 00 00 00 02 00 07 63 6F 6E 6E 65 63 74 00 3F F0 00 00 00 00 00 00 08,数据依次解析的含义
03 表示12字节头,channelid=3
00 00 00 表示时间戳 Timer=0
00 01 02 表示AMFSize=18
14 表示AMFType=Invoke 方法调用
00 00 00 00 表示StreamID = 0 //到此,12字节RTMP头结束下面的是AMF数据分析
02 表示String
00 07 表示String长度7
63 6F 6E 6E 65 63 74 是String的Ascall值"connect"
00 表示Double
3F F0 00 00 00 00 00 00 表示double的0.0
08 表示Map数据开始
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 |
下表显示了这个流产生的块。从消息3开始,数据传输开始优化。在消息3之后,每个消息只有一个字节的开销。
Chunk Stream ID | Chunk Type | Header Data | No.of Bytes After Header | Total No.ofBytes in the Chunk | |
Chunk#1 | 3 | 0 | delta: 1000length: 32type: 8stream ID:1234(11bytes) | 32 | 44 |
Chunk#2 | 3 | 2 | 20 (3 bytes) | 32 | 36 |
Chunk#3 | 3 | 3 | none(0 bytes) | 32 | 33 |
Chunk#4 | 3 | 3 | none(0 bytes) | 32 | 33 |
演示一个消息由于太长,而被分割成128字节的块。
Message Stream ID | Message TYpe ID | Time | Length | |
Msg # 1 | 12346 | 9 (video) | 1000 | 307 |
下面是产生的块。
Chunk Stream ID | Chunk Type | Header Data | No. of Bytes after Header | Total No. of bytes in the chunk | |
Chunk#1 | 4 | 0 | delta: 1000length: 307type: 9streamID: 12346(11 bytes) | 128 | 140 |
Chunk#2 | 4 | 3 | none (0 bytes) | 128 | 129 |
Chunk#3 | 4 | 3 | none (0 bytes) | 51 | 52 |
数据传输的限定大小默认是128bytes,可以通过控制消息来改变,RTMP的约定是当Chunk流ID为2,消息流ID为0的时候,被认为是控制消息。
参考资料:
1、rtmp协议简单解析以及用其发送h264的flv文件