The RTS (Real-Time Streaming) container format is a custom binary format designed to deliver encoded media frames with the minimum possible overhead. Standard container formats such as ISOBMFF (MP4) carry significant per-segment metadata to support random access, seeking, and compatibility with archival playback. RTS discards everything except what is strictly necessary to reconstruct and time-align frames on the receiver side, making it well-suited to both the WebSocket transport path (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/CeeblueTV/wrts-client/llms.txt
Use this file to discover all available pages before exploring further.
WSSource) and the HTTP sequential-segment path (HTTPAdaptiveSource). The format is defined in src/media/reader/RTSReader.ts.
Design Goals
- Minimal container overhead — each packet carries only a header and the raw encoded frame payload; there is no box nesting or codec-initialization metadata in the media stream itself
- LEB128 variable-length encoding — all header fields except the payload itself are encoded in LEB128 (also called 7-bit encoding), which uses a single byte for values 0–127 and grows only for larger values; this keeps headers small for the short timestamps and durations that dominate in real-time media
- Extensible packet types — four packet types cover all current needs (media frames, timed metadata, stream configuration, and control messages) while reserving space for future extension
- Stateful timestamp inference — after the first frame of a track, subsequent timestamps are inferred from the previous frame’s duration rather than transmitted explicitly, saving bytes on every packet
Packet Types
Media Packet — audio or video frame data
Media Packet — audio or video frame data
Carries a single encoded audio or video frame. The
type field in the header distinguishes audio (1) from video (2). Contains a timestamp (only on the first packet per track after an Init Tracks signal), duration, key-frame flag, optional composition offset for B-frames, and the raw encoded payload bytes.Data Packet — generic timed data
Data Packet — generic timed data
Carries a timed non-media payload, typically a JSON object (for example, timed metadata such as subtitles or chapter markers). Uses
trackId = 0 and type = 0 in the header. Includes a time delta (added to the stored next-time for the track), a duration, and then the payload.Metadata Packet — stream-level configuration
Metadata Packet — stream-level configuration
Carries a JSON object with stream-level metadata. This is always the first packet received on a new stream and contains the protocol version, current live time, the tracks array (with IDs, codec, resolution, and bandwidth), and content protection information. Uses
trackId = 0 and type = 0 — distinguished from a Data Packet by position (it always arrives first).Init Tracks Packet — track initialization control message
Init Tracks Packet — track initialization control message
A control message that signals the (re)initialization of the audio and video track IDs. On receipt, the client clears all stored next-timestamps and expects an explicit
time field in the next media packet for each track. Uses trackId = 0 and type = 3.Header Format
Every RTS packet begins with a LEB128-encoded header field that packs both the track identity and packet type into a single compact number:trackId + 1 encoding ensures that a raw header value of zero is never used for a media track — zero is reserved for command messages (Data, Metadata, and Init Tracks packets), which are identified by trackId == 0 after decoding.
Media Packet Structure
Media packets carry the core audio and video frames:| Field | Type | Notes |
|---|---|---|
header | LEB128 | (trackId+1) << 2 | type; type = 1 for audio, 2 for video |
time | LEB128 | Present only for the first packet of a track after an Init Tracks signal; subsequent times are inferred as previousTime + previousDuration |
duration_and_flags | LEB128 | duration << 2 | hasCompositionOffset << 1 | isKeyFrame |
compositionOffset | LEB128 (signed 16-bit) | Present only when hasCompositionOffset flag is set; used for B-frames where decode order differs from presentation order |
payload | bytes | Raw encoded frame bytes (e.g., H.264 NAL units or AAC audio frame) |
value >> 2 in milliseconds. The isKeyFrame flag is value & 1. The hasCompositionOffset flag is value & 2.
Data Packet Structure
Data packets carry generic timed payloads such as JSON metadata events:| Field | Type | Notes |
|---|---|---|
header | LEB128 | (trackId+1) << 2 | 0; type = 0, trackId identifies the data track |
time_delta | LEB128 | Value added to the stored next-time for this track to produce the absolute timestamp (time = time_delta + nextTime) |
duration | LEB128 | Duration of the data event in milliseconds |
payload | bytes | JSON-encoded object |
Metadata Packet Structure
The Metadata Packet is always the first packet in a stream. It establishes the stream configuration before any media packets are processed:| Field | Type | Notes |
|---|---|---|
header | LEB128 | 0 << 2 | 0 → encoded value 0; trackId = 0, type = 0 |
payload | bytes | JSON object containing version, currentTime, tracks array, sequence, and optionally contentProtection |
trackId = 0, type = 0 header encoding. The client distinguishes between them by context: the Metadata Packet is always the first message received; subsequent trackId = 0, type = 0 packets are treated as Data Packets.
Init Tracks Packet Structure
The Init Tracks Packet resets the timestamp state and declares which track IDs are currently active for audio and video:| Field | Type | Notes |
|---|---|---|
header | LEB128 | 0 << 2 | 3 → encoded value 3; trackId = 0, type = 3 |
audioTrackId+1 | LEB128 | Active audio track ID incremented by 1; a value of 0 means no audio track |
videoTrackId+1 | LEB128 | Active video track ID incremented by 1; a value of 0 means no video track |
_nextTimes). The next Media Packet for each track must include an explicit time field rather than inferring it from the previous duration.
LEB128 Encoding
All header fields (except the raw frame payload) use LEB128 variable-length encoding, also referred to as 7-bit encoding:- Each byte contributes 7 bits of value
- The most-significant bit of each byte is a continuation flag:
1means another byte follows,0means this is the last byte - Values 0–127 encode in a single byte; values 128–16383 encode in two bytes; and so on
LEB128 examples
Relation to CMAF / MP4
TheHTTPAdaptiveSource supports two container formats, selected by the mediaExt of the endpoint URL:
| Extension | Reader | Use case |
|---|---|---|
.rts | RTSReader | WebRTS native format; minimal overhead; used by default |
.mp4 / .cmaf | CMAFReader | CMAF segments from ISOBMFF-compatible origin; passthrough to MSE |
RTSReader and CMAFReader extend the abstract Reader base class and emit the same onSample, onMetadata, and onInitTracks events, making them interchangeable from the Source’s perspective.
Backward compatibility: If the stream’s
protocolVersion.major is <= 1, the client automatically selects RTSReaderOld instead of RTSReader. The older format uses a fixed uint8 header-size prefix rather than the inline LEB128 packet framing of the current format. RTSReaderOld is selected transparently — no application-level changes are required.RTSReader, RTSReaderOld, and CMAFReader, see the Media Types API reference.