RTMP协议

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

各类ID

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

Basic Header

有三种格式, 本文只说明最简单的格式 , 另个两个格式不经常使用. 读者本身去看文档翻译

//一个字节的模式, 存放较小的stream id. (一般stream id都是从3开始的, 一个字节够了)
 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt| chunk stream id  
+-+-+-+-+-+-+-+-+

fmt用于代表Message Header的格式, 分别为Type0, Type1, Type2, Type3code

Message Header

根据fmt的的值, 来决定哪一种类型视频

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                                     |message length |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | message length (cont)         |message type id| msg stream id |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | message stream id (4B)   (小端)               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               Chunk Message Header - Type 0

Message Header Type 1 的格式

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

Message Header Type 2 的格式

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同样.

Message Header Type 3 的格式

type3彻底没有这个header了

Extended Timestamp

若是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

TYPE ID 1 (Set Chunk Size)

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)                                        |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TYPE ID 2 (Abort Message)

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)                                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TYPE ID 3 (Acknowledgement)

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)                                     |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TYPE ID 5 (Window Acknowledgement Size)

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)                         |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TYPE ID 6 (Set Peer Bandwidth)

设置对方的带宽输出大小为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    |
+-+-+-+-+-+-+-+-+

TYPE ID 4 (用户控制消息)

这个用于发送用户控制消息, 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消息中的时间戳

TYPE ID 20 17 (命令消息)

20表示AMF0格式的命令消息, 17表示AMF3格式的命令消息, 包括connect, createStream, publish, play, pause 如下数据是抓包得出来的

connect

chunk stream id为3, message stream id为0 Transaction ID为固定值1 若是操做成功, 服务器返回_result, 若是失败, 则返回_error

createStream

chunk stream id为3, message stream id为0
Transaction ID自定义, 能够直接设置为2, 与connect的transaction id区分开
若是操做成功, 服务器返回_result, 若是失败, 则返回_error

publish

chunk stream id为4, message stream id为1
Transaction ID为固定值0 (无心义)
服务器返回onStatus(NetStream_Publish_Start);

play

chunk stream id为4, message stream id为1
Transaction ID为固定值0 (无心义)
服务器返回onStatus(NetStream.Play.Start)

pause

chunk stream id为4, message stream id为1
Transaction ID为固定值0 (无心义)
服务器返回onStatus(NetStream.Pause.Notify)消息

TYPE ID 18 15 (用户自定义消息)

18为AMF0格式, 15为AMF3格式

TYPE ID 8 (音频消息)

消息体是音频数据
chunk stream id为6, message stream id为1 chunk stream id能够随便设置

TYPE ID 9 (视频消息)

消息体是视频数据
chunk stream id为5, message stream id为1 chunk stream id能够随便设置

推流过程抓包

握手

终端发送C0,C1给服务器

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+S1+S2

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

C2中的随机值必须与S1中的随机值相同

Source          Destination      Protocol   Length  Info
192.168.3.212    114.*.*.*        RTMP       1590    Handshake C2

终端发送connect命令

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

相关文章
相关标签/搜索