@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/no-modules.js
CHANGED
|
@@ -414,34 +414,6 @@ function isoBlocksBehindLowerBound(a, b, kMax) {
|
|
|
414
414
|
return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0;
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
-
// ../mmt-base/dist/catalog-fec-source.js
|
|
418
|
-
var ALL_ROLES = Object.freeze(["video", "audio"]);
|
|
419
|
-
function recordOf(value) {
|
|
420
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
421
|
-
}
|
|
422
|
-
function isoBlockSpanUsable(fec) {
|
|
423
|
-
const block = recordOf(fec);
|
|
424
|
-
if (block === void 0 || block["blockProfile"] !== "iso-ssbg1-v1")
|
|
425
|
-
return true;
|
|
426
|
-
const ms = block["interleaveDepthMs"];
|
|
427
|
-
return ms === void 0 || typeof ms === "number" && Number.isSafeInteger(ms) && ms >= 0;
|
|
428
|
-
}
|
|
429
|
-
function fecAlgorithmUnimplemented(algorithm) {
|
|
430
|
-
return typeof algorithm === "string" && algorithm !== "none" && algorithm !== "raptorq" && algorithm !== "reed-solomon";
|
|
431
|
-
}
|
|
432
|
-
function isPositiveSafeInteger(value) {
|
|
433
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
434
|
-
}
|
|
435
|
-
var ISO_SSBG1_MIN_SYMBOL_SIZE = 14;
|
|
436
|
-
function catalogFecDeclaresUnconfigurableInstance(fec) {
|
|
437
|
-
const block = recordOf(fec);
|
|
438
|
-
if (block === void 0 || block["algorithm"] === "none" || fecAlgorithmUnimplemented(block["algorithm"]))
|
|
439
|
-
return false;
|
|
440
|
-
if (!(isPositiveSafeInteger(block["sourceSymbols"]) && isPositiveSafeInteger(block["symbolSize"])))
|
|
441
|
-
return true;
|
|
442
|
-
return block["blockProfile"] === "iso-ssbg1-v1" && block["symbolSize"] < ISO_SSBG1_MIN_SYMBOL_SIZE;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
417
|
// ../mmt-base/dist/multicast-catalog.js
|
|
446
418
|
var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
|
|
447
419
|
var UINT32_MAX = 4294967295;
|
|
@@ -690,6 +662,538 @@ function multicastCatalogIssues(catalog, options = {}) {
|
|
|
690
662
|
return issues;
|
|
691
663
|
}
|
|
692
664
|
|
|
665
|
+
// ../mmt-base/dist/fec-catalog.js
|
|
666
|
+
var DEFAULT_SCHEMES = ["reed-solomon", "raptorq"];
|
|
667
|
+
var DEFAULT_PROFILES = ["moq-object-v1", "iso-ssbg1-v1"];
|
|
668
|
+
var RS_MAX_BLOCK = 255;
|
|
669
|
+
var RAPTORQ_MAX_K = 56403;
|
|
670
|
+
var MAX_24 = 16777215;
|
|
671
|
+
var ISO_MIN_T = 14;
|
|
672
|
+
var GEOMETRY_KEYS = [
|
|
673
|
+
"scheme",
|
|
674
|
+
"blockProfile",
|
|
675
|
+
"mmtpMode",
|
|
676
|
+
"timescale",
|
|
677
|
+
"symbolSize",
|
|
678
|
+
"interleaveDepthMs",
|
|
679
|
+
"reorderToleranceMs",
|
|
680
|
+
"sourceSymbols",
|
|
681
|
+
"fec"
|
|
682
|
+
];
|
|
683
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
684
|
+
var has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
685
|
+
var isInt = (v) => typeof v === "number" && Number.isInteger(v);
|
|
686
|
+
var intIn = (v, lo, hi = Number.MAX_SAFE_INTEGER) => isInt(v) && v >= lo && v <= hi;
|
|
687
|
+
var profileAdmits = (profile, packaging) => profile !== "iso-ssbg1-v1" || packaging === "mmtp";
|
|
688
|
+
function validateFecCatalog(catalog, receiver = {}) {
|
|
689
|
+
const schemes = receiver.schemes ?? DEFAULT_SCHEMES;
|
|
690
|
+
const profiles = receiver.blockProfiles ?? DEFAULT_PROFILES;
|
|
691
|
+
const overlays = receiver.overlays ?? true;
|
|
692
|
+
const all = [];
|
|
693
|
+
const report = (code, path, inst) => {
|
|
694
|
+
const issue = { code, path };
|
|
695
|
+
all.push(issue);
|
|
696
|
+
inst?.issues.push(issue);
|
|
697
|
+
};
|
|
698
|
+
const tracks = isObj(catalog) && Array.isArray(catalog.tracks) ? catalog.tracks : [];
|
|
699
|
+
if (!isObj(catalog) || !Array.isArray(catalog.tracks))
|
|
700
|
+
report("fec_member", "/tracks");
|
|
701
|
+
let rejectAll = false;
|
|
702
|
+
tracks.forEach((t, ti) => {
|
|
703
|
+
if (!isObj(t))
|
|
704
|
+
return;
|
|
705
|
+
if (has(t, "enhancementRepair")) {
|
|
706
|
+
rejectAll = true;
|
|
707
|
+
report("fec_enhancement_repair", `/tracks/${ti}/enhancementRepair`);
|
|
708
|
+
}
|
|
709
|
+
if (Array.isArray(t.fec))
|
|
710
|
+
t.fec.forEach((o, ii) => {
|
|
711
|
+
if (isObj(o) && has(o, "enhancementRepair")) {
|
|
712
|
+
rejectAll = true;
|
|
713
|
+
report("fec_enhancement_repair", `/tracks/${ti}/fec/${ii}/enhancementRepair`);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
const key = (ns, name) => JSON.stringify([ns ?? null, name]);
|
|
718
|
+
const byName = /* @__PURE__ */ new Map();
|
|
719
|
+
tracks.forEach((t, ti) => {
|
|
720
|
+
if (isObj(t) && typeof t.name === "string") {
|
|
721
|
+
const k = key(t.namespace, t.name);
|
|
722
|
+
if (!byName.has(k))
|
|
723
|
+
byName.set(k, ti);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
const trackAt = (ti) => tracks[ti];
|
|
727
|
+
const insts = [];
|
|
728
|
+
tracks.forEach((t, ti) => {
|
|
729
|
+
if (!isObj(t) || !has(t, "fec") || t.packaging === "fec-repair" || t.packaging === "fec-source")
|
|
730
|
+
return;
|
|
731
|
+
if (!Array.isArray(t.fec)) {
|
|
732
|
+
report("fec_member", `/tracks/${ti}/fec`);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
t.fec.forEach((o, ii) => {
|
|
736
|
+
const path = `/tracks/${ti}/fec/${ii}`;
|
|
737
|
+
const inst = {
|
|
738
|
+
path,
|
|
739
|
+
ti,
|
|
740
|
+
track: t,
|
|
741
|
+
obj: isObj(o) ? o : null,
|
|
742
|
+
id: null,
|
|
743
|
+
unavailable: false,
|
|
744
|
+
overlay: false,
|
|
745
|
+
issues: [],
|
|
746
|
+
layer0: null,
|
|
747
|
+
symbolNamed: null,
|
|
748
|
+
attached: []
|
|
749
|
+
};
|
|
750
|
+
insts.push(inst);
|
|
751
|
+
if (!inst.obj)
|
|
752
|
+
return report("fec_member", path, inst);
|
|
753
|
+
const o2 = inst.obj;
|
|
754
|
+
if (!has(o2, "instanceId"))
|
|
755
|
+
inst.id = 0;
|
|
756
|
+
else if (intIn(o2.instanceId, 0, 255))
|
|
757
|
+
inst.id = o2.instanceId;
|
|
758
|
+
else
|
|
759
|
+
report("fec_member", `${path}/instanceId`, inst);
|
|
760
|
+
inst.overlay = has(o2, "scope");
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
const declaresFec = insts.length > 0;
|
|
764
|
+
for (const inst of insts) {
|
|
765
|
+
const o = inst.obj;
|
|
766
|
+
if (!o)
|
|
767
|
+
continue;
|
|
768
|
+
const avail = (field, impl) => {
|
|
769
|
+
const p = `${inst.path}/${field}`;
|
|
770
|
+
const absent = field === "scheme" ? "fec_scheme_absent" : "fec_block_profile_absent";
|
|
771
|
+
const unimpl = field === "scheme" ? "fec_scheme_unimplemented" : "fec_block_profile_unimplemented";
|
|
772
|
+
if (!has(o, field))
|
|
773
|
+
return report(absent, p, inst), false;
|
|
774
|
+
if (typeof o[field] !== "string")
|
|
775
|
+
return report("fec_member", p, inst), true;
|
|
776
|
+
if (!impl.includes(o[field]))
|
|
777
|
+
return report(unimpl, p, inst), false;
|
|
778
|
+
return true;
|
|
779
|
+
};
|
|
780
|
+
if (!avail("scheme", schemes) || !avail("blockProfile", profiles))
|
|
781
|
+
inst.unavailable = true;
|
|
782
|
+
else if (typeof o.blockProfile === "string" && !profileAdmits(o.blockProfile, inst.track.packaging)) {
|
|
783
|
+
report("fec_block_profile_packaging", `${inst.path}/blockProfile`, inst);
|
|
784
|
+
inst.unavailable = true;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
const live = (i) => !i.unavailable;
|
|
788
|
+
for (const inst of insts.filter(live)) {
|
|
789
|
+
const o = inst.obj;
|
|
790
|
+
if (!o)
|
|
791
|
+
continue;
|
|
792
|
+
const p = inst.path;
|
|
793
|
+
for (const m of ["sourceSymbols", "repairSymbols", "symbolSize"])
|
|
794
|
+
if (!intIn(o[m], 1))
|
|
795
|
+
report("fec_member", `${p}/${m}`, inst);
|
|
796
|
+
if (typeof o.repairTrack !== "string")
|
|
797
|
+
report("fec_member", `${p}/repairTrack`, inst);
|
|
798
|
+
if (has(o, "symbolTrack") && typeof o.symbolTrack !== "string")
|
|
799
|
+
report("fec_member", `${p}/symbolTrack`, inst);
|
|
800
|
+
if (has(o, "interleaveDepthMs")) {
|
|
801
|
+
const d = o.interleaveDepthMs;
|
|
802
|
+
if (!intIn(d, 0) || d === 0 && o.blockProfile !== "iso-ssbg1-v1")
|
|
803
|
+
report("fec_member", `${p}/interleaveDepthMs`, inst);
|
|
804
|
+
}
|
|
805
|
+
if (has(o, "reorderToleranceMs")) {
|
|
806
|
+
const r = o.reorderToleranceMs;
|
|
807
|
+
if (typeof r !== "number" || !Number.isFinite(r) || r < 0)
|
|
808
|
+
report("fec_member", `${p}/reorderToleranceMs`, inst);
|
|
809
|
+
}
|
|
810
|
+
if (has(o, "label") && typeof o.label !== "string")
|
|
811
|
+
report("fec_member", `${p}/label`, inst);
|
|
812
|
+
if (inst.overlay && o.scope !== "keyframe")
|
|
813
|
+
report("fec_member", `${p}/scope`, inst);
|
|
814
|
+
}
|
|
815
|
+
const idGroups = /* @__PURE__ */ new Map();
|
|
816
|
+
for (const inst of insts)
|
|
817
|
+
if (inst.id !== null) {
|
|
818
|
+
const k = `${inst.ti}:${inst.id}`;
|
|
819
|
+
idGroups.set(k, [...idGroups.get(k) ?? [], inst]);
|
|
820
|
+
}
|
|
821
|
+
for (const group of idGroups.values())
|
|
822
|
+
if (group.length > 1)
|
|
823
|
+
for (const inst of group.filter(live))
|
|
824
|
+
report("fec_instance_id", inst.obj && has(inst.obj, "instanceId") ? `${inst.path}/instanceId` : inst.path, inst);
|
|
825
|
+
const nameUses = /* @__PURE__ */ new Map();
|
|
826
|
+
for (const inst of insts)
|
|
827
|
+
for (const field of ["repairTrack", "symbolTrack"])
|
|
828
|
+
if (inst.obj && typeof inst.obj[field] === "string") {
|
|
829
|
+
const k = key(inst.track.namespace, inst.obj[field]);
|
|
830
|
+
nameUses.set(k, [...nameUses.get(k) ?? [], { inst, field }]);
|
|
831
|
+
}
|
|
832
|
+
const namedBy = /* @__PURE__ */ new Map();
|
|
833
|
+
for (const [k, uses] of nameUses) {
|
|
834
|
+
const tj = byName.get(k);
|
|
835
|
+
for (const { inst, field } of uses) {
|
|
836
|
+
const path = `${inst.path}/${field}`;
|
|
837
|
+
if (tj === void 0 || trackAt(tj).packaging !== (field === "repairTrack" ? "fec-repair" : "fec-source")) {
|
|
838
|
+
if (live(inst))
|
|
839
|
+
report("fec_naming", path, inst);
|
|
840
|
+
} else if (uses.length > 1) {
|
|
841
|
+
if (live(inst))
|
|
842
|
+
report("fec_naming", path, inst);
|
|
843
|
+
if (!namedBy.has(tj))
|
|
844
|
+
namedBy.set(tj, inst);
|
|
845
|
+
} else {
|
|
846
|
+
namedBy.set(tj, inst);
|
|
847
|
+
if (field === "repairTrack")
|
|
848
|
+
inst.layer0 = tj;
|
|
849
|
+
else
|
|
850
|
+
inst.symbolNamed = tj;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
const owner = /* @__PURE__ */ new Map();
|
|
855
|
+
if (declaresFec)
|
|
856
|
+
tracks.forEach((t, tj) => {
|
|
857
|
+
if (!isObj(t) || t.packaging !== "fec-repair" && t.packaging !== "fec-source")
|
|
858
|
+
return;
|
|
859
|
+
const tp = `/tracks/${tj}`;
|
|
860
|
+
const named = namedBy.get(tj);
|
|
861
|
+
if (named) {
|
|
862
|
+
owner.set(tj, named);
|
|
863
|
+
if (!live(named))
|
|
864
|
+
return;
|
|
865
|
+
if (has(t, "source") && t.source !== named.track.name)
|
|
866
|
+
report("fec_resolution", `${tp}/source`, named);
|
|
867
|
+
if (has(t, "instanceId") && t.instanceId !== named.id)
|
|
868
|
+
report("fec_resolution", `${tp}/instanceId`, named);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
if (!has(t, "source"))
|
|
872
|
+
return report("fec_orphan", tp);
|
|
873
|
+
const id = has(t, "instanceId") ? t.instanceId : 0;
|
|
874
|
+
const src = typeof t.source === "string" ? byName.get(key(t.namespace, t.source)) : void 0;
|
|
875
|
+
const target = src === void 0 ? void 0 : insts.find((i) => i.ti === src && i.id === id);
|
|
876
|
+
if (!target)
|
|
877
|
+
return report("fec_naming", has(t, "instanceId") && src !== void 0 ? `${tp}/instanceId` : `${tp}/source`);
|
|
878
|
+
owner.set(tj, target);
|
|
879
|
+
target.attached.push(tj);
|
|
880
|
+
});
|
|
881
|
+
for (const [tj, inst] of owner) {
|
|
882
|
+
if (!live(inst))
|
|
883
|
+
continue;
|
|
884
|
+
const t = trackAt(tj);
|
|
885
|
+
const tp = `/tracks/${tj}`;
|
|
886
|
+
const symbol = t.packaging === "fec-source";
|
|
887
|
+
if (has(t, "source") && typeof t.source !== "string")
|
|
888
|
+
report("fec_member", `${tp}/source`, inst);
|
|
889
|
+
if (has(t, "instanceId") && !intIn(t.instanceId, 0, 255))
|
|
890
|
+
report("fec_member", `${tp}/instanceId`, inst);
|
|
891
|
+
for (const m of ["repairLayer", "repairSymbols"]) {
|
|
892
|
+
if (!has(t, m))
|
|
893
|
+
continue;
|
|
894
|
+
if (symbol || !intIn(t[m], m === "repairLayer" ? 0 : 1))
|
|
895
|
+
report("fec_member", `${tp}/${m}`, inst);
|
|
896
|
+
}
|
|
897
|
+
if (has(t, "priority") && !intIn(t.priority, symbol ? 0 : 192, 255))
|
|
898
|
+
report("fec_member", `${tp}/priority`, inst);
|
|
899
|
+
for (const k of Object.keys(t))
|
|
900
|
+
if (GEOMETRY_KEYS.includes(k) || k.startsWith("groupDuration"))
|
|
901
|
+
report("fec_track_geometry", `${tp}/${k.replace(/~/g, "~0").replace(/\//g, "~1")}`, inst);
|
|
902
|
+
}
|
|
903
|
+
for (const inst of insts.filter(live)) {
|
|
904
|
+
const o = inst.obj;
|
|
905
|
+
if (!o)
|
|
906
|
+
continue;
|
|
907
|
+
const repairs = inst.attached.filter((tj) => trackAt(tj).packaging === "fec-repair");
|
|
908
|
+
const symbols = inst.attached.filter((tj) => trackAt(tj).packaging === "fec-source");
|
|
909
|
+
if (symbols.length + (inst.symbolNamed !== null ? 1 : 0) > 1)
|
|
910
|
+
for (const tj of symbols)
|
|
911
|
+
report("fec_symbol_tracks", `/tracks/${tj}`, inst);
|
|
912
|
+
const l0 = inst.layer0 === null ? null : trackAt(inst.layer0);
|
|
913
|
+
const l0p = `/tracks/${inst.layer0}`;
|
|
914
|
+
if (inst.overlay) {
|
|
915
|
+
if (l0 && has(l0, "repairLayer"))
|
|
916
|
+
report("fec_overlay", `${l0p}/repairLayer`, inst);
|
|
917
|
+
for (const tj of repairs)
|
|
918
|
+
report("fec_overlay", has(trackAt(tj), "repairLayer") ? `/tracks/${tj}/repairLayer` : `/tracks/${tj}`, inst);
|
|
919
|
+
} else {
|
|
920
|
+
if (l0 && has(l0, "repairLayer") && l0.repairLayer !== 0)
|
|
921
|
+
report("fec_layers", `${l0p}/repairLayer`, inst);
|
|
922
|
+
if (l0 && has(l0, "repairSymbols") && intIn(o.repairSymbols, 1) && l0.repairSymbols !== o.repairSymbols)
|
|
923
|
+
report("fec_layer0", `${l0p}/repairSymbols`, inst);
|
|
924
|
+
const layers = [];
|
|
925
|
+
for (const tj of repairs) {
|
|
926
|
+
const t = trackAt(tj);
|
|
927
|
+
if (!has(t, "repairLayer"))
|
|
928
|
+
report("fec_member", `/tracks/${tj}/repairLayer`, inst);
|
|
929
|
+
else if (t.repairLayer === 0)
|
|
930
|
+
report("fec_layers", `/tracks/${tj}/repairLayer`, inst);
|
|
931
|
+
else if (intIn(t.repairLayer, 1))
|
|
932
|
+
layers.push({ tj, layer: t.repairLayer });
|
|
933
|
+
if (!has(t, "repairSymbols"))
|
|
934
|
+
report("fec_member", `/tracks/${tj}/repairSymbols`, inst);
|
|
935
|
+
}
|
|
936
|
+
const counts = /* @__PURE__ */ new Map();
|
|
937
|
+
for (const { layer } of layers)
|
|
938
|
+
counts.set(layer, (counts.get(layer) ?? 0) + 1);
|
|
939
|
+
let missing = 1;
|
|
940
|
+
while (counts.has(missing))
|
|
941
|
+
missing++;
|
|
942
|
+
for (const { tj, layer } of layers)
|
|
943
|
+
if (counts.get(layer) > 1 || layer > missing)
|
|
944
|
+
report("fec_layers", `/tracks/${tj}/repairLayer`, inst);
|
|
945
|
+
const chain = [
|
|
946
|
+
{ layer: 0, tj: inst.layer0, pri: l0 && intIn(l0.priority, 192, 255) ? l0.priority : l0 && has(l0, "priority") ? null : 240 },
|
|
947
|
+
...layers.map(({ tj, layer }) => {
|
|
948
|
+
const pr = trackAt(tj).priority;
|
|
949
|
+
return { layer, tj, pri: intIn(pr, 192, 255) ? pr : null };
|
|
950
|
+
})
|
|
951
|
+
].sort((a, b) => a.layer - b.layer);
|
|
952
|
+
let prev = null;
|
|
953
|
+
for (const c of chain) {
|
|
954
|
+
if (c.pri === null)
|
|
955
|
+
continue;
|
|
956
|
+
if (prev !== null && c.pri < prev)
|
|
957
|
+
report("fec_priority", `/tracks/${c.tj}/priority`, inst);
|
|
958
|
+
prev = Math.max(prev ?? c.pri, c.pri);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
const K = o.sourceSymbols;
|
|
962
|
+
const T = o.symbolSize;
|
|
963
|
+
const P0 = o.repairSymbols;
|
|
964
|
+
if (intIn(K, 1) && intIn(P0, 1)) {
|
|
965
|
+
let P = P0;
|
|
966
|
+
if (!inst.overlay)
|
|
967
|
+
for (const tj of repairs) {
|
|
968
|
+
const t = trackAt(tj);
|
|
969
|
+
if (intIn(t.repairLayer, 1) && intIn(t.repairSymbols, 1))
|
|
970
|
+
P += t.repairSymbols;
|
|
971
|
+
}
|
|
972
|
+
if (P > MAX_24 || K + P - 1 > MAX_24)
|
|
973
|
+
report("fec_bounds", inst.path, inst);
|
|
974
|
+
else if (o.scheme === "reed-solomon" && K + P > RS_MAX_BLOCK)
|
|
975
|
+
report("fec_bounds", inst.path, inst);
|
|
976
|
+
}
|
|
977
|
+
if (o.scheme === "raptorq" && intIn(K, 1) && K > RAPTORQ_MAX_K)
|
|
978
|
+
report("fec_bounds", `${inst.path}/sourceSymbols`, inst);
|
|
979
|
+
if (intIn(T, 1)) {
|
|
980
|
+
if (o.scheme === "raptorq" && T % 8 !== 0)
|
|
981
|
+
report("fec_bounds", `${inst.path}/symbolSize`, inst);
|
|
982
|
+
else if (o.blockProfile === "iso-ssbg1-v1" && T < ISO_MIN_T)
|
|
983
|
+
report("fec_bounds", `${inst.path}/symbolSize`, inst);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
const overlayTracks = new Set(insts.filter((i) => live(i) && i.overlay).map((i) => i.ti));
|
|
987
|
+
for (const ti of overlayTracks) {
|
|
988
|
+
const ofTrack = insts.filter((i) => i.ti === ti && live(i));
|
|
989
|
+
const ovs = ofTrack.filter((i) => i.overlay);
|
|
990
|
+
if (!overlays) {
|
|
991
|
+
for (const ov of ovs)
|
|
992
|
+
report("fec_overlay_unsupported", `${ov.path}/scope`, ov);
|
|
993
|
+
for (const i of ofTrack)
|
|
994
|
+
i.issues.length === 0 && i.issues.push({ code: "fec_overlay_unsupported", path: `${ovs[0].path}/scope` });
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
if (ovs.length > 1)
|
|
998
|
+
for (const ov of ovs)
|
|
999
|
+
report("fec_overlay", `${ov.path}/scope`, ov);
|
|
1000
|
+
for (const ov of ovs)
|
|
1001
|
+
if (ov.obj?.blockProfile !== "iso-ssbg1-v1")
|
|
1002
|
+
report("fec_overlay", `${ov.path}/blockProfile`, ov);
|
|
1003
|
+
const cause = ovs.flatMap((ov) => ov.issues).find((x) => x.code === "fec_overlay");
|
|
1004
|
+
if (cause) {
|
|
1005
|
+
for (const ov of ovs)
|
|
1006
|
+
if (ov.issues.length === 0)
|
|
1007
|
+
ov.issues.push(cause);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const rule4 = all.find((x) => x.code === "fec_enhancement_repair");
|
|
1011
|
+
if (rejectAll && rule4) {
|
|
1012
|
+
for (const i of insts)
|
|
1013
|
+
if (i.issues.length === 0)
|
|
1014
|
+
i.issues.push(rule4);
|
|
1015
|
+
}
|
|
1016
|
+
return {
|
|
1017
|
+
instances: insts.map((i) => ({
|
|
1018
|
+
path: i.path,
|
|
1019
|
+
track: typeof i.track.name === "string" ? i.track.name : null,
|
|
1020
|
+
instanceId: i.id,
|
|
1021
|
+
outcome: rejectAll || !i.unavailable && i.issues.length > 0 ? "violation" : i.unavailable ? "unavailable" : "ok",
|
|
1022
|
+
issues: i.issues,
|
|
1023
|
+
trackIndex: i.ti,
|
|
1024
|
+
repairTrack: i.layer0,
|
|
1025
|
+
boundRepairTracks: i.attached.filter((tj) => trackAt(tj).packaging === "fec-repair"),
|
|
1026
|
+
symbolTrack: i.symbolNamed ?? i.attached.find((tj) => trackAt(tj).packaging === "fec-source") ?? null
|
|
1027
|
+
})),
|
|
1028
|
+
issues: all
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// ../mmt-base/dist/fec-bindings.js
|
|
1033
|
+
var RUNTIME_FEC_RECEIVER = Object.freeze({
|
|
1034
|
+
schemes: Object.freeze(["raptorq", "reed-solomon"]),
|
|
1035
|
+
blockProfiles: Object.freeze(["iso-ssbg1-v1"]),
|
|
1036
|
+
overlays: true
|
|
1037
|
+
});
|
|
1038
|
+
var DEFAULT_FEC_REPAIR_PRIORITY = 240;
|
|
1039
|
+
var recordOf = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
|
|
1040
|
+
var escape = (s) => s.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
1041
|
+
function fecCatalogTracks(catalog) {
|
|
1042
|
+
const root = recordOf(catalog) ?? {};
|
|
1043
|
+
const tracks = [];
|
|
1044
|
+
const pointers = [];
|
|
1045
|
+
const hangNames = /* @__PURE__ */ new Set();
|
|
1046
|
+
for (const role of ["video", "audio"]) {
|
|
1047
|
+
for (const [name, r] of Object.entries(recordOf(recordOf(root[role])?.["renditions"]) ?? {})) {
|
|
1048
|
+
const entry = recordOf(r);
|
|
1049
|
+
if (!entry)
|
|
1050
|
+
continue;
|
|
1051
|
+
hangNames.add(name);
|
|
1052
|
+
const kind = recordOf(entry["container"])?.["kind"];
|
|
1053
|
+
const view2 = { name, packaging: kind === void 0 || kind === "mmtp-fec" ? "mmtp" : kind };
|
|
1054
|
+
if (entry["fec"] !== void 0)
|
|
1055
|
+
view2["fec"] = entry["fec"];
|
|
1056
|
+
tracks.push(view2);
|
|
1057
|
+
pointers.push(`/${role}/renditions/${escape(name)}`);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const hangRenditions = tracks.length;
|
|
1061
|
+
for (const [name, r] of Object.entries(recordOf(recordOf(root["repair"])?.["renditions"]) ?? {})) {
|
|
1062
|
+
hangNames.add(name);
|
|
1063
|
+
tracks.push({ packaging: "fec-repair", ...recordOf(r), name });
|
|
1064
|
+
pointers.push(`/repair/renditions/${escape(name)}`);
|
|
1065
|
+
}
|
|
1066
|
+
const raw = Array.isArray(root["tracks"]) ? root["tracks"] : [];
|
|
1067
|
+
const named = new Set(hangNames);
|
|
1068
|
+
raw.forEach((t, i) => {
|
|
1069
|
+
const name = recordOf(t)?.["name"];
|
|
1070
|
+
if (typeof name === "string")
|
|
1071
|
+
named.add(name);
|
|
1072
|
+
if (typeof name === "string" && hangNames.has(name))
|
|
1073
|
+
return;
|
|
1074
|
+
tracks.push(t);
|
|
1075
|
+
pointers.push(`/tracks/${i}`);
|
|
1076
|
+
});
|
|
1077
|
+
tracks.slice(0, hangRenditions).forEach((t, ti) => {
|
|
1078
|
+
const view2 = t;
|
|
1079
|
+
if (!Array.isArray(view2["fec"]))
|
|
1080
|
+
return;
|
|
1081
|
+
view2["fec"].forEach((o, ii) => {
|
|
1082
|
+
const repairTrack = recordOf(o)?.["repairTrack"];
|
|
1083
|
+
if (typeof repairTrack !== "string" || named.has(repairTrack))
|
|
1084
|
+
return;
|
|
1085
|
+
named.add(repairTrack);
|
|
1086
|
+
const instanceId = recordOf(o)?.["instanceId"];
|
|
1087
|
+
tracks.push({ name: repairTrack, packaging: "fec-repair", source: view2["name"], ...instanceId === void 0 ? {} : { instanceId } });
|
|
1088
|
+
pointers.push(`${pointers[ti]}/fec/${ii}/repairTrack`);
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
1091
|
+
return { tracks, pointers };
|
|
1092
|
+
}
|
|
1093
|
+
function resolveFecBindings(catalog, receiver = RUNTIME_FEC_RECEIVER) {
|
|
1094
|
+
const { tracks, pointers } = fecCatalogTracks(catalog);
|
|
1095
|
+
const report = validateFecCatalog({ tracks }, receiver);
|
|
1096
|
+
const at = (path) => {
|
|
1097
|
+
const m = /^\/tracks\/(\d+)(.*)$/.exec(path);
|
|
1098
|
+
return m ? pointers[Number(m[1])] + m[2] : path;
|
|
1099
|
+
};
|
|
1100
|
+
const trackAt = (i) => recordOf(tracks[i]) ?? {};
|
|
1101
|
+
const nameAt = (i) => i === null ? void 0 : trackAt(i)["name"];
|
|
1102
|
+
const why = (i) => Object.freeze({
|
|
1103
|
+
code: i.issues[0]?.code ?? "fec_member",
|
|
1104
|
+
path: at(i.issues[0]?.path ?? i.path),
|
|
1105
|
+
outcome: i.outcome === "unavailable" ? "unavailable" : "violation"
|
|
1106
|
+
});
|
|
1107
|
+
const out = /* @__PURE__ */ new Map();
|
|
1108
|
+
tracks.forEach((t, ti) => {
|
|
1109
|
+
const track = recordOf(t);
|
|
1110
|
+
const name = track?.["name"];
|
|
1111
|
+
if (!track || typeof name !== "string" || out.has(name))
|
|
1112
|
+
return;
|
|
1113
|
+
if (track["packaging"] === "fec-repair" || track["packaging"] === "fec-source")
|
|
1114
|
+
return;
|
|
1115
|
+
const fec = track["fec"];
|
|
1116
|
+
if (fec === void 0)
|
|
1117
|
+
return;
|
|
1118
|
+
if (!Array.isArray(fec)) {
|
|
1119
|
+
const reason = { code: "fec_member", path: at(`/tracks/${ti}/fec`), outcome: "violation" };
|
|
1120
|
+
out.set(name, Object.freeze({ status: "unavailable", track: name, pointer: pointers[ti], reason: Object.freeze(reason) }));
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (fec.length === 0)
|
|
1124
|
+
return;
|
|
1125
|
+
const insts = report.instances.filter((i) => i.trackIndex === ti);
|
|
1126
|
+
const isOverlay = (i) => Object.hasOwn(recordOf(fec[Number(i.path.split("/").pop())]) ?? {}, "scope");
|
|
1127
|
+
const bases = insts.filter((i) => !isOverlay(i));
|
|
1128
|
+
const okBases = bases.filter((i) => i.outcome === "ok");
|
|
1129
|
+
if (okBases.length !== 1) {
|
|
1130
|
+
const reason = (
|
|
1131
|
+
// No base instance at all (overlay-only `fec`) is a count of 0, not
|
|
1132
|
+
// the overlay's own issue.
|
|
1133
|
+
okBases.length > 1 || bases.length === 0 ? Object.freeze({ code: "fec_base_instances", path: at(`/tracks/${ti}/fec`), outcome: "violation" }) : why(bases[0] ?? insts[0])
|
|
1134
|
+
);
|
|
1135
|
+
out.set(name, Object.freeze({ status: "unavailable", track: name, pointer: pointers[ti], reason }));
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
const b = okBases[0];
|
|
1139
|
+
const base = geometry(recordOf(fec[Number(b.path.split("/").pop())]), b.instanceId, nameAt(b.symbolTrack));
|
|
1140
|
+
const priorityOf = (i) => {
|
|
1141
|
+
const p = i === null ? void 0 : trackAt(i)["priority"];
|
|
1142
|
+
return typeof p === "number" ? p : DEFAULT_FEC_REPAIR_PRIORITY;
|
|
1143
|
+
};
|
|
1144
|
+
const layers = [
|
|
1145
|
+
{ layer: 0, repairTrackName: base.repairTrack, repairSymbols: base.repairSymbols, rsIdOffset: 0, priority: priorityOf(b.repairTrack) }
|
|
1146
|
+
];
|
|
1147
|
+
const more = b.boundRepairTracks.map((tj) => ({ tj, layer: trackAt(tj)["repairLayer"] })).sort((x, y) => x.layer - y.layer);
|
|
1148
|
+
let offset = base.repairSymbols;
|
|
1149
|
+
for (const { tj, layer } of more) {
|
|
1150
|
+
const repairSymbols = trackAt(tj)["repairSymbols"];
|
|
1151
|
+
layers.push({ layer, repairTrackName: nameAt(tj), repairSymbols, rsIdOffset: offset, priority: priorityOf(tj) });
|
|
1152
|
+
offset += repairSymbols;
|
|
1153
|
+
}
|
|
1154
|
+
let overlay;
|
|
1155
|
+
let overlayUnavailable;
|
|
1156
|
+
const ov = insts.find(isOverlay);
|
|
1157
|
+
if (ov?.outcome === "ok") {
|
|
1158
|
+
const g = geometry(recordOf(fec[Number(ov.path.split("/").pop())]), ov.instanceId, nameAt(ov.symbolTrack));
|
|
1159
|
+
overlay = Object.freeze({
|
|
1160
|
+
repairTrackName: g.repairTrack,
|
|
1161
|
+
scope: "keyframe",
|
|
1162
|
+
sourceSymbols: g.sourceSymbols,
|
|
1163
|
+
repairSymbols: g.repairSymbols,
|
|
1164
|
+
priority: priorityOf(ov.repairTrack),
|
|
1165
|
+
geometry: g
|
|
1166
|
+
});
|
|
1167
|
+
} else if (ov)
|
|
1168
|
+
overlayUnavailable = why(ov);
|
|
1169
|
+
out.set(name, Object.freeze({
|
|
1170
|
+
status: "ok",
|
|
1171
|
+
track: name,
|
|
1172
|
+
pointer: pointers[ti],
|
|
1173
|
+
base,
|
|
1174
|
+
layers: Object.freeze(layers.map((l) => Object.freeze(l))),
|
|
1175
|
+
totalBaseRepairSymbols: offset,
|
|
1176
|
+
...overlay ? { overlay } : {},
|
|
1177
|
+
...overlayUnavailable ? { overlayUnavailable } : {}
|
|
1178
|
+
}));
|
|
1179
|
+
});
|
|
1180
|
+
return out;
|
|
1181
|
+
}
|
|
1182
|
+
function geometry(o, instanceId, symbolTrack) {
|
|
1183
|
+
return Object.freeze({
|
|
1184
|
+
instanceId,
|
|
1185
|
+
scheme: o["scheme"],
|
|
1186
|
+
blockProfile: o["blockProfile"],
|
|
1187
|
+
sourceSymbols: o["sourceSymbols"],
|
|
1188
|
+
repairSymbols: o["repairSymbols"],
|
|
1189
|
+
symbolSize: o["symbolSize"],
|
|
1190
|
+
...typeof o["interleaveDepthMs"] === "number" ? { interleaveDepthMs: o["interleaveDepthMs"] } : {},
|
|
1191
|
+
...typeof o["reorderToleranceMs"] === "number" ? { reorderToleranceMs: o["reorderToleranceMs"] } : {},
|
|
1192
|
+
repairTrack: o["repairTrack"],
|
|
1193
|
+
...symbolTrack !== void 0 ? { symbolTrack } : {}
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
|
|
693
1197
|
// src/mfu-reassembler.ts
|
|
694
1198
|
function fragmentProvenance(fragment) {
|
|
695
1199
|
if (fragment.deliveryProvenance !== void 0) return fragment.deliveryProvenance;
|
|
@@ -7365,321 +7869,6 @@ function createWasmMmtpContainerParser(config) {
|
|
|
7365
7869
|
});
|
|
7366
7870
|
}
|
|
7367
7871
|
|
|
7368
|
-
// src/repair-layers.ts
|
|
7369
|
-
var REPAIR_PRIORITY_MIN = 192;
|
|
7370
|
-
var REPAIR_PRIORITY_MAX = 255;
|
|
7371
|
-
var DEFAULT_REPAIR_PRIORITY = 240;
|
|
7372
|
-
var MAX_RAPTORQ_SOURCE_SYMBOLS = 56403;
|
|
7373
|
-
var MAX_REPAIR_SYMBOLS = 16777215;
|
|
7374
|
-
var MAX_ENCODING_SYMBOL_ID = 16777215;
|
|
7375
|
-
var MAX_REED_SOLOMON_BLOCK_SYMBOLS = 255;
|
|
7376
|
-
var RepairContractError = class extends Error {
|
|
7377
|
-
constructor(code, path, message) {
|
|
7378
|
-
super(`[repair-layers:${code}] ${path}: ${message}`);
|
|
7379
|
-
this.code = code;
|
|
7380
|
-
this.path = path;
|
|
7381
|
-
this.name = "RepairContractError";
|
|
7382
|
-
}
|
|
7383
|
-
};
|
|
7384
|
-
var throwRepairContractError = (code, path, message) => {
|
|
7385
|
-
throw new RepairContractError(code, path, message);
|
|
7386
|
-
};
|
|
7387
|
-
function readFecBlockProfile(value, path, declaresFecInstance, fail) {
|
|
7388
|
-
if (value === void 0 && !declaresFecInstance) return void 0;
|
|
7389
|
-
if (isFecBlockProfile(value)) return value;
|
|
7390
|
-
if (typeof value === "string" && value.length > 0) {
|
|
7391
|
-
return fail(
|
|
7392
|
-
"track_fec_block_profile_unimplemented",
|
|
7393
|
-
path,
|
|
7394
|
-
`${JSON.stringify(value)} is not an implemented FEC block profile; only "iso-ssbg1-v1" is implemented`
|
|
7395
|
-
);
|
|
7396
|
-
}
|
|
7397
|
-
try {
|
|
7398
|
-
return parseFecBlockProfile(value, "blockProfile");
|
|
7399
|
-
} catch (error) {
|
|
7400
|
-
return fail("track_fec_block_profile", path, error.message);
|
|
7401
|
-
}
|
|
7402
|
-
}
|
|
7403
|
-
var REPAIR_FIELDS = ["depends", "repairLayer", "repairSymbols", "priority", "scope", "sourceSymbols"];
|
|
7404
|
-
var REPAIR_TRACK_GEOMETRY_KEYS = [
|
|
7405
|
-
"mmtpMode",
|
|
7406
|
-
"timescale",
|
|
7407
|
-
"groupDurationMs",
|
|
7408
|
-
"groupDurationTicks",
|
|
7409
|
-
"keyframeIntervalMs",
|
|
7410
|
-
"keyframeIntervalTicks",
|
|
7411
|
-
"fec",
|
|
7412
|
-
"symbolSize",
|
|
7413
|
-
"interleaveDepthMs"
|
|
7414
|
-
];
|
|
7415
|
-
var REPAIR_TRACK_ONLY_KEYS = ["repairLayer", "repairSymbols", "sourceSymbols", "scope"];
|
|
7416
|
-
function rejectRepairTrackGeometry(path, fields, fail) {
|
|
7417
|
-
for (const key of REPAIR_TRACK_GEOMETRY_KEYS) {
|
|
7418
|
-
if (fields[key] !== void 0) {
|
|
7419
|
-
fail(
|
|
7420
|
-
"track_repair_geometry",
|
|
7421
|
-
`${path}.${key}`,
|
|
7422
|
-
"a repair track carries no block geometry; it is shared with the source track's fec object"
|
|
7423
|
-
);
|
|
7424
|
-
}
|
|
7425
|
-
}
|
|
7426
|
-
}
|
|
7427
|
-
function repairReachability(fecObjects) {
|
|
7428
|
-
let declaresFecSource = false;
|
|
7429
|
-
const layerZeroNames = /* @__PURE__ */ new Set();
|
|
7430
|
-
for (const candidate of fecObjects) {
|
|
7431
|
-
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) continue;
|
|
7432
|
-
const fec = candidate;
|
|
7433
|
-
if (fec["algorithm"] === "none") continue;
|
|
7434
|
-
declaresFecSource = true;
|
|
7435
|
-
const repairTrack = fec["repairTrack"];
|
|
7436
|
-
if (typeof repairTrack === "string" && repairTrack.length > 0) layerZeroNames.add(repairTrack);
|
|
7437
|
-
}
|
|
7438
|
-
return { declaresFecSource, layerZeroNames };
|
|
7439
|
-
}
|
|
7440
|
-
function assertRepairTrackReachable(descriptor, reachability, fail) {
|
|
7441
|
-
if (dependsTarget(descriptor) !== void 0) return;
|
|
7442
|
-
if (reachability.declaresFecSource && !reachability.layerZeroNames.has(descriptor.name)) {
|
|
7443
|
-
fail(
|
|
7444
|
-
"track_repair_depends",
|
|
7445
|
-
`${descriptor.path}.depends`,
|
|
7446
|
-
"no source names this repair track in fec.repairTrack and it declares no depends"
|
|
7447
|
-
);
|
|
7448
|
-
}
|
|
7449
|
-
}
|
|
7450
|
-
function rejectRepairFieldsOnMediaTrack(path, fields, fail) {
|
|
7451
|
-
for (const key of REPAIR_TRACK_ONLY_KEYS) {
|
|
7452
|
-
if (fields[key] !== void 0) {
|
|
7453
|
-
fail("track_repair_layer", `${path}.${key}`, `${key} is a repair track field`);
|
|
7454
|
-
}
|
|
7455
|
-
}
|
|
7456
|
-
}
|
|
7457
|
-
function describeRepairTrack(name, path, fields) {
|
|
7458
|
-
const descriptor = { name, path };
|
|
7459
|
-
for (const field of REPAIR_FIELDS) {
|
|
7460
|
-
if (field in fields) descriptor[field] = fields[field];
|
|
7461
|
-
}
|
|
7462
|
-
return descriptor;
|
|
7463
|
-
}
|
|
7464
|
-
function isSafeInteger(value) {
|
|
7465
|
-
return typeof value === "number" && Number.isSafeInteger(value);
|
|
7466
|
-
}
|
|
7467
|
-
function positiveInteger(value) {
|
|
7468
|
-
return isSafeInteger(value) && value > 0;
|
|
7469
|
-
}
|
|
7470
|
-
function dependsTarget(descriptor) {
|
|
7471
|
-
const depends = descriptor.depends;
|
|
7472
|
-
return Array.isArray(depends) && depends.length === 1 && typeof depends[0] === "string" && depends[0].length > 0 ? depends[0] : void 0;
|
|
7473
|
-
}
|
|
7474
|
-
function validateRepairTrackFields(descriptor, fail) {
|
|
7475
|
-
const { path } = descriptor;
|
|
7476
|
-
if (descriptor.depends !== void 0 && dependsTarget(descriptor) === void 0) {
|
|
7477
|
-
fail("track_repair_depends", `${path}.depends`, "must name exactly one source track");
|
|
7478
|
-
}
|
|
7479
|
-
if (descriptor.repairLayer !== void 0 && (!isSafeInteger(descriptor.repairLayer) || descriptor.repairLayer < 0)) {
|
|
7480
|
-
fail("track_repair_layer", `${path}.repairLayer`, "must be a non-negative integer");
|
|
7481
|
-
}
|
|
7482
|
-
if (descriptor.repairSymbols !== void 0 && (!positiveInteger(descriptor.repairSymbols) || descriptor.repairSymbols > MAX_REPAIR_SYMBOLS)) {
|
|
7483
|
-
fail("track_repair_layer", `${path}.repairSymbols`, `must be a positive integer at most ${MAX_REPAIR_SYMBOLS}`);
|
|
7484
|
-
}
|
|
7485
|
-
if (descriptor.priority !== void 0 && (!isSafeInteger(descriptor.priority) || descriptor.priority < REPAIR_PRIORITY_MIN || descriptor.priority > REPAIR_PRIORITY_MAX)) {
|
|
7486
|
-
fail(
|
|
7487
|
-
"track_repair_priority",
|
|
7488
|
-
`${path}.priority`,
|
|
7489
|
-
`must be an integer from ${REPAIR_PRIORITY_MIN} to ${REPAIR_PRIORITY_MAX} (MoQT scale, lower first)`
|
|
7490
|
-
);
|
|
7491
|
-
}
|
|
7492
|
-
if (descriptor.scope !== void 0) {
|
|
7493
|
-
if (descriptor.scope !== "keyframe") {
|
|
7494
|
-
fail("track_repair_overlay", `${path}.scope`, 'must be "keyframe"');
|
|
7495
|
-
}
|
|
7496
|
-
if (descriptor.repairLayer !== void 0) {
|
|
7497
|
-
fail("track_repair_overlay", `${path}.repairLayer`, "an overlay is a separate FEC instance and carries no repairLayer");
|
|
7498
|
-
}
|
|
7499
|
-
for (const field of ["depends", "sourceSymbols", "repairSymbols", "priority"]) {
|
|
7500
|
-
if (descriptor[field] === void 0) {
|
|
7501
|
-
fail("track_repair_overlay", `${path}.${field}`, `an overlay must declare ${field}`);
|
|
7502
|
-
}
|
|
7503
|
-
}
|
|
7504
|
-
}
|
|
7505
|
-
if (descriptor.sourceSymbols !== void 0) {
|
|
7506
|
-
if (descriptor.scope === void 0) {
|
|
7507
|
-
fail("track_repair_overlay", `${path}.sourceSymbols`, "only an overlay (scope: keyframe) declares its own sourceSymbols");
|
|
7508
|
-
}
|
|
7509
|
-
if (!positiveInteger(descriptor.sourceSymbols) || descriptor.sourceSymbols > MAX_RAPTORQ_SOURCE_SYMBOLS) {
|
|
7510
|
-
fail(
|
|
7511
|
-
"track_fec_bounds",
|
|
7512
|
-
`${path}.sourceSymbols`,
|
|
7513
|
-
`must be a positive integer at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`
|
|
7514
|
-
);
|
|
7515
|
-
}
|
|
7516
|
-
}
|
|
7517
|
-
if (isSafeInteger(descriptor.repairLayer) && descriptor.repairLayer >= 1) {
|
|
7518
|
-
for (const field of ["depends", "repairSymbols", "priority"]) {
|
|
7519
|
-
if (descriptor[field] === void 0) {
|
|
7520
|
-
fail("track_repair_layer", `${path}.${field}`, `repair layer ${descriptor.repairLayer} must declare ${field}`);
|
|
7521
|
-
}
|
|
7522
|
-
}
|
|
7523
|
-
}
|
|
7524
|
-
}
|
|
7525
|
-
function assembleRepairLayers(source, descriptors, fail = throwRepairContractError, options = {}) {
|
|
7526
|
-
const layerZeroName = typeof source.repairTrack === "string" && source.repairTrack.length > 0 ? source.repairTrack : void 0;
|
|
7527
|
-
const layerZero = layerZeroName === void 0 ? void 0 : descriptors.find((d) => d.name === layerZeroName);
|
|
7528
|
-
if (layerZeroName !== void 0 && layerZero === void 0 && options.layerZeroMustBeDeclared) {
|
|
7529
|
-
fail("track_repair_track", `${source.path}.repairTrack`, `unknown FEC repair track ${JSON.stringify(layerZeroName)}`);
|
|
7530
|
-
}
|
|
7531
|
-
const dependents = descriptors.filter((d) => d.name !== layerZeroName && dependsTarget(d) === source.name);
|
|
7532
|
-
const k = positiveInteger(source.sourceSymbols) ? source.sourceSymbols : 0;
|
|
7533
|
-
if (source.algorithm === "raptorq" && k > MAX_RAPTORQ_SOURCE_SYMBOLS) {
|
|
7534
|
-
fail(
|
|
7535
|
-
"track_fec_bounds",
|
|
7536
|
-
`${source.path}.sourceSymbols`,
|
|
7537
|
-
`must be at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`
|
|
7538
|
-
);
|
|
7539
|
-
}
|
|
7540
|
-
if (layerZeroName === void 0) {
|
|
7541
|
-
if (dependents.length === 0) return void 0;
|
|
7542
|
-
fail(
|
|
7543
|
-
"track_repair_layer",
|
|
7544
|
-
`${dependents[0].path}.depends`,
|
|
7545
|
-
`repair layers of ${JSON.stringify(source.name)} require a layer 0 (fec.repairTrack) on the source`
|
|
7546
|
-
);
|
|
7547
|
-
}
|
|
7548
|
-
const layered = dependents.length > 0;
|
|
7549
|
-
let layerZeroSymbols = 0;
|
|
7550
|
-
if (positiveInteger(source.repairSymbols)) {
|
|
7551
|
-
layerZeroSymbols = source.repairSymbols;
|
|
7552
|
-
} else if (layered) {
|
|
7553
|
-
fail("track_fec", `${source.path}.repairSymbols`, "layered or overlay repair needs a positive integer repairSymbols on layer 0");
|
|
7554
|
-
}
|
|
7555
|
-
if (layered && !positiveInteger(source.sourceSymbols)) {
|
|
7556
|
-
fail("track_fec", `${source.path}.sourceSymbols`, "layered or overlay repair needs a positive integer sourceSymbols on the source");
|
|
7557
|
-
}
|
|
7558
|
-
const reedSolomon = source.algorithm === "reed-solomon";
|
|
7559
|
-
if (layered && source.algorithm !== "raptorq" && !reedSolomon) {
|
|
7560
|
-
fail(
|
|
7561
|
-
"track_fec",
|
|
7562
|
-
`${source.path}.algorithm`,
|
|
7563
|
-
"repair layers and overlays are defined only for raptorq and reed-solomon (draft-ramadan-moq-fec-01 6.3.4 rule 7)"
|
|
7564
|
-
);
|
|
7565
|
-
}
|
|
7566
|
-
let layerZeroPriority = DEFAULT_REPAIR_PRIORITY;
|
|
7567
|
-
if (layerZero !== void 0) {
|
|
7568
|
-
if (layerZero.depends !== void 0 && dependsTarget(layerZero) !== source.name) {
|
|
7569
|
-
fail(
|
|
7570
|
-
"track_repair_depends",
|
|
7571
|
-
`${layerZero.path}.depends`,
|
|
7572
|
-
`layer 0 of ${JSON.stringify(source.name)} must depend on that source`
|
|
7573
|
-
);
|
|
7574
|
-
}
|
|
7575
|
-
if (layerZero.repairLayer !== void 0 && layerZero.repairLayer !== 0) {
|
|
7576
|
-
fail("track_repair_layer", `${layerZero.path}.repairLayer`, "the track named by fec.repairTrack is layer 0");
|
|
7577
|
-
}
|
|
7578
|
-
if (layerZero.scope !== void 0) {
|
|
7579
|
-
fail("track_repair_overlay", `${layerZero.path}.scope`, "the track named by fec.repairTrack is base layer 0, not an overlay");
|
|
7580
|
-
}
|
|
7581
|
-
if (layerZero.repairSymbols !== void 0 && layerZero.repairSymbols !== layerZeroSymbols) {
|
|
7582
|
-
fail(
|
|
7583
|
-
"track_repair_layer",
|
|
7584
|
-
`${layerZero.path}.repairSymbols`,
|
|
7585
|
-
`must agree with the source's fec.repairSymbols (${layerZeroSymbols})`
|
|
7586
|
-
);
|
|
7587
|
-
}
|
|
7588
|
-
if (typeof layerZero.priority === "number") layerZeroPriority = layerZero.priority;
|
|
7589
|
-
}
|
|
7590
|
-
const baseLayers = [
|
|
7591
|
-
{ layer: 0, repairTrackName: layerZeroName, repairSymbols: layerZeroSymbols, rsIdOffset: 0, priority: layerZeroPriority }
|
|
7592
|
-
];
|
|
7593
|
-
const overlays = [];
|
|
7594
|
-
for (const d of dependents) {
|
|
7595
|
-
if (d.scope === void 0 && d.repairLayer === void 0) {
|
|
7596
|
-
fail(
|
|
7597
|
-
"track_repair_layer",
|
|
7598
|
-
`${d.path}.repairLayer`,
|
|
7599
|
-
`a repair track that depends on ${JSON.stringify(source.name)} must declare repairLayer (1..n) or scope: "keyframe"`
|
|
7600
|
-
);
|
|
7601
|
-
}
|
|
7602
|
-
}
|
|
7603
|
-
const layerTracks = dependents.filter((d) => d.scope === void 0).sort((a, b) => a.repairLayer - b.repairLayer);
|
|
7604
|
-
for (const d of dependents) {
|
|
7605
|
-
if (d.scope !== "keyframe") continue;
|
|
7606
|
-
if (overlays.length === 1) {
|
|
7607
|
-
fail(
|
|
7608
|
-
"track_repair_overlay",
|
|
7609
|
-
`${d.path}.scope`,
|
|
7610
|
-
`${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`
|
|
7611
|
-
);
|
|
7612
|
-
}
|
|
7613
|
-
const sourceSymbols = d.sourceSymbols;
|
|
7614
|
-
const repairSymbols = d.repairSymbols;
|
|
7615
|
-
if (sourceSymbols + repairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {
|
|
7616
|
-
fail(
|
|
7617
|
-
"track_fec_bounds",
|
|
7618
|
-
`${d.path}.repairSymbols`,
|
|
7619
|
-
`overlay sourceSymbols + repairSymbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`
|
|
7620
|
-
);
|
|
7621
|
-
}
|
|
7622
|
-
if (reedSolomon && sourceSymbols + repairSymbols > MAX_REED_SOLOMON_BLOCK_SYMBOLS) {
|
|
7623
|
-
fail(
|
|
7624
|
-
"track_fec_bounds",
|
|
7625
|
-
`${d.path}.repairSymbols`,
|
|
7626
|
-
`reed-solomon overlay sourceSymbols + repairSymbols exceeds ${MAX_REED_SOLOMON_BLOCK_SYMBOLS} (draft-ramadan-moq-fec-01 6.3.4 rule 12)`
|
|
7627
|
-
);
|
|
7628
|
-
}
|
|
7629
|
-
overlays.push({ repairTrackName: d.name, scope: "keyframe", sourceSymbols, repairSymbols, priority: d.priority });
|
|
7630
|
-
}
|
|
7631
|
-
let offset = layerZeroSymbols;
|
|
7632
|
-
let previousPriority = layerZeroPriority;
|
|
7633
|
-
layerTracks.forEach((d, i) => {
|
|
7634
|
-
const expected = i + 1;
|
|
7635
|
-
if (d.repairLayer === 0) {
|
|
7636
|
-
fail("track_repair_layer", `${d.path}.repairLayer`, `layer 0 of ${JSON.stringify(source.name)} is ${JSON.stringify(layerZeroName)}, named by fec.repairTrack`);
|
|
7637
|
-
}
|
|
7638
|
-
if (d.repairLayer !== expected) {
|
|
7639
|
-
fail(
|
|
7640
|
-
"track_repair_layer",
|
|
7641
|
-
`${d.path}.repairLayer`,
|
|
7642
|
-
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}`
|
|
7643
|
-
);
|
|
7644
|
-
}
|
|
7645
|
-
const priority = d.priority;
|
|
7646
|
-
if (priority < previousPriority) {
|
|
7647
|
-
fail(
|
|
7648
|
-
"track_repair_priority",
|
|
7649
|
-
`${d.path}.priority`,
|
|
7650
|
-
`must be at least ${previousPriority}, the priority of layer ${expected - 1}: priority is monotonic non-decreasing with layer index`
|
|
7651
|
-
);
|
|
7652
|
-
}
|
|
7653
|
-
previousPriority = priority;
|
|
7654
|
-
const repairSymbols = d.repairSymbols;
|
|
7655
|
-
baseLayers.push({ layer: expected, repairTrackName: d.name, repairSymbols, rsIdOffset: offset, priority });
|
|
7656
|
-
offset += repairSymbols;
|
|
7657
|
-
});
|
|
7658
|
-
const totalBaseRepairSymbols = offset;
|
|
7659
|
-
if (totalBaseRepairSymbols > MAX_REPAIR_SYMBOLS) {
|
|
7660
|
-
fail(
|
|
7661
|
-
"track_fec_bounds",
|
|
7662
|
-
`${layerTracks.length === 0 ? source.path : layerTracks[layerTracks.length - 1].path}.repairSymbols`,
|
|
7663
|
-
`total repair symbols across base layers exceeds the 24-bit RS_ID space (${MAX_REPAIR_SYMBOLS})`
|
|
7664
|
-
);
|
|
7665
|
-
}
|
|
7666
|
-
if (k + totalBaseRepairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {
|
|
7667
|
-
fail(
|
|
7668
|
-
"track_fec_bounds",
|
|
7669
|
-
`${source.path}.sourceSymbols`,
|
|
7670
|
-
`sourceSymbols + total base repair symbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`
|
|
7671
|
-
);
|
|
7672
|
-
}
|
|
7673
|
-
if (reedSolomon && k + totalBaseRepairSymbols > MAX_REED_SOLOMON_BLOCK_SYMBOLS) {
|
|
7674
|
-
fail(
|
|
7675
|
-
"track_fec_bounds",
|
|
7676
|
-
`${source.path}.sourceSymbols`,
|
|
7677
|
-
`reed-solomon sourceSymbols + total base repair symbols exceeds ${MAX_REED_SOLOMON_BLOCK_SYMBOLS} (draft-ramadan-moq-fec-01 6.3.4 rule 12)`
|
|
7678
|
-
);
|
|
7679
|
-
}
|
|
7680
|
-
return { sourceTrackName: source.name, baseLayers, overlays, totalBaseRepairSymbols };
|
|
7681
|
-
}
|
|
7682
|
-
|
|
7683
7872
|
// src/carp-catalog.ts
|
|
7684
7873
|
function isTrackTimingConfig(obj) {
|
|
7685
7874
|
if (typeof obj !== "object" || obj === null) return false;
|
|
@@ -7812,7 +8001,7 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
7812
8001
|
}
|
|
7813
8002
|
const tracks = catalog["tracks"];
|
|
7814
8003
|
const trackRecords = [];
|
|
7815
|
-
const
|
|
8004
|
+
const positiveInteger = (input) => Number.isSafeInteger(input) && input > 0;
|
|
7816
8005
|
const positiveNumber = (input) => typeof input === "number" && Number.isFinite(input) && input > 0;
|
|
7817
8006
|
for (let index = 0; index < tracks.length; index++) {
|
|
7818
8007
|
const raw = tracks[index];
|
|
@@ -7844,7 +8033,7 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
7844
8033
|
);
|
|
7845
8034
|
}
|
|
7846
8035
|
const codec = track["codec"];
|
|
7847
|
-
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" && (typeof codec !== "string" || codec.length === 0)) {
|
|
8036
|
+
if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" && packaging !== "fec-source" && (typeof codec !== "string" || codec.length === 0)) {
|
|
7848
8037
|
catalogError("track_codec", `${path}.codec`, `codec is required for packaging=${packaging}`);
|
|
7849
8038
|
}
|
|
7850
8039
|
const timescale = track["timescale"];
|
|
@@ -7852,13 +8041,13 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
7852
8041
|
const groupDurationTicks = track["groupDurationTicks"];
|
|
7853
8042
|
const keyframeIntervalMs = track["keyframeIntervalMs"];
|
|
7854
8043
|
const keyframeIntervalTicks = track["keyframeIntervalTicks"];
|
|
7855
|
-
if (groupDurationMs !== void 0 && !
|
|
8044
|
+
if (groupDurationMs !== void 0 && !positiveInteger(groupDurationMs)) {
|
|
7856
8045
|
catalogError("track_group_duration_ms", `${path}.groupDurationMs`, "must be a positive integer");
|
|
7857
8046
|
}
|
|
7858
|
-
if (groupDurationTicks !== void 0 && !
|
|
8047
|
+
if (groupDurationTicks !== void 0 && !positiveInteger(groupDurationTicks)) {
|
|
7859
8048
|
catalogError("track_group_duration_ticks", `${path}.groupDurationTicks`, "must be a positive integer");
|
|
7860
8049
|
}
|
|
7861
|
-
if (keyframeIntervalMs !== void 0 && !positiveNumber(keyframeIntervalMs) || keyframeIntervalTicks !== void 0 && !
|
|
8050
|
+
if (keyframeIntervalMs !== void 0 && !positiveNumber(keyframeIntervalMs) || keyframeIntervalTicks !== void 0 && !positiveInteger(keyframeIntervalTicks)) {
|
|
7862
8051
|
catalogError("track_keyframe_interval", path, "keyframe interval fields must be positive numbers");
|
|
7863
8052
|
}
|
|
7864
8053
|
const anchor = track["presentationAnchorTicks"];
|
|
@@ -7869,17 +8058,11 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
7869
8058
|
if (initRef !== void 0 && (typeof initRef !== "string" || initRef.length === 0)) {
|
|
7870
8059
|
catalogError("track_init_ref", `${path}.initRef`, "must be a non-empty string");
|
|
7871
8060
|
}
|
|
7872
|
-
if (packaging === "fec-repair") {
|
|
7873
|
-
rejectRepairTrackGeometry(path, track, catalogError);
|
|
7874
|
-
validateRepairTrackFields(describeRepairTrack(name, path, track), catalogError);
|
|
7875
|
-
} else {
|
|
7876
|
-
rejectRepairFieldsOnMediaTrack(path, track, catalogError);
|
|
7877
|
-
}
|
|
7878
8061
|
if (packaging === "mmtp") {
|
|
7879
8062
|
if (track["mmtpMode"] !== "mfu") {
|
|
7880
8063
|
catalogError("track_mmtp_mode", `${path}.mmtpMode`, "must be mfu");
|
|
7881
8064
|
}
|
|
7882
|
-
if (!
|
|
8065
|
+
if (!positiveInteger(timescale)) {
|
|
7883
8066
|
catalogError("track_timescale", `${path}.timescale`, "must be a positive integer");
|
|
7884
8067
|
}
|
|
7885
8068
|
if (groupDurationMs === void 0 && groupDurationTicks === void 0) {
|
|
@@ -7893,85 +8076,6 @@ function validateCmsfMmtpCatalog(value, options = {}) {
|
|
|
7893
8076
|
catalogError("track_group_duration_exact", `${path}.groupDurationMs`, "does not resolve to an exact integer tick count");
|
|
7894
8077
|
}
|
|
7895
8078
|
}
|
|
7896
|
-
if (track["fec"] !== void 0) {
|
|
7897
|
-
const fec = track["fec"];
|
|
7898
|
-
if (typeof fec !== "object" || fec === null || Array.isArray(fec)) {
|
|
7899
|
-
catalogError("track_fec", `${path}.fec`, "FEC must be an object");
|
|
7900
|
-
}
|
|
7901
|
-
const config = fec;
|
|
7902
|
-
const algorithm = config["algorithm"];
|
|
7903
|
-
if (typeof algorithm !== "string" || algorithm.length === 0) {
|
|
7904
|
-
catalogError("track_fec", `${path}.fec.algorithm`, "must be a non-empty string");
|
|
7905
|
-
}
|
|
7906
|
-
if ("interleaveDepth" in config || "interleaveMs" in config) {
|
|
7907
|
-
catalogError("track_fec", `${path}.fec`, "interleaveDepthMs is the only supported interleave field");
|
|
7908
|
-
}
|
|
7909
|
-
if ("enhancementRepair" in config) {
|
|
7910
|
-
catalogError(
|
|
7911
|
-
"track_fec",
|
|
7912
|
-
`${path}.fec.enhancementRepair`,
|
|
7913
|
-
"repair layers are self-describing fec-repair tracks (depends, repairLayer); fec.enhancementRepair is not supported"
|
|
7914
|
-
);
|
|
7915
|
-
}
|
|
7916
|
-
if (config["interleaveDepthMs"] !== void 0 && (!Number.isSafeInteger(config["interleaveDepthMs"]) || config["interleaveDepthMs"] < 0)) {
|
|
7917
|
-
catalogError("track_fec", `${path}.fec.interleaveDepthMs`, "must be a non-negative safe integer");
|
|
7918
|
-
}
|
|
7919
|
-
if (algorithm !== "none") {
|
|
7920
|
-
for (const field of ["sourceSymbols", "repairSymbols", "symbolSize"]) {
|
|
7921
|
-
if (!positiveInteger2(config[field])) {
|
|
7922
|
-
catalogError("track_fec", `${path}.fec.${field}`, "must be a positive integer");
|
|
7923
|
-
}
|
|
7924
|
-
}
|
|
7925
|
-
if (algorithm === "raptorq" && config["symbolSize"] % 8 !== 0) {
|
|
7926
|
-
catalogError("track_fec", `${path}.fec.symbolSize`, "RaptorQ symbolSize must be a multiple of 8");
|
|
7927
|
-
}
|
|
7928
|
-
if (typeof config["repairTrack"] !== "string" || config["repairTrack"].length === 0) {
|
|
7929
|
-
catalogError("track_fec", `${path}.fec.repairTrack`, "must be a non-empty string");
|
|
7930
|
-
}
|
|
7931
|
-
}
|
|
7932
|
-
readFecBlockProfile(config["blockProfile"], `${path}.fec.blockProfile`, algorithm !== "none", catalogError);
|
|
7933
|
-
if (algorithm !== "none" && config["symbolSize"] < ISO_SSBG1_MIN_SYMBOL_SIZE) {
|
|
7934
|
-
catalogError("track_fec", `${path}.fec.symbolSize`, "iso-ssbg1-v1 symbolSize must be at least 14");
|
|
7935
|
-
}
|
|
7936
|
-
}
|
|
7937
|
-
}
|
|
7938
|
-
const repairDescriptors = trackRecords.flatMap(
|
|
7939
|
-
(track, index) => track["packaging"] === "fec-repair" ? [describeRepairTrack(track["name"], `$.tracks[${index}]`, track)] : []
|
|
7940
|
-
);
|
|
7941
|
-
const reachability = repairReachability(
|
|
7942
|
-
trackRecords.filter((track) => track["packaging"] !== "fec-repair").map((track) => track["fec"])
|
|
7943
|
-
);
|
|
7944
|
-
for (const descriptor of repairDescriptors) {
|
|
7945
|
-
assertRepairTrackReachable(descriptor, reachability, catalogError);
|
|
7946
|
-
const target = dependsTarget(descriptor);
|
|
7947
|
-
if (target === void 0) continue;
|
|
7948
|
-
const source = trackRecords.find((candidate) => candidate["name"] === target);
|
|
7949
|
-
const sourceFec = source?.["fec"];
|
|
7950
|
-
if (!source || source["packaging"] !== "mmtp" || !sourceFec || sourceFec["algorithm"] === "none") {
|
|
7951
|
-
catalogError(
|
|
7952
|
-
"track_repair_depends",
|
|
7953
|
-
`${descriptor.path}.depends`,
|
|
7954
|
-
`${JSON.stringify(target)} is not an mmtp track with FEC`
|
|
7955
|
-
);
|
|
7956
|
-
}
|
|
7957
|
-
}
|
|
7958
|
-
for (let index = 0; index < trackRecords.length; index++) {
|
|
7959
|
-
const track = trackRecords[index];
|
|
7960
|
-
const fec = track["fec"];
|
|
7961
|
-
if (!fec || fec["algorithm"] === "none") continue;
|
|
7962
|
-
assembleRepairLayers(
|
|
7963
|
-
{
|
|
7964
|
-
name: track["name"],
|
|
7965
|
-
path: `$.tracks[${index}].fec`,
|
|
7966
|
-
algorithm: fec["algorithm"],
|
|
7967
|
-
sourceSymbols: fec["sourceSymbols"],
|
|
7968
|
-
repairSymbols: fec["repairSymbols"],
|
|
7969
|
-
repairTrack: fec["repairTrack"]
|
|
7970
|
-
},
|
|
7971
|
-
repairDescriptors,
|
|
7972
|
-
catalogError,
|
|
7973
|
-
{ layerZeroMustBeDeclared: true }
|
|
7974
|
-
);
|
|
7975
8079
|
}
|
|
7976
8080
|
const switchingSets = /* @__PURE__ */ new Map();
|
|
7977
8081
|
for (let index = 0; index < trackRecords.length; index++) {
|
|
@@ -8073,13 +8177,8 @@ var INACTIVE = Object.freeze({
|
|
|
8073
8177
|
function recordOf2(value) {
|
|
8074
8178
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
8075
8179
|
}
|
|
8076
|
-
function
|
|
8077
|
-
return
|
|
8078
|
-
}
|
|
8079
|
-
function catalogDeclaresFec(fec) {
|
|
8080
|
-
const block = recordOf2(fec);
|
|
8081
|
-
if (block === void 0 || block["algorithm"] === "none" || fecAlgorithmUnimplemented(block["algorithm"])) return false;
|
|
8082
|
-
return isPositiveSafeInteger2(block["sourceSymbols"]) && isPositiveSafeInteger2(block["symbolSize"]) && isFecBlockProfile(block["blockProfile"]) && isoBlockSpanUsable(block) && !catalogFecDeclaresUnconfigurableInstance(block);
|
|
8180
|
+
function catalogDeclaresFec(catalog, track) {
|
|
8181
|
+
return resolveFecBindings(catalog).get(track)?.status === "ok";
|
|
8083
8182
|
}
|
|
8084
8183
|
function requireBoolean(value, field) {
|
|
8085
8184
|
if (typeof value !== "boolean") {
|
|
@@ -8220,20 +8319,20 @@ var MmtpFecSession = class {
|
|
|
8220
8319
|
failClosed: null
|
|
8221
8320
|
};
|
|
8222
8321
|
/**
|
|
8223
|
-
* @throws when the
|
|
8224
|
-
*
|
|
8225
|
-
* `reorderToleranceMs`. None is defaulted.
|
|
8322
|
+
* @throws when the instance lacks or misstates a field the session needs:
|
|
8323
|
+
* the scheme, the block profile, K, P_0 (`repairSymbols`), T,
|
|
8324
|
+
* `interleaveDepthMs` or `reorderToleranceMs`. None is defaulted.
|
|
8226
8325
|
*/
|
|
8227
8326
|
constructor(config) {
|
|
8228
8327
|
const fec = config?.fec;
|
|
8229
8328
|
if (!fec || typeof fec !== "object") {
|
|
8230
|
-
throw new Error("MmtpFecSession: the catalog fec
|
|
8329
|
+
throw new Error("MmtpFecSession: the catalog fec instance is required");
|
|
8231
8330
|
}
|
|
8232
8331
|
this.blockProfile = parseFecBlockProfile(fec.blockProfile, "fec.blockProfile");
|
|
8233
|
-
if (fec.
|
|
8234
|
-
throw new Error(`MmtpFecSession: fec.
|
|
8332
|
+
if (fec.scheme !== "raptorq" && fec.scheme !== "reed-solomon") {
|
|
8333
|
+
throw new Error(`MmtpFecSession: fec.scheme ${JSON.stringify(fec.scheme)} is not implemented`);
|
|
8235
8334
|
}
|
|
8236
|
-
this.algorithm = fec.
|
|
8335
|
+
this.algorithm = fec.scheme;
|
|
8237
8336
|
this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, "fec.sourceSymbols");
|
|
8238
8337
|
this.repairSymbols = requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols");
|
|
8239
8338
|
this.symbolSize = requirePositiveInteger(fec.symbolSize, "fec.symbolSize");
|
|
@@ -8575,7 +8674,7 @@ function catalogAmtNetworkSource(networkSource) {
|
|
|
8575
8674
|
}
|
|
8576
8675
|
return void 0;
|
|
8577
8676
|
}
|
|
8578
|
-
function
|
|
8677
|
+
function intIn2(value, min, max) {
|
|
8579
8678
|
return Number.isInteger(value) && value >= min && value <= max;
|
|
8580
8679
|
}
|
|
8581
8680
|
function catalogAtsc3NetworkSource(networkSource) {
|
|
@@ -8583,12 +8682,12 @@ function catalogAtsc3NetworkSource(networkSource) {
|
|
|
8583
8682
|
for (const raw of entries) {
|
|
8584
8683
|
if (!raw || typeof raw !== "object") continue;
|
|
8585
8684
|
const ns = raw;
|
|
8586
|
-
if (ns.type !== "atsc3" || !
|
|
8685
|
+
if (ns.type !== "atsc3" || !intIn2(ns.frequency, 1, Number.MAX_SAFE_INTEGER)) continue;
|
|
8587
8686
|
const slsUri = typeof ns.slsUri === "string" ? ns.slsUri.trim() : "";
|
|
8588
8687
|
return {
|
|
8589
8688
|
frequency: ns.frequency,
|
|
8590
|
-
...
|
|
8591
|
-
...
|
|
8689
|
+
...intIn2(ns.plpId, 0, 63) ? { plpId: ns.plpId } : {},
|
|
8690
|
+
...intIn2(ns.serviceId, 0, 65535) ? { serviceId: ns.serviceId } : {},
|
|
8592
8691
|
...slsUri ? { slsUri } : {}
|
|
8593
8692
|
};
|
|
8594
8693
|
}
|