@blockcast/mmt-container 2.1.0 → 2.2.0-main.050b52fb9b00
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 +29 -17
- package/dist/browser-runtime.js +1949 -374
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.js.map +1 -1
- package/dist/catalog-fec-params.d.ts +17 -1
- package/dist/catalog-fec-params.d.ts.map +1 -1
- package/dist/catalog-fec-params.js +15 -0
- package/dist/catalog-fec-params.js.map +2 -2
- package/dist/catalog-network-source.js.map +1 -1
- package/dist/cmcd-producer.d.ts +65 -47
- package/dist/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js +138 -109
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/delivery-catalog.js +26 -16
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/delta-fallback-config.js.map +1 -1
- package/dist/fec-manager.d.ts +70 -12
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +403 -58
- package/dist/fec-manager.js.map +2 -2
- package/dist/fec-state.js.map +1 -1
- package/dist/fec-worker-setup.d.ts +1 -1
- package/dist/fec-worker-setup.js +1 -1
- package/dist/fec-worker-setup.js.map +1 -1
- package/dist/held-layer-admission.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.d.ts +48 -13
- package/dist/iso-block-locator.d.ts.map +1 -1
- package/dist/iso-block-locator.js +97 -25
- package/dist/iso-block-locator.js.map +2 -2
- package/dist/iso-bmff-timing.d.ts +10 -1
- package/dist/iso-bmff-timing.d.ts.map +1 -1
- package/dist/iso-bmff-timing.js +36 -2
- package/dist/iso-bmff-timing.js.map +2 -2
- package/dist/media-router.js.map +1 -1
- package/dist/mfu-reassembler.js +12 -19
- package/dist/mfu-reassembler.js.map +2 -2
- package/dist/mmtp-container-parser.d.ts +79 -10
- package/dist/mmtp-container-parser.d.ts.map +1 -1
- package/dist/mmtp-container-parser.js +219 -45
- package/dist/mmtp-container-parser.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +342 -63
- package/dist/mmtp-container-parser.no-modules.js.map +3 -3
- package/dist/mmtp-decode-pipeline.js +218 -45
- package/dist/mmtp-decode-pipeline.js.map +2 -2
- package/dist/mmtp-fec-session.d.ts +41 -3
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +118 -13
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +43 -9
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +2006 -95
- package/dist/mmtp-router.js.map +4 -4
- package/dist/multicast-auth-catalog.js.map +1 -1
- package/dist/multicast-input.js.map +1 -1
- package/dist/multicast-manager.d.ts +28 -5
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +16116 -15005
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +11 -3
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +281 -35
- package/dist/multicast-transport.js.map +4 -4
- package/dist/multicast-unwrap.js +12 -19
- package/dist/multicast-unwrap.js.map +2 -2
- package/dist/no-modules.js +336 -59
- package/dist/no-modules.js.map +3 -3
- package/dist/repair-fetch.d.ts +36 -8
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +285 -26
- package/dist/repair-fetch.js.map +3 -3
- package/dist/repair-layer-subscriptions.d.ts +82 -0
- package/dist/repair-layer-subscriptions.d.ts.map +1 -0
- package/dist/repair-layer-subscriptions.js +192 -0
- package/dist/repair-layer-subscriptions.js.map +7 -0
- package/dist/signaling.js.map +2 -2
- package/dist/timestamp-codec.js +12 -19
- package/dist/timestamp-codec.js.map +2 -2
- package/dist/track-session.d.ts +1 -1
- package/dist/track-session.d.ts.map +1 -1
- package/dist/track-session.js +1 -0
- package/dist/track-session.js.map +2 -2
- package/dist/wasm-mfu-reassembler.js.map +1 -1
- package/dist/wasm-mmtp-container-parser.js +218 -45
- package/dist/wasm-mmtp-container-parser.js.map +2 -2
- package/package.json +8 -5
- package/schemas/catalog.schema.json +4 -0
- package/src/catalog-fec-params.ts +36 -1
- package/src/cmcd-producer.ts +194 -179
- package/src/fec-manager.ts +433 -32
- package/src/fec-worker-setup.ts +2 -2
- package/src/index.ts +20 -2
- package/src/iso-block-locator.ts +133 -31
- package/src/iso-bmff-timing.ts +40 -1
- package/src/mmtp-container-parser.ts +306 -29
- package/src/mmtp-fec-session.ts +115 -15
- package/src/mmtp-router.ts +629 -54
- package/src/multicast-manager.ts +70 -11
- package/src/multicast-transport.ts +44 -20
- package/src/repair-fetch.ts +363 -49
- package/src/repair-layer-subscriptions.ts +261 -0
- package/src/track-session.ts +4 -1
package/src/mmtp-fec-session.ts
CHANGED
|
@@ -23,6 +23,15 @@
|
|
|
23
23
|
* uses. A packet below B_0 at or after the anchor fails the session closed:
|
|
24
24
|
* `stats.failClosed` reports why, and every later repair is refused.
|
|
25
25
|
*
|
|
26
|
+
* Catalog-less (draft-ramadan-moq-mmt-02 8.3, 13.2): a receiver holding
|
|
27
|
+
* layer 0 under the AL-FEC message alone builds the session from the message's
|
|
28
|
+
* fec_flow entry (`alFecFlow`) instead. K, T, the code point and the repair
|
|
29
|
+
* packet_id come from that entry, and layer 0 is held at
|
|
30
|
+
* maximum_p_for_repair_flow, standing for O_0 + P_0, by the anchored clause of
|
|
31
|
+
* draft-ramadan-moq-fec-core-00 7.3.2 alone. A later version that raises the
|
|
32
|
+
* field raises the held total (updateAlFecFlow()); the message carries no
|
|
33
|
+
* millisecond budget, so the caller states the retention (fec-core-00 9.4).
|
|
34
|
+
*
|
|
26
35
|
* Every repair packet that cannot be used is counted under the reason it was
|
|
27
36
|
* refused, and every block ends under exactly one outcome, so a stream whose
|
|
28
37
|
* FEC is doing nothing can be told apart from one with nothing to repair.
|
|
@@ -30,6 +39,8 @@
|
|
|
30
39
|
|
|
31
40
|
import {
|
|
32
41
|
admitMmtpRepairObject,
|
|
42
|
+
alFecRaptorQFlowConfig,
|
|
43
|
+
FEC_CODE_RAPTORQ,
|
|
33
44
|
isoBlockContains,
|
|
34
45
|
isoBlocksBehindLowerBound,
|
|
35
46
|
parseFecBlockProfile,
|
|
@@ -39,6 +50,8 @@ import {
|
|
|
39
50
|
serial32Precedes,
|
|
40
51
|
ssbg1FitsSymbol,
|
|
41
52
|
trimSsbg1SourceSymbol,
|
|
53
|
+
type AlFecFlow,
|
|
54
|
+
type AlFecRaptorQFlowConfig,
|
|
42
55
|
type FecBlockProfile,
|
|
43
56
|
} from '@blockcast/mmt-base'
|
|
44
57
|
import { deriveFecRetentionBlocks, fecBlockBudgetMs } from '@blockcast/mmt-fec'
|
|
@@ -105,6 +118,25 @@ export interface MmtpFecSessionConfig {
|
|
|
105
118
|
heldLayers?: readonly number[]
|
|
106
119
|
}
|
|
107
120
|
|
|
121
|
+
/**
|
|
122
|
+
* A catalog-less session: one fec_flow entry of the AL-FEC message
|
|
123
|
+
* (`parseAlFecMessage(...).flows[i]`), draft-ramadan-moq-mmt-02 8.3.
|
|
124
|
+
*/
|
|
125
|
+
export interface MmtpFecSessionMessageConfig {
|
|
126
|
+
/** The source track's entry; it must pass `alFecRaptorQFlowConfig`. */
|
|
127
|
+
alFecFlow: AlFecFlow
|
|
128
|
+
/**
|
|
129
|
+
* The decoder retention in blocks. The message carries no elapsed-time
|
|
130
|
+
* budget (mmt-02 7.5), so open-block state is bounded by local policy
|
|
131
|
+
* (fec-core-00 9.4); required, never defaulted.
|
|
132
|
+
*/
|
|
133
|
+
retentionBlocks: number
|
|
134
|
+
Decoder: MmtpFecSessionDecoderConstructor
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** What updateAlFecFlow() did with a later version of the message. */
|
|
138
|
+
export type MmtpFecSessionMessageUpdate = 'raised' | 'unchanged' | 'refused'
|
|
139
|
+
|
|
108
140
|
/**
|
|
109
141
|
* The path a symbol arrived on: its MoQT subscription, or the multicast
|
|
110
142
|
* group (native SSM or AMT) carrying the same asset. The caller states it;
|
|
@@ -197,12 +229,17 @@ export class MmtpFecSession {
|
|
|
197
229
|
readonly algorithm: 'raptorq' | 'reed-solomon'
|
|
198
230
|
readonly sourceSymbols: number
|
|
199
231
|
/** P_0 = B_0, the held total every repair packet is checked against. */
|
|
200
|
-
|
|
232
|
+
get repairSymbols(): number {
|
|
233
|
+
return this.#repairSymbols
|
|
234
|
+
}
|
|
201
235
|
readonly symbolSize: number
|
|
202
236
|
/** The decoder's retention, in blocks, derived from the catalog budget. */
|
|
203
237
|
readonly retentionBlocks: number
|
|
204
238
|
|
|
205
239
|
#decoder: MmtpFecSessionDecoder | null
|
|
240
|
+
#repairSymbols: number
|
|
241
|
+
/** The message entry a catalog-less session was built from; undefined under a catalog. */
|
|
242
|
+
#message: AlFecRaptorQFlowConfig | undefined
|
|
206
243
|
/** Blocks a repair has defined and that are not settled yet, by SS_Start. */
|
|
207
244
|
#blocks = new Map<number, BlockState>()
|
|
208
245
|
/** SS_Starts of settled blocks, so a later repair for one is refused. */
|
|
@@ -240,7 +277,26 @@ export class MmtpFecSession {
|
|
|
240
277
|
* the scheme, the block profile, K, P_0 (`repairSymbols`), T,
|
|
241
278
|
* `interleaveDepthMs` or `reorderToleranceMs`. None is defaulted.
|
|
242
279
|
*/
|
|
243
|
-
constructor(config: MmtpFecSessionConfig) {
|
|
280
|
+
constructor(config: MmtpFecSessionConfig | MmtpFecSessionMessageConfig) {
|
|
281
|
+
if (config && 'alFecFlow' in config) {
|
|
282
|
+
const message = alFecRaptorQFlowConfig(config.alFecFlow)
|
|
283
|
+
if (!message) {
|
|
284
|
+
throw new Error('MmtpFecSession: the AL-FEC entry is outside draft-ramadan-moq-mmt-02 8.3 (iso-ssbg1-v1)')
|
|
285
|
+
}
|
|
286
|
+
this.#message = message
|
|
287
|
+
// Coding byte 0x14 is the iso-ssbg1-v1 profile (mmt-02 8.3).
|
|
288
|
+
this.blockProfile = parseFecBlockProfile('iso-ssbg1-v1', 'alFecFlow')
|
|
289
|
+
this.algorithm = message.fecCodeId === FEC_CODE_RAPTORQ ? 'raptorq' : 'reed-solomon'
|
|
290
|
+
this.sourceSymbols = message.maximumK
|
|
291
|
+
this.#repairSymbols = message.maximumP
|
|
292
|
+
this.symbolSize = message.repairSymbolSize
|
|
293
|
+
this.retentionBlocks = requirePositiveInteger(config.retentionBlocks, 'retentionBlocks')
|
|
294
|
+
this.#repairPacketId = message.repairFlowId
|
|
295
|
+
this.#heldLayers = layerZero(message.maximumP)
|
|
296
|
+
this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols)
|
|
297
|
+
this.#decoder = buildDecoder(config.Decoder, this)
|
|
298
|
+
return
|
|
299
|
+
}
|
|
244
300
|
const fec = config?.fec
|
|
245
301
|
if (!fec || typeof fec !== 'object') {
|
|
246
302
|
throw new Error('MmtpFecSession: the catalog fec instance is required')
|
|
@@ -251,7 +307,7 @@ export class MmtpFecSession {
|
|
|
251
307
|
}
|
|
252
308
|
this.algorithm = fec.scheme
|
|
253
309
|
this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, 'fec.sourceSymbols')
|
|
254
|
-
this
|
|
310
|
+
this.#repairSymbols = requirePositiveInteger(fec.repairSymbols, 'fec.repairSymbols')
|
|
255
311
|
this.symbolSize = requirePositiveInteger(fec.symbolSize, 'fec.symbolSize')
|
|
256
312
|
const interleaveDepthMs = requireNumber(fec.interleaveDepthMs, 'fec.interleaveDepthMs')
|
|
257
313
|
const reorderToleranceMs = requireNumber(fec.reorderToleranceMs, 'fec.reorderToleranceMs')
|
|
@@ -271,17 +327,38 @@ export class MmtpFecSession {
|
|
|
271
327
|
config.heldLayers,
|
|
272
328
|
)
|
|
273
329
|
this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
330
|
+
this.#decoder = buildDecoder(config.Decoder, this)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* A later version of the AL-FEC message, catalog-less sessions only. A
|
|
335
|
+
* maximum_p_for_repair_flow above the held total raises it (`raised`): the
|
|
336
|
+
* anchored check restarts at the new total, so a block generated under the
|
|
337
|
+
* previous one stays valid before the first block carrying the new total
|
|
338
|
+
* (fec-core-00 7.3.2, mmt-02 8.3). A field at or below it changes nothing
|
|
339
|
+
* (`unchanged`): no publisher lowers it, and the check catches a wire that
|
|
340
|
+
* does. An entry with another asset, repair flow, code point, K or T, or
|
|
341
|
+
* one outside 8.3, is `refused`: that is a new source encoding, which needs
|
|
342
|
+
* a new session. A latched fail-closed stays latched either way.
|
|
343
|
+
*/
|
|
344
|
+
updateAlFecFlow(flow: AlFecFlow): MmtpFecSessionMessageUpdate {
|
|
345
|
+
const held = this.#message
|
|
346
|
+
if (!held) throw new Error('MmtpFecSession: updateAlFecFlow() needs a session built from an AL-FEC entry')
|
|
347
|
+
const next = alFecRaptorQFlowConfig(flow)
|
|
348
|
+
if (
|
|
349
|
+
!next ||
|
|
350
|
+
next.sourcePacketIds[0] !== held.sourcePacketIds[0] ||
|
|
351
|
+
next.repairFlowId !== held.repairFlowId ||
|
|
352
|
+
next.fecCodeId !== held.fecCodeId ||
|
|
353
|
+
next.maximumK !== held.maximumK ||
|
|
354
|
+
next.repairSymbolSize !== held.repairSymbolSize
|
|
355
|
+
) {
|
|
356
|
+
return 'refused'
|
|
283
357
|
}
|
|
284
|
-
this.#
|
|
358
|
+
if (next.maximumP <= this.#repairSymbols) return 'unchanged'
|
|
359
|
+
this.#repairSymbols = next.maximumP
|
|
360
|
+
this.#heldLayers = layerZero(next.maximumP)
|
|
361
|
+
return 'raised'
|
|
285
362
|
}
|
|
286
363
|
|
|
287
364
|
/** True once release() ran. */
|
|
@@ -386,7 +463,7 @@ export class MmtpFecSession {
|
|
|
386
463
|
// An RSB_length below a held layer's total goes to the 7.3.2 stop, not
|
|
387
464
|
// to the discard; the block state is unchanged either way.
|
|
388
465
|
const verdict = this.#heldLayers.failsClosed(
|
|
389
|
-
repairTrackName, frame.ssStart, frame.rsbLength, this.#anchorHorizon, this.#newestSource,
|
|
466
|
+
this.#trackFor(repairTrackName), frame.ssStart, frame.rsbLength, this.#anchorHorizon, this.#newestSource,
|
|
390
467
|
)
|
|
391
468
|
if (verdict) {
|
|
392
469
|
this.#stats.failClosed = verdict
|
|
@@ -399,7 +476,7 @@ export class MmtpFecSession {
|
|
|
399
476
|
// fec-01 6.3.9, after the 4.5 discards and the size check, before a block
|
|
400
477
|
// is placed. Without a track name only the anchored rule (b) applies.
|
|
401
478
|
const verdict = this.#heldLayers.admit(
|
|
402
|
-
repairTrackName,
|
|
479
|
+
this.#trackFor(repairTrackName),
|
|
403
480
|
frame.ssStart,
|
|
404
481
|
frame.rsbLength,
|
|
405
482
|
this.#anchorHorizon,
|
|
@@ -464,6 +541,11 @@ export class MmtpFecSession {
|
|
|
464
541
|
return true
|
|
465
542
|
}
|
|
466
543
|
|
|
544
|
+
/** Catalog-less: layer 0 has no own-track clause (mmt-02 8.3), so no track name is judged. */
|
|
545
|
+
#trackFor(repairTrackName: string | undefined): string | undefined {
|
|
546
|
+
return this.#message ? undefined : repairTrackName
|
|
547
|
+
}
|
|
548
|
+
|
|
467
549
|
#drop(reason: string): Uint8Array[] {
|
|
468
550
|
this.#stats.dropped[reason] = (this.#stats.dropped[reason] ?? 0) + 1
|
|
469
551
|
return []
|
|
@@ -600,6 +682,24 @@ export class MmtpFecSession {
|
|
|
600
682
|
}
|
|
601
683
|
}
|
|
602
684
|
|
|
685
|
+
function layerZero(total: number): HeldLayerAdmission {
|
|
686
|
+
return new HeldLayerAdmission([{ layer: 0, repairTrackName: '', rsIdOffset: 0, repairSymbols: total }])
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function buildDecoder(Decoder: MmtpFecSessionDecoderConstructor, session: MmtpFecSession): MmtpFecSessionDecoder {
|
|
690
|
+
if (typeof Decoder !== 'function') {
|
|
691
|
+
throw new Error('MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required')
|
|
692
|
+
}
|
|
693
|
+
const decoder = new Decoder(session.retentionBlocks, session.blockProfile, session.algorithm)
|
|
694
|
+
try {
|
|
695
|
+
decoder.configure_params(session.sourceSymbols, session.symbolSize)
|
|
696
|
+
} catch (error) {
|
|
697
|
+
decoder.free()
|
|
698
|
+
throw error
|
|
699
|
+
}
|
|
700
|
+
return decoder
|
|
701
|
+
}
|
|
702
|
+
|
|
603
703
|
function requirePositiveInteger(value: unknown, path: string): number {
|
|
604
704
|
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
|
|
605
705
|
throw new Error(`MmtpFecSession: ${path} must be a positive integer (got ${String(value)})`)
|