@blockcast/mmt-transport 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +44 -58
  2. package/dist/abr-controller.d.ts +34 -4
  3. package/dist/abr-controller.d.ts.map +1 -1
  4. package/dist/abr-controller.js +66 -17
  5. package/dist/abr-controller.js.map +1 -1
  6. package/dist/amt-gateway.d.ts.map +1 -1
  7. package/dist/amt-gateway.js +3 -4
  8. package/dist/amt-gateway.js.map +1 -1
  9. package/dist/driad-discovery.d.ts.map +1 -1
  10. package/dist/driad-discovery.js +3 -6
  11. package/dist/driad-discovery.js.map +1 -1
  12. package/dist/endpoint-config.d.ts +68 -0
  13. package/dist/endpoint-config.d.ts.map +1 -1
  14. package/dist/endpoint-config.js +118 -3
  15. package/dist/endpoint-config.js.map +1 -1
  16. package/dist/index.d.ts +10 -8
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +8 -11
  19. package/dist/index.js.map +1 -1
  20. package/dist/mmt-transport.no-modules.js +881 -60
  21. package/dist/mmt-transport.no-modules.js.map +4 -4
  22. package/dist/moqt-priority.d.ts +39 -0
  23. package/dist/moqt-priority.d.ts.map +1 -0
  24. package/dist/moqt-priority.js +41 -0
  25. package/dist/moqt-priority.js.map +1 -0
  26. package/dist/multicast-catalog-policy.d.ts +96 -0
  27. package/dist/multicast-catalog-policy.d.ts.map +1 -0
  28. package/dist/multicast-catalog-policy.js +660 -0
  29. package/dist/multicast-catalog-policy.js.map +1 -0
  30. package/dist/multicast-orchestrator.d.ts +24 -0
  31. package/dist/multicast-orchestrator.d.ts.map +1 -1
  32. package/dist/multicast-orchestrator.js +102 -9
  33. package/dist/multicast-orchestrator.js.map +1 -1
  34. package/dist/multicast-signaling-reconciler.d.ts +37 -0
  35. package/dist/multicast-signaling-reconciler.d.ts.map +1 -0
  36. package/dist/multicast-signaling-reconciler.js +161 -0
  37. package/dist/multicast-signaling-reconciler.js.map +1 -0
  38. package/dist/multicast-subscription-pool.d.ts.map +1 -1
  39. package/dist/multicast-subscription-pool.js +37 -10
  40. package/dist/multicast-subscription-pool.js.map +1 -1
  41. package/dist/multicast-tier.d.ts +19 -0
  42. package/dist/multicast-tier.d.ts.map +1 -1
  43. package/dist/multicast-tier.js +21 -4
  44. package/dist/multicast-tier.js.map +1 -1
  45. package/dist/no-modules.d.ts +40 -3
  46. package/dist/no-modules.d.ts.map +1 -1
  47. package/dist/no-modules.js +30 -3
  48. package/dist/no-modules.js.map +1 -1
  49. package/dist/packet-batch.d.ts +13 -0
  50. package/dist/packet-batch.d.ts.map +1 -0
  51. package/dist/packet-batch.js +14 -0
  52. package/dist/packet-batch.js.map +1 -0
  53. package/dist/stall-detector.d.ts +10 -0
  54. package/dist/stall-detector.d.ts.map +1 -1
  55. package/dist/stall-detector.js +21 -0
  56. package/dist/stall-detector.js.map +1 -1
  57. package/dist/takeover.d.ts +3 -0
  58. package/dist/takeover.d.ts.map +1 -1
  59. package/dist/takeover.js +35 -2
  60. package/dist/takeover.js.map +1 -1
  61. package/dist/transport-manager.d.ts +21 -17
  62. package/dist/transport-manager.d.ts.map +1 -1
  63. package/dist/transport-manager.js +63 -29
  64. package/dist/transport-manager.js.map +1 -1
  65. package/dist/types.d.ts +98 -123
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js +12 -50
  68. package/dist/types.js.map +1 -1
  69. package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  70. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  71. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  72. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  73. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  74. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  75. package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  76. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  77. package/node_modules/@blockcast/mmt-base/README.md +134 -0
  78. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  79. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
  80. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
  81. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
  82. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
  83. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
  84. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  85. package/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
  86. package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  87. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
  88. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  89. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
  90. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  91. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
  92. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
  93. package/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
  94. package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
  95. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
  96. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
  97. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
  98. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
  99. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
  100. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
  101. package/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
  102. package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
  103. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
  104. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
  105. package/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
  106. package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
  107. package/node_modules/@blockcast/mmt-base/package.json +49 -0
  108. package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
  109. package/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
  110. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
  111. package/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
  112. package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
  113. package/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
  114. package/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
  115. package/package.json +13 -6
  116. package/src/abr-controller.ts +81 -17
  117. package/src/amt-gateway.ts +3 -5
  118. package/src/driad-discovery.ts +3 -6
  119. package/src/endpoint-config.ts +157 -3
  120. package/src/index.ts +54 -33
  121. package/src/moqt-priority.ts +51 -0
  122. package/src/multicast-catalog-policy.ts +753 -0
  123. package/src/multicast-orchestrator.ts +114 -10
  124. package/src/multicast-signaling-reconciler.ts +156 -0
  125. package/src/multicast-subscription-pool.ts +46 -11
  126. package/src/multicast-tier.ts +30 -3
  127. package/src/no-modules.ts +54 -2
  128. package/src/packet-batch.ts +17 -0
  129. package/src/stall-detector.ts +37 -0
  130. package/src/takeover.ts +41 -3
  131. package/src/transport-manager.ts +64 -34
  132. package/src/types.ts +103 -160
  133. package/dist/endpoint-config.test.d.ts +0 -2
  134. package/dist/endpoint-config.test.d.ts.map +0 -1
  135. package/dist/endpoint-config.test.js +0 -39
  136. package/dist/endpoint-config.test.js.map +0 -1
  137. package/dist/fec-client.d.ts +0 -442
  138. package/dist/fec-client.d.ts.map +0 -1
  139. package/dist/fec-client.js +0 -789
  140. package/dist/fec-client.js.map +0 -1
  141. package/dist/fec-client.test.d.ts +0 -8
  142. package/dist/fec-client.test.d.ts.map +0 -1
  143. package/dist/fec-client.test.js +0 -112
  144. package/dist/fec-client.test.js.map +0 -1
  145. package/dist/multicast-subscription-pool.test.d.ts +0 -2
  146. package/dist/multicast-subscription-pool.test.d.ts.map +0 -1
  147. package/dist/multicast-subscription-pool.test.js +0 -108
  148. package/dist/multicast-subscription-pool.test.js.map +0 -1
  149. package/dist/multicast-tier.test.d.ts +0 -2
  150. package/dist/multicast-tier.test.d.ts.map +0 -1
  151. package/dist/multicast-tier.test.js +0 -75
  152. package/dist/multicast-tier.test.js.map +0 -1
  153. package/dist/stall-detector.test.d.ts +0 -2
  154. package/dist/stall-detector.test.d.ts.map +0 -1
  155. package/dist/stall-detector.test.js +0 -33
  156. package/dist/stall-detector.test.js.map +0 -1
  157. package/dist/transport-manager.test.d.ts +0 -2
  158. package/dist/transport-manager.test.d.ts.map +0 -1
  159. package/dist/transport-manager.test.js +0 -155
  160. package/dist/transport-manager.test.js.map +0 -1
  161. package/src/endpoint-config.test.ts +0 -54
  162. package/src/fec-client.test.ts +0 -140
  163. package/src/fec-client.ts +0 -1103
  164. package/src/multicast-subscription-pool.test.ts +0 -115
  165. package/src/multicast-tier.test.ts +0 -80
  166. package/src/stall-detector.test.ts +0 -34
  167. package/src/transport-manager.test.ts +0 -192
@@ -0,0 +1,134 @@
1
+ # @blockcast/mmt-base
2
+
3
+ The shared MMTP packet-parsing leaf for libmmt's TypeScript packages. It hosts the low-level MMTP / MPU / MFU header parsers, FEC payload-id predicates, and ALTA-trailer extractor — everything that operates on raw MMTP packet bytes **with zero FEC-decode dependency**. RFC 6330 OTI geometry belongs to `@blockcast/mmt-fec`.
4
+
5
+ It exists for one structural reason. Both `@blockcast/mmt-fec` and `@blockcast/mmt-container` need these primitives. Before this package they lived in `mmt-container`, so `mmt-fec` had to import `mmt-container` for them — while `mmt-container`'s `FecManager` already imports `mmt-fec`. That is a circular dependency. Extracting the primitives into this leaf breaks the cycle: both packages now import **down** into `mmt-base` rather than across into each other. Its only dependency is `@blockcast/mmt-alta-parse` (for the ALTA trailer types it re-exports).
6
+
7
+ ```bash
8
+ pnpm add @blockcast/mmt-base
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import {
15
+ unwrapMMTP, extractMFUPayload, extractTimestamp,
16
+ isFecRepairPacket, hasSourceFecPayloadId,
17
+ PACKET_TYPE_MPU,
18
+ } from "@blockcast/mmt-base"
19
+
20
+ const pkt = unwrapMMTP(datagram) // { header, payload }
21
+ if (pkt.header.packetType === PACKET_TYPE_MPU) {
22
+ const mfu = extractMFUPayload(datagram) // { data, fragmentationIndicator, mpuSequenceNumber, ... }
23
+ const ts = extractTimestamp(datagram) // bigint MPU timestamp
24
+ }
25
+
26
+ if (isFecRepairPacket(datagram)) {
27
+ // repair flow — hand the payload to @blockcast/mmt-fec
28
+ }
29
+ ```
30
+
31
+ ## Exports
32
+
33
+ Single entry point — `package.json#exports` declares only `.`, and `src/index.ts` re-exports the packet primitives. Group them as three families:
34
+
35
+ ### Header, MPU & MFU parsing
36
+
37
+ | Symbol | Kind | Purpose |
38
+ |--------|------|---------|
39
+ | `parseMMTPHeader` | function | Parse the 12-byte MMTP header → `MMTPHeader` |
40
+ | `unwrapMMTP` | function | Header + payload split → `MMTPPacket` |
41
+ | `parseMPUHeader` | function | Parse the 8-byte MPU header → `MPUHeader` |
42
+ | `extractMFUPayload` | function | Pull the MFU data unit out of an MMTP packet → `MFUExtraction` (handles timed `T=1` and non-timed `T=0` DU headers) |
43
+ | `extractNALUnit` | function | Extract the NAL unit bytes from an MMTP packet |
44
+ | `extractTimestamp` | function | MPU presentation timestamp as `bigint` |
45
+ | `MMTPHeader`, `MPUHeader`, `MMTPPacket`, `MFUExtraction` | type | Parsed-structure shapes |
46
+ | `MMTP_HEADER_SIZE` (12), `MPU_HEADER_SIZE` (8), `MFU_DU_HEADER_SIZE_TIMED` (14), `MFU_DU_HEADER_SIZE_NONTIMED` (4) | const | Fixed header sizes |
47
+ | `PACKET_TYPE_MPU` (0x00), `PACKET_TYPE_SIGNALING` (0x02), `PACKET_TYPE_REPAIR` (0x03) | const | MMTP `packet_type` values |
48
+
49
+ MPU `payload_length` is interpreted strictly as ISO's length-after-field value: six remaining MPU-header bytes plus the data-unit header and media. ALTA and Source FEC Payload ID suffixes are outside that span; malformed, zero-length, media-only, and ambiguous declarations are rejected.
50
+
51
+ ### FEC payload-id, repair & AL-FEC signaling
52
+
53
+ | Symbol | Kind | Purpose |
54
+ |--------|------|---------|
55
+ | `isFecRepairPacket` | function | Is this a `PACKET_TYPE_REPAIR` packet? |
56
+ | `hasSourceFecPayloadId` | function | Strict predicate for a source packet's declared four-byte FEC payload ID |
57
+ | `extractFecPayloadId` | function | `{ ssId }` from a source packet's FEC payload id (or `null`) |
58
+ | `extractRepairFecPayloadId` | function | Repair-flow FEC payload id (13-byte, `REPAIR_FEC_PID_SIZE`) |
59
+ | `parseMoqRepairFrame` | function | Strictly parse a mode-0 MMTP Annex C repair frame → `MoqRepairFrame` |
60
+ | `MoqRepairFrame` | type | Repair-frame shape |
61
+ | `ALFEC_MESSAGE_ID` (0x0203), `FEC_CODE_XOR` (1), `FEC_CODE_RAPTORQ` (2), `REPAIR_FEC_PID_SIZE` (13) | const | AL-FEC signaling + payload-id constants |
62
+ | `MMTP_SIGNALING_PACKET_ID` (0), `MMTP_PACKET_ID_MAX` (0xffff) | const | Signaling-flow `packet_id` (never a track's) and the 16-bit `packet_id` ceiling; a track's `packet_id` is 1..65535 |
63
+
64
+ ### Signalling messages (`signalling.ts`, packet_id 0)
65
+
66
+ Total parsers (malformed or unsupported input → `null`, never a throw), per
67
+ ISO/IEC 23008-1:2023 9.3.4.2, 10.2-10.3, 10.6 and Annex C Table C.3 as
68
+ replaced by Amd 1:2025.
69
+
70
+ | Symbol | Kind | Purpose |
71
+ |--------|------|---------|
72
+ | `parseSignallingPayload` | function | Split a signalling packet into complete messages; reports the payload form: `iso-9.3.4.2` (Figure 15 header) or `legacy-bare-alfec` (message 0x0203 directly after the MMTP header, recognised by its exact `0x02 0x03` peek) |
73
+ | `parseSignallingPacket` | function | Decode every message of a signalling packet → `al-fec` / `pa` / `mpt` / `other` |
74
+ | `parseAlFecMessage`, `parseAlFecMessageBody` | function | Every FEC flow of an AL-FEC message: all asset packet_ids, one-stage / two-stage / LA-FEC repair flows, private fields → `AlFecMessage` |
75
+ | `alFecRaptorQFlowConfig` | function | One flow's decoder geometry under the Blockcast one-stage RaptorQ profile (private field = OTI + interleave depth) → `AlFecRaptorQFlowConfig` |
76
+ | `parsePaMessageBody`, `parseMptMessageBody`, `parseMpTable`, `mpAssetPacketIds` | function | PA / MPT messages and the MP table: asset → packet_id mapping |
77
+
78
+ ### ALTA trailer (re-exported from `@blockcast/mmt-alta-parse`)
79
+
80
+ | Symbol | Kind | Purpose |
81
+ |--------|------|---------|
82
+ | `extractAltaTrailer` | function | Extract the ALTA authentication trailer from an MMTP packet |
83
+ | `AltaTrailer` | type | Parsed trailer shape |
84
+ | `MMTP_EXT_TYPE_ALTA_AUTH`, `MMTP_EXT_TRAILER_HEADER_SIZE` | const | ALTA extension-header identifiers |
85
+
86
+ These four are re-exported so callers that already pull MMTP primitives from here don't need a second import from `@blockcast/mmt-alta-parse` just for the trailer.
87
+
88
+ ## Why this package exists
89
+
90
+ ```
91
+ before after
92
+ ┌──────────────┐ ┌──────────────┐
93
+ │ mmt-fec │──needs primitives──► │ mmt-fec │─┐
94
+ └──────────────┘ └──────────────┘ │ import down
95
+ ┌──────────────┐ ┌──────────────┐ │
96
+ │ mmt-container│◄─FecManager needs─┐ │ mmt-container│─┤
97
+ │ (hosts the │ mmt-fec │ └──────────────┘ │
98
+ │ primitives) │───────────────────┘ ▼
99
+ └──────────────┘ ⟲ CYCLE ┌──────────────────────────┐
100
+ │ @blockcast/mmt-base │
101
+ │ (MMTP/MPU/MFU/PID) │
102
+ └──────────────────────────┘
103
+ ```
104
+
105
+ - `@blockcast/mmt-fec` imports the parsers in `src/block-processor.ts` (splitting a decoded FEC block back into MFU fragments).
106
+ - `@blockcast/mmt-container` imports these primitives directly. The obsolete
107
+ container re-export shim and `@blockcast/mmt-container/mmtp` subpath were
108
+ removed so there is one parser authority.
109
+
110
+ Keep this package dependency-light. OTI geometry and anything that needs the WASM FEC decoder belong in `@blockcast/mmt-fec`; anything that needs catalog/CMAF/reassembly state belongs in `@blockcast/mmt-container`. `mmt-base` stays pure MMTP packet parsing so both can depend on it without pulling in the other.
111
+
112
+ ## Build & test
113
+
114
+ ```bash
115
+ npm run build # tsc → dist/
116
+ npm run typecheck # tsc --noEmit
117
+ npm test # vitest run
118
+ ```
119
+
120
+ The direct suite owns header parsing, MFU extraction, payload-ID predicates,
121
+ declared-boundary rejection, C/X optional headers, and
122
+ ALTA/FEC suffix coverage. Downstream suites exercise integration only.
123
+
124
+ ## Related packages
125
+
126
+ | Package | Relationship |
127
+ |---------|-------------|
128
+ | [`@blockcast/mmt-alta-parse`](../mmt-alta-parse) | Only dependency — supplies the ALTA trailer types this package re-exports |
129
+ | [`@blockcast/mmt-fec`](../fec) | Imports these parsers in `block-processor` to turn decoded FEC blocks back into MFU fragments |
130
+ | [`@blockcast/mmt-container`](../container) | Imports these primitives directly and builds the `FecManager`, MFU reassembler, and CMAF demuxer on top |
131
+
132
+ ## License
133
+
134
+ Apache-2.0 — see `LICENSE`.
@@ -0,0 +1,55 @@
1
+ /** The two block profiles a catalog `fec.blockProfile` may name. */
2
+ export type FecBlockProfile = 'k-padded-v1' | 'iso-ssbg1-v1';
3
+ /** Every accepted `fec.blockProfile` token, legacy first. */
4
+ export declare const FEC_BLOCK_PROFILES: readonly FecBlockProfile[];
5
+ /** True for a known block-profile token. */
6
+ export declare function isFecBlockProfile(value: unknown): value is FecBlockProfile;
7
+ /**
8
+ * Read a required `fec.blockProfile`. Throws on an absent or unknown token:
9
+ * there is no default and nothing is inferred, because the two profiles carry
10
+ * identical wire tuples whose repair math differs.
11
+ *
12
+ * @param where the catalog path of the field, for the error message.
13
+ */
14
+ export declare function parseFecBlockProfile(value: unknown, where: string): FecBlockProfile;
15
+ /** ISO/IEC 23008-1 C.3.3 ssbg_mode1: the two-octet packet length field. */
16
+ export declare const SSBG1_LENGTH_FIELD_BYTES = 2;
17
+ /** The largest protected packet an ssbg_mode1 symbol of size T can carry. */
18
+ export declare function ssbg1MaxPacketLength(symbolSize: number): number;
19
+ /** Whether a protected packet of `packetLength` bytes fits one T-byte ssbg_mode1 symbol. */
20
+ export declare function ssbg1FitsSymbol(packetLength: number, symbolSize: number): boolean;
21
+ /**
22
+ * Build one ssbg_mode1 source symbol (C.3.3): the protected packet's length
23
+ * in two octets, network byte order, then the packet, then zero octets up to
24
+ * T. The protected packet is the MMTP packet before its Source FEC Payload ID
25
+ * is appended (C.4.2).
26
+ *
27
+ * @throws when `length + 2 > T`: the packet cannot be protected under this
28
+ * profile, and silently truncating it would corrupt every repair symbol.
29
+ */
30
+ export declare function buildSsbg1SourceSymbol(packet: Uint8Array, symbolSize: number): Uint8Array;
31
+ /**
32
+ * Recover the protected packet from one ssbg_mode1 source symbol: the bytes
33
+ * the length field names. Returns null, never a guess, when the length field
34
+ * points past the symbol or any padding octet is non-zero (C.3.3 padding is
35
+ * all 00h), since either means the recovered symbol is not what was sent.
36
+ */
37
+ export declare function trimSsbg1SourceSymbol(symbol: Uint8Array): Uint8Array | null;
38
+ /** The SS_ID after the last source symbol of `[ssStart, ssStart + ssbLength)`. */
39
+ export declare function isoBlockEnd(ssStart: number, ssbLength: number): number;
40
+ /**
41
+ * Range membership (C.5.2, C.5.3): whether `ssId` is one of the
42
+ * `ssbLength` consecutive SS_IDs starting at `ssStart`, modulo 2^32.
43
+ */
44
+ export declare function isoBlockContains(ssStart: number, ssbLength: number, ssId: number): boolean;
45
+ /**
46
+ * A lower bound on how many blocks the block starting at `a` lies behind the
47
+ * one starting at `b` (0 when `a` does not precede `b`). An iso-ssbg1-v1
48
+ * block holds at most `kMax` source symbols (the catalog `sourceSymbols`, the
49
+ * AL-FEC maximum_k_for_repair_flow), so `n * kMax` SS_IDs always span at least
50
+ * `n` blocks. Every interleave horizon in the receiver (decoder retention,
51
+ * worker retirement, ordered-relay settlement) uses this one bound, so the
52
+ * manager's horizon can never fall inside the decoder's.
53
+ */
54
+ export declare function isoBlocksBehindLowerBound(a: number, b: number, kMax: number): number;
55
+ //# sourceMappingURL=fec-block-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fec-block-profile.d.ts","sourceRoot":"","sources":["../src/fec-block-profile.ts"],"names":[],"mappings":"AAwBA,oEAAoE;AACpE,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,cAAc,CAAA;AAE5D,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAGvD,CAAA;AAEF,4CAA4C;AAC5C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAUnF;AAED,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAOjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAczF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAS3E;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpF"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * FEC block profile (BLO-35597, ISO/IEC 23008-1 Annex C).
3
+ *
4
+ * Every catalog `fec` object that declares an FEC instance names exactly one
5
+ * block profile in `fec.blockProfile`. A receiver applies all of a profile's
6
+ * rules or none of them, so the token is the one explicit per-stream
7
+ * discriminator: it is never defaulted and never inferred from the wire.
8
+ *
9
+ * - `k-padded-v1` (legacy, Blockcast-private): a source symbol is the
10
+ * protected MMTP packet zero-padded to T with no length field; every block
11
+ * is a K-symbol RaptorQ block whose ESIs `SSB_length..K-1` are implicit
12
+ * zero padding; `SS_ID = SBN*K + ESI`, so `SS_Start = SBN*K`; repair
13
+ * ESI = K + RS_ID; the MoQ repair group id is the SBN.
14
+ * - `iso-ssbg1-v1`: ssbg_mode1 (C.3.3): a source symbol is
15
+ * `u16 length || protected packet || zero padding` to T; a block is a
16
+ * K' = SSB_length RaptorQ block (C.5.3) and repair ESI = SSB_length + RS_ID;
17
+ * SS_ID increments by one per FEC source packet and wraps at 2^32 (C.5.2),
18
+ * so a block is located by membership in `[SS_Start, SS_Start + SSB_length)`
19
+ * under RFC 1982 serial arithmetic; the MoQ repair group id is the 62-bit
20
+ * wrap-epoch-extended SS_Start. The AL-FEC coding byte for this profile is
21
+ * 0x14 (one-stage, ssbg_mode1, fec_payload_id_mode 0, Amd 1:2025 Table C.3).
22
+ */
23
+ import { serial32Add, serial32Distance, serial32Precedes } from './serial-number.js';
24
+ /** Every accepted `fec.blockProfile` token, legacy first. */
25
+ export const FEC_BLOCK_PROFILES = Object.freeze([
26
+ 'k-padded-v1',
27
+ 'iso-ssbg1-v1',
28
+ ]);
29
+ /** True for a known block-profile token. */
30
+ export function isFecBlockProfile(value) {
31
+ return value === 'k-padded-v1' || value === 'iso-ssbg1-v1';
32
+ }
33
+ /**
34
+ * Read a required `fec.blockProfile`. Throws on an absent or unknown token:
35
+ * there is no default and nothing is inferred, because the two profiles carry
36
+ * identical wire tuples whose repair math differs.
37
+ *
38
+ * @param where the catalog path of the field, for the error message.
39
+ */
40
+ export function parseFecBlockProfile(value, where) {
41
+ if (isFecBlockProfile(value))
42
+ return value;
43
+ if (value === undefined) {
44
+ throw new Error(`${where} is required: name the FEC block profile ("k-padded-v1" or "iso-ssbg1-v1")`);
45
+ }
46
+ throw new Error(`${where} must be "k-padded-v1" or "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
47
+ }
48
+ /** ISO/IEC 23008-1 C.3.3 ssbg_mode1: the two-octet packet length field. */
49
+ export const SSBG1_LENGTH_FIELD_BYTES = 2;
50
+ /** The largest protected packet an ssbg_mode1 symbol of size T can carry. */
51
+ export function ssbg1MaxPacketLength(symbolSize) {
52
+ return symbolSize - SSBG1_LENGTH_FIELD_BYTES;
53
+ }
54
+ /** Whether a protected packet of `packetLength` bytes fits one T-byte ssbg_mode1 symbol. */
55
+ export function ssbg1FitsSymbol(packetLength, symbolSize) {
56
+ return (Number.isSafeInteger(packetLength) &&
57
+ packetLength >= 0 &&
58
+ packetLength <= ssbg1MaxPacketLength(symbolSize) &&
59
+ packetLength <= 0xffff);
60
+ }
61
+ /**
62
+ * Build one ssbg_mode1 source symbol (C.3.3): the protected packet's length
63
+ * in two octets, network byte order, then the packet, then zero octets up to
64
+ * T. The protected packet is the MMTP packet before its Source FEC Payload ID
65
+ * is appended (C.4.2).
66
+ *
67
+ * @throws when `length + 2 > T`: the packet cannot be protected under this
68
+ * profile, and silently truncating it would corrupt every repair symbol.
69
+ */
70
+ export function buildSsbg1SourceSymbol(packet, symbolSize) {
71
+ if (!Number.isSafeInteger(symbolSize) || symbolSize <= SSBG1_LENGTH_FIELD_BYTES) {
72
+ throw new Error(`ssbg_mode1 symbol size must exceed ${SSBG1_LENGTH_FIELD_BYTES} (got ${symbolSize})`);
73
+ }
74
+ if (!ssbg1FitsSymbol(packet.byteLength, symbolSize)) {
75
+ throw new Error(`ssbg_mode1 source symbol needs ${packet.byteLength} + ${SSBG1_LENGTH_FIELD_BYTES} bytes but T is ${symbolSize}`);
76
+ }
77
+ const symbol = new Uint8Array(symbolSize);
78
+ symbol[0] = (packet.byteLength >>> 8) & 0xff;
79
+ symbol[1] = packet.byteLength & 0xff;
80
+ symbol.set(packet, SSBG1_LENGTH_FIELD_BYTES);
81
+ return symbol;
82
+ }
83
+ /**
84
+ * Recover the protected packet from one ssbg_mode1 source symbol: the bytes
85
+ * the length field names. Returns null, never a guess, when the length field
86
+ * points past the symbol or any padding octet is non-zero (C.3.3 padding is
87
+ * all 00h), since either means the recovered symbol is not what was sent.
88
+ */
89
+ export function trimSsbg1SourceSymbol(symbol) {
90
+ if (symbol.byteLength < SSBG1_LENGTH_FIELD_BYTES)
91
+ return null;
92
+ const length = (symbol[0] << 8) | symbol[1];
93
+ const end = SSBG1_LENGTH_FIELD_BYTES + length;
94
+ if (end > symbol.byteLength)
95
+ return null;
96
+ for (let i = end; i < symbol.byteLength; i++) {
97
+ if (symbol[i] !== 0)
98
+ return null;
99
+ }
100
+ return symbol.subarray(SSBG1_LENGTH_FIELD_BYTES, end);
101
+ }
102
+ /** The SS_ID after the last source symbol of `[ssStart, ssStart + ssbLength)`. */
103
+ export function isoBlockEnd(ssStart, ssbLength) {
104
+ return serial32Add(ssStart, ssbLength);
105
+ }
106
+ /**
107
+ * Range membership (C.5.2, C.5.3): whether `ssId` is one of the
108
+ * `ssbLength` consecutive SS_IDs starting at `ssStart`, modulo 2^32.
109
+ */
110
+ export function isoBlockContains(ssStart, ssbLength, ssId) {
111
+ return serial32Distance(ssStart, ssId) < ssbLength;
112
+ }
113
+ /**
114
+ * A lower bound on how many blocks the block starting at `a` lies behind the
115
+ * one starting at `b` (0 when `a` does not precede `b`). An iso-ssbg1-v1
116
+ * block holds at most `kMax` source symbols (the catalog `sourceSymbols`, the
117
+ * AL-FEC maximum_k_for_repair_flow), so `n * kMax` SS_IDs always span at least
118
+ * `n` blocks. Every interleave horizon in the receiver (decoder retention,
119
+ * worker retirement, ordered-relay settlement) uses this one bound, so the
120
+ * manager's horizon can never fall inside the decoder's.
121
+ */
122
+ export function isoBlocksBehindLowerBound(a, b, kMax) {
123
+ if (!Number.isSafeInteger(kMax) || kMax <= 0) {
124
+ throw new Error(`iso-ssbg1-v1 block horizon needs a positive maximum K (got ${kMax})`);
125
+ }
126
+ return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0;
127
+ }
128
+ //# sourceMappingURL=fec-block-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fec-block-profile.js","sourceRoot":"","sources":["../src/fec-block-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from './mmtp.js';
2
+ export * from './net-constants.js';
3
+ export * from './ntp-short.js';
4
+ export * from './signalling.js';
5
+ export * from './serial-number.js';
6
+ export * from './fec-block-profile.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA"}
@@ -0,0 +1,7 @@
1
+ export * from './mmtp.js';
2
+ export * from './net-constants.js';
3
+ export * from './ntp-short.js';
4
+ export * from './signalling.js';
5
+ export * from './serial-number.js';
6
+ export * from './fec-block-profile.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,oBAAoB,CAAA;AAClC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA"}
@@ -0,0 +1,292 @@
1
+ import { extractAltaTrailer as _leafExtractAltaTrailer, type AltaTrailer as _LeafAltaTrailer } from '@blockcast/mmt-alta-parse';
2
+ /**
3
+ * @mmt/container - MMTP Parser
4
+ *
5
+ * MMTP (MMT Protocol) packet parser based on ISO/IEC 23008-1:2023 Section 9.2
6
+ *
7
+ * MMTP header format (12 bytes):
8
+ * - Byte 0: V(2) | C(1) | FEC_type(2) | r(1) | X(1) | R(1)
9
+ * - Byte 1: reserved(2) | packet_type(6)
10
+ * - Bytes 2-3: packet_id (big-endian)
11
+ * - Bytes 4-7: timestamp (big-endian, 32-bit)
12
+ * - Bytes 8-11: sequence_number (big-endian)
13
+ *
14
+ * FEC_type values:
15
+ * - 0: No FEC / source packet
16
+ * - 1: FEC mode 0 (source with FEC payload ID)
17
+ * - 2: FEC repair packet mode 0
18
+ * - 3: FEC repair packet mode 1
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+ export interface MMTPHeader {
23
+ version: number;
24
+ /** Packet type: 0x00=MPU, 0x02=Signaling, 0x03=Repair */
25
+ packetType: number;
26
+ packetId: number;
27
+ timestamp: bigint;
28
+ sequenceNumber: number;
29
+ payloadLength: number;
30
+ fecType: number;
31
+ /** C flag: header carries a 32-bit packet_counter. */
32
+ packetCounterFlag: boolean;
33
+ packetCounter?: number;
34
+ /** X flag: header carries an extension header and value. */
35
+ extensionFlag: boolean;
36
+ extension?: {
37
+ type: number;
38
+ data: Uint8Array;
39
+ };
40
+ /** Byte offset at which the MMTP payload begins. */
41
+ headerLength: number;
42
+ /** Random Access Point flag - indicates keyframe (ISO 23008-1, byte 0 bit 0) */
43
+ rapFlag: boolean;
44
+ }
45
+ export interface MPUHeader {
46
+ mpuSequenceNumber: number;
47
+ fragmentationIndicator: number;
48
+ /** 8-bit MPU fragment_counter from byte 3. */
49
+ fragmentCounter: number;
50
+ /** T flag: 1=timed media (14-byte MFU DU header), 0=non-timed (4-byte header) */
51
+ timedFlag: number;
52
+ /** A flag: aggregated MPU payload. This strict profile requires A=0. */
53
+ aggregationFlag: boolean;
54
+ /** MPU payload_length (bytes 0-1): bytes after the length field, including
55
+ * the remaining six MPU-header bytes and all data-unit bytes. */
56
+ payloadLength: number;
57
+ }
58
+ export interface MFUDuHeader {
59
+ movieFragmentSequenceNumber: number;
60
+ sampleNumber: number;
61
+ offset: number;
62
+ priority: number;
63
+ dependencyCounter: number;
64
+ }
65
+ export interface MMTPPacket {
66
+ header: MMTPHeader;
67
+ payload: Uint8Array;
68
+ }
69
+ export declare const MMTP_HEADER_SIZE = 12;
70
+ export declare const MPU_HEADER_SIZE = 8;
71
+ export declare const MFU_DU_HEADER_SIZE_TIMED = 14;
72
+ export declare const MFU_DU_HEADER_SIZE_NONTIMED = 4;
73
+ export declare const PACKET_TYPE_MPU = 0;
74
+ export declare const PACKET_TYPE_SIGNALING = 2;
75
+ export declare const PACKET_TYPE_REPAIR = 3;
76
+ export declare const ALFEC_MESSAGE_ID = 515;
77
+ /**
78
+ * MMTP `packet_id` of the signaling flow, which carries PACKET_TYPE_SIGNALING
79
+ * packets such as the AL-FEC message (ALFEC_MESSAGE_ID). The FFmpeg `moq_mmt`
80
+ * muxer sends that message on this packet_id into the same multicast group as
81
+ * the media, and moqcast-draft (PR #44) makes advertising it for a track a
82
+ * MUST NOT. A track's packet_id is therefore 1..MMTP_PACKET_ID_MAX.
83
+ */
84
+ export declare const MMTP_SIGNALING_PACKET_ID = 0;
85
+ /** Largest value of the 16-bit MMTP `packet_id` header field. */
86
+ export declare const MMTP_PACKET_ID_MAX = 65535;
87
+ export declare const FEC_CODE_XOR = 1;
88
+ export declare const FEC_CODE_RAPTORQ = 2;
89
+ /**
90
+ * Parse MMTP header from buffer
91
+ */
92
+ export declare function parseMMTPHeader(buffer: Uint8Array): MMTPHeader;
93
+ /**
94
+ * The MMTP `packet_id` of `buffer`, or null exactly when parseMMTPHeader would
95
+ * throw for it (short buffer, V != 0, truncated packet_counter or header
96
+ * extension).
97
+ *
98
+ * This is the per-packet filter a receiver runs before any other work, so it
99
+ * builds no header object, DataView or BigInt timestamp (BLO-36599). Use
100
+ * parseMMTPHeader when any other header field is needed.
101
+ */
102
+ export declare function readMMTPPacketId(buffer: Uint8Array): number | null;
103
+ /**
104
+ * Unwrap MMTP packet to extract payload
105
+ */
106
+ export declare function unwrapMMTP(buffer: Uint8Array): MMTPPacket;
107
+ /**
108
+ * Parse MPU header from payload
109
+ * ISO/IEC 23008-1:2023 Section 9.2.3.3
110
+ *
111
+ * MPU Header format (8 bytes) as written by FFmpeg/libmmt:
112
+ * - Bytes 0-1: payload_length (16 bits, big-endian)
113
+ * - Byte 2: FT(4) | T(1) | FI(2) | A(1)
114
+ * - Byte 3: fragment_counter (8 bits)
115
+ * - Bytes 4-7: MPU_sequence_number (32 bits, big-endian)
116
+ */
117
+ export declare function parseMPUHeader(payload: Uint8Array): MPUHeader;
118
+ export declare function parseTimedMfuDuHeader(payload: Uint8Array, offset: number): MFUDuHeader | null;
119
+ /**
120
+ * Why `extractMFUPayload` refused a packet.
121
+ *
122
+ * These identifiers are part of the public contract: callers surface them in
123
+ * diagnostics, so renaming one is a breaking change.
124
+ */
125
+ 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';
126
+ export interface MFURejection {
127
+ gate: MFURejectionGate;
128
+ /** Human-readable detail, including the byte arithmetic that decided it. */
129
+ detail: string;
130
+ }
131
+ /**
132
+ * Extract NAL unit payload from MMTP packet
133
+ */
134
+ export declare function extractNALUnit(mmtpPacket: Uint8Array): Uint8Array;
135
+ /** Result of extracting MFU payload from an MMTP packet */
136
+ export interface MFUExtraction {
137
+ /** MMTP header fields */
138
+ header: MMTPHeader;
139
+ /** MPU header fields */
140
+ mpu: MPUHeader;
141
+ /** Fragment type (FT field, 4 bits): 0=metadata, 2=MFU data */
142
+ fragmentType: number;
143
+ /** MFU payload data (video/audio bytes, headers stripped, FEC ID excluded) */
144
+ data: Uint8Array;
145
+ /** Parsed timed MFU DU header when present, otherwise null */
146
+ mfuDuHeader: MFUDuHeader | null;
147
+ /** Non-timed MFU Item_ID when the 4-byte DU header is present. */
148
+ itemId: number | null;
149
+ /** Source FEC Payload ID if present (`FEC_type == 1`), or null */
150
+ fecPayloadId: {
151
+ ssId: number;
152
+ } | null;
153
+ }
154
+ /**
155
+ * Report why `extractMFUPayload` would refuse this packet, or null if it would
156
+ * accept it.
157
+ *
158
+ * `extractMFUPayload` returns bare `null` on ten distinct conditions, which
159
+ * makes a producer/consumer framing disagreement indistinguishable from a
160
+ * publisher that never started — both render as silence. Use this wherever
161
+ * that distinction matters: diagnostics, contract tests, and operator-facing
162
+ * logs.
163
+ */
164
+ export declare function explainMFURejection(mmtpPacket: Uint8Array, options?: {
165
+ skipFecId?: boolean;
166
+ }): MFURejection | null;
167
+ export declare function extractMFUPayload(mmtpPacket: Uint8Array, options?: {
168
+ skipFecId?: boolean;
169
+ }): MFUExtraction | null;
170
+ /**
171
+ * extractMFUPayload for a caller that already holds this packet's parsed MMTP
172
+ * header, so the header is not parsed a second time (BLO-36599).
173
+ *
174
+ * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet; the
175
+ * result is then identical to `extractMFUPayload(mmtpPacket, options)`.
176
+ */
177
+ export declare function extractMFUPayloadWithHeader(mmtpPacket: Uint8Array, header: MMTPHeader, options?: {
178
+ skipFecId?: boolean;
179
+ }): MFUExtraction | null;
180
+ /**
181
+ * Extract timestamp from MMTP packet
182
+ */
183
+ export declare function extractTimestamp(mmtpPacket: Uint8Array): bigint;
184
+ /**
185
+ * Check if MMTP packet is an FEC repair packet
186
+ * FEC_type 2 and 3 are repair packets
187
+ */
188
+ export declare function isFecRepairPacket(mmtpPacket: Uint8Array): boolean;
189
+ /**
190
+ * Extract Source FEC Payload ID (SS_ID) from the last 4 bytes of an MMTP
191
+ * source packet per ISO/IEC 23008-1:2023 Section C.5.2 Figure C.10.
192
+ *
193
+ * SS_ID is a flat 32-bit monotonic counter (incremented by 1 per packet
194
+ * for ssbg_mode0 and ssbg_mode1). Block membership and ESI use the
195
+ * configured K; repair SS_Start identifies the first SS_ID of the block and
196
+ * SSB_length records the authored source-symbol count:
197
+ * SBN = floor(SS_ID / K), ESI = SS_ID % K
198
+ * SS_Start = SBN * K (first SS_ID of the block)
199
+ *
200
+ * This matches FFmpeg moqenc_mmt.c, the drafts (draft-ramadan-moq-fec §6.2),
201
+ * and ATSC A/331 §8.1.2.5 which defers to ISO 23008-1 Annex C.
202
+ */
203
+ export declare function extractFecPayloadId(mmtpPacket: Uint8Array): {
204
+ ssId: number;
205
+ } | null;
206
+ /**
207
+ * Return true when FEC_type=1 and the exact suffix after the ISO-declared MPU
208
+ * payload contains a four-byte Source FEC Payload ID.
209
+ */
210
+ export declare function hasSourceFecPayloadId(mmtpPacket: Uint8Array): boolean;
211
+ /**
212
+ * The Source FEC Payload ID of a packet whose MMTP header the caller already
213
+ * parsed, or null when the packet carries none: present only when FEC_type=1
214
+ * and the exact suffix after the ISO-declared MPU payload holds the four-byte
215
+ * ID (ISO/IEC 23008-1 §C.5.2). Same answer as extractFecPayloadId, without a
216
+ * second header parse (BLO-36599).
217
+ *
218
+ * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet.
219
+ */
220
+ export declare function sourceFecPayloadIdOf(mmtpPacket: Uint8Array, header: MMTPHeader): {
221
+ ssId: number;
222
+ } | null;
223
+ /**
224
+ * MMTP header-extension ext_type assigned to the detached ALTA authenticator
225
+ * trailer (Path 2 / 08-07). Carried in an `[ext_type][ext_length][auth]`
226
+ * block appended after the MPU's declared payload bytes, before the 4-byte
227
+ * Source FEC Payload ID. MMTP X-bit stays 0 — non-ALTA MMTP receivers treat
228
+ * the trailer as bytes past `payload_length` and skip it (per ISO 23008-1
229
+ * §9.2.2 "may be discarded without impacting correct processing").
230
+ */
231
+ export declare const MMTP_EXT_TYPE_ALTA_AUTH = 61601;
232
+ export declare const MMTP_EXT_TRAILER_HEADER_SIZE = 4;
233
+ export type AltaTrailer = _LeafAltaTrailer;
234
+ export declare const extractAltaTrailer: typeof _leafExtractAltaTrailer;
235
+ /** Size of Repair FEC Payload ID per ISO/IEC 23008-1 Section C.5.3 */
236
+ export declare const REPAIR_FEC_PID_SIZE = 13;
237
+ /**
238
+ * Extract Repair FEC Payload ID from a raw-multicast MMTP repair packet.
239
+ * Per ISO/IEC 23008-1:2023 Section C.5.3 (ssbg_mode0, one-stage, no FFSRP_TS):
240
+ * [MMTP header][SS_Start:4][RSB_length:3][RS_ID:3][SSB_length:3][repair data]
241
+ * OTI is delivered via AL-FEC signaling message (Table C.3), not per-packet.
242
+ *
243
+ * Byte 1 is reserved(2)|packet_type(6) for V=0, so repair type 0x03 is 0x03.
244
+ * Pre-migration high-bits bytes (0x0c) are rejected — no compatibility decode.
245
+ */
246
+ export declare function extractRepairFecPayloadId(mmtpPacket: Uint8Array): {
247
+ ssStart: number;
248
+ rsbLength: number;
249
+ rsId: number;
250
+ ssbLength: number;
251
+ } | null;
252
+ /** Parsed MoQ repair frame per ISO/IEC 23008-1 Section C.4.3 + C.5.3 */
253
+ export interface MoqRepairFrame {
254
+ /** MMTP packetId (1=video, 2=audio) — for routing to correct FEC decoder */
255
+ packetId: number;
256
+ /** SS_Start (32 bits): SS_ID of first source symbol in block */
257
+ ssStart: number;
258
+ /** RSB_length (24 bits): number of repair symbols (P) */
259
+ rsbLength: number;
260
+ /** RS_ID (24 bits): repair symbol index (0-based) */
261
+ rsId: number;
262
+ /** SSB_length (24 bits): authored source-symbol count (1..=configured K) */
263
+ ssbLength: number;
264
+ /** Repair symbol data after the dynamic MMTP header and 13-byte FEC payload ID. */
265
+ symbolData: Uint8Array;
266
+ }
267
+ /**
268
+ * Parse a MoQ repair frame (ISO/IEC 23008-1 Annex C format).
269
+ *
270
+ * Wire format per ISO 23008-1 §C.4.3 + §C.5.3:
271
+ * MMTP Header: fixed 12 bytes plus C/X optional fields
272
+ * then SS_Start (4 bytes) → block identifier
273
+ * then RSB_length (3 bytes) → P
274
+ * then RS_ID (3 bytes) → ESI = K + RS_ID
275
+ * then SSB_length (3 bytes) → authored source-symbol count (1..=K)
276
+ * then Repair Symbol Data (T bytes)
277
+ *
278
+ * @param frame - Raw MoQ repair frame bytes
279
+ * @returns Parsed frame or null if invalid
280
+ */
281
+ export declare function parseMoqRepairFrame(frame: Uint8Array): MoqRepairFrame | null;
282
+ /**
283
+ * parseMoqRepairFrame for a caller that already holds this frame's parsed MMTP
284
+ * header, so the header is not parsed a second time (BLO-36599). The router's
285
+ * multicast repair branch uses it: one repair packet in every K + P otherwise
286
+ * paid two header parses.
287
+ *
288
+ * `header` must be `parseMMTPHeader(frame)` of this same frame; the result is
289
+ * then identical to `parseMoqRepairFrame(frame)`.
290
+ */
291
+ export declare function parseMoqRepairFrameWithHeader(frame: Uint8Array, header: MMTPHeader): MoqRepairFrame | null;
292
+ //# sourceMappingURL=mmtp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mmtp.d.ts","sourceRoot":"","sources":["../src/mmtp.ts"],"names":[],"mappings":"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"}