@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.
Files changed (136) hide show
  1. package/README.md +21 -16
  2. package/dist/browser-runtime.js +1024 -1078
  3. package/dist/browser-runtime.js.map +4 -4
  4. package/dist/carp-catalog.d.ts +33 -87
  5. package/dist/carp-catalog.d.ts.map +1 -1
  6. package/dist/carp-catalog.js +12 -434
  7. package/dist/carp-catalog.js.map +4 -4
  8. package/dist/catalog-fec-params.d.ts +48 -161
  9. package/dist/catalog-fec-params.d.ts.map +1 -1
  10. package/dist/catalog-fec-params.js +567 -592
  11. package/dist/catalog-fec-params.js.map +4 -4
  12. package/dist/catalog-network-source.js +6 -2
  13. package/dist/catalog-network-source.js.map +3 -3
  14. package/dist/delivery-catalog.js +12 -4
  15. package/dist/delivery-catalog.js.map +4 -4
  16. package/dist/delta-fallback-config.d.ts +21 -26
  17. package/dist/delta-fallback-config.d.ts.map +1 -1
  18. package/dist/delta-fallback-config.js +582 -24
  19. package/dist/delta-fallback-config.js.map +4 -4
  20. package/dist/fec-manager.js +6 -2
  21. package/dist/fec-manager.js.map +3 -3
  22. package/dist/fec-state.d.ts +5 -43
  23. package/dist/fec-state.d.ts.map +1 -1
  24. package/dist/fec-state.js +529 -34
  25. package/dist/fec-state.js.map +4 -4
  26. package/dist/fec-worker-setup.js +6 -2
  27. package/dist/fec-worker-setup.js.map +3 -3
  28. package/dist/held-layer-admission.js +6 -2
  29. package/dist/held-layer-admission.js.map +3 -3
  30. package/dist/index.d.ts +3 -3
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +5 -39
  33. package/dist/index.js.map +2 -2
  34. package/dist/iso-block-locator.js +6 -2
  35. package/dist/iso-block-locator.js.map +3 -3
  36. package/dist/mfu-reassembler.js +6 -2
  37. package/dist/mfu-reassembler.js.map +3 -3
  38. package/dist/mmtp-container-parser.js +6 -2
  39. package/dist/mmtp-container-parser.js.map +3 -3
  40. package/dist/mmtp-container-parser.no-modules.js +551 -452
  41. package/dist/mmtp-container-parser.no-modules.js.map +4 -4
  42. package/dist/mmtp-decode-pipeline.js +6 -2
  43. package/dist/mmtp-decode-pipeline.js.map +3 -3
  44. package/dist/mmtp-fec-session.d.ts +7 -6
  45. package/dist/mmtp-fec-session.d.ts.map +1 -1
  46. package/dist/mmtp-fec-session.js +13 -9
  47. package/dist/mmtp-fec-session.js.map +3 -3
  48. package/dist/mmtp-router.d.ts +7 -16
  49. package/dist/mmtp-router.d.ts.map +1 -1
  50. package/dist/mmtp-router.js +19 -29
  51. package/dist/mmtp-router.js.map +3 -3
  52. package/dist/multicast-auth-catalog.js +7 -3
  53. package/dist/multicast-auth-catalog.js.map +3 -3
  54. package/dist/multicast-input.js +7 -3
  55. package/dist/multicast-input.js.map +3 -3
  56. package/dist/multicast-manager.d.ts +29 -32
  57. package/dist/multicast-manager.d.ts.map +1 -1
  58. package/dist/multicast-manager.js +682 -253
  59. package/dist/multicast-manager.js.map +4 -4
  60. package/dist/multicast-transport.d.ts +23 -2
  61. package/dist/multicast-transport.d.ts.map +1 -1
  62. package/dist/multicast-transport.js +57 -16
  63. package/dist/multicast-transport.js.map +4 -4
  64. package/dist/multicast-unwrap.js +6 -2
  65. package/dist/multicast-unwrap.js.map +3 -3
  66. package/dist/no-modules.js +551 -452
  67. package/dist/no-modules.js.map +4 -4
  68. package/dist/phase14-catalog-override.d.ts +10 -5
  69. package/dist/phase14-catalog-override.d.ts.map +1 -1
  70. package/dist/phase14-catalog-override.js +35 -11
  71. package/dist/phase14-catalog-override.js.map +2 -2
  72. package/dist/repair-fetch.d.ts +3 -1
  73. package/dist/repair-fetch.d.ts.map +1 -1
  74. package/dist/repair-fetch.js +16 -3
  75. package/dist/repair-fetch.js.map +3 -3
  76. package/dist/repair-layers.d.ts +0 -123
  77. package/dist/repair-layers.d.ts.map +1 -1
  78. package/dist/repair-layers.js +1 -349
  79. package/dist/repair-layers.js.map +3 -3
  80. package/dist/signaling.js +6 -2
  81. package/dist/signaling.js.map +3 -3
  82. package/dist/timestamp-codec.js +6 -2
  83. package/dist/timestamp-codec.js.map +3 -3
  84. package/dist/wasm-mfu-reassembler.js +6 -2
  85. package/dist/wasm-mfu-reassembler.js.map +3 -3
  86. package/dist/wasm-mmtp-container-parser.js +6 -2
  87. package/dist/wasm-mmtp-container-parser.js.map +3 -3
  88. package/package.json +9 -5
  89. package/schemas/catalog.schema.json +53 -58
  90. package/src/carp-catalog.ts +39 -229
  91. package/src/catalog-fec-params.ts +130 -493
  92. package/src/delta-fallback-config.ts +63 -50
  93. package/src/fec-state.ts +7 -62
  94. package/src/index.ts +10 -30
  95. package/src/mmtp-fec-session.ts +11 -10
  96. package/src/mmtp-router.ts +14 -29
  97. package/src/multicast-manager.ts +89 -121
  98. package/src/multicast-transport.ts +65 -12
  99. package/src/phase14-catalog-override.ts +67 -16
  100. package/src/repair-fetch.ts +27 -3
  101. package/src/repair-layers.ts +7 -520
  102. package/test-vectors/catalog/README.md +39 -67
  103. package/test-vectors/catalog/fec-01-runtime/README.md +15 -0
  104. package/test-vectors/catalog/{negative/fec-block-profile-retired.json → fec-01-runtime/fec-iso-ssbg1.json} +1 -1
  105. package/test-vectors/catalog/fec-01-runtime/fec-layered-repair.json +106 -0
  106. package/test-vectors/catalog/{negative/fec-block-profile-unknown.json → fec-01-runtime/fec-multicast.json} +2 -1
  107. package/test-vectors/catalog/fec-01-runtime/multicast-native-ssm-no-network-source.json +44 -0
  108. package/test-vectors/catalog/negative/fec-block-profile-missing.json +24 -11
  109. package/test-vectors/catalog/negative/fec-iso-symbol-size-below-floor.json +13 -8
  110. package/test-vectors/catalog/negative/fec-reed-solomon-iso-symbol-size-13.json +13 -8
  111. package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +20 -10
  112. package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +20 -10
  113. package/test-vectors/catalog/negative/fec-repair-overlay-source-symbols-zero.json +25 -17
  114. package/test-vectors/catalog/negative/fec-repair-symbols-zero.json +13 -8
  115. package/test-vectors/catalog/negative/fec-source-symbols-zero.json +13 -8
  116. package/test-vectors/catalog/negative/fec-symbol-size-zero.json +13 -8
  117. package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +18 -9
  118. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +13 -8
  119. package/test-vectors/catalog/positive/fec-algorithm-unimplemented.json +14 -9
  120. package/test-vectors/catalog/positive/fec-iso-ssbg1.json +25 -12
  121. package/test-vectors/catalog/positive/fec-layered-repair.json +79 -35
  122. package/test-vectors/catalog/positive/fec-multicast.json +26 -13
  123. package/test-vectors/catalog/positive/fec-reed-solomon-layered.json +29 -18
  124. package/test-vectors/catalog/positive/multicast-draft01-4-1-example.json +28 -20
  125. package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +14 -10
  126. package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +22 -12
  127. package/test-vectors/catalog/section-codes.json +13 -22
  128. package/test-vectors/catalog-timing.json +2 -2
  129. package/test-vectors/catalog-validation.json +30 -243
  130. package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +0 -28
  131. package/test-vectors/catalog/negative/fec-reed-solomon-k-plus-p-over-255.json +0 -39
  132. package/test-vectors/catalog/negative/fec-reed-solomon-overlay-over-255.json +0 -40
  133. package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +0 -32
  134. package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +0 -34
  135. package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +0 -24
  136. package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +0 -33
@@ -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 positiveInteger2 = (input) => Number.isSafeInteger(input) && input > 0;
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 && !positiveInteger2(groupDurationMs)) {
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 && !positiveInteger2(groupDurationTicks)) {
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 && !positiveInteger2(keyframeIntervalTicks)) {
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 (!positiveInteger2(timescale)) {
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 isPositiveSafeInteger2(value) {
8077
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
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 catalog block lacks or misstates a field the session
8224
- * needs: the block profile, K, P_0 (`repairSymbols`), T, `interleaveDepthMs` or
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 block is required");
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.algorithm !== void 0 && fec.algorithm !== "raptorq" && fec.algorithm !== "reed-solomon") {
8234
- throw new Error(`MmtpFecSession: fec.algorithm ${JSON.stringify(fec.algorithm)} is not implemented`);
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.algorithm === "reed-solomon" ? "reed-solomon" : "raptorq";
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 intIn(value, min, max) {
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" || !intIn(ns.frequency, 1, Number.MAX_SAFE_INTEGER)) continue;
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
- ...intIn(ns.plpId, 0, 63) ? { plpId: ns.plpId } : {},
8591
- ...intIn(ns.serviceId, 0, 65535) ? { serviceId: ns.serviceId } : {},
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
  }