RTMP相关记录:
【流媒体】RTMP协议概述
1. 消息(Message)
在RTMP协议中,定义的数据格式称为消息(Message),消息是网络传输的数据载体,其中会携带RTMP协议命令、用户控制命令和传输的数据等等,是client和server之间进行交流通信的核心
RTMP消息分为2个部分:RTMP Message Header和RTMP Message Payload
1.1 消息头(Message Header)
消息的头部主要包括以下几个部分:
(1)消息类型(Message Type)
长度为1字节,描述了当前消息的类型
(2)载荷长度(Payload Length)
长度为3字节,描述了消息当中载荷的长度,以大端格式保存
(3)时间戳(Timestamp)
长度为4字节,描述了当前消息的时间戳,以大端格式保存
(4)流编号(Stream ID)
长度为3字节,描述了当前消息所在的流编号,以大端格式保存
1.2 消息载荷(Message Payload)
消息载荷长度不固定,可能是音频数据,或者是视频数据
2. 块(Chunk)
前面提到RTMP协议传输的数据包称为消息,包含了所要传输的各式各样的信息,但是考虑到数据冗余,数据优先级,数据传输效率,容错控制等一系列的问题,在实际网络传输时,message会被拆分成为更小的数据单元,这个数据单元称之为chunk,每个chunk的大小默认为128个字节。chunk的格式如下图所示
Chunk可以分为Chunk Header和Chunk Data两个部分
2.1 块头(Chunk Header)
块头分成三个部分,基本头(Basic Header),消息头(Message Header)和扩展时间戳(Extended Timestamp),它们的定义为:
2.1.1 基本头(Basic Header)
该字段占据1到3个字节,描述了块类型(fmt)和块流ID(cs_id)两个内容,其中fmt决定了当前chunk中message header的编码格式,cs_id用于识别同一个消息内的不同数据块。fmt长度固定为2个比特,而cs_id这个字段是可变长的,需要先对cs_id这个属性进行描述
2.1.1.1 块流ID(cs_id)
根据cs_id长度的不同,Basic Header可以分为3种类型:
2.1.1.1.1 类型1
如果cs_id的值属于 [3, 63],则Basic Header的长度为1字节,其中cs_id占据5个比特,如下图所示
2.1.1.1.2 类型2
如果cs_id的值属于 [64, 319],则Basic Header的长度为2字节,如下图所示。第1个字节中除了存储fmt之外,其余位均为0;第2个字节中存储的值为cs_id - 64。在计算流ID时,streamID = 第2个字节值 + 64
2.1.1.1.3 类型3
如果cs_id的值属于 [320, 65599],则Basic Header的长度为3字节,如下图所示。第1个字节中除了存储fmt之外,其余均为1;第2、3个字节中存储的值为cs_id - 64。在计算流ID时,streamID = 第3个字节值 * 256 + 第2个字节值 + 64
2.1.1.2 块类型(fmt)
块类型fmt这个属性固定占据2个比特,表明了当前chunk中message header的编码格式,共有4种取值,每种取值对应不同的message header格式:
取值 | 说明 |
---|---|
fmt = 0 | message header长度为11字节 |
fmt = 1 | message header长度为7字节 |
fmt = 2 | message header长度为3字节 |
fmt = 3 | message header长度为0字节 |
2.1.2 消息头(message header)
消息头格式由基本头中的fmt决定,共有4种类型
2.1.2.1 类型0(fmt = 0)
如果fmt为0,表明message header的长度为11字节,如下图所示
其中的字段为:
(a)时间戳(timestamp)
占据3字节,描述当前chunk的时间戳,如果 timestamp 大于或者等于 16777215 (十六进制 0xFFFFFF),这一字段必须是 16777215,表明有扩展 timestamp 字段来补充完整的 32 位 timestamp。否则的话,这一字段必须是整个的 timestamp
(b)消息长度(message length)
占据3字节,描述当前chunk的消息长度
(c)消息类型ID(message type id)
占据1字节,描述当前chunk的消息类型编号
(d)消息流ID(message stream id)
占据4字节,描述当前chunk的消息流编号
2.1.2.2 类型1(fmt = 1)
如果fmt为1,表明message header长度为7字节,其中不包含消息流 ID,因为这一块使用前一块一样的流 ID。格式如下图所示
其中的字段为:
(1)时间戳偏移(timestamp delta)
占据3字节,表示和上一个chunk之间距离的时间差
(2)消息长度(message length)
占据3字节,描述当前chunk的消息长度
(3)消息类型编号(message type id)
占据1字节,描述当前chunk的消息类型编号
2.1.2.3 类型2(fmt = 2)
如果fmt为2,表明message header长度为3字节,这种类型的message header只包含时间戳偏移量。如果使用这种类型,streamID、stream type id和message length与前面的chunk一致
2.1.2.4 类型3(fmt = 3)
如果fmt为3,message header长度为0字节,表明这个chunk的message header和上一个chunk是一致的,不需要额外再传输一次。如果第一个消息和第二个消息之间的 delta 和第一个消息的 timestamp 一样的话,那么在类型 0 的块之后要紧跟一个类型 3 的块,因为无需再来一个类型 2 的块来注册 delta 了。如果一个类型 3 的块跟着一个类型 0 的块,那么这个类型 3 块的 timestamp delta 和类型 0 块的 timestamp 是一样的
2.1.3 扩展时间戳(Extended Timestamp)
占据0或4字节,该字段用于对大于 16777215 (0xFFFFFF) 的 timestamp 或者 timestamp delta 进行编码;可以通过设置类型 0 块的 timestamp 字段、类型 1 或者 2 块的 timestamp delta 字段 16777215 (0xFFFFFF) 来启用这一字段。当最近的具有同一块流的类型 0、1 或 2 块指示扩展 timestamp 字段出现时,这一字段才会在类型为 3 的块中出现
2.2 块数据(Chunk Data)
占据 [0, chunkSize] 长度,是RTMP协议所携带的实际数据,例如音视频数据
3. 消息与块之间的关系
前面提到消息与块两者的关系是,通过将消息分成多个块,之后在网络上进行传输,那么消息是按照何种方式分成多个块的呢?先借用雷博的图展示,随后借用RTMP标准文档的介绍进行解释
下面引用参考文档中的解释
有如下一个数据包需要传输,stream ID为12346,message type id为9(视频数据),发送时间戳为1000,长度为307个字节
由于数据长度为307,超过了默认chunk大小128,所以需要分成若干个chunk进行传输,划分的结果如下
对每个chunk的信息进行分析:
(1)Chunk 1
chunk header一共占12字节,其中basic header占1字节,message header长度为11字节。chunk data占128字节,所以一共占140字节
属性 | 长度 | 取值 |
---|---|---|
basic header | 1字节 | fmt = 0 (即 chunk type = 0) cs_id = 4 (即 chunk stream id = 4) |
message header | 11字节 | delta = 1000(即 timestamp = 1000) length = 307(即 message length = 307) type = 9(即 message type id = 9) stream ID = 12346(即 message stream id = 12346) |
(2)Chunk 2
chunk header一共占1字节,其中basic header占1字节,basic header中的fmt为3,表示当前chunk中message header当中的数据与前面一致,所以message header不占任何字节。chunk data占128字节,所以共占129字节
(3)Chunk 3
chunk header占1字节,chunk data占51字节,所以共占52字节
通过上面的拆分方式,将一个message分成若干个chunk进行发送了。另外还可以注意到,通过设置basic header中的fmt字段,去除了Chunk 2和Chunk 3当中的冗余字段(message header字段),节省了传输的比特数
4. 结束语
本文记录了RTMP协议中的数据格式,包括消息格式和块格式当中的字段,但是缺少对其中一些字段的取值进行描述,例如stream id的取值和cs_id的取值,如何取值决定了当前传输消息的具体功能,后续需要记录