@blockcast/mmt-container 0.4.1 → 0.5.0-main.2c1f81817995
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 +2 -2
- package/dist/browser-runtime.js +3458 -356
- package/dist/browser-runtime.js.map +4 -4
- package/dist/cmcd-producer.d.ts +5 -1
- package/dist/cmcd-producer.d.ts.map +1 -1
- package/dist/cmcd-producer.js.map +2 -2
- package/dist/delivery-catalog.js +18 -2
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/fec-manager.d.ts +21 -16
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +177 -80
- package/dist/fec-manager.js.map +3 -3
- package/dist/held-layer-admission.d.ts +58 -0
- package/dist/held-layer-admission.d.ts.map +1 -0
- package/dist/held-layer-admission.js +105 -0
- package/dist/held-layer-admission.js.map +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +2682 -1023
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-fec-session.d.ts +36 -3
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +114 -5
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +72 -28
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +321 -203
- package/dist/mmtp-router.js.map +2 -2
- package/dist/multicast-auth-catalog.d.ts +10 -2
- package/dist/multicast-auth-catalog.d.ts.map +1 -1
- package/dist/multicast-auth-catalog.js +422 -22
- package/dist/multicast-auth-catalog.js.map +4 -4
- package/dist/multicast-manager.d.ts +48 -9
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +3406 -340
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +4 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +20 -3
- package/dist/multicast-transport.js.map +2 -2
- package/dist/no-modules.d.ts +1 -2
- package/dist/no-modules.d.ts.map +1 -1
- package/dist/no-modules.js +2668 -1007
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.d.ts +10 -1
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +5 -2
- package/dist/repair-fetch.js.map +2 -2
- package/package.json +5 -14
- package/src/cmcd-producer.ts +6 -1
- package/src/fec-manager.ts +121 -131
- package/src/held-layer-admission.ts +161 -0
- package/src/index.ts +5 -9
- package/src/mmtp-fec-session.ts +76 -3
- package/src/mmtp-router.ts +390 -272
- package/src/multicast-auth-catalog.ts +81 -8
- package/src/multicast-manager.ts +189 -48
- package/src/multicast-transport.ts +6 -2
- package/src/no-modules.ts +1 -6
- package/src/repair-fetch.ts +24 -4
- package/dist/manifest-auth-plan.d.ts +0 -31
- package/dist/manifest-auth-plan.d.ts.map +0 -1
- package/dist/manifest-auth-plan.js +0 -426
- package/dist/manifest-auth-plan.js.map +0 -7
- package/dist/source-auth.d.ts +0 -2
- package/dist/source-auth.d.ts.map +0 -1
- package/dist/source-auth.js +0 -3912
- package/dist/source-auth.js.map +0 -7
- package/src/manifest-auth-plan.ts +0 -55
- package/src/source-auth.ts +0 -1
- package/test-vectors/generate-source-auth-fixture.mjs +0 -114
- package/test-vectors/source-auth-v1.json +0 -61
package/src/index.ts
CHANGED
|
@@ -39,15 +39,11 @@ export {
|
|
|
39
39
|
export { isCompleteFmp4DataUnit } from './fmp4-completion.js'
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
// from the eager index barrel
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// "Failed to resolve module specifier". No browser player uses source-auth.)
|
|
48
|
-
// Consumers that need it import the dedicated subpath: '@blockcast/mmt-container/source-auth'.
|
|
49
|
-
// The same rule covers the @blockcast/mmt-manifest-verify surface (ManifestVerifier et al.):
|
|
50
|
-
// import it from that package (or the subpath above) directly, never from this barrel.
|
|
42
|
+
// The bc-provenance v2 surface (@blockcast/mmt-manifest-verify) is intentionally
|
|
43
|
+
// NOT re-exported from the eager index barrel: it imports @noble/curves and
|
|
44
|
+
// @noble/hashes, bare specifiers the moq-lib / shred / moqtail players (which
|
|
45
|
+
// load the barrel as raw ESM via an import map) cannot resolve. Import it from
|
|
46
|
+
// that package directly. The router's provenance hooks take it by type only.
|
|
51
47
|
|
|
52
48
|
// H.264/AVC + H.265/HEVC Codec Utilities
|
|
53
49
|
export {
|
package/src/mmtp-fec-session.ts
CHANGED
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
* The retention is derived from the catalog's `fecBlockBudgetMs`, and a
|
|
19
19
|
* catalog missing either input throws rather than defaulting.
|
|
20
20
|
*
|
|
21
|
+
* Every repair packet runs the fec-01 6.3.9 held-layer check (layer 0, B_0 =
|
|
22
|
+
* catalog `fec.repairSymbols`) through the same HeldLayerAdmission FECManager
|
|
23
|
+
* uses. A packet below B_0 at or after the anchor fails the session closed:
|
|
24
|
+
* `stats.failClosed` reports why, and every later repair is refused.
|
|
25
|
+
*
|
|
21
26
|
* Every repair packet that cannot be used is counted under the reason it was
|
|
22
27
|
* refused, and every block ends under exactly one outcome, so a stream whose
|
|
23
28
|
* FEC is doing nothing can be told apart from one with nothing to repair.
|
|
@@ -37,6 +42,14 @@ import {
|
|
|
37
42
|
type FecBlockProfile,
|
|
38
43
|
} from '@blockcast/mmt-base'
|
|
39
44
|
import { deriveFecRetentionBlocks, fecBlockBudgetMs } from '@blockcast/mmt-fec'
|
|
45
|
+
import {
|
|
46
|
+
HeldLayerAdmission,
|
|
47
|
+
heldLayerAnchorHorizon,
|
|
48
|
+
type FecFailClosedReason,
|
|
49
|
+
type HeldLayerInput,
|
|
50
|
+
} from './held-layer-admission.js'
|
|
51
|
+
|
|
52
|
+
export type { FecFailClosedReason, HeldLayerInput } from './held-layer-admission.js'
|
|
40
53
|
|
|
41
54
|
/** The mmt-wasm `MmtFecDecoder` members the session drives. */
|
|
42
55
|
export interface MmtpFecSessionDecoder {
|
|
@@ -67,6 +80,10 @@ export interface MmtpFecSessionCatalogFec {
|
|
|
67
80
|
algorithm?: unknown
|
|
68
81
|
blockProfile?: unknown
|
|
69
82
|
sourceSymbols?: unknown
|
|
83
|
+
/** P_0: layer 0's repair symbols per block, the fec-01 6.3.9 total B_0. */
|
|
84
|
+
repairSymbols?: unknown
|
|
85
|
+
/** Layer 0's repair track; names it for addRepair()'s `repairTrackName`. */
|
|
86
|
+
repairTrack?: unknown
|
|
70
87
|
symbolSize?: unknown
|
|
71
88
|
interleaveDepthMs?: unknown
|
|
72
89
|
reorderToleranceMs?: unknown
|
|
@@ -77,6 +94,14 @@ export interface MmtpFecSessionConfig {
|
|
|
77
94
|
fec: MmtpFecSessionCatalogFec
|
|
78
95
|
/** Builds the RaptorQ decoder, normally `wasm_bindgen.MmtFecDecoder`. */
|
|
79
96
|
Decoder: MmtpFecSessionDecoderConstructor
|
|
97
|
+
/**
|
|
98
|
+
* The source's repair layers as the catalog resolves them
|
|
99
|
+
* (`TrackFecParams.repairLayers`), for a caller that subscribes more than
|
|
100
|
+
* layer 0. Absent: layer 0 alone, B_0 = `fec.repairSymbols`.
|
|
101
|
+
*/
|
|
102
|
+
repairLayers?: readonly HeldLayerInput[]
|
|
103
|
+
/** The layers the caller holds (fec-01 6.3.9). Absent: [0]. */
|
|
104
|
+
heldLayers?: readonly number[]
|
|
80
105
|
}
|
|
81
106
|
|
|
82
107
|
/**
|
|
@@ -139,6 +164,16 @@ export interface MmtpFecSessionStats {
|
|
|
139
164
|
* contributed nothing to its block and do not make it mixed.
|
|
140
165
|
*/
|
|
141
166
|
mixedPath: MmtpFecSessionMixedPathStats
|
|
167
|
+
/**
|
|
168
|
+
* fec-01 6.3.9: null until a repair packet at or after the anchor (the
|
|
169
|
+
* earliest block, in SS_Start serial order, whose repair carried
|
|
170
|
+
* RSB_length >= B_0) states an RSB_length below B_0; then why. Terminal:
|
|
171
|
+
* every later repair is refused under `repair:fail-closed` (the packet that
|
|
172
|
+
* failed it is counted there too), and block state stays as it was. Sources
|
|
173
|
+
* are still taken and can still complete a block from repair admitted
|
|
174
|
+
* before the failure.
|
|
175
|
+
*/
|
|
176
|
+
failClosed: FecFailClosedReason | null
|
|
142
177
|
}
|
|
143
178
|
|
|
144
179
|
interface BlockState {
|
|
@@ -160,6 +195,8 @@ export class MmtpFecSession {
|
|
|
160
195
|
readonly blockProfile: FecBlockProfile
|
|
161
196
|
readonly algorithm: 'raptorq' | 'reed-solomon'
|
|
162
197
|
readonly sourceSymbols: number
|
|
198
|
+
/** P_0 = B_0, the held total every repair packet is checked against. */
|
|
199
|
+
readonly repairSymbols: number
|
|
163
200
|
readonly symbolSize: number
|
|
164
201
|
/** The decoder's retention, in blocks, derived from the catalog budget. */
|
|
165
202
|
readonly retentionBlocks: number
|
|
@@ -178,6 +215,9 @@ export class MmtpFecSession {
|
|
|
178
215
|
/** The newest SS_ID when the remembered SS_IDs were last pruned. */
|
|
179
216
|
#prunedAt: number | undefined
|
|
180
217
|
#repairPacketId: number | undefined
|
|
218
|
+
#newestSource: number | undefined
|
|
219
|
+
#heldLayers: HeldLayerAdmission
|
|
220
|
+
#anchorHorizon: number
|
|
181
221
|
#stats: MmtpFecSessionStats = {
|
|
182
222
|
sourceSymbols: 0,
|
|
183
223
|
repairSymbols: 0,
|
|
@@ -191,11 +231,12 @@ export class MmtpFecSession {
|
|
|
191
231
|
sourceSymbolsByPath: { moq: 0, multicast: 0 },
|
|
192
232
|
repairSymbolsByPath: { moq: 0, multicast: 0 },
|
|
193
233
|
mixedPath: { clean: 0, recovered: 0, failed: 0, released: 0 },
|
|
234
|
+
failClosed: null,
|
|
194
235
|
}
|
|
195
236
|
|
|
196
237
|
/**
|
|
197
238
|
* @throws when the catalog block lacks or misstates a field the session
|
|
198
|
-
* needs: the block profile, K, T, `interleaveDepthMs` or
|
|
239
|
+
* needs: the block profile, K, P_0 (`repairSymbols`), T, `interleaveDepthMs` or
|
|
199
240
|
* `reorderToleranceMs`. None is defaulted.
|
|
200
241
|
*/
|
|
201
242
|
constructor(config: MmtpFecSessionConfig) {
|
|
@@ -209,6 +250,7 @@ export class MmtpFecSession {
|
|
|
209
250
|
}
|
|
210
251
|
this.algorithm = fec.algorithm === 'reed-solomon' ? 'reed-solomon' : 'raptorq'
|
|
211
252
|
this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, 'fec.sourceSymbols')
|
|
253
|
+
this.repairSymbols = requirePositiveInteger(fec.repairSymbols, 'fec.repairSymbols')
|
|
212
254
|
this.symbolSize = requirePositiveInteger(fec.symbolSize, 'fec.symbolSize')
|
|
213
255
|
const interleaveDepthMs = requireNumber(fec.interleaveDepthMs, 'fec.interleaveDepthMs')
|
|
214
256
|
const reorderToleranceMs = requireNumber(fec.reorderToleranceMs, 'fec.reorderToleranceMs')
|
|
@@ -216,6 +258,18 @@ export class MmtpFecSession {
|
|
|
216
258
|
fecBlockBudgetMs(interleaveDepthMs, reorderToleranceMs),
|
|
217
259
|
interleaveDepthMs,
|
|
218
260
|
)
|
|
261
|
+
this.#heldLayers = new HeldLayerAdmission(
|
|
262
|
+
config.repairLayers ?? [
|
|
263
|
+
{
|
|
264
|
+
layer: 0,
|
|
265
|
+
repairTrackName: typeof fec.repairTrack === 'string' ? fec.repairTrack : '',
|
|
266
|
+
rsIdOffset: 0,
|
|
267
|
+
repairSymbols: this.repairSymbols,
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
config.heldLayers,
|
|
271
|
+
)
|
|
272
|
+
this.#anchorHorizon = heldLayerAnchorHorizon(this.retentionBlocks, this.sourceSymbols)
|
|
219
273
|
if (typeof config.Decoder !== 'function') {
|
|
220
274
|
throw new Error('MmtpFecSession: a decoder constructor (wasm_bindgen.MmtFecDecoder) is required')
|
|
221
275
|
}
|
|
@@ -241,6 +295,7 @@ export class MmtpFecSession {
|
|
|
241
295
|
sourceSymbolsByPath: { ...this.#stats.sourceSymbolsByPath },
|
|
242
296
|
repairSymbolsByPath: { ...this.#stats.repairSymbolsByPath },
|
|
243
297
|
mixedPath: { ...this.#stats.mixedPath },
|
|
298
|
+
failClosed: this.#stats.failClosed && { ...this.#stats.failClosed },
|
|
244
299
|
}
|
|
245
300
|
}
|
|
246
301
|
|
|
@@ -266,6 +321,7 @@ export class MmtpFecSession {
|
|
|
266
321
|
this.#stats.sourceSymbols++
|
|
267
322
|
this.#stats.sourceSymbolsByPath[path]++
|
|
268
323
|
this.#noteNewest(ssId)
|
|
324
|
+
if (this.#newestSource === undefined || serial32Precedes(this.#newestSource, ssId)) this.#newestSource = ssId
|
|
269
325
|
|
|
270
326
|
const start = this.#blockStartOf(ssId)
|
|
271
327
|
const block = start === undefined ? undefined : this.#blocks.get(start)
|
|
@@ -298,14 +354,17 @@ export class MmtpFecSession {
|
|
|
298
354
|
* Feeds one object of the repair track: a whole MMTP repair packet (ISO/IEC
|
|
299
355
|
* 23008-1 C.5.3). A repair track carries one repair flow, so the first
|
|
300
356
|
* repair packet fixes its packet ID and a packet of any other is refused.
|
|
301
|
-
* `path` is where it was received, as for addSource().
|
|
357
|
+
* `path` is where it was received, as for addSource(). `repairTrackName`
|
|
358
|
+
* is the repair track it was received on, for the 6.3.9 own-track rule; a
|
|
359
|
+
* track the layers do not list is refused as `repair:unlisted-track`.
|
|
302
360
|
*
|
|
303
361
|
* @returns the protected packets this repair symbol recovered, as for
|
|
304
362
|
* addSource().
|
|
305
363
|
*/
|
|
306
|
-
addRepair(packet: Uint8Array, path: MmtpFecSessionPath = 'moq'): Uint8Array[] {
|
|
364
|
+
addRepair(packet: Uint8Array, path: MmtpFecSessionPath = 'moq', repairTrackName?: string): Uint8Array[] {
|
|
307
365
|
const decoder = this.#decoder
|
|
308
366
|
if (!decoder) return this.#drop('repair:released')
|
|
367
|
+
if (this.#stats.failClosed) return this.#drop('repair:fail-closed')
|
|
309
368
|
const pathBit = PATH_BITS[path]
|
|
310
369
|
if (!pathBit) return this.#drop('repair:unknown-path')
|
|
311
370
|
let header
|
|
@@ -328,6 +387,20 @@ export class MmtpFecSession {
|
|
|
328
387
|
if (this.algorithm === 'reed-solomon' && frame.ssbLength + frame.rsbLength > 255) {
|
|
329
388
|
return this.#drop('repair:rs-block-length')
|
|
330
389
|
}
|
|
390
|
+
// fec-01 6.3.9, after the 4.5 discards and the size check, before a block
|
|
391
|
+
// is placed. Without a track name only the anchored rule (b) applies.
|
|
392
|
+
const verdict = this.#heldLayers.admit(
|
|
393
|
+
repairTrackName,
|
|
394
|
+
frame.ssStart,
|
|
395
|
+
frame.rsbLength,
|
|
396
|
+
this.#anchorHorizon,
|
|
397
|
+
this.#newestSource,
|
|
398
|
+
)
|
|
399
|
+
if (verdict === 'discard') return this.#drop('repair:unlisted-track')
|
|
400
|
+
if (verdict !== undefined) {
|
|
401
|
+
this.#stats.failClosed = verdict
|
|
402
|
+
return this.#drop('repair:fail-closed')
|
|
403
|
+
}
|
|
331
404
|
|
|
332
405
|
const start = frame.ssStart
|
|
333
406
|
let block = this.#blocks.get(start)
|