@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
@@ -477,34 +477,6 @@ var BlockcastMmt = (() => {
477
477
  return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0;
478
478
  }
479
479
 
480
- // ../mmt-base/dist/catalog-fec-source.js
481
- var ALL_ROLES = Object.freeze(["video", "audio"]);
482
- function recordOf(value) {
483
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
484
- }
485
- function isoBlockSpanUsable(fec) {
486
- const block = recordOf(fec);
487
- if (block === void 0 || block["blockProfile"] !== "iso-ssbg1-v1")
488
- return true;
489
- const ms = block["interleaveDepthMs"];
490
- return ms === void 0 || typeof ms === "number" && Number.isSafeInteger(ms) && ms >= 0;
491
- }
492
- function fecAlgorithmUnimplemented(algorithm) {
493
- return typeof algorithm === "string" && algorithm !== "none" && algorithm !== "raptorq" && algorithm !== "reed-solomon";
494
- }
495
- function isPositiveSafeInteger(value) {
496
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
497
- }
498
- var ISO_SSBG1_MIN_SYMBOL_SIZE = 14;
499
- function catalogFecDeclaresUnconfigurableInstance(fec) {
500
- const block = recordOf(fec);
501
- if (block === void 0 || block["algorithm"] === "none" || fecAlgorithmUnimplemented(block["algorithm"]))
502
- return false;
503
- if (!(isPositiveSafeInteger(block["sourceSymbols"]) && isPositiveSafeInteger(block["symbolSize"])))
504
- return true;
505
- return block["blockProfile"] === "iso-ssbg1-v1" && block["symbolSize"] < ISO_SSBG1_MIN_SYMBOL_SIZE;
506
- }
507
-
508
480
  // ../mmt-base/dist/multicast-catalog.js
509
481
  var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
510
482
  var UINT32_MAX = 4294967295;
@@ -753,6 +725,538 @@ var BlockcastMmt = (() => {
753
725
  return issues;
754
726
  }
755
727
 
728
+ // ../mmt-base/dist/fec-catalog.js
729
+ var DEFAULT_SCHEMES = ["reed-solomon", "raptorq"];
730
+ var DEFAULT_PROFILES = ["moq-object-v1", "iso-ssbg1-v1"];
731
+ var RS_MAX_BLOCK = 255;
732
+ var RAPTORQ_MAX_K = 56403;
733
+ var MAX_24 = 16777215;
734
+ var ISO_MIN_T = 14;
735
+ var GEOMETRY_KEYS = [
736
+ "scheme",
737
+ "blockProfile",
738
+ "mmtpMode",
739
+ "timescale",
740
+ "symbolSize",
741
+ "interleaveDepthMs",
742
+ "reorderToleranceMs",
743
+ "sourceSymbols",
744
+ "fec"
745
+ ];
746
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
747
+ var has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
748
+ var isInt = (v) => typeof v === "number" && Number.isInteger(v);
749
+ var intIn = (v, lo, hi = Number.MAX_SAFE_INTEGER) => isInt(v) && v >= lo && v <= hi;
750
+ var profileAdmits = (profile, packaging) => profile !== "iso-ssbg1-v1" || packaging === "mmtp";
751
+ function validateFecCatalog(catalog, receiver = {}) {
752
+ const schemes = receiver.schemes ?? DEFAULT_SCHEMES;
753
+ const profiles = receiver.blockProfiles ?? DEFAULT_PROFILES;
754
+ const overlays = receiver.overlays ?? true;
755
+ const all = [];
756
+ const report = (code, path, inst) => {
757
+ const issue = { code, path };
758
+ all.push(issue);
759
+ inst?.issues.push(issue);
760
+ };
761
+ const tracks = isObj(catalog) && Array.isArray(catalog.tracks) ? catalog.tracks : [];
762
+ if (!isObj(catalog) || !Array.isArray(catalog.tracks))
763
+ report("fec_member", "/tracks");
764
+ let rejectAll = false;
765
+ tracks.forEach((t, ti) => {
766
+ if (!isObj(t))
767
+ return;
768
+ if (has(t, "enhancementRepair")) {
769
+ rejectAll = true;
770
+ report("fec_enhancement_repair", `/tracks/${ti}/enhancementRepair`);
771
+ }
772
+ if (Array.isArray(t.fec))
773
+ t.fec.forEach((o, ii) => {
774
+ if (isObj(o) && has(o, "enhancementRepair")) {
775
+ rejectAll = true;
776
+ report("fec_enhancement_repair", `/tracks/${ti}/fec/${ii}/enhancementRepair`);
777
+ }
778
+ });
779
+ });
780
+ const key = (ns, name) => JSON.stringify([ns ?? null, name]);
781
+ const byName = /* @__PURE__ */ new Map();
782
+ tracks.forEach((t, ti) => {
783
+ if (isObj(t) && typeof t.name === "string") {
784
+ const k = key(t.namespace, t.name);
785
+ if (!byName.has(k))
786
+ byName.set(k, ti);
787
+ }
788
+ });
789
+ const trackAt = (ti) => tracks[ti];
790
+ const insts = [];
791
+ tracks.forEach((t, ti) => {
792
+ if (!isObj(t) || !has(t, "fec") || t.packaging === "fec-repair" || t.packaging === "fec-source")
793
+ return;
794
+ if (!Array.isArray(t.fec)) {
795
+ report("fec_member", `/tracks/${ti}/fec`);
796
+ return;
797
+ }
798
+ t.fec.forEach((o, ii) => {
799
+ const path = `/tracks/${ti}/fec/${ii}`;
800
+ const inst = {
801
+ path,
802
+ ti,
803
+ track: t,
804
+ obj: isObj(o) ? o : null,
805
+ id: null,
806
+ unavailable: false,
807
+ overlay: false,
808
+ issues: [],
809
+ layer0: null,
810
+ symbolNamed: null,
811
+ attached: []
812
+ };
813
+ insts.push(inst);
814
+ if (!inst.obj)
815
+ return report("fec_member", path, inst);
816
+ const o2 = inst.obj;
817
+ if (!has(o2, "instanceId"))
818
+ inst.id = 0;
819
+ else if (intIn(o2.instanceId, 0, 255))
820
+ inst.id = o2.instanceId;
821
+ else
822
+ report("fec_member", `${path}/instanceId`, inst);
823
+ inst.overlay = has(o2, "scope");
824
+ });
825
+ });
826
+ const declaresFec = insts.length > 0;
827
+ for (const inst of insts) {
828
+ const o = inst.obj;
829
+ if (!o)
830
+ continue;
831
+ const avail = (field, impl) => {
832
+ const p = `${inst.path}/${field}`;
833
+ const absent = field === "scheme" ? "fec_scheme_absent" : "fec_block_profile_absent";
834
+ const unimpl = field === "scheme" ? "fec_scheme_unimplemented" : "fec_block_profile_unimplemented";
835
+ if (!has(o, field))
836
+ return report(absent, p, inst), false;
837
+ if (typeof o[field] !== "string")
838
+ return report("fec_member", p, inst), true;
839
+ if (!impl.includes(o[field]))
840
+ return report(unimpl, p, inst), false;
841
+ return true;
842
+ };
843
+ if (!avail("scheme", schemes) || !avail("blockProfile", profiles))
844
+ inst.unavailable = true;
845
+ else if (typeof o.blockProfile === "string" && !profileAdmits(o.blockProfile, inst.track.packaging)) {
846
+ report("fec_block_profile_packaging", `${inst.path}/blockProfile`, inst);
847
+ inst.unavailable = true;
848
+ }
849
+ }
850
+ const live = (i) => !i.unavailable;
851
+ for (const inst of insts.filter(live)) {
852
+ const o = inst.obj;
853
+ if (!o)
854
+ continue;
855
+ const p = inst.path;
856
+ for (const m of ["sourceSymbols", "repairSymbols", "symbolSize"])
857
+ if (!intIn(o[m], 1))
858
+ report("fec_member", `${p}/${m}`, inst);
859
+ if (typeof o.repairTrack !== "string")
860
+ report("fec_member", `${p}/repairTrack`, inst);
861
+ if (has(o, "symbolTrack") && typeof o.symbolTrack !== "string")
862
+ report("fec_member", `${p}/symbolTrack`, inst);
863
+ if (has(o, "interleaveDepthMs")) {
864
+ const d = o.interleaveDepthMs;
865
+ if (!intIn(d, 0) || d === 0 && o.blockProfile !== "iso-ssbg1-v1")
866
+ report("fec_member", `${p}/interleaveDepthMs`, inst);
867
+ }
868
+ if (has(o, "reorderToleranceMs")) {
869
+ const r = o.reorderToleranceMs;
870
+ if (typeof r !== "number" || !Number.isFinite(r) || r < 0)
871
+ report("fec_member", `${p}/reorderToleranceMs`, inst);
872
+ }
873
+ if (has(o, "label") && typeof o.label !== "string")
874
+ report("fec_member", `${p}/label`, inst);
875
+ if (inst.overlay && o.scope !== "keyframe")
876
+ report("fec_member", `${p}/scope`, inst);
877
+ }
878
+ const idGroups = /* @__PURE__ */ new Map();
879
+ for (const inst of insts)
880
+ if (inst.id !== null) {
881
+ const k = `${inst.ti}:${inst.id}`;
882
+ idGroups.set(k, [...idGroups.get(k) ?? [], inst]);
883
+ }
884
+ for (const group of idGroups.values())
885
+ if (group.length > 1)
886
+ for (const inst of group.filter(live))
887
+ report("fec_instance_id", inst.obj && has(inst.obj, "instanceId") ? `${inst.path}/instanceId` : inst.path, inst);
888
+ const nameUses = /* @__PURE__ */ new Map();
889
+ for (const inst of insts)
890
+ for (const field of ["repairTrack", "symbolTrack"])
891
+ if (inst.obj && typeof inst.obj[field] === "string") {
892
+ const k = key(inst.track.namespace, inst.obj[field]);
893
+ nameUses.set(k, [...nameUses.get(k) ?? [], { inst, field }]);
894
+ }
895
+ const namedBy = /* @__PURE__ */ new Map();
896
+ for (const [k, uses] of nameUses) {
897
+ const tj = byName.get(k);
898
+ for (const { inst, field } of uses) {
899
+ const path = `${inst.path}/${field}`;
900
+ if (tj === void 0 || trackAt(tj).packaging !== (field === "repairTrack" ? "fec-repair" : "fec-source")) {
901
+ if (live(inst))
902
+ report("fec_naming", path, inst);
903
+ } else if (uses.length > 1) {
904
+ if (live(inst))
905
+ report("fec_naming", path, inst);
906
+ if (!namedBy.has(tj))
907
+ namedBy.set(tj, inst);
908
+ } else {
909
+ namedBy.set(tj, inst);
910
+ if (field === "repairTrack")
911
+ inst.layer0 = tj;
912
+ else
913
+ inst.symbolNamed = tj;
914
+ }
915
+ }
916
+ }
917
+ const owner = /* @__PURE__ */ new Map();
918
+ if (declaresFec)
919
+ tracks.forEach((t, tj) => {
920
+ if (!isObj(t) || t.packaging !== "fec-repair" && t.packaging !== "fec-source")
921
+ return;
922
+ const tp = `/tracks/${tj}`;
923
+ const named = namedBy.get(tj);
924
+ if (named) {
925
+ owner.set(tj, named);
926
+ if (!live(named))
927
+ return;
928
+ if (has(t, "source") && t.source !== named.track.name)
929
+ report("fec_resolution", `${tp}/source`, named);
930
+ if (has(t, "instanceId") && t.instanceId !== named.id)
931
+ report("fec_resolution", `${tp}/instanceId`, named);
932
+ return;
933
+ }
934
+ if (!has(t, "source"))
935
+ return report("fec_orphan", tp);
936
+ const id = has(t, "instanceId") ? t.instanceId : 0;
937
+ const src = typeof t.source === "string" ? byName.get(key(t.namespace, t.source)) : void 0;
938
+ const target = src === void 0 ? void 0 : insts.find((i) => i.ti === src && i.id === id);
939
+ if (!target)
940
+ return report("fec_naming", has(t, "instanceId") && src !== void 0 ? `${tp}/instanceId` : `${tp}/source`);
941
+ owner.set(tj, target);
942
+ target.attached.push(tj);
943
+ });
944
+ for (const [tj, inst] of owner) {
945
+ if (!live(inst))
946
+ continue;
947
+ const t = trackAt(tj);
948
+ const tp = `/tracks/${tj}`;
949
+ const symbol = t.packaging === "fec-source";
950
+ if (has(t, "source") && typeof t.source !== "string")
951
+ report("fec_member", `${tp}/source`, inst);
952
+ if (has(t, "instanceId") && !intIn(t.instanceId, 0, 255))
953
+ report("fec_member", `${tp}/instanceId`, inst);
954
+ for (const m of ["repairLayer", "repairSymbols"]) {
955
+ if (!has(t, m))
956
+ continue;
957
+ if (symbol || !intIn(t[m], m === "repairLayer" ? 0 : 1))
958
+ report("fec_member", `${tp}/${m}`, inst);
959
+ }
960
+ if (has(t, "priority") && !intIn(t.priority, symbol ? 0 : 192, 255))
961
+ report("fec_member", `${tp}/priority`, inst);
962
+ for (const k of Object.keys(t))
963
+ if (GEOMETRY_KEYS.includes(k) || k.startsWith("groupDuration"))
964
+ report("fec_track_geometry", `${tp}/${k.replace(/~/g, "~0").replace(/\//g, "~1")}`, inst);
965
+ }
966
+ for (const inst of insts.filter(live)) {
967
+ const o = inst.obj;
968
+ if (!o)
969
+ continue;
970
+ const repairs = inst.attached.filter((tj) => trackAt(tj).packaging === "fec-repair");
971
+ const symbols = inst.attached.filter((tj) => trackAt(tj).packaging === "fec-source");
972
+ if (symbols.length + (inst.symbolNamed !== null ? 1 : 0) > 1)
973
+ for (const tj of symbols)
974
+ report("fec_symbol_tracks", `/tracks/${tj}`, inst);
975
+ const l0 = inst.layer0 === null ? null : trackAt(inst.layer0);
976
+ const l0p = `/tracks/${inst.layer0}`;
977
+ if (inst.overlay) {
978
+ if (l0 && has(l0, "repairLayer"))
979
+ report("fec_overlay", `${l0p}/repairLayer`, inst);
980
+ for (const tj of repairs)
981
+ report("fec_overlay", has(trackAt(tj), "repairLayer") ? `/tracks/${tj}/repairLayer` : `/tracks/${tj}`, inst);
982
+ } else {
983
+ if (l0 && has(l0, "repairLayer") && l0.repairLayer !== 0)
984
+ report("fec_layers", `${l0p}/repairLayer`, inst);
985
+ if (l0 && has(l0, "repairSymbols") && intIn(o.repairSymbols, 1) && l0.repairSymbols !== o.repairSymbols)
986
+ report("fec_layer0", `${l0p}/repairSymbols`, inst);
987
+ const layers = [];
988
+ for (const tj of repairs) {
989
+ const t = trackAt(tj);
990
+ if (!has(t, "repairLayer"))
991
+ report("fec_member", `/tracks/${tj}/repairLayer`, inst);
992
+ else if (t.repairLayer === 0)
993
+ report("fec_layers", `/tracks/${tj}/repairLayer`, inst);
994
+ else if (intIn(t.repairLayer, 1))
995
+ layers.push({ tj, layer: t.repairLayer });
996
+ if (!has(t, "repairSymbols"))
997
+ report("fec_member", `/tracks/${tj}/repairSymbols`, inst);
998
+ }
999
+ const counts = /* @__PURE__ */ new Map();
1000
+ for (const { layer } of layers)
1001
+ counts.set(layer, (counts.get(layer) ?? 0) + 1);
1002
+ let missing = 1;
1003
+ while (counts.has(missing))
1004
+ missing++;
1005
+ for (const { tj, layer } of layers)
1006
+ if (counts.get(layer) > 1 || layer > missing)
1007
+ report("fec_layers", `/tracks/${tj}/repairLayer`, inst);
1008
+ const chain = [
1009
+ { layer: 0, tj: inst.layer0, pri: l0 && intIn(l0.priority, 192, 255) ? l0.priority : l0 && has(l0, "priority") ? null : 240 },
1010
+ ...layers.map(({ tj, layer }) => {
1011
+ const pr = trackAt(tj).priority;
1012
+ return { layer, tj, pri: intIn(pr, 192, 255) ? pr : null };
1013
+ })
1014
+ ].sort((a, b) => a.layer - b.layer);
1015
+ let prev = null;
1016
+ for (const c of chain) {
1017
+ if (c.pri === null)
1018
+ continue;
1019
+ if (prev !== null && c.pri < prev)
1020
+ report("fec_priority", `/tracks/${c.tj}/priority`, inst);
1021
+ prev = Math.max(prev ?? c.pri, c.pri);
1022
+ }
1023
+ }
1024
+ const K = o.sourceSymbols;
1025
+ const T = o.symbolSize;
1026
+ const P0 = o.repairSymbols;
1027
+ if (intIn(K, 1) && intIn(P0, 1)) {
1028
+ let P = P0;
1029
+ if (!inst.overlay)
1030
+ for (const tj of repairs) {
1031
+ const t = trackAt(tj);
1032
+ if (intIn(t.repairLayer, 1) && intIn(t.repairSymbols, 1))
1033
+ P += t.repairSymbols;
1034
+ }
1035
+ if (P > MAX_24 || K + P - 1 > MAX_24)
1036
+ report("fec_bounds", inst.path, inst);
1037
+ else if (o.scheme === "reed-solomon" && K + P > RS_MAX_BLOCK)
1038
+ report("fec_bounds", inst.path, inst);
1039
+ }
1040
+ if (o.scheme === "raptorq" && intIn(K, 1) && K > RAPTORQ_MAX_K)
1041
+ report("fec_bounds", `${inst.path}/sourceSymbols`, inst);
1042
+ if (intIn(T, 1)) {
1043
+ if (o.scheme === "raptorq" && T % 8 !== 0)
1044
+ report("fec_bounds", `${inst.path}/symbolSize`, inst);
1045
+ else if (o.blockProfile === "iso-ssbg1-v1" && T < ISO_MIN_T)
1046
+ report("fec_bounds", `${inst.path}/symbolSize`, inst);
1047
+ }
1048
+ }
1049
+ const overlayTracks = new Set(insts.filter((i) => live(i) && i.overlay).map((i) => i.ti));
1050
+ for (const ti of overlayTracks) {
1051
+ const ofTrack = insts.filter((i) => i.ti === ti && live(i));
1052
+ const ovs = ofTrack.filter((i) => i.overlay);
1053
+ if (!overlays) {
1054
+ for (const ov of ovs)
1055
+ report("fec_overlay_unsupported", `${ov.path}/scope`, ov);
1056
+ for (const i of ofTrack)
1057
+ i.issues.length === 0 && i.issues.push({ code: "fec_overlay_unsupported", path: `${ovs[0].path}/scope` });
1058
+ continue;
1059
+ }
1060
+ if (ovs.length > 1)
1061
+ for (const ov of ovs)
1062
+ report("fec_overlay", `${ov.path}/scope`, ov);
1063
+ for (const ov of ovs)
1064
+ if (ov.obj?.blockProfile !== "iso-ssbg1-v1")
1065
+ report("fec_overlay", `${ov.path}/blockProfile`, ov);
1066
+ const cause = ovs.flatMap((ov) => ov.issues).find((x) => x.code === "fec_overlay");
1067
+ if (cause) {
1068
+ for (const ov of ovs)
1069
+ if (ov.issues.length === 0)
1070
+ ov.issues.push(cause);
1071
+ }
1072
+ }
1073
+ const rule4 = all.find((x) => x.code === "fec_enhancement_repair");
1074
+ if (rejectAll && rule4) {
1075
+ for (const i of insts)
1076
+ if (i.issues.length === 0)
1077
+ i.issues.push(rule4);
1078
+ }
1079
+ return {
1080
+ instances: insts.map((i) => ({
1081
+ path: i.path,
1082
+ track: typeof i.track.name === "string" ? i.track.name : null,
1083
+ instanceId: i.id,
1084
+ outcome: rejectAll || !i.unavailable && i.issues.length > 0 ? "violation" : i.unavailable ? "unavailable" : "ok",
1085
+ issues: i.issues,
1086
+ trackIndex: i.ti,
1087
+ repairTrack: i.layer0,
1088
+ boundRepairTracks: i.attached.filter((tj) => trackAt(tj).packaging === "fec-repair"),
1089
+ symbolTrack: i.symbolNamed ?? i.attached.find((tj) => trackAt(tj).packaging === "fec-source") ?? null
1090
+ })),
1091
+ issues: all
1092
+ };
1093
+ }
1094
+
1095
+ // ../mmt-base/dist/fec-bindings.js
1096
+ var RUNTIME_FEC_RECEIVER = Object.freeze({
1097
+ schemes: Object.freeze(["raptorq", "reed-solomon"]),
1098
+ blockProfiles: Object.freeze(["iso-ssbg1-v1"]),
1099
+ overlays: true
1100
+ });
1101
+ var DEFAULT_FEC_REPAIR_PRIORITY = 240;
1102
+ var recordOf = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
1103
+ var escape = (s) => s.replace(/~/g, "~0").replace(/\//g, "~1");
1104
+ function fecCatalogTracks(catalog) {
1105
+ const root = recordOf(catalog) ?? {};
1106
+ const tracks = [];
1107
+ const pointers = [];
1108
+ const hangNames = /* @__PURE__ */ new Set();
1109
+ for (const role of ["video", "audio"]) {
1110
+ for (const [name, r] of Object.entries(recordOf(recordOf(root[role])?.["renditions"]) ?? {})) {
1111
+ const entry = recordOf(r);
1112
+ if (!entry)
1113
+ continue;
1114
+ hangNames.add(name);
1115
+ const kind = recordOf(entry["container"])?.["kind"];
1116
+ const view2 = { name, packaging: kind === void 0 || kind === "mmtp-fec" ? "mmtp" : kind };
1117
+ if (entry["fec"] !== void 0)
1118
+ view2["fec"] = entry["fec"];
1119
+ tracks.push(view2);
1120
+ pointers.push(`/${role}/renditions/${escape(name)}`);
1121
+ }
1122
+ }
1123
+ const hangRenditions = tracks.length;
1124
+ for (const [name, r] of Object.entries(recordOf(recordOf(root["repair"])?.["renditions"]) ?? {})) {
1125
+ hangNames.add(name);
1126
+ tracks.push({ packaging: "fec-repair", ...recordOf(r), name });
1127
+ pointers.push(`/repair/renditions/${escape(name)}`);
1128
+ }
1129
+ const raw = Array.isArray(root["tracks"]) ? root["tracks"] : [];
1130
+ const named = new Set(hangNames);
1131
+ raw.forEach((t, i) => {
1132
+ const name = recordOf(t)?.["name"];
1133
+ if (typeof name === "string")
1134
+ named.add(name);
1135
+ if (typeof name === "string" && hangNames.has(name))
1136
+ return;
1137
+ tracks.push(t);
1138
+ pointers.push(`/tracks/${i}`);
1139
+ });
1140
+ tracks.slice(0, hangRenditions).forEach((t, ti) => {
1141
+ const view2 = t;
1142
+ if (!Array.isArray(view2["fec"]))
1143
+ return;
1144
+ view2["fec"].forEach((o, ii) => {
1145
+ const repairTrack = recordOf(o)?.["repairTrack"];
1146
+ if (typeof repairTrack !== "string" || named.has(repairTrack))
1147
+ return;
1148
+ named.add(repairTrack);
1149
+ const instanceId = recordOf(o)?.["instanceId"];
1150
+ tracks.push({ name: repairTrack, packaging: "fec-repair", source: view2["name"], ...instanceId === void 0 ? {} : { instanceId } });
1151
+ pointers.push(`${pointers[ti]}/fec/${ii}/repairTrack`);
1152
+ });
1153
+ });
1154
+ return { tracks, pointers };
1155
+ }
1156
+ function resolveFecBindings(catalog, receiver = RUNTIME_FEC_RECEIVER) {
1157
+ const { tracks, pointers } = fecCatalogTracks(catalog);
1158
+ const report = validateFecCatalog({ tracks }, receiver);
1159
+ const at = (path) => {
1160
+ const m = /^\/tracks\/(\d+)(.*)$/.exec(path);
1161
+ return m ? pointers[Number(m[1])] + m[2] : path;
1162
+ };
1163
+ const trackAt = (i) => recordOf(tracks[i]) ?? {};
1164
+ const nameAt = (i) => i === null ? void 0 : trackAt(i)["name"];
1165
+ const why = (i) => Object.freeze({
1166
+ code: i.issues[0]?.code ?? "fec_member",
1167
+ path: at(i.issues[0]?.path ?? i.path),
1168
+ outcome: i.outcome === "unavailable" ? "unavailable" : "violation"
1169
+ });
1170
+ const out = /* @__PURE__ */ new Map();
1171
+ tracks.forEach((t, ti) => {
1172
+ const track = recordOf(t);
1173
+ const name = track?.["name"];
1174
+ if (!track || typeof name !== "string" || out.has(name))
1175
+ return;
1176
+ if (track["packaging"] === "fec-repair" || track["packaging"] === "fec-source")
1177
+ return;
1178
+ const fec = track["fec"];
1179
+ if (fec === void 0)
1180
+ return;
1181
+ if (!Array.isArray(fec)) {
1182
+ const reason = { code: "fec_member", path: at(`/tracks/${ti}/fec`), outcome: "violation" };
1183
+ out.set(name, Object.freeze({ status: "unavailable", track: name, pointer: pointers[ti], reason: Object.freeze(reason) }));
1184
+ return;
1185
+ }
1186
+ if (fec.length === 0)
1187
+ return;
1188
+ const insts = report.instances.filter((i) => i.trackIndex === ti);
1189
+ const isOverlay = (i) => Object.hasOwn(recordOf(fec[Number(i.path.split("/").pop())]) ?? {}, "scope");
1190
+ const bases = insts.filter((i) => !isOverlay(i));
1191
+ const okBases = bases.filter((i) => i.outcome === "ok");
1192
+ if (okBases.length !== 1) {
1193
+ const reason = (
1194
+ // No base instance at all (overlay-only `fec`) is a count of 0, not
1195
+ // the overlay's own issue.
1196
+ okBases.length > 1 || bases.length === 0 ? Object.freeze({ code: "fec_base_instances", path: at(`/tracks/${ti}/fec`), outcome: "violation" }) : why(bases[0] ?? insts[0])
1197
+ );
1198
+ out.set(name, Object.freeze({ status: "unavailable", track: name, pointer: pointers[ti], reason }));
1199
+ return;
1200
+ }
1201
+ const b = okBases[0];
1202
+ const base = geometry(recordOf(fec[Number(b.path.split("/").pop())]), b.instanceId, nameAt(b.symbolTrack));
1203
+ const priorityOf = (i) => {
1204
+ const p = i === null ? void 0 : trackAt(i)["priority"];
1205
+ return typeof p === "number" ? p : DEFAULT_FEC_REPAIR_PRIORITY;
1206
+ };
1207
+ const layers = [
1208
+ { layer: 0, repairTrackName: base.repairTrack, repairSymbols: base.repairSymbols, rsIdOffset: 0, priority: priorityOf(b.repairTrack) }
1209
+ ];
1210
+ const more = b.boundRepairTracks.map((tj) => ({ tj, layer: trackAt(tj)["repairLayer"] })).sort((x, y) => x.layer - y.layer);
1211
+ let offset = base.repairSymbols;
1212
+ for (const { tj, layer } of more) {
1213
+ const repairSymbols = trackAt(tj)["repairSymbols"];
1214
+ layers.push({ layer, repairTrackName: nameAt(tj), repairSymbols, rsIdOffset: offset, priority: priorityOf(tj) });
1215
+ offset += repairSymbols;
1216
+ }
1217
+ let overlay;
1218
+ let overlayUnavailable;
1219
+ const ov = insts.find(isOverlay);
1220
+ if (ov?.outcome === "ok") {
1221
+ const g = geometry(recordOf(fec[Number(ov.path.split("/").pop())]), ov.instanceId, nameAt(ov.symbolTrack));
1222
+ overlay = Object.freeze({
1223
+ repairTrackName: g.repairTrack,
1224
+ scope: "keyframe",
1225
+ sourceSymbols: g.sourceSymbols,
1226
+ repairSymbols: g.repairSymbols,
1227
+ priority: priorityOf(ov.repairTrack),
1228
+ geometry: g
1229
+ });
1230
+ } else if (ov)
1231
+ overlayUnavailable = why(ov);
1232
+ out.set(name, Object.freeze({
1233
+ status: "ok",
1234
+ track: name,
1235
+ pointer: pointers[ti],
1236
+ base,
1237
+ layers: Object.freeze(layers.map((l) => Object.freeze(l))),
1238
+ totalBaseRepairSymbols: offset,
1239
+ ...overlay ? { overlay } : {},
1240
+ ...overlayUnavailable ? { overlayUnavailable } : {}
1241
+ }));
1242
+ });
1243
+ return out;
1244
+ }
1245
+ function geometry(o, instanceId, symbolTrack) {
1246
+ return Object.freeze({
1247
+ instanceId,
1248
+ scheme: o["scheme"],
1249
+ blockProfile: o["blockProfile"],
1250
+ sourceSymbols: o["sourceSymbols"],
1251
+ repairSymbols: o["repairSymbols"],
1252
+ symbolSize: o["symbolSize"],
1253
+ ...typeof o["interleaveDepthMs"] === "number" ? { interleaveDepthMs: o["interleaveDepthMs"] } : {},
1254
+ ...typeof o["reorderToleranceMs"] === "number" ? { reorderToleranceMs: o["reorderToleranceMs"] } : {},
1255
+ repairTrack: o["repairTrack"],
1256
+ ...symbolTrack !== void 0 ? { symbolTrack } : {}
1257
+ });
1258
+ }
1259
+
756
1260
  // src/mfu-reassembler.ts
757
1261
  function fragmentProvenance(fragment) {
758
1262
  if (fragment.deliveryProvenance !== void 0) return fragment.deliveryProvenance;
@@ -7464,321 +7968,6 @@ var BlockcastMmt = (() => {
7464
7968
  });
7465
7969
  }
7466
7970
 
7467
- // src/repair-layers.ts
7468
- var REPAIR_PRIORITY_MIN = 192;
7469
- var REPAIR_PRIORITY_MAX = 255;
7470
- var DEFAULT_REPAIR_PRIORITY = 240;
7471
- var MAX_RAPTORQ_SOURCE_SYMBOLS = 56403;
7472
- var MAX_REPAIR_SYMBOLS = 16777215;
7473
- var MAX_ENCODING_SYMBOL_ID = 16777215;
7474
- var MAX_REED_SOLOMON_BLOCK_SYMBOLS = 255;
7475
- var RepairContractError = class extends Error {
7476
- constructor(code, path, message) {
7477
- super(`[repair-layers:${code}] ${path}: ${message}`);
7478
- this.code = code;
7479
- this.path = path;
7480
- this.name = "RepairContractError";
7481
- }
7482
- };
7483
- var throwRepairContractError = (code, path, message) => {
7484
- throw new RepairContractError(code, path, message);
7485
- };
7486
- function readFecBlockProfile(value, path, declaresFecInstance, fail) {
7487
- if (value === void 0 && !declaresFecInstance) return void 0;
7488
- if (isFecBlockProfile(value)) return value;
7489
- if (typeof value === "string" && value.length > 0) {
7490
- return fail(
7491
- "track_fec_block_profile_unimplemented",
7492
- path,
7493
- `${JSON.stringify(value)} is not an implemented FEC block profile; only "iso-ssbg1-v1" is implemented`
7494
- );
7495
- }
7496
- try {
7497
- return parseFecBlockProfile(value, "blockProfile");
7498
- } catch (error) {
7499
- return fail("track_fec_block_profile", path, error.message);
7500
- }
7501
- }
7502
- var REPAIR_FIELDS = ["depends", "repairLayer", "repairSymbols", "priority", "scope", "sourceSymbols"];
7503
- var REPAIR_TRACK_GEOMETRY_KEYS = [
7504
- "mmtpMode",
7505
- "timescale",
7506
- "groupDurationMs",
7507
- "groupDurationTicks",
7508
- "keyframeIntervalMs",
7509
- "keyframeIntervalTicks",
7510
- "fec",
7511
- "symbolSize",
7512
- "interleaveDepthMs"
7513
- ];
7514
- var REPAIR_TRACK_ONLY_KEYS = ["repairLayer", "repairSymbols", "sourceSymbols", "scope"];
7515
- function rejectRepairTrackGeometry(path, fields, fail) {
7516
- for (const key of REPAIR_TRACK_GEOMETRY_KEYS) {
7517
- if (fields[key] !== void 0) {
7518
- fail(
7519
- "track_repair_geometry",
7520
- `${path}.${key}`,
7521
- "a repair track carries no block geometry; it is shared with the source track's fec object"
7522
- );
7523
- }
7524
- }
7525
- }
7526
- function repairReachability(fecObjects) {
7527
- let declaresFecSource = false;
7528
- const layerZeroNames = /* @__PURE__ */ new Set();
7529
- for (const candidate of fecObjects) {
7530
- if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) continue;
7531
- const fec = candidate;
7532
- if (fec["algorithm"] === "none") continue;
7533
- declaresFecSource = true;
7534
- const repairTrack = fec["repairTrack"];
7535
- if (typeof repairTrack === "string" && repairTrack.length > 0) layerZeroNames.add(repairTrack);
7536
- }
7537
- return { declaresFecSource, layerZeroNames };
7538
- }
7539
- function assertRepairTrackReachable(descriptor, reachability, fail) {
7540
- if (dependsTarget(descriptor) !== void 0) return;
7541
- if (reachability.declaresFecSource && !reachability.layerZeroNames.has(descriptor.name)) {
7542
- fail(
7543
- "track_repair_depends",
7544
- `${descriptor.path}.depends`,
7545
- "no source names this repair track in fec.repairTrack and it declares no depends"
7546
- );
7547
- }
7548
- }
7549
- function rejectRepairFieldsOnMediaTrack(path, fields, fail) {
7550
- for (const key of REPAIR_TRACK_ONLY_KEYS) {
7551
- if (fields[key] !== void 0) {
7552
- fail("track_repair_layer", `${path}.${key}`, `${key} is a repair track field`);
7553
- }
7554
- }
7555
- }
7556
- function describeRepairTrack(name, path, fields) {
7557
- const descriptor = { name, path };
7558
- for (const field of REPAIR_FIELDS) {
7559
- if (field in fields) descriptor[field] = fields[field];
7560
- }
7561
- return descriptor;
7562
- }
7563
- function isSafeInteger(value) {
7564
- return typeof value === "number" && Number.isSafeInteger(value);
7565
- }
7566
- function positiveInteger(value) {
7567
- return isSafeInteger(value) && value > 0;
7568
- }
7569
- function dependsTarget(descriptor) {
7570
- const depends = descriptor.depends;
7571
- return Array.isArray(depends) && depends.length === 1 && typeof depends[0] === "string" && depends[0].length > 0 ? depends[0] : void 0;
7572
- }
7573
- function validateRepairTrackFields(descriptor, fail) {
7574
- const { path } = descriptor;
7575
- if (descriptor.depends !== void 0 && dependsTarget(descriptor) === void 0) {
7576
- fail("track_repair_depends", `${path}.depends`, "must name exactly one source track");
7577
- }
7578
- if (descriptor.repairLayer !== void 0 && (!isSafeInteger(descriptor.repairLayer) || descriptor.repairLayer < 0)) {
7579
- fail("track_repair_layer", `${path}.repairLayer`, "must be a non-negative integer");
7580
- }
7581
- if (descriptor.repairSymbols !== void 0 && (!positiveInteger(descriptor.repairSymbols) || descriptor.repairSymbols > MAX_REPAIR_SYMBOLS)) {
7582
- fail("track_repair_layer", `${path}.repairSymbols`, `must be a positive integer at most ${MAX_REPAIR_SYMBOLS}`);
7583
- }
7584
- if (descriptor.priority !== void 0 && (!isSafeInteger(descriptor.priority) || descriptor.priority < REPAIR_PRIORITY_MIN || descriptor.priority > REPAIR_PRIORITY_MAX)) {
7585
- fail(
7586
- "track_repair_priority",
7587
- `${path}.priority`,
7588
- `must be an integer from ${REPAIR_PRIORITY_MIN} to ${REPAIR_PRIORITY_MAX} (MoQT scale, lower first)`
7589
- );
7590
- }
7591
- if (descriptor.scope !== void 0) {
7592
- if (descriptor.scope !== "keyframe") {
7593
- fail("track_repair_overlay", `${path}.scope`, 'must be "keyframe"');
7594
- }
7595
- if (descriptor.repairLayer !== void 0) {
7596
- fail("track_repair_overlay", `${path}.repairLayer`, "an overlay is a separate FEC instance and carries no repairLayer");
7597
- }
7598
- for (const field of ["depends", "sourceSymbols", "repairSymbols", "priority"]) {
7599
- if (descriptor[field] === void 0) {
7600
- fail("track_repair_overlay", `${path}.${field}`, `an overlay must declare ${field}`);
7601
- }
7602
- }
7603
- }
7604
- if (descriptor.sourceSymbols !== void 0) {
7605
- if (descriptor.scope === void 0) {
7606
- fail("track_repair_overlay", `${path}.sourceSymbols`, "only an overlay (scope: keyframe) declares its own sourceSymbols");
7607
- }
7608
- if (!positiveInteger(descriptor.sourceSymbols) || descriptor.sourceSymbols > MAX_RAPTORQ_SOURCE_SYMBOLS) {
7609
- fail(
7610
- "track_fec_bounds",
7611
- `${path}.sourceSymbols`,
7612
- `must be a positive integer at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`
7613
- );
7614
- }
7615
- }
7616
- if (isSafeInteger(descriptor.repairLayer) && descriptor.repairLayer >= 1) {
7617
- for (const field of ["depends", "repairSymbols", "priority"]) {
7618
- if (descriptor[field] === void 0) {
7619
- fail("track_repair_layer", `${path}.${field}`, `repair layer ${descriptor.repairLayer} must declare ${field}`);
7620
- }
7621
- }
7622
- }
7623
- }
7624
- function assembleRepairLayers(source, descriptors, fail = throwRepairContractError, options = {}) {
7625
- const layerZeroName = typeof source.repairTrack === "string" && source.repairTrack.length > 0 ? source.repairTrack : void 0;
7626
- const layerZero = layerZeroName === void 0 ? void 0 : descriptors.find((d) => d.name === layerZeroName);
7627
- if (layerZeroName !== void 0 && layerZero === void 0 && options.layerZeroMustBeDeclared) {
7628
- fail("track_repair_track", `${source.path}.repairTrack`, `unknown FEC repair track ${JSON.stringify(layerZeroName)}`);
7629
- }
7630
- const dependents = descriptors.filter((d) => d.name !== layerZeroName && dependsTarget(d) === source.name);
7631
- const k = positiveInteger(source.sourceSymbols) ? source.sourceSymbols : 0;
7632
- if (source.algorithm === "raptorq" && k > MAX_RAPTORQ_SOURCE_SYMBOLS) {
7633
- fail(
7634
- "track_fec_bounds",
7635
- `${source.path}.sourceSymbols`,
7636
- `must be at most ${MAX_RAPTORQ_SOURCE_SYMBOLS} (RFC 6330 source block limit)`
7637
- );
7638
- }
7639
- if (layerZeroName === void 0) {
7640
- if (dependents.length === 0) return void 0;
7641
- fail(
7642
- "track_repair_layer",
7643
- `${dependents[0].path}.depends`,
7644
- `repair layers of ${JSON.stringify(source.name)} require a layer 0 (fec.repairTrack) on the source`
7645
- );
7646
- }
7647
- const layered = dependents.length > 0;
7648
- let layerZeroSymbols = 0;
7649
- if (positiveInteger(source.repairSymbols)) {
7650
- layerZeroSymbols = source.repairSymbols;
7651
- } else if (layered) {
7652
- fail("track_fec", `${source.path}.repairSymbols`, "layered or overlay repair needs a positive integer repairSymbols on layer 0");
7653
- }
7654
- if (layered && !positiveInteger(source.sourceSymbols)) {
7655
- fail("track_fec", `${source.path}.sourceSymbols`, "layered or overlay repair needs a positive integer sourceSymbols on the source");
7656
- }
7657
- const reedSolomon = source.algorithm === "reed-solomon";
7658
- if (layered && source.algorithm !== "raptorq" && !reedSolomon) {
7659
- fail(
7660
- "track_fec",
7661
- `${source.path}.algorithm`,
7662
- "repair layers and overlays are defined only for raptorq and reed-solomon (draft-ramadan-moq-fec-01 6.3.4 rule 7)"
7663
- );
7664
- }
7665
- let layerZeroPriority = DEFAULT_REPAIR_PRIORITY;
7666
- if (layerZero !== void 0) {
7667
- if (layerZero.depends !== void 0 && dependsTarget(layerZero) !== source.name) {
7668
- fail(
7669
- "track_repair_depends",
7670
- `${layerZero.path}.depends`,
7671
- `layer 0 of ${JSON.stringify(source.name)} must depend on that source`
7672
- );
7673
- }
7674
- if (layerZero.repairLayer !== void 0 && layerZero.repairLayer !== 0) {
7675
- fail("track_repair_layer", `${layerZero.path}.repairLayer`, "the track named by fec.repairTrack is layer 0");
7676
- }
7677
- if (layerZero.scope !== void 0) {
7678
- fail("track_repair_overlay", `${layerZero.path}.scope`, "the track named by fec.repairTrack is base layer 0, not an overlay");
7679
- }
7680
- if (layerZero.repairSymbols !== void 0 && layerZero.repairSymbols !== layerZeroSymbols) {
7681
- fail(
7682
- "track_repair_layer",
7683
- `${layerZero.path}.repairSymbols`,
7684
- `must agree with the source's fec.repairSymbols (${layerZeroSymbols})`
7685
- );
7686
- }
7687
- if (typeof layerZero.priority === "number") layerZeroPriority = layerZero.priority;
7688
- }
7689
- const baseLayers = [
7690
- { layer: 0, repairTrackName: layerZeroName, repairSymbols: layerZeroSymbols, rsIdOffset: 0, priority: layerZeroPriority }
7691
- ];
7692
- const overlays = [];
7693
- for (const d of dependents) {
7694
- if (d.scope === void 0 && d.repairLayer === void 0) {
7695
- fail(
7696
- "track_repair_layer",
7697
- `${d.path}.repairLayer`,
7698
- `a repair track that depends on ${JSON.stringify(source.name)} must declare repairLayer (1..n) or scope: "keyframe"`
7699
- );
7700
- }
7701
- }
7702
- const layerTracks = dependents.filter((d) => d.scope === void 0).sort((a, b) => a.repairLayer - b.repairLayer);
7703
- for (const d of dependents) {
7704
- if (d.scope !== "keyframe") continue;
7705
- if (overlays.length === 1) {
7706
- fail(
7707
- "track_repair_overlay",
7708
- `${d.path}.scope`,
7709
- `${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`
7710
- );
7711
- }
7712
- const sourceSymbols = d.sourceSymbols;
7713
- const repairSymbols = d.repairSymbols;
7714
- if (sourceSymbols + repairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {
7715
- fail(
7716
- "track_fec_bounds",
7717
- `${d.path}.repairSymbols`,
7718
- `overlay sourceSymbols + repairSymbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`
7719
- );
7720
- }
7721
- if (reedSolomon && sourceSymbols + repairSymbols > MAX_REED_SOLOMON_BLOCK_SYMBOLS) {
7722
- fail(
7723
- "track_fec_bounds",
7724
- `${d.path}.repairSymbols`,
7725
- `reed-solomon overlay sourceSymbols + repairSymbols exceeds ${MAX_REED_SOLOMON_BLOCK_SYMBOLS} (draft-ramadan-moq-fec-01 6.3.4 rule 12)`
7726
- );
7727
- }
7728
- overlays.push({ repairTrackName: d.name, scope: "keyframe", sourceSymbols, repairSymbols, priority: d.priority });
7729
- }
7730
- let offset = layerZeroSymbols;
7731
- let previousPriority = layerZeroPriority;
7732
- layerTracks.forEach((d, i) => {
7733
- const expected = i + 1;
7734
- if (d.repairLayer === 0) {
7735
- fail("track_repair_layer", `${d.path}.repairLayer`, `layer 0 of ${JSON.stringify(source.name)} is ${JSON.stringify(layerZeroName)}, named by fec.repairTrack`);
7736
- }
7737
- if (d.repairLayer !== expected) {
7738
- fail(
7739
- "track_repair_layer",
7740
- `${d.path}.repairLayer`,
7741
- 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}`
7742
- );
7743
- }
7744
- const priority = d.priority;
7745
- if (priority < previousPriority) {
7746
- fail(
7747
- "track_repair_priority",
7748
- `${d.path}.priority`,
7749
- `must be at least ${previousPriority}, the priority of layer ${expected - 1}: priority is monotonic non-decreasing with layer index`
7750
- );
7751
- }
7752
- previousPriority = priority;
7753
- const repairSymbols = d.repairSymbols;
7754
- baseLayers.push({ layer: expected, repairTrackName: d.name, repairSymbols, rsIdOffset: offset, priority });
7755
- offset += repairSymbols;
7756
- });
7757
- const totalBaseRepairSymbols = offset;
7758
- if (totalBaseRepairSymbols > MAX_REPAIR_SYMBOLS) {
7759
- fail(
7760
- "track_fec_bounds",
7761
- `${layerTracks.length === 0 ? source.path : layerTracks[layerTracks.length - 1].path}.repairSymbols`,
7762
- `total repair symbols across base layers exceeds the 24-bit RS_ID space (${MAX_REPAIR_SYMBOLS})`
7763
- );
7764
- }
7765
- if (k + totalBaseRepairSymbols - 1 > MAX_ENCODING_SYMBOL_ID) {
7766
- fail(
7767
- "track_fec_bounds",
7768
- `${source.path}.sourceSymbols`,
7769
- `sourceSymbols + total base repair symbols - 1 exceeds the 24-bit ESI space (${MAX_ENCODING_SYMBOL_ID})`
7770
- );
7771
- }
7772
- if (reedSolomon && k + totalBaseRepairSymbols > MAX_REED_SOLOMON_BLOCK_SYMBOLS) {
7773
- fail(
7774
- "track_fec_bounds",
7775
- `${source.path}.sourceSymbols`,
7776
- `reed-solomon sourceSymbols + total base repair symbols exceeds ${MAX_REED_SOLOMON_BLOCK_SYMBOLS} (draft-ramadan-moq-fec-01 6.3.4 rule 12)`
7777
- );
7778
- }
7779
- return { sourceTrackName: source.name, baseLayers, overlays, totalBaseRepairSymbols };
7780
- }
7781
-
7782
7971
  // src/carp-catalog.ts
7783
7972
  function isTrackTimingConfig(obj) {
7784
7973
  if (typeof obj !== "object" || obj === null) return false;
@@ -7911,7 +8100,7 @@ var BlockcastMmt = (() => {
7911
8100
  }
7912
8101
  const tracks = catalog["tracks"];
7913
8102
  const trackRecords = [];
7914
- const positiveInteger2 = (input) => Number.isSafeInteger(input) && input > 0;
8103
+ const positiveInteger = (input) => Number.isSafeInteger(input) && input > 0;
7915
8104
  const positiveNumber = (input) => typeof input === "number" && Number.isFinite(input) && input > 0;
7916
8105
  for (let index = 0; index < tracks.length; index++) {
7917
8106
  const raw = tracks[index];
@@ -7943,7 +8132,7 @@ var BlockcastMmt = (() => {
7943
8132
  );
7944
8133
  }
7945
8134
  const codec = track["codec"];
7946
- if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" && (typeof codec !== "string" || codec.length === 0)) {
8135
+ if (packaging !== "timeline" && packaging !== "mediatimeline" && packaging !== "eventtimeline" && packaging !== "fec-repair" && packaging !== "fec-source" && (typeof codec !== "string" || codec.length === 0)) {
7947
8136
  catalogError("track_codec", `${path}.codec`, `codec is required for packaging=${packaging}`);
7948
8137
  }
7949
8138
  const timescale = track["timescale"];
@@ -7951,13 +8140,13 @@ var BlockcastMmt = (() => {
7951
8140
  const groupDurationTicks = track["groupDurationTicks"];
7952
8141
  const keyframeIntervalMs = track["keyframeIntervalMs"];
7953
8142
  const keyframeIntervalTicks = track["keyframeIntervalTicks"];
7954
- if (groupDurationMs !== void 0 && !positiveInteger2(groupDurationMs)) {
8143
+ if (groupDurationMs !== void 0 && !positiveInteger(groupDurationMs)) {
7955
8144
  catalogError("track_group_duration_ms", `${path}.groupDurationMs`, "must be a positive integer");
7956
8145
  }
7957
- if (groupDurationTicks !== void 0 && !positiveInteger2(groupDurationTicks)) {
8146
+ if (groupDurationTicks !== void 0 && !positiveInteger(groupDurationTicks)) {
7958
8147
  catalogError("track_group_duration_ticks", `${path}.groupDurationTicks`, "must be a positive integer");
7959
8148
  }
7960
- if (keyframeIntervalMs !== void 0 && !positiveNumber(keyframeIntervalMs) || keyframeIntervalTicks !== void 0 && !positiveInteger2(keyframeIntervalTicks)) {
8149
+ if (keyframeIntervalMs !== void 0 && !positiveNumber(keyframeIntervalMs) || keyframeIntervalTicks !== void 0 && !positiveInteger(keyframeIntervalTicks)) {
7961
8150
  catalogError("track_keyframe_interval", path, "keyframe interval fields must be positive numbers");
7962
8151
  }
7963
8152
  const anchor = track["presentationAnchorTicks"];
@@ -7968,17 +8157,11 @@ var BlockcastMmt = (() => {
7968
8157
  if (initRef !== void 0 && (typeof initRef !== "string" || initRef.length === 0)) {
7969
8158
  catalogError("track_init_ref", `${path}.initRef`, "must be a non-empty string");
7970
8159
  }
7971
- if (packaging === "fec-repair") {
7972
- rejectRepairTrackGeometry(path, track, catalogError);
7973
- validateRepairTrackFields(describeRepairTrack(name, path, track), catalogError);
7974
- } else {
7975
- rejectRepairFieldsOnMediaTrack(path, track, catalogError);
7976
- }
7977
8160
  if (packaging === "mmtp") {
7978
8161
  if (track["mmtpMode"] !== "mfu") {
7979
8162
  catalogError("track_mmtp_mode", `${path}.mmtpMode`, "must be mfu");
7980
8163
  }
7981
- if (!positiveInteger2(timescale)) {
8164
+ if (!positiveInteger(timescale)) {
7982
8165
  catalogError("track_timescale", `${path}.timescale`, "must be a positive integer");
7983
8166
  }
7984
8167
  if (groupDurationMs === void 0 && groupDurationTicks === void 0) {
@@ -7992,85 +8175,6 @@ var BlockcastMmt = (() => {
7992
8175
  catalogError("track_group_duration_exact", `${path}.groupDurationMs`, "does not resolve to an exact integer tick count");
7993
8176
  }
7994
8177
  }
7995
- if (track["fec"] !== void 0) {
7996
- const fec = track["fec"];
7997
- if (typeof fec !== "object" || fec === null || Array.isArray(fec)) {
7998
- catalogError("track_fec", `${path}.fec`, "FEC must be an object");
7999
- }
8000
- const config = fec;
8001
- const algorithm = config["algorithm"];
8002
- if (typeof algorithm !== "string" || algorithm.length === 0) {
8003
- catalogError("track_fec", `${path}.fec.algorithm`, "must be a non-empty string");
8004
- }
8005
- if ("interleaveDepth" in config || "interleaveMs" in config) {
8006
- catalogError("track_fec", `${path}.fec`, "interleaveDepthMs is the only supported interleave field");
8007
- }
8008
- if ("enhancementRepair" in config) {
8009
- catalogError(
8010
- "track_fec",
8011
- `${path}.fec.enhancementRepair`,
8012
- "repair layers are self-describing fec-repair tracks (depends, repairLayer); fec.enhancementRepair is not supported"
8013
- );
8014
- }
8015
- if (config["interleaveDepthMs"] !== void 0 && (!Number.isSafeInteger(config["interleaveDepthMs"]) || config["interleaveDepthMs"] < 0)) {
8016
- catalogError("track_fec", `${path}.fec.interleaveDepthMs`, "must be a non-negative safe integer");
8017
- }
8018
- if (algorithm !== "none") {
8019
- for (const field of ["sourceSymbols", "repairSymbols", "symbolSize"]) {
8020
- if (!positiveInteger2(config[field])) {
8021
- catalogError("track_fec", `${path}.fec.${field}`, "must be a positive integer");
8022
- }
8023
- }
8024
- if (algorithm === "raptorq" && config["symbolSize"] % 8 !== 0) {
8025
- catalogError("track_fec", `${path}.fec.symbolSize`, "RaptorQ symbolSize must be a multiple of 8");
8026
- }
8027
- if (typeof config["repairTrack"] !== "string" || config["repairTrack"].length === 0) {
8028
- catalogError("track_fec", `${path}.fec.repairTrack`, "must be a non-empty string");
8029
- }
8030
- }
8031
- readFecBlockProfile(config["blockProfile"], `${path}.fec.blockProfile`, algorithm !== "none", catalogError);
8032
- if (algorithm !== "none" && config["symbolSize"] < ISO_SSBG1_MIN_SYMBOL_SIZE) {
8033
- catalogError("track_fec", `${path}.fec.symbolSize`, "iso-ssbg1-v1 symbolSize must be at least 14");
8034
- }
8035
- }
8036
- }
8037
- const repairDescriptors = trackRecords.flatMap(
8038
- (track, index) => track["packaging"] === "fec-repair" ? [describeRepairTrack(track["name"], `$.tracks[${index}]`, track)] : []
8039
- );
8040
- const reachability = repairReachability(
8041
- trackRecords.filter((track) => track["packaging"] !== "fec-repair").map((track) => track["fec"])
8042
- );
8043
- for (const descriptor of repairDescriptors) {
8044
- assertRepairTrackReachable(descriptor, reachability, catalogError);
8045
- const target = dependsTarget(descriptor);
8046
- if (target === void 0) continue;
8047
- const source = trackRecords.find((candidate) => candidate["name"] === target);
8048
- const sourceFec = source?.["fec"];
8049
- if (!source || source["packaging"] !== "mmtp" || !sourceFec || sourceFec["algorithm"] === "none") {
8050
- catalogError(
8051
- "track_repair_depends",
8052
- `${descriptor.path}.depends`,
8053
- `${JSON.stringify(target)} is not an mmtp track with FEC`
8054
- );
8055
- }
8056
- }
8057
- for (let index = 0; index < trackRecords.length; index++) {
8058
- const track = trackRecords[index];
8059
- const fec = track["fec"];
8060
- if (!fec || fec["algorithm"] === "none") continue;
8061
- assembleRepairLayers(
8062
- {
8063
- name: track["name"],
8064
- path: `$.tracks[${index}].fec`,
8065
- algorithm: fec["algorithm"],
8066
- sourceSymbols: fec["sourceSymbols"],
8067
- repairSymbols: fec["repairSymbols"],
8068
- repairTrack: fec["repairTrack"]
8069
- },
8070
- repairDescriptors,
8071
- catalogError,
8072
- { layerZeroMustBeDeclared: true }
8073
- );
8074
8178
  }
8075
8179
  const switchingSets = /* @__PURE__ */ new Map();
8076
8180
  for (let index = 0; index < trackRecords.length; index++) {
@@ -8218,13 +8322,8 @@ var BlockcastMmt = (() => {
8218
8322
  function recordOf2(value) {
8219
8323
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
8220
8324
  }
8221
- function isPositiveSafeInteger2(value) {
8222
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
8223
- }
8224
- function catalogDeclaresFec(fec) {
8225
- const block = recordOf2(fec);
8226
- if (block === void 0 || block["algorithm"] === "none" || fecAlgorithmUnimplemented(block["algorithm"])) return false;
8227
- return isPositiveSafeInteger2(block["sourceSymbols"]) && isPositiveSafeInteger2(block["symbolSize"]) && isFecBlockProfile(block["blockProfile"]) && isoBlockSpanUsable(block) && !catalogFecDeclaresUnconfigurableInstance(block);
8325
+ function catalogDeclaresFec(catalog, track) {
8326
+ return resolveFecBindings(catalog).get(track)?.status === "ok";
8228
8327
  }
8229
8328
  function requireBoolean(value, field) {
8230
8329
  if (typeof value !== "boolean") {
@@ -8334,9 +8433,9 @@ var BlockcastMmt = (() => {
8334
8433
  var _decoder, _blocks, _settled, _have, _newest, _prunedAt, _repairPacketId, _newestSource, _heldLayers, _anchorHorizon, _stats3, _MmtpFecSession_instances, drop_fn2, noteNewest_fn, blockStartOf_fn, countHeld_fn, noteMixed_fn, behindHorizon_fn, settle_fn, settleClean_fn, settleDecoded_fn, retireBehindHorizon_fn;
8335
8434
  var MmtpFecSession = class {
8336
8435
  /**
8337
- * @throws when the catalog block lacks or misstates a field the session
8338
- * needs: the block profile, K, P_0 (`repairSymbols`), T, `interleaveDepthMs` or
8339
- * `reorderToleranceMs`. None is defaulted.
8436
+ * @throws when the instance lacks or misstates a field the session needs:
8437
+ * the scheme, the block profile, K, P_0 (`repairSymbols`), T,
8438
+ * `interleaveDepthMs` or `reorderToleranceMs`. None is defaulted.
8340
8439
  */
8341
8440
  constructor(config) {
8342
8441
  __privateAdd(this, _MmtpFecSession_instances);
@@ -8374,13 +8473,13 @@ var BlockcastMmt = (() => {
8374
8473
  });
8375
8474
  const fec = config?.fec;
8376
8475
  if (!fec || typeof fec !== "object") {
8377
- throw new Error("MmtpFecSession: the catalog fec block is required");
8476
+ throw new Error("MmtpFecSession: the catalog fec instance is required");
8378
8477
  }
8379
8478
  this.blockProfile = parseFecBlockProfile(fec.blockProfile, "fec.blockProfile");
8380
- if (fec.algorithm !== void 0 && fec.algorithm !== "raptorq" && fec.algorithm !== "reed-solomon") {
8381
- throw new Error(`MmtpFecSession: fec.algorithm ${JSON.stringify(fec.algorithm)} is not implemented`);
8479
+ if (fec.scheme !== "raptorq" && fec.scheme !== "reed-solomon") {
8480
+ throw new Error(`MmtpFecSession: fec.scheme ${JSON.stringify(fec.scheme)} is not implemented`);
8382
8481
  }
8383
- this.algorithm = fec.algorithm === "reed-solomon" ? "reed-solomon" : "raptorq";
8482
+ this.algorithm = fec.scheme;
8384
8483
  this.sourceSymbols = requirePositiveInteger(fec.sourceSymbols, "fec.sourceSymbols");
8385
8484
  this.repairSymbols = requirePositiveInteger(fec.repairSymbols, "fec.repairSymbols");
8386
8485
  this.symbolSize = requirePositiveInteger(fec.symbolSize, "fec.symbolSize");
@@ -8734,7 +8833,7 @@ var BlockcastMmt = (() => {
8734
8833
  }
8735
8834
  return void 0;
8736
8835
  }
8737
- function intIn(value, min, max) {
8836
+ function intIn2(value, min, max) {
8738
8837
  return Number.isInteger(value) && value >= min && value <= max;
8739
8838
  }
8740
8839
  function catalogAtsc3NetworkSource(networkSource) {
@@ -8742,12 +8841,12 @@ var BlockcastMmt = (() => {
8742
8841
  for (const raw of entries) {
8743
8842
  if (!raw || typeof raw !== "object") continue;
8744
8843
  const ns = raw;
8745
- if (ns.type !== "atsc3" || !intIn(ns.frequency, 1, Number.MAX_SAFE_INTEGER)) continue;
8844
+ if (ns.type !== "atsc3" || !intIn2(ns.frequency, 1, Number.MAX_SAFE_INTEGER)) continue;
8746
8845
  const slsUri = typeof ns.slsUri === "string" ? ns.slsUri.trim() : "";
8747
8846
  return {
8748
8847
  frequency: ns.frequency,
8749
- ...intIn(ns.plpId, 0, 63) ? { plpId: ns.plpId } : {},
8750
- ...intIn(ns.serviceId, 0, 65535) ? { serviceId: ns.serviceId } : {},
8848
+ ...intIn2(ns.plpId, 0, 63) ? { plpId: ns.plpId } : {},
8849
+ ...intIn2(ns.serviceId, 0, 65535) ? { serviceId: ns.serviceId } : {},
8751
8850
  ...slsUri ? { slsUri } : {}
8752
8851
  };
8753
8852
  }