@blockcast/mmt-container 0.5.0 → 1.0.0-main.3cb7359ea316
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 +21 -16
- package/dist/browser-runtime.js +1024 -1078
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +33 -87
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +12 -434
- package/dist/carp-catalog.js.map +4 -4
- package/dist/catalog-fec-params.d.ts +48 -161
- package/dist/catalog-fec-params.d.ts.map +1 -1
- package/dist/catalog-fec-params.js +567 -592
- package/dist/catalog-fec-params.js.map +4 -4
- package/dist/catalog-network-source.js +6 -2
- package/dist/catalog-network-source.js.map +3 -3
- package/dist/delivery-catalog.js +12 -4
- package/dist/delivery-catalog.js.map +4 -4
- package/dist/delta-fallback-config.d.ts +21 -26
- package/dist/delta-fallback-config.d.ts.map +1 -1
- package/dist/delta-fallback-config.js +582 -24
- package/dist/delta-fallback-config.js.map +4 -4
- package/dist/fec-manager.js +6 -2
- package/dist/fec-manager.js.map +3 -3
- package/dist/fec-state.d.ts +5 -43
- package/dist/fec-state.d.ts.map +1 -1
- package/dist/fec-state.js +529 -34
- package/dist/fec-state.js.map +4 -4
- package/dist/fec-worker-setup.js +6 -2
- package/dist/fec-worker-setup.js.map +3 -3
- package/dist/held-layer-admission.js +6 -2
- package/dist/held-layer-admission.js.map +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -39
- package/dist/index.js.map +2 -2
- package/dist/iso-block-locator.js +6 -2
- package/dist/iso-block-locator.js.map +3 -3
- package/dist/mfu-reassembler.js +6 -2
- package/dist/mfu-reassembler.js.map +3 -3
- package/dist/mmtp-container-parser.js +6 -2
- package/dist/mmtp-container-parser.js.map +3 -3
- package/dist/mmtp-container-parser.no-modules.js +551 -452
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js +6 -2
- package/dist/mmtp-decode-pipeline.js.map +3 -3
- package/dist/mmtp-fec-session.d.ts +7 -6
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +13 -9
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +7 -16
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +19 -29
- package/dist/mmtp-router.js.map +3 -3
- package/dist/multicast-auth-catalog.js +7 -3
- package/dist/multicast-auth-catalog.js.map +3 -3
- package/dist/multicast-input.js +7 -3
- package/dist/multicast-input.js.map +3 -3
- package/dist/multicast-manager.d.ts +29 -32
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +682 -253
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +23 -2
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +57 -16
- package/dist/multicast-transport.js.map +4 -4
- package/dist/multicast-unwrap.js +6 -2
- package/dist/multicast-unwrap.js.map +3 -3
- package/dist/no-modules.js +551 -452
- package/dist/no-modules.js.map +4 -4
- package/dist/phase14-catalog-override.d.ts +10 -5
- package/dist/phase14-catalog-override.d.ts.map +1 -1
- package/dist/phase14-catalog-override.js +35 -11
- package/dist/phase14-catalog-override.js.map +2 -2
- package/dist/repair-fetch.d.ts +3 -1
- package/dist/repair-fetch.d.ts.map +1 -1
- package/dist/repair-fetch.js +16 -3
- package/dist/repair-fetch.js.map +3 -3
- package/dist/repair-layers.d.ts +0 -123
- package/dist/repair-layers.d.ts.map +1 -1
- package/dist/repair-layers.js +1 -349
- package/dist/repair-layers.js.map +3 -3
- package/dist/signaling.js +6 -2
- package/dist/signaling.js.map +3 -3
- package/dist/timestamp-codec.js +6 -2
- package/dist/timestamp-codec.js.map +3 -3
- package/dist/wasm-mfu-reassembler.js +6 -2
- package/dist/wasm-mfu-reassembler.js.map +3 -3
- package/dist/wasm-mmtp-container-parser.js +6 -2
- package/dist/wasm-mmtp-container-parser.js.map +3 -3
- package/package.json +9 -5
- package/schemas/catalog.schema.json +53 -58
- package/src/carp-catalog.ts +39 -229
- package/src/catalog-fec-params.ts +130 -493
- package/src/delta-fallback-config.ts +63 -50
- package/src/fec-state.ts +7 -62
- package/src/index.ts +10 -30
- package/src/mmtp-fec-session.ts +11 -10
- package/src/mmtp-router.ts +14 -29
- package/src/multicast-manager.ts +89 -121
- package/src/multicast-transport.ts +65 -12
- package/src/phase14-catalog-override.ts +67 -16
- package/src/repair-fetch.ts +27 -3
- package/src/repair-layers.ts +7 -520
- package/test-vectors/catalog/README.md +39 -67
- package/test-vectors/catalog/fec-01-runtime/README.md +15 -0
- package/test-vectors/catalog/{negative/fec-block-profile-retired.json → fec-01-runtime/fec-iso-ssbg1.json} +1 -1
- package/test-vectors/catalog/fec-01-runtime/fec-layered-repair.json +106 -0
- package/test-vectors/catalog/{negative/fec-block-profile-unknown.json → fec-01-runtime/fec-multicast.json} +2 -1
- package/test-vectors/catalog/fec-01-runtime/multicast-native-ssm-no-network-source.json +44 -0
- package/test-vectors/catalog/negative/fec-block-profile-missing.json +24 -11
- package/test-vectors/catalog/negative/fec-iso-symbol-size-below-floor.json +13 -8
- package/test-vectors/catalog/negative/fec-reed-solomon-iso-symbol-size-13.json +13 -8
- package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +20 -10
- package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +20 -10
- package/test-vectors/catalog/negative/fec-repair-overlay-source-symbols-zero.json +25 -17
- package/test-vectors/catalog/negative/fec-repair-symbols-zero.json +13 -8
- package/test-vectors/catalog/negative/fec-source-symbols-zero.json +13 -8
- package/test-vectors/catalog/negative/fec-symbol-size-zero.json +13 -8
- package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +18 -9
- package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +13 -8
- package/test-vectors/catalog/positive/fec-algorithm-unimplemented.json +14 -9
- package/test-vectors/catalog/positive/fec-iso-ssbg1.json +25 -12
- package/test-vectors/catalog/positive/fec-layered-repair.json +79 -35
- package/test-vectors/catalog/positive/fec-multicast.json +26 -13
- package/test-vectors/catalog/positive/fec-reed-solomon-layered.json +29 -18
- package/test-vectors/catalog/positive/multicast-draft01-4-1-example.json +28 -20
- package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +14 -10
- package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +22 -12
- package/test-vectors/catalog/section-codes.json +13 -22
- package/test-vectors/catalog-timing.json +2 -2
- package/test-vectors/catalog-validation.json +30 -243
- package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +0 -28
- package/test-vectors/catalog/negative/fec-reed-solomon-k-plus-p-over-255.json +0 -39
- package/test-vectors/catalog/negative/fec-reed-solomon-overlay-over-255.json +0 -40
- package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +0 -32
- package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +0 -34
- package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +0 -24
- package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +0 -33
package/dist/carp-catalog.js
CHANGED
|
@@ -14,21 +14,6 @@ var NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1);
|
|
|
14
14
|
var FEC_BLOCK_PROFILES = Object.freeze([
|
|
15
15
|
"iso-ssbg1-v1"
|
|
16
16
|
]);
|
|
17
|
-
function isFecBlockProfile(value) {
|
|
18
|
-
return value === "iso-ssbg1-v1";
|
|
19
|
-
}
|
|
20
|
-
function parseFecBlockProfile(value, where) {
|
|
21
|
-
if (isFecBlockProfile(value))
|
|
22
|
-
return value;
|
|
23
|
-
if (value === void 0) {
|
|
24
|
-
throw new Error(`${where} is required: name the FEC block profile ("iso-ssbg1-v1")`);
|
|
25
|
-
}
|
|
26
|
-
throw new Error(`${where} must be "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ../mmt-base/dist/catalog-fec-source.js
|
|
30
|
-
var ALL_ROLES = Object.freeze(["video", "audio"]);
|
|
31
|
-
var ISO_SSBG1_MIN_SYMBOL_SIZE = 14;
|
|
32
17
|
|
|
33
18
|
// ../mmt-base/dist/multicast-catalog.js
|
|
34
19
|
var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
|
|
@@ -278,320 +263,12 @@ function multicastCatalogIssues(catalog, options = {}) {
|
|
|
278
263
|
return issues;
|
|
279
264
|
}
|
|
280
265
|
|
|
281
|
-
//
|
|
282
|
-
var
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
var MAX_ENCODING_SYMBOL_ID = 16777215;
|
|
288
|
-
var MAX_REED_SOLOMON_BLOCK_SYMBOLS = 255;
|
|
289
|
-
var RepairContractError = class extends Error {
|
|
290
|
-
constructor(code, path, message) {
|
|
291
|
-
super(`[repair-layers:${code}] ${path}: ${message}`);
|
|
292
|
-
this.code = code;
|
|
293
|
-
this.path = path;
|
|
294
|
-
this.name = "RepairContractError";
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
var throwRepairContractError = (code, path, message) => {
|
|
298
|
-
throw new RepairContractError(code, path, message);
|
|
299
|
-
};
|
|
300
|
-
function readFecBlockProfile(value, path, declaresFecInstance, fail) {
|
|
301
|
-
if (value === void 0 && !declaresFecInstance) return void 0;
|
|
302
|
-
if (isFecBlockProfile(value)) return value;
|
|
303
|
-
if (typeof value === "string" && value.length > 0) {
|
|
304
|
-
return fail(
|
|
305
|
-
"track_fec_block_profile_unimplemented",
|
|
306
|
-
path,
|
|
307
|
-
`${JSON.stringify(value)} is not an implemented FEC block profile; only "iso-ssbg1-v1" is implemented`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
try {
|
|
311
|
-
return parseFecBlockProfile(value, "blockProfile");
|
|
312
|
-
} catch (error) {
|
|
313
|
-
return fail("track_fec_block_profile", path, error.message);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
var REPAIR_FIELDS = ["depends", "repairLayer", "repairSymbols", "priority", "scope", "sourceSymbols"];
|
|
317
|
-
var REPAIR_TRACK_GEOMETRY_KEYS = [
|
|
318
|
-
"mmtpMode",
|
|
319
|
-
"timescale",
|
|
320
|
-
"groupDurationMs",
|
|
321
|
-
"groupDurationTicks",
|
|
322
|
-
"keyframeIntervalMs",
|
|
323
|
-
"keyframeIntervalTicks",
|
|
324
|
-
"fec",
|
|
325
|
-
"symbolSize",
|
|
326
|
-
"interleaveDepthMs"
|
|
327
|
-
];
|
|
328
|
-
var REPAIR_TRACK_ONLY_KEYS = ["repairLayer", "repairSymbols", "sourceSymbols", "scope"];
|
|
329
|
-
function rejectRepairTrackGeometry(path, fields, fail) {
|
|
330
|
-
for (const key of REPAIR_TRACK_GEOMETRY_KEYS) {
|
|
331
|
-
if (fields[key] !== void 0) {
|
|
332
|
-
fail(
|
|
333
|
-
"track_repair_geometry",
|
|
334
|
-
`${path}.${key}`,
|
|
335
|
-
"a repair track carries no block geometry; it is shared with the source track's fec object"
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
function repairReachability(fecObjects) {
|
|
341
|
-
let declaresFecSource = false;
|
|
342
|
-
const layerZeroNames = /* @__PURE__ */ new Set();
|
|
343
|
-
for (const candidate of fecObjects) {
|
|
344
|
-
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) continue;
|
|
345
|
-
const fec = candidate;
|
|
346
|
-
if (fec["algorithm"] === "none") continue;
|
|
347
|
-
declaresFecSource = true;
|
|
348
|
-
const repairTrack = fec["repairTrack"];
|
|
349
|
-
if (typeof repairTrack === "string" && repairTrack.length > 0) layerZeroNames.add(repairTrack);
|
|
350
|
-
}
|
|
351
|
-
return { declaresFecSource, layerZeroNames };
|
|
352
|
-
}
|
|
353
|
-
function assertRepairTrackReachable(descriptor, reachability, fail) {
|
|
354
|
-
if (dependsTarget(descriptor) !== void 0) return;
|
|
355
|
-
if (reachability.declaresFecSource && !reachability.layerZeroNames.has(descriptor.name)) {
|
|
356
|
-
fail(
|
|
357
|
-
"track_repair_depends",
|
|
358
|
-
`${descriptor.path}.depends`,
|
|
359
|
-
"no source names this repair track in fec.repairTrack and it declares no depends"
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
function rejectRepairFieldsOnMediaTrack(path, fields, fail) {
|
|
364
|
-
for (const key of REPAIR_TRACK_ONLY_KEYS) {
|
|
365
|
-
if (fields[key] !== void 0) {
|
|
366
|
-
fail("track_repair_layer", `${path}.${key}`, `${key} is a repair track field`);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
function describeRepairTrack(name, path, fields) {
|
|
371
|
-
const descriptor = { name, path };
|
|
372
|
-
for (const field of REPAIR_FIELDS) {
|
|
373
|
-
if (field in fields) descriptor[field] = fields[field];
|
|
374
|
-
}
|
|
375
|
-
return descriptor;
|
|
376
|
-
}
|
|
377
|
-
function isSafeInteger(value) {
|
|
378
|
-
return typeof value === "number" && Number.isSafeInteger(value);
|
|
379
|
-
}
|
|
380
|
-
function positiveInteger(value) {
|
|
381
|
-
return isSafeInteger(value) && value > 0;
|
|
382
|
-
}
|
|
383
|
-
function dependsTarget(descriptor) {
|
|
384
|
-
const depends = descriptor.depends;
|
|
385
|
-
return Array.isArray(depends) && depends.length === 1 && typeof depends[0] === "string" && depends[0].length > 0 ? depends[0] : void 0;
|
|
386
|
-
}
|
|
387
|
-
function validateRepairTrackFields(descriptor, fail) {
|
|
388
|
-
const { path } = descriptor;
|
|
389
|
-
if (descriptor.depends !== void 0 && dependsTarget(descriptor) === void 0) {
|
|
390
|
-
fail("track_repair_depends", `${path}.depends`, "must name exactly one source track");
|
|
391
|
-
}
|
|
392
|
-
if (descriptor.repairLayer !== void 0 && (!isSafeInteger(descriptor.repairLayer) || descriptor.repairLayer < 0)) {
|
|
393
|
-
fail("track_repair_layer", `${path}.repairLayer`, "must be a non-negative integer");
|
|
394
|
-
}
|
|
395
|
-
if (descriptor.repairSymbols !== void 0 && (!positiveInteger(descriptor.repairSymbols) || descriptor.repairSymbols > MAX_REPAIR_SYMBOLS)) {
|
|
396
|
-
fail("track_repair_layer", `${path}.repairSymbols`, `must be a positive integer at most ${MAX_REPAIR_SYMBOLS}`);
|
|
397
|
-
}
|
|
398
|
-
if (descriptor.priority !== void 0 && (!isSafeInteger(descriptor.priority) || descriptor.priority < REPAIR_PRIORITY_MIN || descriptor.priority > REPAIR_PRIORITY_MAX)) {
|
|
399
|
-
fail(
|
|
400
|
-
"track_repair_priority",
|
|
401
|
-
`${path}.priority`,
|
|
402
|
-
`must be an integer from ${REPAIR_PRIORITY_MIN} to ${REPAIR_PRIORITY_MAX} (MoQT scale, lower first)`
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
if (descriptor.scope !== void 0) {
|
|
406
|
-
if (descriptor.scope !== "keyframe") {
|
|
407
|
-
fail("track_repair_overlay", `${path}.scope`, 'must be "keyframe"');
|
|
408
|
-
}
|
|
409
|
-
if (descriptor.repairLayer !== void 0) {
|
|
410
|
-
fail("track_repair_overlay", `${path}.repairLayer`, "an overlay is a separate FEC instance and carries no repairLayer");
|
|
411
|
-
}
|
|
412
|
-
for (const field of ["depends", "sourceSymbols", "repairSymbols", "priority"]) {
|
|
413
|
-
if (descriptor[field] === void 0) {
|
|
414
|
-
fail("track_repair_overlay", `${path}.${field}`, `an overlay must declare ${field}`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
if (descriptor.sourceSymbols !== void 0) {
|
|
419
|
-
if (descriptor.scope === void 0) {
|
|
420
|
-
fail("track_repair_overlay", `${path}.sourceSymbols`, "only an overlay (scope: keyframe) declares its own sourceSymbols");
|
|
421
|
-
}
|
|
422
|
-
if (!positiveInteger(descriptor.sourceSymbols) || descriptor.sourceSymbols > MAX_RAPTORQ_SOURCE_SYMBOLS) {
|
|
423
|
-
fail(
|
|
424
|
-
"track_fec_bounds",
|
|
425
|
-
`${path}.sourceSymbols`,
|
|
426
|
-
`must be a positive integer at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
if (isSafeInteger(descriptor.repairLayer) && descriptor.repairLayer >= 1) {
|
|
431
|
-
for (const field of ["depends", "repairSymbols", "priority"]) {
|
|
432
|
-
if (descriptor[field] === void 0) {
|
|
433
|
-
fail("track_repair_layer", `${path}.${field}`, `repair layer ${descriptor.repairLayer} must declare ${field}`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
function assembleRepairLayers(source, descriptors, fail = throwRepairContractError, options = {}) {
|
|
439
|
-
const layerZeroName = typeof source.repairTrack === "string" && source.repairTrack.length > 0 ? source.repairTrack : void 0;
|
|
440
|
-
const layerZero = layerZeroName === void 0 ? void 0 : descriptors.find((d) => d.name === layerZeroName);
|
|
441
|
-
if (layerZeroName !== void 0 && layerZero === void 0 && options.layerZeroMustBeDeclared) {
|
|
442
|
-
fail("track_repair_track", `${source.path}.repairTrack`, `unknown FEC repair track ${JSON.stringify(layerZeroName)}`);
|
|
443
|
-
}
|
|
444
|
-
const dependents = descriptors.filter((d) => d.name !== layerZeroName && dependsTarget(d) === source.name);
|
|
445
|
-
const k = positiveInteger(source.sourceSymbols) ? source.sourceSymbols : 0;
|
|
446
|
-
if (source.algorithm === "raptorq" && k > MAX_RAPTORQ_SOURCE_SYMBOLS) {
|
|
447
|
-
fail(
|
|
448
|
-
"track_fec_bounds",
|
|
449
|
-
`${source.path}.sourceSymbols`,
|
|
450
|
-
`must be at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
if (layerZeroName === void 0) {
|
|
454
|
-
if (dependents.length === 0) return void 0;
|
|
455
|
-
fail(
|
|
456
|
-
"track_repair_layer",
|
|
457
|
-
`${dependents[0].path}.depends`,
|
|
458
|
-
`repair layers of ${JSON.stringify(source.name)} require a layer 0 (fec.repairTrack) on the source`
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
const layered = dependents.length > 0;
|
|
462
|
-
let layerZeroSymbols = 0;
|
|
463
|
-
if (positiveInteger(source.repairSymbols)) {
|
|
464
|
-
layerZeroSymbols = source.repairSymbols;
|
|
465
|
-
} else if (layered) {
|
|
466
|
-
fail("track_fec", `${source.path}.repairSymbols`, "layered or overlay repair needs a positive integer repairSymbols on layer 0");
|
|
467
|
-
}
|
|
468
|
-
if (layered && !positiveInteger(source.sourceSymbols)) {
|
|
469
|
-
fail("track_fec", `${source.path}.sourceSymbols`, "layered or overlay repair needs a positive integer sourceSymbols on the source");
|
|
470
|
-
}
|
|
471
|
-
const reedSolomon = source.algorithm === "reed-solomon";
|
|
472
|
-
if (layered && source.algorithm !== "raptorq" && !reedSolomon) {
|
|
473
|
-
fail(
|
|
474
|
-
"track_fec",
|
|
475
|
-
`${source.path}.algorithm`,
|
|
476
|
-
"repair layers and overlays are defined only for raptorq and reed-solomon (draft-ramadan-moq-fec-01 6.3.4 rule 7)"
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
let layerZeroPriority = DEFAULT_REPAIR_PRIORITY;
|
|
480
|
-
if (layerZero !== void 0) {
|
|
481
|
-
if (layerZero.depends !== void 0 && dependsTarget(layerZero) !== source.name) {
|
|
482
|
-
fail(
|
|
483
|
-
"track_repair_depends",
|
|
484
|
-
`${layerZero.path}.depends`,
|
|
485
|
-
`layer 0 of ${JSON.stringify(source.name)} must depend on that source`
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
if (layerZero.repairLayer !== void 0 && layerZero.repairLayer !== 0) {
|
|
489
|
-
fail("track_repair_layer", `${layerZero.path}.repairLayer`, "the track named by fec.repairTrack is layer 0");
|
|
490
|
-
}
|
|
491
|
-
if (layerZero.scope !== void 0) {
|
|
492
|
-
fail("track_repair_overlay", `${layerZero.path}.scope`, "the track named by fec.repairTrack is base layer 0, not an overlay");
|
|
493
|
-
}
|
|
494
|
-
if (layerZero.repairSymbols !== void 0 && layerZero.repairSymbols !== layerZeroSymbols) {
|
|
495
|
-
fail(
|
|
496
|
-
"track_repair_layer",
|
|
497
|
-
`${layerZero.path}.repairSymbols`,
|
|
498
|
-
`must agree with the source's fec.repairSymbols (${layerZeroSymbols})`
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
if (typeof layerZero.priority === "number") layerZeroPriority = layerZero.priority;
|
|
502
|
-
}
|
|
503
|
-
const baseLayers = [
|
|
504
|
-
{ layer: 0, repairTrackName: layerZeroName, repairSymbols: layerZeroSymbols, rsIdOffset: 0, priority: layerZeroPriority }
|
|
505
|
-
];
|
|
506
|
-
const overlays = [];
|
|
507
|
-
for (const d of dependents) {
|
|
508
|
-
if (d.scope === void 0 && d.repairLayer === void 0) {
|
|
509
|
-
fail(
|
|
510
|
-
"track_repair_layer",
|
|
511
|
-
`${d.path}.repairLayer`,
|
|
512
|
-
`a repair track that depends on ${JSON.stringify(source.name)} must declare repairLayer (1..n) or scope: "keyframe"`
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
const layerTracks = dependents.filter((d) => d.scope === void 0).sort((a, b) => a.repairLayer - b.repairLayer);
|
|
517
|
-
for (const d of dependents) {
|
|
518
|
-
if (d.scope !== "keyframe") continue;
|
|
519
|
-
if (overlays.length === 1) {
|
|
520
|
-
fail(
|
|
521
|
-
"track_repair_overlay",
|
|
522
|
-
`${d.path}.scope`,
|
|
523
|
-
`${JSON.stringify(source.name)} already has keyframe overlay ${JSON.stringify(overlays[0].repairTrackName)}: a RAP packet carries exactly two Source FEC Payload IDs (base, then overlay), so one overlay per source`
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
const sourceSymbols = d.sourceSymbols;
|
|
527
|
-
const repairSymbols = d.repairSymbols;
|
|
528
|
-
if (sourceSymbols + repairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {
|
|
529
|
-
fail(
|
|
530
|
-
"track_fec_bounds",
|
|
531
|
-
`${d.path}.repairSymbols`,
|
|
532
|
-
`overlay sourceSymbols + repairSymbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
if (reedSolomon && sourceSymbols + repairSymbols > MAX_REED_SOLOMON_BLOCK_SYMBOLS) {
|
|
536
|
-
fail(
|
|
537
|
-
"track_fec_bounds",
|
|
538
|
-
`${d.path}.repairSymbols`,
|
|
539
|
-
`reed-solomon overlay sourceSymbols + repairSymbols exceeds ${MAX_REED_SOLOMON_BLOCK_SYMBOLS} (draft-ramadan-moq-fec-01 6.3.4 rule 12)`
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
overlays.push({ repairTrackName: d.name, scope: "keyframe", sourceSymbols, repairSymbols, priority: d.priority });
|
|
543
|
-
}
|
|
544
|
-
let offset = layerZeroSymbols;
|
|
545
|
-
let previousPriority = layerZeroPriority;
|
|
546
|
-
layerTracks.forEach((d, i) => {
|
|
547
|
-
const expected = i + 1;
|
|
548
|
-
if (d.repairLayer === 0) {
|
|
549
|
-
fail("track_repair_layer", `${d.path}.repairLayer`, `layer 0 of ${JSON.stringify(source.name)} is ${JSON.stringify(layerZeroName)}, named by fec.repairTrack`);
|
|
550
|
-
}
|
|
551
|
-
if (d.repairLayer !== expected) {
|
|
552
|
-
fail(
|
|
553
|
-
"track_repair_layer",
|
|
554
|
-
`${d.path}.repairLayer`,
|
|
555
|
-
i > 0 && d.repairLayer === layerTracks[i - 1].repairLayer ? `repair layer ${d.repairLayer} is declared twice for ${JSON.stringify(source.name)}` : `repair layers must be contiguous from 1: expected ${expected}, found ${d.repairLayer}`
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
const priority = d.priority;
|
|
559
|
-
if (priority < previousPriority) {
|
|
560
|
-
fail(
|
|
561
|
-
"track_repair_priority",
|
|
562
|
-
`${d.path}.priority`,
|
|
563
|
-
`must be at least ${previousPriority}, the priority of layer ${expected - 1}: priority is monotonic non-decreasing with layer index`
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
previousPriority = priority;
|
|
567
|
-
const repairSymbols = d.repairSymbols;
|
|
568
|
-
baseLayers.push({ layer: expected, repairTrackName: d.name, repairSymbols, rsIdOffset: offset, priority });
|
|
569
|
-
offset += repairSymbols;
|
|
570
|
-
});
|
|
571
|
-
const totalBaseRepairSymbols = offset;
|
|
572
|
-
if (totalBaseRepairSymbols > MAX_REPAIR_SYMBOLS) {
|
|
573
|
-
fail(
|
|
574
|
-
"track_fec_bounds",
|
|
575
|
-
`${layerTracks.length === 0 ? source.path : layerTracks[layerTracks.length - 1].path}.repairSymbols`,
|
|
576
|
-
`total repair symbols across base layers exceeds the 24-bit RS_ID space (${MAX_REPAIR_SYMBOLS})`
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
if (k + totalBaseRepairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {
|
|
580
|
-
fail(
|
|
581
|
-
"track_fec_bounds",
|
|
582
|
-
`${source.path}.sourceSymbols`,
|
|
583
|
-
`sourceSymbols + total base repair symbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
if (reedSolomon && k + totalBaseRepairSymbols > MAX_REED_SOLOMON_BLOCK_SYMBOLS) {
|
|
587
|
-
fail(
|
|
588
|
-
"track_fec_bounds",
|
|
589
|
-
`${source.path}.sourceSymbols`,
|
|
590
|
-
`reed-solomon sourceSymbols + total base repair symbols exceeds ${MAX_REED_SOLOMON_BLOCK_SYMBOLS} (draft-ramadan-moq-fec-01 6.3.4 rule 12)`
|
|
591
|
-
);
|
|
592
|
-
}
|
|
593
|
-
return { sourceTrackName: source.name, baseLayers, overlays, totalBaseRepairSymbols };
|
|
594
|
-
}
|
|
266
|
+
// ../mmt-base/dist/fec-bindings.js
|
|
267
|
+
var RUNTIME_FEC_RECEIVER = Object.freeze({
|
|
268
|
+
schemes: Object.freeze(["raptorq", "reed-solomon"]),
|
|
269
|
+
blockProfiles: Object.freeze(["iso-ssbg1-v1"]),
|
|
270
|
+
overlays: true
|
|
271
|
+
});
|
|
595
272
|
|
|
596
273
|
// src/carp-catalog.ts
|
|
597
274
|
var VALID_ROLE_SUFFIXES = /* @__PURE__ */ new Set(["base", "delta", "repair"]);
|
|
@@ -646,18 +323,6 @@ function validateSwitchingSet(trackNames) {
|
|
|
646
323
|
}
|
|
647
324
|
return { ok: true };
|
|
648
325
|
}
|
|
649
|
-
function isFecCatalogGeometry(obj) {
|
|
650
|
-
if (typeof obj !== "object" || obj === null) return false;
|
|
651
|
-
const o = obj;
|
|
652
|
-
const algorithm = o["algorithm"];
|
|
653
|
-
const symbolSize = o["symbolSize"];
|
|
654
|
-
return typeof algorithm === "string" && algorithm.length > 0 && typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 && typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 && typeof symbolSize === "number" && symbolSize >= 1 && (algorithm !== "raptorq" || symbolSize % 8 === 0) && typeof o["repairTrack"] === "string" && o["repairTrack"].length > 0;
|
|
655
|
-
}
|
|
656
|
-
function isFecCatalogConfig(obj) {
|
|
657
|
-
if (!isFecCatalogGeometry(obj)) return false;
|
|
658
|
-
const o = obj;
|
|
659
|
-
return isFecBlockProfile(o["blockProfile"]) || o["algorithm"] === "none" && o["blockProfile"] === void 0;
|
|
660
|
-
}
|
|
661
326
|
function isMulticastTrackObject(obj) {
|
|
662
327
|
if (typeof obj !== "object" || obj === null) return false;
|
|
663
328
|
const o = obj;
|
|
@@ -818,7 +483,7 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
818
483
|
}
|
|
819
484
|
const tracks = catalog["tracks"];
|
|
820
485
|
const trackRecords = [];
|
|
821
|
-
const
|
|
486
|
+
const positiveInteger = (input) => Number.isSafeInteger(input) && input > 0;
|
|
822
487
|
const positiveNumber = (input) => typeof input === "number" && Number.isFinite(input) && input > 0;
|
|
823
488
|
for (let index = 0; index < tracks.length; index++) {
|
|
824
489
|
const raw = tracks[index];
|
|
@@ -850,7 +515,7 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
850
515
|
);
|
|
851
516
|
}
|
|
852
517
|
const codec = track["codec"];
|
|
853
|
-
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" && (typeof codec !== "string" || codec.length === 0)) {
|
|
518
|
+
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" && packaging !== "fec-source" && (typeof codec !== "string" || codec.length === 0)) {
|
|
854
519
|
catalogError("track_codec", `${path}.codec`, `codec is required for packaging=${packaging}`);
|
|
855
520
|
}
|
|
856
521
|
const timescale = track["timescale"];
|
|
@@ -858,13 +523,13 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
858
523
|
const groupDurationTicks = track["groupDurationTicks"];
|
|
859
524
|
const keyframeIntervalMs = track["keyframeIntervalMs"];
|
|
860
525
|
const keyframeIntervalTicks = track["keyframeIntervalTicks"];
|
|
861
|
-
if (groupDurationMs !== void 0 && !
|
|
526
|
+
if (groupDurationMs !== void 0 && !positiveInteger(groupDurationMs)) {
|
|
862
527
|
catalogError("track_group_duration_ms", `${path}.groupDurationMs`, "must be a positive integer");
|
|
863
528
|
}
|
|
864
|
-
if (groupDurationTicks !== void 0 && !
|
|
529
|
+
if (groupDurationTicks !== void 0 && !positiveInteger(groupDurationTicks)) {
|
|
865
530
|
catalogError("track_group_duration_ticks", `${path}.groupDurationTicks`, "must be a positive integer");
|
|
866
531
|
}
|
|
867
|
-
if (keyframeIntervalMs !== void 0 && !positiveNumber(keyframeIntervalMs) || keyframeIntervalTicks !== void 0 && !
|
|
532
|
+
if (keyframeIntervalMs !== void 0 && !positiveNumber(keyframeIntervalMs) || keyframeIntervalTicks !== void 0 && !positiveInteger(keyframeIntervalTicks)) {
|
|
868
533
|
catalogError("track_keyframe_interval", path, "keyframe interval fields must be positive numbers");
|
|
869
534
|
}
|
|
870
535
|
const anchor = track["presentationAnchorTicks"];
|
|
@@ -875,17 +540,11 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
875
540
|
if (initRef !== void 0 && (typeof initRef !== "string" || initRef.length === 0)) {
|
|
876
541
|
catalogError("track_init_ref", `${path}.initRef`, "must be a non-empty string");
|
|
877
542
|
}
|
|
878
|
-
if (packaging === "fec-repair") {
|
|
879
|
-
rejectRepairTrackGeometry(path, track, catalogError);
|
|
880
|
-
validateRepairTrackFields(describeRepairTrack(name, path, track), catalogError);
|
|
881
|
-
} else {
|
|
882
|
-
rejectRepairFieldsOnMediaTrack(path, track, catalogError);
|
|
883
|
-
}
|
|
884
543
|
if (packaging === "mmtp") {
|
|
885
544
|
if (track["mmtpMode"] !== "mfu") {
|
|
886
545
|
catalogError("track_mmtp_mode", `${path}.mmtpMode`, "must be mfu");
|
|
887
546
|
}
|
|
888
|
-
if (!
|
|
547
|
+
if (!positiveInteger(timescale)) {
|
|
889
548
|
catalogError("track_timescale", `${path}.timescale`, "must be a positive integer");
|
|
890
549
|
}
|
|
891
550
|
if (groupDurationMs === void 0 && groupDurationTicks === void 0) {
|
|
@@ -899,85 +558,6 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
899
558
|
catalogError("track_group_duration_exact", `${path}.groupDurationMs`, "does not resolve to an exact integer tick count");
|
|
900
559
|
}
|
|
901
560
|
}
|
|
902
|
-
if (track["fec"] !== void 0) {
|
|
903
|
-
const fec = track["fec"];
|
|
904
|
-
if (typeof fec !== "object" || fec === null || Array.isArray(fec)) {
|
|
905
|
-
catalogError("track_fec", `${path}.fec`, "FEC must be an object");
|
|
906
|
-
}
|
|
907
|
-
const config = fec;
|
|
908
|
-
const algorithm = config["algorithm"];
|
|
909
|
-
if (typeof algorithm !== "string" || algorithm.length === 0) {
|
|
910
|
-
catalogError("track_fec", `${path}.fec.algorithm`, "must be a non-empty string");
|
|
911
|
-
}
|
|
912
|
-
if ("interleaveDepth" in config || "interleaveMs" in config) {
|
|
913
|
-
catalogError("track_fec", `${path}.fec`, "interleaveDepthMs is the only supported interleave field");
|
|
914
|
-
}
|
|
915
|
-
if ("enhancementRepair" in config) {
|
|
916
|
-
catalogError(
|
|
917
|
-
"track_fec",
|
|
918
|
-
`${path}.fec.enhancementRepair`,
|
|
919
|
-
"repair layers are self-describing fec-repair tracks (depends, repairLayer); fec.enhancementRepair is not supported"
|
|
920
|
-
);
|
|
921
|
-
}
|
|
922
|
-
if (config["interleaveDepthMs"] !== void 0 && (!Number.isSafeInteger(config["interleaveDepthMs"]) || config["interleaveDepthMs"] < 0)) {
|
|
923
|
-
catalogError("track_fec", `${path}.fec.interleaveDepthMs`, "must be a non-negative safe integer");
|
|
924
|
-
}
|
|
925
|
-
if (algorithm !== "none") {
|
|
926
|
-
for (const field of ["sourceSymbols", "repairSymbols", "symbolSize"]) {
|
|
927
|
-
if (!positiveInteger2(config[field])) {
|
|
928
|
-
catalogError("track_fec", `${path}.fec.${field}`, "must be a positive integer");
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
if (algorithm === "raptorq" && config["symbolSize"] % 8 !== 0) {
|
|
932
|
-
catalogError("track_fec", `${path}.fec.symbolSize`, "RaptorQ symbolSize must be a multiple of 8");
|
|
933
|
-
}
|
|
934
|
-
if (typeof config["repairTrack"] !== "string" || config["repairTrack"].length === 0) {
|
|
935
|
-
catalogError("track_fec", `${path}.fec.repairTrack`, "must be a non-empty string");
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
readFecBlockProfile(config["blockProfile"], `${path}.fec.blockProfile`, algorithm !== "none", catalogError);
|
|
939
|
-
if (algorithm !== "none" && config["symbolSize"] < ISO_SSBG1_MIN_SYMBOL_SIZE) {
|
|
940
|
-
catalogError("track_fec", `${path}.fec.symbolSize`, "iso-ssbg1-v1 symbolSize must be at least 14");
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
const repairDescriptors = trackRecords.flatMap(
|
|
945
|
-
(track, index) => track["packaging"] === "fec-repair" ? [describeRepairTrack(track["name"], `$.tracks[${index}]`, track)] : []
|
|
946
|
-
);
|
|
947
|
-
const reachability = repairReachability(
|
|
948
|
-
trackRecords.filter((track) => track["packaging"] !== "fec-repair").map((track) => track["fec"])
|
|
949
|
-
);
|
|
950
|
-
for (const descriptor of repairDescriptors) {
|
|
951
|
-
assertRepairTrackReachable(descriptor, reachability, catalogError);
|
|
952
|
-
const target = dependsTarget(descriptor);
|
|
953
|
-
if (target === void 0) continue;
|
|
954
|
-
const source = trackRecords.find((candidate) => candidate["name"] === target);
|
|
955
|
-
const sourceFec = source?.["fec"];
|
|
956
|
-
if (!source || source["packaging"] !== "mmtp" || !sourceFec || sourceFec["algorithm"] === "none") {
|
|
957
|
-
catalogError(
|
|
958
|
-
"track_repair_depends",
|
|
959
|
-
`${descriptor.path}.depends`,
|
|
960
|
-
`${JSON.stringify(target)} is not an mmtp track with FEC`
|
|
961
|
-
);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
for (let index = 0; index < trackRecords.length; index++) {
|
|
965
|
-
const track = trackRecords[index];
|
|
966
|
-
const fec = track["fec"];
|
|
967
|
-
if (!fec || fec["algorithm"] === "none") continue;
|
|
968
|
-
assembleRepairLayers(
|
|
969
|
-
{
|
|
970
|
-
name: track["name"],
|
|
971
|
-
path: `$.tracks[${index}].fec`,
|
|
972
|
-
algorithm: fec["algorithm"],
|
|
973
|
-
sourceSymbols: fec["sourceSymbols"],
|
|
974
|
-
repairSymbols: fec["repairSymbols"],
|
|
975
|
-
repairTrack: fec["repairTrack"]
|
|
976
|
-
},
|
|
977
|
-
repairDescriptors,
|
|
978
|
-
catalogError,
|
|
979
|
-
{ layerZeroMustBeDeclared: true }
|
|
980
|
-
);
|
|
981
561
|
}
|
|
982
562
|
const switchingSets = /* @__PURE__ */ new Map();
|
|
983
563
|
for (let index = 0; index < trackRecords.length; index++) {
|
|
@@ -1106,8 +686,6 @@ export {
|
|
|
1106
686
|
CmsfCatalogValidationError,
|
|
1107
687
|
archiveOrigin,
|
|
1108
688
|
hasSwitchingSetRole,
|
|
1109
|
-
isFecCatalogConfig,
|
|
1110
|
-
isFecCatalogGeometry,
|
|
1111
689
|
isMulticastCatalogConfig,
|
|
1112
690
|
isMulticastEndpoint,
|
|
1113
691
|
isMulticastTrackObject,
|