Introduction

Chunking provides multiplexes multiple chunk stream.

(def row-header-fn {})
(def boxes-per-row 12)
(draw-box "Basic Header" {:span 3})
(draw-box "Message Header" {:span 4})
(draw-box "Extended Timestamp" {:span 5})
(draw-gap "Payload")
(draw-bottom)

Default Chunk size is 128 bytes. Chunk size is maximum length of payload. So, If you want to send 200 bytes. than that message split into 2 chunk. (like below diagram)

(def row-header-fn {})
(def boxes-per-row 12)
(draw-box "Basic Header" {:span 3})
(draw-box "Message Header" {:span 4})
(draw-gap "Payload (128 bytes)")
(draw-bottom)
(draw-box "Basic Header" {:span 3})
(draw-gap "Payload (72 bytes)")
(draw-bottom)

Header

Basic header

  • cs_id is chunk stream id that indicates following chunk’s stream context.
  • cs_id 0, 1 and 2 are reserved.
  • cs_id 2 is low-level protocol control messages and commands.

Basic header has 3 type. 1, 2, 3 bytes.

1 byte header can represent cs_id range 2-63

{reg: [
  {"bits": 2, "name": "fmt", "type": 2},
  {"bits": 6, "name": "cs_id"},
  {"bits": 16},
], config: {vflip: true, bits: 24}}

Calculation: cs_id $= 0b00111111 | \text{(1st byte)}$

else if cs_id range 64~319(64+255(0xFF)), use 2 bytes

{reg: [
  {"bits": 2, "name": "fmt", "type": 2},
  {"bits": 6, "name": 64},
  {"bits": 8, "name": "cs_id - 64"},
  {"bits": 8},
], config: {vflip: true, bits: 24}}

Calculation: cs_id $= \text{(2nd byte)} + 64$

else cs_id range 64~65599(64+65535(0xFFFF))

{reg: [
  {"bits": 2, "name": "fmt", "type": 2},
  {"bits": 6, "name": 63},
  {"bits": 16, "name": "cs_id - 64"},
], config: {vflip: true, bits: 24}}

Calculation: cs_id $= \text{(2nd byte)} + 64 + \text{(3rd byte)} \times 256$

3 bytes basic header can cover even 2 bytes. but, when send chunk message prefer shorter is better performance.

An implementation SHOULD use the smallest representation that can hold the ID.

Message header

Type 0

(def box-width 160)
(def boxes-per-row 4)
(draw-column-headers)
(draw-box "timestamp" {:span 3})
(draw-box "message length" {:span 1})
(draw-box "message length" {:span 2})
(draw-box "message type id" {:span 1})
(draw-box "message stream id" {:span 1})
(draw-box "message stream id" {:span 3})
  • 11 bytes long
  • All message reqiore timestamp, message lenagh, message type, and message stream id. Nothing optional.
  • All chunk stream start with Type 0 header

MUST be used at the start of a chunk stream

Type 1

(def box-width 160)
(def boxes-per-row 4)
(draw-column-headers)
(draw-box "timestamp delta" {:span 3})
(draw-box "message length" {:span 1})
(draw-box "message length" {:span 2})
(draw-box "message type id" {:span 1})
  • 7 bytes long

  • Same message stream id from previous chunk that has same cs_id

  • Timestamp delta in Type 1, 2.

the difference between the previous chunk’s timestamp and the current chunk’s timestamp is sent here.

Type 2

(def box-width 160)
(def boxes-per-row 4)
(draw-column-headers)
(draw-box "timestamp delta" {:span 3})

Type 3

  • When Message spliting(Fragmentation), then this type sent

When a single message is split into chunks, all chunks of a message except the first one SHOULD use this type.

  • Using previous received chunk’s header information

A stream consisting of messages of exactly the same size, stream ID and spacing in time SHOULD use this type for all chunks after a chunk of Type 2.

  • Use previous received timestamp setup

If the delta between the first message and the second message is same as the timestamp of the first message, then a chunk of Type 3 could immediately follow the chunk of Type 0 as there is no need for a chunk of Type 2 to register the delta. If a Type 3 chunk follows a Type 0 chunk, then the timestamp delta for this Type 3 chunk is the same as the timestamp of the Type 0 chunk.

Example

Example 1: Keep message header

Given case is Audio stream.

Message Stream ID Message Type ID Time Length
#1 12345 8 1000 32
#2 12345 8 1020 32
#3 12345 8 1040 32
#4 12345 8 1060 32
Chunk Stream ID Chunk Type Header Data Payload Length Chunk Size
#1 3 0 Timestamp: 1000; Length: 32; Type: 8; Stream ID: 12345 32 44
#2 3 2 Timestamp Delta: 20 32 36
#3 3 3 - 32 33
#4 3 3 - 32 33

Message #1~#4 has same Stream ID, Type ID, and Length;

Difference is Timestamp and payload data.

So, Chunk stream keep message header that recorded at #1 and keep using determine by chunk stream id.

Chunk #2 setting timestamp delta, is affect #2~#4.

#3~#4, Only transmit payload data .

Example 2: Message spliting(Fragmentation)

Given case is Video stream.

Message Stream ID Message Type ID Time Length
#1 12346 9 1000 307
Chunk Stream ID Chunk Type Header Data Payload Length Chunk Size
#1 3 0 Timestamp: 1000; Length: 307; Type: 9; Stream ID: 12346 128 140
#2 3 3 - 128 129
#3 3 3 - 51 52

Default chunk size is 128 bytes. (Chunk size meaning maximum payload length, not chunk packet size)

307 bytes is greater than 128.

Chunk #1 Setup message information(Type 0: fully described header), with 128 bytes payload.

Chunk #2, Using same message header. with 128 bytes payload.

Chunk #2, Left payload = (message lenth 307 bytes) - (previous received payload 256 bytes( = 128 + 128)) = 51 bytes.

Read 51 bytes. then process received message.

Discussion

  • THe primary objective of RTMP Chunk Stream is multiplexing and packetizing.
  • Multiplexing is shown as cs_id, than identify each stream.
  • Packetizing is shown as Fragmentaion(message spliting).
    • Prevent occupy connection by large message passing.
  • Chunk stream keeping previous message header to reduce transmition same information.

References