RTMP分块(chunk)

130 阅读6分钟

Chunk

每条RTMP消息在传输前都先被分割为若干数据块,这些数据块被称为分块(chunk).

不同消息的分块可以交替发送,同一条信息的分块按时间戳顺序依次发送,一条信息的传递通道叫做块流(chunk stream),流传递中的每个分块都包含一个流ID,用于分块传输后重组成完整的流.

注意:chunk stream ID不等于stream ID.

块大小在128Byte-65536Byte之间。

大分块cpu负载降低,在低带宽环境传输会延迟;小分块延迟低,但cpu消耗大,不利于高码率传输。

块流格式

分块基本头(chunk basic header)+ 分块消息头(chunk message header) + 拓展时间戳(extended timestamp) + 分块数据(chunk data==Message Payload)

分块头(分块基本头+分块消息头+拓展时间戳):属于 传输层结构,包含 fmt(复用类型)和 Chunk Stream ID(物理通道标识),用于分块传输。

  • 分块基本头(chunk basic header):分块流ID和分块类型。
  • 分块消息头(chunk message header):表示该分块所属的RTMP消息的数据。
  • 拓展时间戳(extended timestamp):当普通时间戳为0xFFFFFF,必须添加拓展时间戳,4字节。

Chunk Data( Message Payload :当前分块所承载的实际数据(RTMP消息),属于 应用层 消息(Message)的负载(Payload)的分段数据.  定义消息的完整元数据(如时间戳、长度、类型)。

Chunk Size:每个 Chunk Data 的最大长度(默认128字节),可通过控制消息动态调整。

当消息长度大于Chunk Size时,无法通过一个Chunk Data传递完所有数据,此时需要拆分成多个Chunk Data进行传输。拆分规则如下:

分块传输规则

  • 消息拆分
    • 若消息负载(Message Payload)长度超过 Chunk Size,消息将被拆分为多个块(Chunk)。
    • 示例:消息长度=300字节,Chunk Size=128 → 拆分为3块(128+128+44)。
  • 分成多个Chunk传输
    • 首块(Type 0或1):Type=分块基本头中的fmt, 携带完整的元数据(Message Header),后续块复用元数据(Type 2或3)。
    • 后续块(Type 3):仅包含 Chunk Data,无额外头部(复用前序块的元数据)。
  • 重组逻辑
    • 接收端根据 传输层Chunk Stream ID 和应用层 Stream ID 重组完整消息。
    • 按顺序拼接所有 Chunk Data,恢复原始消息负载。

分块基本头(chunk basic header)

Chunk Basic Header = fmt + Chunk Stream ID

fmt(Format Type): 取值范围 [0~3],占用 高2位(bit 7-6),决定了后续 chunk Message Header 的结构

块流ID(Chunk Stream ID):取值范围[3,65599], 决定了chunk basic header的长度

Chunk Basic Header 长度可以是 1、2 或 3 字节,具体由第一字节的后6bit决定的:

  • 后6bit取值为0时,2字节,streamId=第2字节整型+64。
|         第1字节      |            第2字节               |
|fmt(2位)|(6位),取值[0]|streamId=第2字节整型+64,取值[64,319]|
  • 后6bit取值为1时,3字节,streamId=(第2和第3字节组成的16位整型) + 64。
|         第1字节      |           第2字节                |       第3字节          |
|fmt(2位)|(6位),取值[1]| streamId=(第2和第3字节组成的16位整型) + 64,取值[64,65599]|
  • 后6bit取值为2时,保留。
  • 后6bit取值为3-63时,1字节,streamID=[3-63]。
|             第1字节             |
|fmt(2位)|streamId(6位),取值[1-62]|

也就是说:

块流ID范围Basic Header长度编码方式
2–631字节直接存储块流ID
64–3192字节首字节后6位为0,第二字节为ID+64
64–655993字节首字节后6位为1,后两字节为ID+64

分块消息头(chunk message header)

表示分块所属的RTMP消息的数据,长度取决于Chunk Basic Header的fmt,类型为0的分块消息头最复杂,类型值越大,结构越简单。

fmt 值类型长度(字节)适用场景
0b00Type 011新消息的第一个块(完整元数据)
0b01Type 17同一流的新消息(省略流ID)
0b10Type 23同一消息的后续块(仅时间戳增量)
0b11Type 30完全复用前一个块的元数据

Type 0(11字节)

用于新消息的第一个块,一般在分块流的开端或者时间戳回跳(如向后拖动播放)的位置,携带完整元数据:

| 字段               | 字节数 | 说明                                                                 |
|--------------------|--------|----------------------------------------------------------------------|
| Timestamp          | 3      | 消息的绝对时间戳(单位:毫秒)。若 ≥ 0xFFFFFF,则固定为0xFFFFFF,需扩展为4字节(见后) |
| Message Length     | 3      | 消息的总长度(单位:字节)                                           |
| Message Type ID    | 1      | 消息类型(如 8=音频,9=视频,18=AMF命令等)                         |
| Stream ID          | 4      | 消息所属流的ID(小端序存储)                                         |

Type 1(7字节)

用于同一流(Stream ID相同)的新消息,省略 Stream ID,表示当前分块与前序分块(携带chunk steam ID)属于一个信息流(相同Stream ID)

| 字段               | 字节数 | 说明                                                                 |
|--------------------|--------|----------------------------------------------------------------------|
| Timestamp Delta    | 3      | 相对于前一个消息的时间戳增量:表示当前分块的时间戳与前序分块的时间戳的差值                                         |
| Message Length     | 3      | 消息的总长度                                                         |
| Message Type ID    | 1      | 消息类型                                                             |

Type 2(3字节)

用于同一消息的后续块(分块传输时),仅携带时间戳增量,表示当前分块的时间戳和前序分块的时间戳的差值,与前序分块下相同stream id, 且其中信息均为固定长度:

| 字段               | 字节数 | 说明                                                                 |
|--------------------|--------|----------------------------------------------------------------------|
| Timestamp Delta    | 3      | 时间戳增量(相对于前一个块)                                         |

Type 3(0字节)

完全复用前一个块的元数据,不携带任何字段(适用于连续分块传输)。

当一条信息被拆分为多个分块传输时,后续分块均使用此种分块

Extended Timestamp(扩展时间戳)

当 Type 0/1/2 的 Timestamp 或 Timestamp Delta 值 ≥ 0xFFFFFF(即3字节最大值)时:

  • Type 0/1/2 中的 Timestamp 或 Timestamp Delta 固定为 0xFFFFFF。
  • 在 Message Header 后添加 4字节的扩展时间戳字段,存储完整的32位时间戳(大端序)。
Basic Header: 0x40 (fmt=0b01, Chunk Stream ID=0)
Message Header:
  Timestamp Delta: FF FF FF      原始值  0xFFFFFF,需扩展
  Message Length:  00 00 80      总长度 0x8000 = 32768字节
  Message Type:    09            视频数据
Extended Timestamp: 00 01 00 00  扩展时间戳 0x01000000 = 16777216ms
Payload: 消息体