rtmp的英文官方文档
http://wwwimages.adobe.com/www.adobe.com/content/dam/acom/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdfhtml
国人的翻译
https://blog.csdn.net/noahsun1024/article/details/52177007
https://www.cnblogs.com/shishuo365/p/5862613.html服务器
大多数整数都是大端格式(即网络字节序), 可是有些是例外的网络
单位为毫秒app
chunk stream id 大端字节序, 同一个chunk的数据包必须是有序的, 不能乱. message stream id 有的地方是大端格式, 有些地方是小端格式.
message type id 代表消息体的格式的类型. 这是最须要关注的ID. 这些ID很烦人, 有的消息, 这些ID是规定死的.测试
+--------------+----------------+--------------------+--------------+ | Basic Header | Message Header | Extended Timestamp | Chunk Data | +--------------+----------------+--------------------+--------------+
Basic Header是变长的, 有3种格式
Message Header也是变长的, 有4种格式
Extended Timestamp不必定会出现, 4个字节.net
有三种格式, 本文只说明最简单的格式 , 另个两个格式不经常使用. 读者本身去看文档翻译
//一个字节的模式, 存放较小的stream id. (一般stream id都是从3开始的, 一个字节够了) 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |fmt| chunk stream id +-+-+-+-+-+-+-+-+
fmt用于代表Message Header的格式, 分别为Type0, Type1, Type2, Type3code
根据fmt的的值, 来决定哪一种类型视频
0 1 2 3 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp |message length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message length (cont) |message type id| msg stream id | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message stream id (4B) (小端) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Chunk Message Header - Type 0
0 1 2 3 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp delta |message length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | message length (cont) |message type id| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
相比于type0, type1省略了stream id, 表示与上一个chunk的stream id同样htm
0 1 2 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp delta | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
省略了更多字段, 表示这些省略的字段与上一个chunk同样.
type3彻底没有这个header了
若是Message Header中的timestamp或timestamp delta的值为0xFFFFFF(由于3个字节可能不够存), 这个字段只才存在
Extended Timestamp存放完整的时间戳
若是Message Header为TYPE3类型, 若是上一个chunk有Extended Timestamp, 那么本次的chunk也有Extended Timestamp.
不一样类型的消息, 消息体各不相同
使用message type id来区分不一样的协议控制消息, 取值为1, 2, 3, 5, 6, message stream id必须为0, chunk stream id必须为2
4个字节, 大端格式. 设置chunk的最大大小(是指有效载荷的大小, 不包含Header)
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| chunk size (31 bits) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4字节, 大端格式, 通知对方丢弃不完整的数据包.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | chunk stream id (32 bits) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4字节, 大端格式, 接收者每收到window size大小的数据, 就回复一个Acknowledgement
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sequence number (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4字节, 设置window size; 见TYPE ID 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgement Window size (4 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
设置对方的带宽输出大小为Acknowledgement Window size
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgement Window size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Limit Type | +-+-+-+-+-+-+-+-+
这个用于发送用户控制消息, message stream id必须为0, chunk stream id必须为2
+------------------------------+------------------------- | Event Type (16 bits) | Event Data (变长) +------------------------------+-------------------------
消息类型有
Event | Description |
---|---|
Stream Begin (0) | 服务器发给客户端, 说明流已经可使用了; 消息体为4字节的message stream id |
Stream EOF (1) | 服务器发给客户端, 流已经结束; 消息体为4字节的message stream id |
StreamDry (2) | 服务器发给客户端, 没有数据了; 消息体为4字节的message stream id |
SetBuffer (3) | 设置服务器的缓冲时间(毫秒), 消息体为4字节的message stream id + 4字节的大小 |
StreamIs Recorded (4) | 服务器发送给客户端, 代表服务器会录制这个流, 消息体为4字节的message stream id |
PingRequest (6) | ping-pong测试, 服务器发给客户端, 消息体为4字节的时间戳 |
PingResponse (7) | ping-pong测试, 客户端回给服务器, 消息体为ping消息中的时间戳 |
20表示AMF0格式的命令消息, 17表示AMF3格式的命令消息, 包括connect, createStream, publish, play, pause 如下数据是抓包得出来的
chunk stream id为3, message stream id为0 Transaction ID为固定值1 若是操做成功, 服务器返回_result
, 若是失败, 则返回_error
chunk stream id为3, message stream id为0
Transaction ID自定义, 能够直接设置为2, 与connect的transaction id区分开
若是操做成功, 服务器返回_result
, 若是失败, 则返回_error
chunk stream id为4, message stream id为1
Transaction ID为固定值0 (无心义)
服务器返回onStatus(NetStream_Publish_Start);
chunk stream id为4, message stream id为1
Transaction ID为固定值0 (无心义)
服务器返回onStatus(NetStream.Play.Start)
chunk stream id为4, message stream id为1
Transaction ID为固定值0 (无心义)
服务器返回onStatus(NetStream.Pause.Notify)消息
18为AMF0格式, 15为AMF3格式
消息体是音频数据
chunk stream id为6, message stream id为1 chunk stream id能够随便设置
消息体是视频数据
chunk stream id为5, message stream id为1 chunk stream id能够随便设置
C0为固定值0x03, C1为1536个字值
Source Destination Protocol Length Info 192.168.3.212 114.*.*.* RTMP 1591 Handshake C0+C1 //1591-(以太网首部14 + IP首部20 + TCP首部20) = 1537
S0(固定值0x03), S1(1536个字节), S2(1536个字节)
其中S2中的随机值必须与C1中的随机值相同
Source Destination Protocol Length Info 114.*.*.* 192.168.3.212 TCP 1514 1935 → 51691 [ACK] Seq=1 Ack=1538 Win=32128 Len=1460 114.*.*.* 192.168.3.212 TCP 1514 1935 → 51691 [ACK] Seq=1461 Ack=1538 Win=32128 Len=1460 114.*.*.* 192.168.3.212 RTMP 207 Handshake S0+S1+S2 //S0+S1+S2数据比较长, 按MTU大小拆分红多个TCP数据包
C2中的随机值必须与S1中的随机值相同
Source Destination Protocol Length Info 192.168.3.212 114.*.*.* RTMP 1590 Handshake C2
Source Destination Protocol Length Info 192.168.3.212 114.*.*.* RTMP 336 connect('mylive')
二进制内容为
03 //Chunk Basic Header, Stream ID为3, Message Header类型为0 (下面有图) 00 00 00 //3个字节的timestamp 00 01 0c //3个字节的message length, 本例中大小为268, 不包含header(不包含下面的type id和stream id) 14 //message type id, 下文有说明. 此处值为20, 表示AMF0格式的命令消息 00 00 00 00 //message stream id, 客户端能够填任意值 //如下的内部就是message的body. 须要了解AMF0的协议, 才能读懂 02 00 07 63 6f 6e 6e 65 63 74 //7个字符的字符串, 即'connect', 下面的内容都是connect命令的参数,官方文档都有说明 00 3f f0 00 00 00 00 00 00 //IEEE754数字 1, Transaction ID 03 //object对象 00 03 61 70 70 //key为,"app", 02 00 06 6d 79 6c 69 76 65, //value为字符串"mylive" 00 0e 6f 62 6a 65 63 74 45 6e 63 6f 64 69 6e 67 //key为"objectEncoding", 使用AMF0仍是AMF3 00 00 00 00 00 00 00 00 00 //数字0, 表示使用AMF0 00 04 66 70 61 64 01 00 00 //如下的类型都是这样的格式 08 66 6c 61 73 68 56 65 72 02 00 10 57 49 4e 20 31 31 2c 32 2c 32 30 32 2c 32 33 35 00 0b 61 75 64 69 6f 43 6f 64 65 63 73 00 40 90 00 00 00 00 00 00 00 0d 76 69 64 65 6f 46 75 6e 63 74 69 6f 6e 00 3f f0 00 00 00 00 00 00 00 07 70 61 67 65 55 72 6c 05 00 04 70 61 74 68 02 00 06 6d 79 6c 69 76 65 00 0c 63 61 70 61 62 69 6c 69 74 69 65 73 00 40 2e 00 00 00 00 00 00 00 06 73 77 66 55 72 6c 05 00 0b 76 69 64 65 6f 43 6f 64 65 63 73 00 40 60 00 00 00 00 00 00 00 05 74 63 55 72 6c ...
c->s : c0+c1 s->c : s0+s1+s2 c->s : c2 c->s : connect('mylive') s->c : set window size s->c : set bandwidth s->c : connect success c->s : createStream s->c : _result c->s : set buffer length s->c : _result c->s : play s->c : _set chunk size