/** * @mmt/container - MMTP Parser * * MMTP (MMT Protocol) packet parser based on ISO/IEC 23008-1:2023 Section 9.2 * * MMTP header format (12 bytes): * - Byte 0: V(2) | C(1) | FEC_type(2) | r(1) | X(1) | R(1) * - Byte 1: reserved(2) | packet_type(6) * - Bytes 2-3: packet_id (big-endian) * - Bytes 4-7: timestamp (big-endian, 32-bit) * - Bytes 8-11: sequence_number (big-endian) * * FEC_type values: * - 0: No FEC / source packet * - 1: FEC mode 0 (source with FEC payload ID) * - 2: FEC repair packet mode 0 * - 3: FEC repair packet mode 1 * * @packageDocumentation */ export interface MMTPHeader { version: number; /** Packet type: 0x00=MPU, 0x02=Signaling, 0x03=Repair */ packetType: number; packetId: number; timestamp: bigint; sequenceNumber: number; payloadLength: number; fecType: number; /** C flag: header carries a 32-bit packet_counter. */ packetCounterFlag: boolean; packetCounter?: number; /** X flag: header carries an extension header and value. */ extensionFlag: boolean; extension?: { type: number; data: Uint8Array; }; /** Byte offset at which the MMTP payload begins. */ headerLength: number; /** Random Access Point flag - indicates keyframe (ISO 23008-1, byte 0 bit 0) */ rapFlag: boolean; } export interface MPUHeader { mpuSequenceNumber: number; fragmentationIndicator: number; /** 8-bit MPU fragment_counter from byte 3. */ fragmentCounter: number; /** T flag: 1=timed media (14-byte MFU DU header), 0=non-timed (4-byte header) */ timedFlag: number; /** A flag: aggregated MPU payload. This strict profile requires A=0. */ aggregationFlag: boolean; /** MPU payload_length (bytes 0-1): bytes after the length field, including * the remaining six MPU-header bytes and all data-unit bytes. */ payloadLength: number; } export interface MFUDuHeader { movieFragmentSequenceNumber: number; sampleNumber: number; offset: number; priority: number; dependencyCounter: number; } export interface MMTPPacket { header: MMTPHeader; payload: Uint8Array; } export declare const MMTP_HEADER_SIZE = 12; export declare const MPU_HEADER_SIZE = 8; export declare const MFU_DU_HEADER_SIZE_TIMED = 14; export declare const MFU_DU_HEADER_SIZE_NONTIMED = 4; export declare const PACKET_TYPE_MPU = 0; export declare const PACKET_TYPE_SIGNALING = 2; export declare const PACKET_TYPE_REPAIR = 3; export declare const ALFEC_MESSAGE_ID = 515; /** * MMTP `packet_id` of the signaling flow, which carries PACKET_TYPE_SIGNALING * packets such as the AL-FEC message (ALFEC_MESSAGE_ID). The FFmpeg `moq_mmt` * muxer sends that message on this packet_id into the same multicast group as * the media, and moqcast-draft (PR #44) makes advertising it for a track a * MUST NOT. A track's packet_id is therefore 1..MMTP_PACKET_ID_MAX. */ export declare const MMTP_SIGNALING_PACKET_ID = 0; /** Largest value of the 16-bit MMTP `packet_id` header field. */ export declare const MMTP_PACKET_ID_MAX = 65535; export declare const FEC_CODE_XOR = 1; export declare const FEC_CODE_RAPTORQ = 2; /** * Parse MMTP header from buffer */ export declare function parseMMTPHeader(buffer: Uint8Array): MMTPHeader; /** * The MMTP `packet_id` of `buffer`, or null exactly when parseMMTPHeader would * throw for it (short buffer, V != 0, truncated packet_counter or header * extension). * * This is the per-packet filter a receiver runs before any other work, so it * builds no header object, DataView or BigInt timestamp (BLO-36599). Use * parseMMTPHeader when any other header field is needed. */ export declare function readMMTPPacketId(buffer: Uint8Array): number | null; /** * Unwrap MMTP packet to extract payload */ export declare function unwrapMMTP(buffer: Uint8Array): MMTPPacket; /** * Parse MPU header from payload * ISO/IEC 23008-1:2023 Section 9.2.3.3 * * MPU Header format (8 bytes) as written by FFmpeg/libmmt: * - Bytes 0-1: payload_length (16 bits, big-endian) * - Byte 2: FT(4) | T(1) | FI(2) | A(1) * - Byte 3: fragment_counter (8 bits) * - Bytes 4-7: MPU_sequence_number (32 bits, big-endian) */ export declare function parseMPUHeader(payload: Uint8Array): MPUHeader; export declare function parseTimedMfuDuHeader(payload: Uint8Array, offset: number): MFUDuHeader | null; /** * Why `extractMFUPayload` refused a packet. * * These identifiers are part of the public contract: callers surface them in * diagnostics, so renaming one is a breaking change. */ export type MFURejectionGate = 'packet-shorter-than-mmtp-plus-mpu-header' | 'mmtp-header-unparseable' | 'not-an-mpu-packet' | 'payload-shorter-than-mpu-header' | 'aggregation-flag-set' | 'mpu-payload-length-below-header-remainder' | 'mpu-declared-end-exceeds-payload' | 'mfu-offset-at-or-past-declared-end' | 'source-fec-payload-id-width-mismatch' | 'declared-trailers-unresolved'; export interface MFURejection { gate: MFURejectionGate; /** Human-readable detail, including the byte arithmetic that decided it. */ detail: string; } /** * Extract NAL unit payload from MMTP packet */ export declare function extractNALUnit(mmtpPacket: Uint8Array): Uint8Array; /** Result of extracting MFU payload from an MMTP packet */ export interface MFUExtraction { /** MMTP header fields */ header: MMTPHeader; /** MPU header fields */ mpu: MPUHeader; /** Fragment type (FT field, 4 bits): 0=metadata, 2=MFU data */ fragmentType: number; /** MFU payload data (video/audio bytes, headers stripped, FEC ID excluded) */ data: Uint8Array; /** Parsed timed MFU DU header when present, otherwise null */ mfuDuHeader: MFUDuHeader | null; /** Non-timed MFU Item_ID when the 4-byte DU header is present. */ itemId: number | null; /** Source FEC Payload ID if present (`FEC_type == 1`), or null */ fecPayloadId: { ssId: number; } | null; } /** * Report why `extractMFUPayload` would refuse this packet, or null if it would * accept it. * * `extractMFUPayload` returns bare `null` on ten distinct conditions, which * makes a producer/consumer framing disagreement indistinguishable from a * publisher that never started — both render as silence. Use this wherever * that distinction matters: diagnostics, contract tests, and operator-facing * logs. */ export declare function explainMFURejection(mmtpPacket: Uint8Array, options?: { skipFecId?: boolean; }): MFURejection | null; export declare function extractMFUPayload(mmtpPacket: Uint8Array, options?: { skipFecId?: boolean; }): MFUExtraction | null; /** * extractMFUPayload for a caller that already holds this packet's parsed MMTP * header, so the header is not parsed a second time (BLO-36599). * * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet; the * result is then identical to `extractMFUPayload(mmtpPacket, options)`. */ export declare function extractMFUPayloadWithHeader(mmtpPacket: Uint8Array, header: MMTPHeader, options?: { skipFecId?: boolean; }): MFUExtraction | null; /** * Extract timestamp from MMTP packet */ export declare function extractTimestamp(mmtpPacket: Uint8Array): bigint; /** * Check if MMTP packet is an FEC repair packet * FEC_type 2 and 3 are repair packets */ export declare function isFecRepairPacket(mmtpPacket: Uint8Array): boolean; /** * Extract Source FEC Payload ID (SS_ID) from the last 4 bytes of an MMTP * source packet per ISO/IEC 23008-1:2023 Section C.5.2 Figure C.10. * * SS_ID is a flat 32-bit monotonic counter (incremented by 1 per packet * for ssbg_mode0 and ssbg_mode1). Block membership and ESI use the * configured K; repair SS_Start identifies the first SS_ID of the block and * SSB_length records the authored source-symbol count: * SBN = floor(SS_ID / K), ESI = SS_ID % K * SS_Start = SBN * K (first SS_ID of the block) * * This matches FFmpeg moqenc_mmt.c, the drafts (draft-ramadan-moq-fec §6.2), * and ATSC A/331 §8.1.2.5 which defers to ISO 23008-1 Annex C. */ export declare function extractFecPayloadId(mmtpPacket: Uint8Array): { ssId: number; } | null; /** * Return true when FEC_type=1 and the exact suffix after the ISO-declared MPU * payload contains a four-byte Source FEC Payload ID. */ export declare function hasSourceFecPayloadId(mmtpPacket: Uint8Array): boolean; /** * The Source FEC Payload ID of a packet whose MMTP header the caller already * parsed, or null when the packet carries none: present only when FEC_type=1 * and the exact suffix after the ISO-declared MPU payload holds the four-byte * ID (ISO/IEC 23008-1 §C.5.2). Same answer as extractFecPayloadId, without a * second header parse (BLO-36599). * * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet. */ export declare function sourceFecPayloadIdOf(mmtpPacket: Uint8Array, header: MMTPHeader): { ssId: number; } | null; /** Size of Repair FEC Payload ID per ISO/IEC 23008-1 Section C.5.3 */ export declare const REPAIR_FEC_PID_SIZE = 13; /** * Extract Repair FEC Payload ID from a raw-multicast MMTP repair packet. * Per ISO/IEC 23008-1:2023 Section C.5.3 (ssbg_mode0, one-stage, no FFSRP_TS): * [MMTP header][SS_Start:4][RSB_length:3][RS_ID:3][SSB_length:3][repair data] * OTI is delivered via AL-FEC signaling message (Table C.3), not per-packet. * * Byte 1 is reserved(2)|packet_type(6) for V=0, so repair type 0x03 is 0x03. * Pre-migration high-bits bytes (0x0c) are rejected — no compatibility decode. */ export declare function extractRepairFecPayloadId(mmtpPacket: Uint8Array): { ssStart: number; rsbLength: number; rsId: number; ssbLength: number; } | null; /** Parsed MoQ repair frame per ISO/IEC 23008-1 Section C.4.3 + C.5.3 */ export interface MoqRepairFrame { /** MMTP packetId (1=video, 2=audio) — for routing to correct FEC decoder */ packetId: number; /** SS_Start (32 bits): SS_ID of first source symbol in block */ ssStart: number; /** RSB_length (24 bits): number of repair symbols (P) */ rsbLength: number; /** RS_ID (24 bits): repair symbol index (0-based) */ rsId: number; /** SSB_length (24 bits): authored source-symbol count (1..=configured K) */ ssbLength: number; /** Repair symbol data after the dynamic MMTP header and 13-byte FEC payload ID. */ symbolData: Uint8Array; } /** * Parse a MoQ repair frame (ISO/IEC 23008-1 Annex C format). * * Wire format per ISO 23008-1 §C.4.3 + §C.5.3: * MMTP Header: fixed 12 bytes plus C/X optional fields * then SS_Start (4 bytes) → block identifier * then RSB_length (3 bytes) → P * then RS_ID (3 bytes) → ESI = K + RS_ID * then SSB_length (3 bytes) → authored source-symbol count (1..=K) * then Repair Symbol Data (T bytes) * * @param frame - Raw MoQ repair frame bytes * @returns Parsed frame or null if invalid */ export declare function parseMoqRepairFrame(frame: Uint8Array): MoqRepairFrame | null; /** * parseMoqRepairFrame for a caller that already holds this frame's parsed MMTP * header, so the header is not parsed a second time (BLO-36599). The router's * multicast repair branch uses it: one repair packet in every K + P otherwise * paid two header parses. * * `header` must be `parseMMTPHeader(frame)` of this same frame; the result is * then identical to `parseMoqRepairFrame(frame)`. */ export declare function parseMoqRepairFrameWithHeader(frame: Uint8Array, header: MMTPHeader): MoqRepairFrame | null; //# sourceMappingURL=mmtp.d.ts.map