RTP fixed header encoding and decoding (RFC 3550).
Every RTP packet opens with a 12-byte fixed header, an optional
contributing-source (CSRC) list sized by the header's csrcCount field,
and the codec payload:
0 1 2 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC |M| PT | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Synchronization Source (SSRC) Identifier | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | Contributing Source (CSRC) Identifiers | | .... | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | Payload (variable) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
csrcCount (the low 4 bits of the first octet) gives the number of 32-bit
CSRC identifiers that follow the SSRC, so the CSRC list is always
csrcCount words long. Bit-packed fields (version/padding/extension/CC,
marker/payload-type) are exposed as nested objects via bitStruct,
keeping the on-wire layout faithful while preserving named-field access.
This package covers the fixed header and CSRC list only. When extension
is set, RFC 3550 §5.3.1 defines a header-extension block between the CSRC
list and the payload; this coder does not parse it and instead treats
everything after the CSRC list as opaque payload bytes. Deeper parsing,
extension headers, and payload-format-specific decoding (RTCP, SRTP, codec
payloads) are left to future packages — v0.0.1 is header-level only.
Round-trip a packet with no CSRC entries
Round-trip a packet with no CSRC entries
import { assertEquals } from "@std/assert"; import { rtpPacket, RTP_HEADER_MIN_SIZE, RTP_VERSION } from "@binstruct/rtp"; const coder = rtpPacket(); const payload = new Uint8Array([0xde, 0xad, 0xbe, 0xef]); const packet = { versionFlags: { version: RTP_VERSION, padding: 0, extension: 0, csrcCount: 0 }, markerPayloadType: { marker: 0, payloadType: 0 }, sequenceNumber: 1, timestamp: 0, ssrc: 0x11223344, csrc: [], payload, }; const buffer = new Uint8Array(RTP_HEADER_MIN_SIZE + payload.length); const written = coder.encode(packet, buffer); const [decoded, read] = coder.decode(buffer.subarray(0, written)); assertEquals(written, RTP_HEADER_MIN_SIZE + payload.length); assertEquals(read, written); assertEquals(decoded.ssrc, packet.ssrc); assertEquals(decoded.payload, payload);