@blockcast/mmt-fec 0.1.0-main.149f78a4adae
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.
- package/README.md +270 -0
- package/dist/.build-stamp +0 -0
- package/dist/block-manager.d.ts +80 -0
- package/dist/block-manager.d.ts.map +1 -0
- package/dist/block-manager.js +294 -0
- package/dist/block-manager.js.map +1 -0
- package/dist/block-processor.d.ts +72 -0
- package/dist/block-processor.d.ts.map +1 -0
- package/dist/block-processor.js +192 -0
- package/dist/block-processor.js.map +1 -0
- package/dist/cmaf-recovery.d.ts +46 -0
- package/dist/cmaf-recovery.d.ts.map +1 -0
- package/dist/cmaf-recovery.js +96 -0
- package/dist/cmaf-recovery.js.map +1 -0
- package/dist/cmaf-symbols.d.ts +33 -0
- package/dist/cmaf-symbols.d.ts.map +1 -0
- package/dist/cmaf-symbols.js +57 -0
- package/dist/cmaf-symbols.js.map +1 -0
- package/dist/fec-block-mapper.d.ts +185 -0
- package/dist/fec-block-mapper.d.ts.map +1 -0
- package/dist/fec-block-mapper.js +273 -0
- package/dist/fec-block-mapper.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/loc-symbols.d.ts +20 -0
- package/dist/loc-symbols.d.ts.map +1 -0
- package/dist/loc-symbols.js +28 -0
- package/dist/loc-symbols.js.map +1 -0
- package/dist/oti.d.ts +174 -0
- package/dist/oti.d.ts.map +1 -0
- package/dist/oti.js +256 -0
- package/dist/oti.js.map +1 -0
- package/dist/reorder-buffer.d.ts +41 -0
- package/dist/reorder-buffer.d.ts.map +1 -0
- package/dist/reorder-buffer.js +81 -0
- package/dist/reorder-buffer.js.map +1 -0
- package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
- package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
- package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
- package/node_modules/@blockcast/mmt-base/README.md +120 -0
- package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
- package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
- package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +275 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js +644 -0
- package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
- package/node_modules/@blockcast/mmt-base/package.json +49 -0
- package/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
- package/node_modules/@blockcast/mmt-base/src/mmtp.ts +898 -0
- package/package.json +79 -0
- package/src/block-manager.ts +369 -0
- package/src/block-processor.ts +277 -0
- package/src/cmaf-recovery.ts +127 -0
- package/src/cmaf-symbols.ts +68 -0
- package/src/fec-block-mapper.ts +332 -0
- package/src/index.ts +77 -0
- package/src/loc-symbols.ts +34 -0
- package/src/oti.ts +319 -0
- package/src/reorder-buffer.ts +101 -0
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# @blockcast/mmt-fec
|
|
2
|
+
|
|
3
|
+
Container-agnostic RaptorQ (RFC 6330) geometry and recovery primitives: block lifecycle, OTI parsing, CMAF/LOC symbol splitting, MoQ ↔ FEC coordinate derivation, and reorder buffering. The canonical stateful WASM decoder is owned by `@blockcast/mmt-container` or `@blockcast/fec-worker`, both of which consume libmmt's [`mmt-wasm`](../../mmt-wasm) `MmtFecDecoder` directly.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add @blockcast/mmt-fec
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { FecBlockManager, processFecBlock } from "@blockcast/mmt-fec"
|
|
13
|
+
import { buildOTIFromCatalog, computeFecTimeout, deriveFecInterleaveDepth } from "@blockcast/mmt-fec"
|
|
14
|
+
|
|
15
|
+
// Catalog says K=32 source symbols, P=8 repair, T=1320 byte symbols,
|
|
16
|
+
// interleaveDepthMs=200 (the FULL FEC block span in ms, §5.1), framerate=30.
|
|
17
|
+
const K = 32
|
|
18
|
+
const T = 1320
|
|
19
|
+
const interleaveDepthMs = 200
|
|
20
|
+
const fps = 30
|
|
21
|
+
const gopDurationMs = 1000 / fps
|
|
22
|
+
|
|
23
|
+
const oti = buildOTIFromCatalog(K, T)
|
|
24
|
+
const D = deriveFecInterleaveDepth(interleaveDepthMs, gopDurationMs)
|
|
25
|
+
const timeoutMs = computeFecTimeout(interleaveDepthMs, 3.0)
|
|
26
|
+
const blocks = new FecBlockManager({ k: K, timeoutMs })
|
|
27
|
+
|
|
28
|
+
// `recovered` comes from the container/worker-owned MmtFecDecoder.
|
|
29
|
+
if (recoveredBlock) {
|
|
30
|
+
// K source symbols concatenated. Split them back into MFU fragments.
|
|
31
|
+
const { symbols } = processFecBlock(recoveredBlock, { k: K, symbolSize: T })
|
|
32
|
+
for (const s of symbols) reassembler.addFragment(s.fragment)
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Exports
|
|
37
|
+
|
|
38
|
+
Mirrors `package.json#exports`. Each subpath is a tree-shakeable entry point.
|
|
39
|
+
|
|
40
|
+
| Subpath | Module | Purpose |
|
|
41
|
+
|---------|--------|---------|
|
|
42
|
+
| `.` | `src/index.ts` | Re-export aggregator (everything below) |
|
|
43
|
+
| `./block-manager` | `src/block-manager.ts` | `FecBlockManager` — block state machine with timeout-driven repair fallback |
|
|
44
|
+
| `./block-processor` | `src/block-processor.ts` | `processFecBlock` — split a decoded block back into MMTP/MFU fragments |
|
|
45
|
+
| `./reorder-buffer` | `src/reorder-buffer.ts` | `ReorderBuffer<T>` — sequence-ordered flush for FEC-interleaved streams |
|
|
46
|
+
| `./cmaf` | `src/cmaf-symbols.ts` | `splitToSymbols`, `recoverFromSymbols`, `computeK` — CMAF chunk ↔ symbols |
|
|
47
|
+
| `./cmaf-recovery` | `src/cmaf-recovery.ts` | `CmafFecRecovery` — stateful recovery for CMAF chunks + LOC frames |
|
|
48
|
+
| `./loc` | `src/loc-symbols.ts` | `splitFrameToSymbol`, `recoverFrameFromSymbol` — LOC 1:1 frame ↔ symbol |
|
|
49
|
+
|
|
50
|
+
The aggregator entry also re-exports OTI helpers (`buildOTI`, `buildOTIFromCatalog`, `parseOTI`, `computeFecTimeout`, `parseRepairPacket`) and the §8.3 mapper family (`FecBlockMapper`, `NullBlockMapper`, `ObjectBlockMapper`, `createMapper`, `FecTimestampBridge`). Canonical MMTP Source FEC Payload ID parsing lives in `@blockcast/mmt-base`; canonical LOC FEC source information lives in `@blockcast/mmt-container`.
|
|
51
|
+
|
|
52
|
+
## Architecture
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Application / container / worker
|
|
56
|
+
├─ owns mmt-wasm MmtFecDecoder state
|
|
57
|
+
├─ feeds ISO SS_ID and Repair FEC Payload ID coordinates
|
|
58
|
+
└─ passes recovered blocks into @blockcast/mmt-fec
|
|
59
|
+
├─ FecBlockManager
|
|
60
|
+
├─ processFecBlock
|
|
61
|
+
├─ ReorderBuffer
|
|
62
|
+
├─ FecBlockMapper
|
|
63
|
+
└─ CMAF / LOC recovery helpers
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The TypeScript package contains geometry and block-lifecycle glue, not codec
|
|
67
|
+
code. Stateful WASM ownership and symbol marshalling live in the composing
|
|
68
|
+
container or worker.
|
|
69
|
+
|
|
70
|
+
## Block lifecycle
|
|
71
|
+
|
|
72
|
+
`FecBlockManager` (`src/block-manager.ts:127`) runs the per-block state machine. State constants live in `FEC_STATE` (`src/block-manager.ts:19`).
|
|
73
|
+
|
|
74
|
+
| State | Trigger | Next |
|
|
75
|
+
|-------|---------|------|
|
|
76
|
+
| `EMPTY` (0) | Block instantiated | `COLLECTING` on first symbol |
|
|
77
|
+
| `COLLECTING` (1) | Symbols arriving, fewer than K total | `COMPLETE` on Kth source, or `FALLBACK` on timeout |
|
|
78
|
+
| `COMPLETE` (2) | K source symbols received — no FEC decode needed | `DECODED` |
|
|
79
|
+
| `FALLBACK` (3) | `timeoutMs` elapsed before K reached — `repairCallback(blockId, missingEsis)` fires (e.g. WebTransport NACK) | `DECODED` if K reached after repairs, else `FAILED` after another `timeoutMs` |
|
|
80
|
+
| `DECODED` (4) | Block assembled, `decodeCallback(blockId, data)` fires, block evicted from map | terminal |
|
|
81
|
+
| `FAILED` (5) | Insufficient symbols after second timeout | terminal — block dropped, `blocksFailed` incremented |
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
addSourcePacket / addRepairPacket
|
|
85
|
+
│
|
|
86
|
+
▼
|
|
87
|
+
EMPTY ──first symbol──► COLLECTING ──Kth symbol──► COMPLETE / DECODED
|
|
88
|
+
│
|
|
89
|
+
timeoutMs
|
|
90
|
+
│
|
|
91
|
+
▼
|
|
92
|
+
FALLBACK ──repairs fill K──► DECODED
|
|
93
|
+
│
|
|
94
|
+
2×timeoutMs (no repair)
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
FAILED
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`maxBlocks` (default 64) caps in-flight blocks; on overflow, the oldest by `createdAt` is evicted (`#evictOldestBlock`, `src/block-manager.ts:350`). All values must come from the catalog — `timeoutMs` and `k` throw on `<= 0` (`src/block-manager.ts:145`), matching libmmt's "no defaults, no magic floors" rule.
|
|
101
|
+
|
|
102
|
+
### `processFecBlock` — block → MFU fragments
|
|
103
|
+
|
|
104
|
+
The WASM decoder hands back `K * symbolSize` bytes (K source symbols concatenated). `processFecBlock` (`src/block-processor.ts:68`) walks each symbol, parses its MMTP header, extracts the MFU payload via `@blockcast/mmt-base`'s `extractMFUPayload`, and emits a `ParsedFecSymbol[]` sorted with wrapping 32-bit MMTP serial-number arithmetic (source-symbol index breaks duplicate ties) — undoing FEC interleaving so the reassembler sees `FI=1` before `FI=2/3` for the same MPU, including across `0xffff_ffff → 0`.
|
|
105
|
+
|
|
106
|
+
Recovered fixed-width symbols are trimmed only at the ISO-declared end (`MPU start + 2 + payload_length`), extended by an ALTA trailer only when one begins exactly there. Remaining bytes must be uniform decoder padding; the processor never infers packet boundaries from MFU contents or alternate length conventions.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
interface ParsedFecSymbol {
|
|
110
|
+
index: number // 0..K-1
|
|
111
|
+
packetId: number // MMTP packet ID (1=video, 2=audio by convention)
|
|
112
|
+
packetSequenceNumber: number
|
|
113
|
+
fragmentType: number // 0=init (ftyp+moov), 2=MFU data
|
|
114
|
+
fragment: { mpuSequenceNumber, fragmentationIndicator, fragmentCounter,
|
|
115
|
+
data, timestamp, rapFlag }
|
|
116
|
+
isKeyframe: boolean
|
|
117
|
+
rawSymbol: Uint8Array
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Pass `ownerPacketId` to filter out cross-track noise (e.g. zero-padded recovery slots whose `packetId === 0`) — important when the decoder backfills missing slots with zeros that happen to parse as a valid MMTP header.
|
|
122
|
+
|
|
123
|
+
## Reorder buffer
|
|
124
|
+
|
|
125
|
+
`ReorderBuffer<T>` (`src/reorder-buffer.ts:26`) is a generic sequence-ordered flush buffer. FEC interleaving causes frames to *complete* out of decode order (e.g. block-N+3 finishes before block-N+1 because the missing symbol arrived first); H.264/HEVC P-frames reference the previous frame, so feeding them out of order to `VideoDecoder` produces green-tinted artifacts from reference-frame mismatch.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const reorder = new ReorderBuffer<EncodedVideoChunk>({
|
|
129
|
+
maxSize: K, // size to FEC block depth
|
|
130
|
+
getSequence: (c) => c.timestamp,
|
|
131
|
+
isKeyframe: (c) => c.type === "key",
|
|
132
|
+
})
|
|
133
|
+
for (const ready of reorder.push(chunk)) decoder.decode(ready)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Behaviour (`src/reorder-buffer.ts:39`):
|
|
137
|
+
- In-order pushes flush immediately — zero added latency on well-ordered streams.
|
|
138
|
+
- Out-of-order pushes buffer until the gap fills.
|
|
139
|
+
- Keyframes flush all pending then emit themselves (random access point).
|
|
140
|
+
- `maxSize` overflow force-flushes the front of the buffer.
|
|
141
|
+
- Stale frames (`seq <= lastFlushedSeq`) are dropped.
|
|
142
|
+
|
|
143
|
+
**Capacity:** size `maxSize` to one FEC block depth (K). The buffer only needs to span the maximum possible inter-arrival gap, which is bounded by FEC interleave depth — anything larger means FEC has already given up. Used on the WebCodecs decode path; **not** needed for MSE (the browser's source buffer handles ordering internally).
|
|
144
|
+
|
|
145
|
+
## CMAF & LOC recovery
|
|
146
|
+
|
|
147
|
+
`CmafFecRecovery` (`src/cmaf-recovery.ts:33`) is the stateful wrapper that bridges the CMAF/LOC delivery path (whole chunks or raw frames) to a packet-oriented `FecDecoder`. Three feed modes:
|
|
148
|
+
|
|
149
|
+
| Method | Input | Use |
|
|
150
|
+
|--------|-------|-----|
|
|
151
|
+
| `feedSourceChunk(sbn, chunk)` | Whole CMAF chunk over unicast | Splits into K symbols (`splitToSymbols`), registers them all so the decoder can mint cross-block repair if requested. Returns the chunk as-is. |
|
|
152
|
+
| `feedSourceSymbol(sbn, esi, sym)` | Multicast packet | Adds one symbol; returns recovered chunk if K reached. |
|
|
153
|
+
| `feedRepairSymbol(sbn, esi, sym)` | Repair-track packet | Adds repair; returns recovered chunk on success. |
|
|
154
|
+
|
|
155
|
+
The `FecDecoder` interface (`src/cmaf-recovery.ts`) is the minimum surface the
|
|
156
|
+
composing application supplies. The recovery class strips zero-padding using
|
|
157
|
+
the chunk's known `originalLength` (recorded on `feedSourceChunk` or stored at
|
|
158
|
+
recovery time).
|
|
159
|
+
|
|
160
|
+
`./cmaf` exports the pure splitting primitives (`splitToSymbols`, `recoverFromSymbols`, `computeK`) and `./loc` exports the LOC variant (`splitFrameToSymbol`, `recoverFrameFromSymbol`) where each frame maps 1:1 to a single symbol — pad up or truncate to `symbolSize`, no sub-block math.
|
|
161
|
+
|
|
162
|
+
The higher-level `FecManager` that orchestrates CMSF catalog parsing, base/delta/repair track wiring, and per-track block-mapper construction lives in `@blockcast/mmt-container/fec-manager` — that's the file most consumers will actually instantiate. This package provides the primitives `FecManager` is built on.
|
|
163
|
+
|
|
164
|
+
## OTI and FEC payload IDs
|
|
165
|
+
|
|
166
|
+
`./oti` exposes the 12-byte RFC 6330 Common + Scheme-Specific OTI codec (`src/oti.ts:33`):
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
buildOTIFromCatalog(K, T) // fixed profile: F=K*T, Z=1, N=1, Al=8
|
|
170
|
+
parseOTI(otiBytes) // returns { transferLength, symbolSize, ... K }
|
|
171
|
+
computeFecTimeout(interleaveDepthMs, safetyFactor=3)
|
|
172
|
+
// interleaveDepthMs * safetyFactor (K-independent)
|
|
173
|
+
parseRepairPacket(mmtpPacket) // ISO 23008-1 §C.5.3 repair FEC PID
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`computeFecTimeout` derives the block-recovery deadline from the catalog interleave value — see `src/oti.ts:121`. The catalog's `interleaveDepthMs` IS the full FEC block span (§5.1), so the timeout is `interleaveDepthMs * safetyFactor`, independent of K; the safety factor is the only tunable. **Do not** hardcode `setTimeout` values upstream.
|
|
177
|
+
|
|
178
|
+
`./` (root) exposes the §8.3 SBN/ESI helpers via `FecBlockMapper`. `FecBlockMapper.derive(groupId, objectId)` (`src/fec-block-mapper.ts:69`) is the coordinate mapping API; `NullBlockMapper` and `ObjectBlockMapper` cover no-FEC and §9.1 object-FEC respectively. `FecTimestampBridge` (`src/fec-block-mapper.ts:257`) wall-clock-aligns SBNs across tracks with different physical-layer FEC configs (WiFi K=4/80ms vs cellular K=32/4000ms vs ATSC K=64/10000ms).
|
|
179
|
+
|
|
180
|
+
## WASM module loading
|
|
181
|
+
|
|
182
|
+
This package does not load WASM. The host imports one application-owned module,
|
|
183
|
+
and the container or worker consumes its actual `MmtFecDecoder` export. No
|
|
184
|
+
package depends on generated output inside this checkout.
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
const wasm = await import("mmt-wasm") // exact host import-map specifier
|
|
188
|
+
await wasm.default()
|
|
189
|
+
const decoder = new wasm.MmtFecDecoder(interleaveDepth, trackType === "video")
|
|
190
|
+
decoder.configure_params(K, T)
|
|
191
|
+
|
|
192
|
+
decoder.add_source(ssId, sourcePacket, performance.now())
|
|
193
|
+
decoder.add_repair(ssStart, K, rsId, repairSymbol, performance.now())
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Construction policy plus K/T configuration are mandatory; the decoder has no
|
|
197
|
+
unconfigured slow path or guessed geometry. Those three-argument `add_source`
|
|
198
|
+
and five-argument `add_repair` signatures are
|
|
199
|
+
the source-owned wasm-bindgen contract. Do not reconstruct a different
|
|
200
|
+
`MoqFec*` binding API from generated artifacts.
|
|
201
|
+
|
|
202
|
+
The upstream Rust crate is `mmt-wasm` in this repo (`../../mmt-wasm`). Build it
|
|
203
|
+
from the repository root with an external destination:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
BLOCKCAST_MMT_WASM_BUILD_ROOT=/tmp/blockcast-libmmt-wasm make mmt-wasm
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Its FEC implementation uses the pure-Rust `raptorq` crate from the sibling
|
|
210
|
+
`fec-raptorq` checkout.
|
|
211
|
+
|
|
212
|
+
## RFC 6330 + AL-FEC parameters
|
|
213
|
+
|
|
214
|
+
This package is parameter-driven. `FecBlockManager` throws if `timeoutMs <= 0`
|
|
215
|
+
or `k <= 0` (`src/block-manager.ts`); all values flow from the CMSF catalog.
|
|
216
|
+
|
|
217
|
+
Common broadcast configuration (also documented in the parent README):
|
|
218
|
+
|
|
219
|
+
| Param | Typical | Catalog source |
|
|
220
|
+
|-------|---------|----------------|
|
|
221
|
+
| `T` (symbol size) | 1312 / 1320 bytes | `fec.symbolSize` |
|
|
222
|
+
| `K` (source symbols) | 32 | `fec.sourceSymbols` |
|
|
223
|
+
| `P` (repair symbols) | 8 (25% overhead) | `fec.repairSymbols` |
|
|
224
|
+
| Interleave depth | 200 ms block span (D = positive round-half-up(ms / GOP), minimum 1) | `fec.interleaveDepthMs` |
|
|
225
|
+
| `Al` (alignment) | 8 | (OTI byte 11) |
|
|
226
|
+
|
|
227
|
+
Specs:
|
|
228
|
+
- **RFC 6330** — RaptorQ Forward Error Correction Scheme (incl. §3.3.2.1 interleaved scheme)
|
|
229
|
+
- **ISO/IEC 23008-1:2023** — MMT, Annex C; Amendment 1:2025 — AL-FEC framework
|
|
230
|
+
- **ATSC A/331 §6.4** — cross-session FEC (one repair flow protecting many source flows)
|
|
231
|
+
- **draft-ramadan-moq-fec-00** — AL-FEC over MoQ wire format and §8.3 SBN/ESI derivation, §9.1 object-mode FEC
|
|
232
|
+
|
|
233
|
+
## Performance
|
|
234
|
+
|
|
235
|
+
SIMD is a property of the application-owned WASM build. Verify the generated
|
|
236
|
+
binary during `make mmt-wasm`; this TypeScript package does not infer it.
|
|
237
|
+
|
|
238
|
+
- **x86_64**: AVX2 (256-bit) → SSSE3 (128-bit) fallback
|
|
239
|
+
- **ARM64**: NEON
|
|
240
|
+
- **Other**: scalar fallback
|
|
241
|
+
|
|
242
|
+
The TypeScript layer is allocation-conscious but not zero-copy — symbols cross the WASM boundary as `Uint8Array` copies. The hot allocation lives in `#assembleBlock` (`src/block-manager.ts:335`) and the per-symbol concatenation in `processFecBlock`. Block eviction happens immediately on terminal state (`DECODED` / `FAILED`) — symbols are released the moment the callback returns, no GC delay. For the pure off-main-thread variant with structured-clone-free `ArrayBuffer` transfers, use `@blockcast/fec-worker`.
|
|
243
|
+
|
|
244
|
+
Block deadlines are owned by the composing container/transport policy. This
|
|
245
|
+
package does not start a global cleanup interval.
|
|
246
|
+
|
|
247
|
+
## Testing
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
npm ci # install this package from a clean checkout
|
|
251
|
+
npm test # vitest run (resolves local dependencies from source)
|
|
252
|
+
npm run test:watch # vitest watch
|
|
253
|
+
npm run typecheck # tsc --noEmit
|
|
254
|
+
npm run build # tsc → dist/
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Test files live next to source (`src/*.test.ts`) and cover `FecBlockManager` (state-machine transitions and timeout fallback), `processFecBlock` (interleaving sort, packet-id filtering, malformed-symbol counting), `ReorderBuffer` (gap fill, keyframe flush, overflow), `CmafFecRecovery` (chunk + symbol + repair feed paths), `FecBlockMapper` (§8.3 derivations across all three mapper variants), and `cmaf-symbols` / `loc-symbols` (round-trip split/recover).
|
|
258
|
+
|
|
259
|
+
## Related packages
|
|
260
|
+
|
|
261
|
+
| Package | Relationship |
|
|
262
|
+
|---------|-------------|
|
|
263
|
+
| [`mmt-wasm`](../../mmt-wasm) | Rust crate compiled to WebAssembly — provides the canonical `MmtFecDecoder` |
|
|
264
|
+
| [`@blockcast/fec-worker`](../fec-worker) | Web Worker owner of `MmtFecDecoder`; keeps decode off the main thread with transferable `ArrayBuffer`s |
|
|
265
|
+
| [`@blockcast/mmt-container`](../container) | Higher-level integration: CMSF catalog parser, MMTP container, MFU reassembler, and `FecManager` |
|
|
266
|
+
| [`@blockcast/mmt-transport`](../transport) | MoQ / SSM / AMT / DRIAD transport manager; emits raw packets and owns no decoder state |
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
Apache-2.0 — see `LICENSE`.
|
|
File without changes
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FEC Block State Machine for Multicast
|
|
3
|
+
*
|
|
4
|
+
* Manages FEC block collection, timeout-based fallback, and WebTransport
|
|
5
|
+
* repair integration. Tracks source and repair symbols per block, triggering
|
|
6
|
+
* decode when complete or requesting repairs when timed out.
|
|
7
|
+
*
|
|
8
|
+
* State Machine:
|
|
9
|
+
* EMPTY → COLLECTING → COMPLETE → DECODED
|
|
10
|
+
* ↓
|
|
11
|
+
* FALLBACK (timeout) → WebTransport repair → DECODED
|
|
12
|
+
*
|
|
13
|
+
* Per draft-ramadan-moq-fec-00 Section 4.2:
|
|
14
|
+
* - k source symbols complete a block (no FEC decode needed)
|
|
15
|
+
* - After timeout, missing symbols are requested via WebTransport
|
|
16
|
+
* - With k symbols (source + repair), FEC decode recovers block
|
|
17
|
+
*/
|
|
18
|
+
export declare const FEC_STATE: {
|
|
19
|
+
readonly EMPTY: 0;
|
|
20
|
+
readonly COLLECTING: 1;
|
|
21
|
+
readonly COMPLETE: 2;
|
|
22
|
+
readonly FALLBACK: 3;
|
|
23
|
+
readonly DECODED: 4;
|
|
24
|
+
readonly FAILED: 5;
|
|
25
|
+
};
|
|
26
|
+
export type FecState = (typeof FEC_STATE)[keyof typeof FEC_STATE];
|
|
27
|
+
export interface FecBlockManagerOptions {
|
|
28
|
+
/** Block timeout (ms). Derive from catalog: interleaveDepthMs or frameDurationMs * K. Required. */
|
|
29
|
+
timeoutMs: number;
|
|
30
|
+
/** K — source symbols per block from catalog fec.sourceSymbols. Required for interleaved mode.
|
|
31
|
+
* For object FEC (§9.1), this is the max K — actual K varies per block via addSourcePacket/addRepairPacket blockK param. */
|
|
32
|
+
k: number;
|
|
33
|
+
/** Max concurrent blocks in flight. Resource bound, not timing — default 64. */
|
|
34
|
+
maxBlocks?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface FecBlockStats {
|
|
37
|
+
sourcePackets: number;
|
|
38
|
+
repairPackets: number;
|
|
39
|
+
blocksComplete: number;
|
|
40
|
+
blocksRecovered: number;
|
|
41
|
+
blocksFailed: number;
|
|
42
|
+
fallbackRequests: number;
|
|
43
|
+
activeBlocks: number;
|
|
44
|
+
recoveryRate: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* FEC Block Manager
|
|
48
|
+
*
|
|
49
|
+
* Manages multiple FEC blocks, handling:
|
|
50
|
+
* - Source symbol registration
|
|
51
|
+
* - Repair symbol registration
|
|
52
|
+
* - Timeout-based WebTransport fallback
|
|
53
|
+
* - Block completion and decoding
|
|
54
|
+
*/
|
|
55
|
+
export declare class FecBlockManager {
|
|
56
|
+
#private;
|
|
57
|
+
maxBlocks: number;
|
|
58
|
+
constructor(options: FecBlockManagerOptions);
|
|
59
|
+
setRepairCallback(callback: (blockId: number, missingEsis: number[]) => void): void;
|
|
60
|
+
setDecodeCallback(callback: (blockId: number, data: Uint8Array) => void): void;
|
|
61
|
+
updateParams(params: {
|
|
62
|
+
k?: number;
|
|
63
|
+
timeoutMs?: number;
|
|
64
|
+
}): void;
|
|
65
|
+
/**
|
|
66
|
+
* Add a source symbol to a block.
|
|
67
|
+
* @param blockK — per-block K for object FEC (§9.1). When provided, overrides
|
|
68
|
+
* the global K for this block. For interleaved mode, omit (uses global K).
|
|
69
|
+
*/
|
|
70
|
+
addSourcePacket(blockId: number, esi: number, data: Uint8Array, blockK?: number): Uint8Array | null;
|
|
71
|
+
/**
|
|
72
|
+
* Add a repair symbol to a block.
|
|
73
|
+
* @param blockK — per-block K from RSB_length in repair FEC Payload ID (§9.1.2).
|
|
74
|
+
* Required for object FEC; omit for interleaved mode.
|
|
75
|
+
*/
|
|
76
|
+
addRepairPacket(blockId: number, esi: number, data: Uint8Array, blockK?: number): Uint8Array | null;
|
|
77
|
+
getStats(): FecBlockStats;
|
|
78
|
+
reset(): void;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=block-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-manager.d.ts","sourceRoot":"","sources":["../src/block-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,SAAS;;;;;;;CAOZ,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAElE,MAAM,WAAW,sBAAsB;IACtC,mGAAmG;IACnG,SAAS,EAAE,MAAM,CAAC;IAClB;iIAC6H;IAC7H,CAAC,EAAE,MAAM,CAAC;IACV,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAqED;;;;;;;;GAQG;AACH,qBAAa,eAAe;;IAI3B,SAAS,EAAE,MAAM,CAAC;gBAaN,OAAO,EAAE,sBAAsB;IAQ3C,iBAAiB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,GAAG,IAAI;IAInF,iBAAiB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI;IAI9E,YAAY,CAAC,MAAM,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAK9D;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAgBnG;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAoBnG,QAAQ,IAAI,aAAa;IAWzB,KAAK,IAAI,IAAI;CAmJb"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FEC Block State Machine for Multicast
|
|
3
|
+
*
|
|
4
|
+
* Manages FEC block collection, timeout-based fallback, and WebTransport
|
|
5
|
+
* repair integration. Tracks source and repair symbols per block, triggering
|
|
6
|
+
* decode when complete or requesting repairs when timed out.
|
|
7
|
+
*
|
|
8
|
+
* State Machine:
|
|
9
|
+
* EMPTY → COLLECTING → COMPLETE → DECODED
|
|
10
|
+
* ↓
|
|
11
|
+
* FALLBACK (timeout) → WebTransport repair → DECODED
|
|
12
|
+
*
|
|
13
|
+
* Per draft-ramadan-moq-fec-00 Section 4.2:
|
|
14
|
+
* - k source symbols complete a block (no FEC decode needed)
|
|
15
|
+
* - After timeout, missing symbols are requested via WebTransport
|
|
16
|
+
* - With k symbols (source + repair), FEC decode recovers block
|
|
17
|
+
*/
|
|
18
|
+
export const FEC_STATE = {
|
|
19
|
+
EMPTY: 0,
|
|
20
|
+
COLLECTING: 1,
|
|
21
|
+
COMPLETE: 2,
|
|
22
|
+
FALLBACK: 3,
|
|
23
|
+
DECODED: 4,
|
|
24
|
+
FAILED: 5,
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Individual FEC Block.
|
|
28
|
+
* Tracks symbols and state for a single source block.
|
|
29
|
+
*/
|
|
30
|
+
class FecBlock {
|
|
31
|
+
blockId;
|
|
32
|
+
state = FEC_STATE.EMPTY;
|
|
33
|
+
sourceSymbols = new Map();
|
|
34
|
+
repairSymbols = new Map();
|
|
35
|
+
k;
|
|
36
|
+
timeoutHandle;
|
|
37
|
+
createdAt;
|
|
38
|
+
decodedAt;
|
|
39
|
+
constructor(blockId, k) {
|
|
40
|
+
this.blockId = blockId;
|
|
41
|
+
this.k = k;
|
|
42
|
+
this.createdAt = performance.now();
|
|
43
|
+
}
|
|
44
|
+
addSourceSymbol(esi, data) {
|
|
45
|
+
if (this.state >= FEC_STATE.COMPLETE)
|
|
46
|
+
return false;
|
|
47
|
+
this.sourceSymbols.set(esi, data);
|
|
48
|
+
this.state = FEC_STATE.COLLECTING;
|
|
49
|
+
if (this.sourceSymbols.size >= this.k) {
|
|
50
|
+
this.state = FEC_STATE.COMPLETE;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
addRepairSymbol(esi, data) {
|
|
56
|
+
if (this.state >= FEC_STATE.DECODED)
|
|
57
|
+
return false;
|
|
58
|
+
this.repairSymbols.set(esi, data);
|
|
59
|
+
const totalSymbols = this.sourceSymbols.size + this.repairSymbols.size;
|
|
60
|
+
if (totalSymbols >= this.k) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
getMissingEsis() {
|
|
66
|
+
const missing = [];
|
|
67
|
+
for (let esi = 0; esi < this.k; esi++) {
|
|
68
|
+
if (!this.sourceSymbols.has(esi) && !this.repairSymbols.has(esi)) {
|
|
69
|
+
missing.push(esi);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return missing;
|
|
73
|
+
}
|
|
74
|
+
isDecodable() {
|
|
75
|
+
return this.sourceSymbols.size + this.repairSymbols.size >= this.k;
|
|
76
|
+
}
|
|
77
|
+
clearTimeout() {
|
|
78
|
+
if (this.timeoutHandle !== undefined) {
|
|
79
|
+
clearTimeout(this.timeoutHandle);
|
|
80
|
+
this.timeoutHandle = undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* FEC Block Manager
|
|
86
|
+
*
|
|
87
|
+
* Manages multiple FEC blocks, handling:
|
|
88
|
+
* - Source symbol registration
|
|
89
|
+
* - Repair symbol registration
|
|
90
|
+
* - Timeout-based WebTransport fallback
|
|
91
|
+
* - Block completion and decoding
|
|
92
|
+
*/
|
|
93
|
+
export class FecBlockManager {
|
|
94
|
+
#blocks = new Map();
|
|
95
|
+
#timeoutMs;
|
|
96
|
+
#k;
|
|
97
|
+
maxBlocks;
|
|
98
|
+
#repairCallback = null;
|
|
99
|
+
#decodeCallback = null;
|
|
100
|
+
#stats = {
|
|
101
|
+
sourcePackets: 0,
|
|
102
|
+
repairPackets: 0,
|
|
103
|
+
blocksComplete: 0,
|
|
104
|
+
blocksRecovered: 0,
|
|
105
|
+
blocksFailed: 0,
|
|
106
|
+
fallbackRequests: 0,
|
|
107
|
+
};
|
|
108
|
+
constructor(options) {
|
|
109
|
+
if (options.timeoutMs <= 0)
|
|
110
|
+
throw new Error('[FecBlockManager] timeoutMs must be > 0 (derive from catalog FEC config)');
|
|
111
|
+
if (options.k <= 0)
|
|
112
|
+
throw new Error('[FecBlockManager] k must be > 0 (from catalog fec.sourceSymbols)');
|
|
113
|
+
this.#timeoutMs = options.timeoutMs;
|
|
114
|
+
this.#k = options.k;
|
|
115
|
+
this.maxBlocks = options.maxBlocks ?? 64;
|
|
116
|
+
}
|
|
117
|
+
setRepairCallback(callback) {
|
|
118
|
+
this.#repairCallback = callback;
|
|
119
|
+
}
|
|
120
|
+
setDecodeCallback(callback) {
|
|
121
|
+
this.#decodeCallback = callback;
|
|
122
|
+
}
|
|
123
|
+
updateParams(params) {
|
|
124
|
+
if (params.k !== undefined)
|
|
125
|
+
this.#k = params.k;
|
|
126
|
+
if (params.timeoutMs !== undefined)
|
|
127
|
+
this.#timeoutMs = params.timeoutMs;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Add a source symbol to a block.
|
|
131
|
+
* @param blockK — per-block K for object FEC (§9.1). When provided, overrides
|
|
132
|
+
* the global K for this block. For interleaved mode, omit (uses global K).
|
|
133
|
+
*/
|
|
134
|
+
addSourcePacket(blockId, esi, data, blockK) {
|
|
135
|
+
this.#stats.sourcePackets++;
|
|
136
|
+
let block = this.#blocks.get(blockId);
|
|
137
|
+
if (!block) {
|
|
138
|
+
block = this.#createBlock(blockId, blockK);
|
|
139
|
+
}
|
|
140
|
+
const complete = block.addSourceSymbol(esi, data);
|
|
141
|
+
if (complete) {
|
|
142
|
+
return this.#completeBlock(block);
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Add a repair symbol to a block.
|
|
148
|
+
* @param blockK — per-block K from RSB_length in repair FEC Payload ID (§9.1.2).
|
|
149
|
+
* Required for object FEC; omit for interleaved mode.
|
|
150
|
+
*/
|
|
151
|
+
addRepairPacket(blockId, esi, data, blockK) {
|
|
152
|
+
this.#stats.repairPackets++;
|
|
153
|
+
let block = this.#blocks.get(blockId);
|
|
154
|
+
if (!block) {
|
|
155
|
+
block = this.#createBlock(blockId, blockK);
|
|
156
|
+
}
|
|
157
|
+
else if (blockK && block.k !== blockK) {
|
|
158
|
+
// Object FEC: repair reveals actual K — update block if first repair arrives
|
|
159
|
+
// before enough source symbols to infer K.
|
|
160
|
+
block.k = blockK;
|
|
161
|
+
}
|
|
162
|
+
const canDecode = block.addRepairSymbol(esi, data);
|
|
163
|
+
if (canDecode && block.state !== FEC_STATE.DECODED) {
|
|
164
|
+
return this.#decodeBlock(block);
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
getStats() {
|
|
169
|
+
return {
|
|
170
|
+
...this.#stats,
|
|
171
|
+
activeBlocks: this.#blocks.size,
|
|
172
|
+
recoveryRate: this.#stats.blocksComplete > 0
|
|
173
|
+
? (this.#stats.blocksRecovered / this.#stats.blocksComplete) * 100
|
|
174
|
+
: 100,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
reset() {
|
|
178
|
+
for (const block of this.#blocks.values()) {
|
|
179
|
+
block.clearTimeout();
|
|
180
|
+
}
|
|
181
|
+
this.#blocks.clear();
|
|
182
|
+
this.#stats = {
|
|
183
|
+
sourcePackets: 0,
|
|
184
|
+
repairPackets: 0,
|
|
185
|
+
blocksComplete: 0,
|
|
186
|
+
blocksRecovered: 0,
|
|
187
|
+
blocksFailed: 0,
|
|
188
|
+
fallbackRequests: 0,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* @param blockK — per-block K for object FEC (§9.1). Falls back to global #k.
|
|
193
|
+
*/
|
|
194
|
+
#createBlock(blockId, blockK) {
|
|
195
|
+
if (this.#blocks.size >= this.maxBlocks) {
|
|
196
|
+
this.#evictOldestBlock();
|
|
197
|
+
}
|
|
198
|
+
const k = blockK ?? this.#k;
|
|
199
|
+
if (k <= 0) {
|
|
200
|
+
throw new Error(`[FecBlockManager] Cannot create block ${blockId}: K=${k}. Derive K from catalog (interleaved) or frame size (object §9.1).`);
|
|
201
|
+
}
|
|
202
|
+
const block = new FecBlock(blockId, k);
|
|
203
|
+
this.#blocks.set(blockId, block);
|
|
204
|
+
block.timeoutHandle = setTimeout(() => {
|
|
205
|
+
this.#onBlockTimeout(blockId);
|
|
206
|
+
}, this.#timeoutMs);
|
|
207
|
+
return block;
|
|
208
|
+
}
|
|
209
|
+
#onBlockTimeout(blockId) {
|
|
210
|
+
const block = this.#blocks.get(blockId);
|
|
211
|
+
if (!block || block.state >= FEC_STATE.COMPLETE) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
block.state = FEC_STATE.FALLBACK;
|
|
215
|
+
this.#stats.fallbackRequests++;
|
|
216
|
+
const missingEsis = block.getMissingEsis();
|
|
217
|
+
if (missingEsis.length > 0 && this.#repairCallback) {
|
|
218
|
+
console.log(`[FecBlockManager] Block ${blockId} timeout, requesting ${missingEsis.length} repair symbols`);
|
|
219
|
+
this.#repairCallback(blockId, missingEsis);
|
|
220
|
+
}
|
|
221
|
+
block.timeoutHandle = setTimeout(() => {
|
|
222
|
+
if (block.state === FEC_STATE.FALLBACK) {
|
|
223
|
+
block.state = FEC_STATE.FAILED;
|
|
224
|
+
this.#stats.blocksFailed++;
|
|
225
|
+
// Throttle: log only every 50th failure to avoid console flood
|
|
226
|
+
if (this.#stats.blocksFailed <= 3 || this.#stats.blocksFailed % 50 === 0) {
|
|
227
|
+
console.warn(`[FecBlockManager] Block ${blockId} failed - insufficient symbols (total: ${this.#stats.blocksFailed})`);
|
|
228
|
+
}
|
|
229
|
+
this.#blocks.delete(blockId);
|
|
230
|
+
}
|
|
231
|
+
}, this.#timeoutMs * 2);
|
|
232
|
+
}
|
|
233
|
+
#completeBlock(block) {
|
|
234
|
+
block.clearTimeout();
|
|
235
|
+
block.state = FEC_STATE.DECODED;
|
|
236
|
+
block.decodedAt = performance.now();
|
|
237
|
+
this.#stats.blocksComplete++;
|
|
238
|
+
const data = this.#assembleBlock(block);
|
|
239
|
+
if (this.#decodeCallback) {
|
|
240
|
+
this.#decodeCallback(block.blockId, data);
|
|
241
|
+
}
|
|
242
|
+
// GC: block reached terminal state — release symbols immediately.
|
|
243
|
+
// No delay needed: callers already have the assembled data.
|
|
244
|
+
this.#blocks.delete(block.blockId);
|
|
245
|
+
return data;
|
|
246
|
+
}
|
|
247
|
+
#decodeBlock(block) {
|
|
248
|
+
block.clearTimeout();
|
|
249
|
+
block.state = FEC_STATE.DECODED;
|
|
250
|
+
block.decodedAt = performance.now();
|
|
251
|
+
this.#stats.blocksComplete++;
|
|
252
|
+
this.#stats.blocksRecovered++;
|
|
253
|
+
console.log(`[FecBlockManager] Block ${block.blockId} recovered via FEC ` +
|
|
254
|
+
`(${block.sourceSymbols.size} source + ${block.repairSymbols.size} repair)`);
|
|
255
|
+
// TODO: Integrate with RaptorQ WASM decoder for actual FEC recovery
|
|
256
|
+
const data = this.#assembleBlock(block);
|
|
257
|
+
if (this.#decodeCallback) {
|
|
258
|
+
this.#decodeCallback(block.blockId, data);
|
|
259
|
+
}
|
|
260
|
+
// GC: block reached terminal state — release symbols immediately.
|
|
261
|
+
this.#blocks.delete(block.blockId);
|
|
262
|
+
return data;
|
|
263
|
+
}
|
|
264
|
+
#assembleBlock(block) {
|
|
265
|
+
const esis = [...block.sourceSymbols.keys()].sort((a, b) => a - b);
|
|
266
|
+
const totalSize = esis.reduce((sum, esi) => sum + block.sourceSymbols.get(esi).length, 0);
|
|
267
|
+
const result = new Uint8Array(totalSize);
|
|
268
|
+
let offset = 0;
|
|
269
|
+
for (const esi of esis) {
|
|
270
|
+
const symbol = block.sourceSymbols.get(esi);
|
|
271
|
+
result.set(symbol, offset);
|
|
272
|
+
offset += symbol.length;
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
#evictOldestBlock() {
|
|
277
|
+
let oldest = null;
|
|
278
|
+
let oldestTime = Infinity;
|
|
279
|
+
for (const block of this.#blocks.values()) {
|
|
280
|
+
if (block.createdAt < oldestTime) {
|
|
281
|
+
oldestTime = block.createdAt;
|
|
282
|
+
oldest = block;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (oldest) {
|
|
286
|
+
oldest.clearTimeout();
|
|
287
|
+
if (oldest.state < FEC_STATE.DECODED) {
|
|
288
|
+
this.#stats.blocksFailed++;
|
|
289
|
+
}
|
|
290
|
+
this.#blocks.delete(oldest.blockId);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=block-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-manager.js","sourceRoot":"","sources":["../src/block-manager.ts"],"names":[],"mappings":"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"}
|