@blockcast/mmt-container 0.3.0 → 0.4.0-main.71d61b2a53f2

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 (605) hide show
  1. package/README.md +33 -16
  2. package/dist/browser-runtime.js +14743 -14412
  3. package/dist/browser-runtime.js.map +4 -4
  4. package/dist/carp-catalog.d.ts +24 -19
  5. package/dist/carp-catalog.d.ts.map +1 -1
  6. package/dist/carp-catalog.js +29 -14
  7. package/dist/carp-catalog.js.map +4 -4
  8. package/dist/catalog-fec-params.d.ts +88 -28
  9. package/dist/catalog-fec-params.d.ts.map +1 -1
  10. package/dist/catalog-fec-params.js +60 -23
  11. package/dist/catalog-fec-params.js.map +4 -4
  12. package/dist/catalog-network-source.js +1 -7
  13. package/dist/catalog-network-source.js.map +4 -4
  14. package/dist/cmaf-block-assembler.d.ts +0 -2
  15. package/dist/cmaf-block-assembler.d.ts.map +1 -1
  16. package/dist/cmaf-block-assembler.js +0 -1
  17. package/dist/cmaf-block-assembler.js.map +2 -2
  18. package/dist/cmcd-producer.d.ts.map +1 -1
  19. package/dist/cmcd-producer.js +0 -4
  20. package/dist/cmcd-producer.js.map +2 -2
  21. package/dist/delivery-catalog.d.ts +124 -0
  22. package/dist/delivery-catalog.d.ts.map +1 -0
  23. package/dist/delivery-catalog.js +1857 -0
  24. package/dist/delivery-catalog.js.map +7 -0
  25. package/dist/delta-fallback-config.d.ts +42 -0
  26. package/dist/delta-fallback-config.d.ts.map +1 -0
  27. package/dist/delta-fallback-config.js +59 -0
  28. package/dist/delta-fallback-config.js.map +7 -0
  29. package/dist/fec-manager.d.ts +36 -30
  30. package/dist/fec-manager.d.ts.map +1 -1
  31. package/dist/fec-manager.js +303 -293
  32. package/dist/fec-manager.js.map +4 -4
  33. package/dist/fec-state.d.ts +19 -31
  34. package/dist/fec-state.d.ts.map +1 -1
  35. package/dist/fec-state.js +4 -10
  36. package/dist/fec-state.js.map +4 -4
  37. package/dist/fec-worker-setup.d.ts +18 -14
  38. package/dist/fec-worker-setup.d.ts.map +1 -1
  39. package/dist/fec-worker-setup.js +81 -96
  40. package/dist/fec-worker-setup.js.map +4 -4
  41. package/dist/index.d.ts +5 -3
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +16 -0
  44. package/dist/index.js.map +2 -2
  45. package/dist/iso-block-locator.js +1 -7
  46. package/dist/iso-block-locator.js.map +4 -4
  47. package/dist/media-router.d.ts +20 -8
  48. package/dist/media-router.d.ts.map +1 -1
  49. package/dist/media-router.js +46 -21
  50. package/dist/media-router.js.map +2 -2
  51. package/dist/mfu-reassembler.js +24 -30
  52. package/dist/mfu-reassembler.js.map +4 -4
  53. package/dist/mmtp-container-parser.d.ts +6 -11
  54. package/dist/mmtp-container-parser.d.ts.map +1 -1
  55. package/dist/mmtp-container-parser.js +56 -97
  56. package/dist/mmtp-container-parser.js.map +4 -4
  57. package/dist/mmtp-container-parser.no-modules.js +78 -105
  58. package/dist/mmtp-container-parser.no-modules.js.map +4 -4
  59. package/dist/mmtp-decode-pipeline.js +56 -97
  60. package/dist/mmtp-decode-pipeline.js.map +4 -4
  61. package/dist/mmtp-router.d.ts +37 -26
  62. package/dist/mmtp-router.d.ts.map +1 -1
  63. package/dist/mmtp-router.js +162 -313
  64. package/dist/mmtp-router.js.map +4 -4
  65. package/dist/multicast-input.js +1 -7
  66. package/dist/multicast-input.js.map +4 -4
  67. package/dist/multicast-manager.d.ts +34 -6
  68. package/dist/multicast-manager.d.ts.map +1 -1
  69. package/dist/multicast-manager.js +709 -658
  70. package/dist/multicast-manager.js.map +4 -4
  71. package/dist/multicast-transport.d.ts +1 -1
  72. package/dist/multicast-transport.d.ts.map +1 -1
  73. package/dist/multicast-transport.js +44 -26
  74. package/dist/multicast-transport.js.map +4 -4
  75. package/dist/multicast-unwrap.js +29 -35
  76. package/dist/multicast-unwrap.js.map +4 -4
  77. package/dist/no-modules.js +77 -104
  78. package/dist/no-modules.js.map +4 -4
  79. package/dist/repair-fetch.d.ts +30 -19
  80. package/dist/repair-fetch.d.ts.map +1 -1
  81. package/dist/repair-fetch.js +33 -24
  82. package/dist/repair-fetch.js.map +4 -4
  83. package/dist/repair-layers.d.ts +18 -7
  84. package/dist/repair-layers.d.ts.map +1 -1
  85. package/dist/repair-layers.js +12 -10
  86. package/dist/repair-layers.js.map +4 -4
  87. package/dist/signaling.js +2 -11
  88. package/dist/signaling.js.map +4 -4
  89. package/dist/timestamp-codec.js +24 -30
  90. package/dist/timestamp-codec.js.map +4 -4
  91. package/dist/track-session.d.ts +7 -0
  92. package/dist/track-session.d.ts.map +1 -1
  93. package/dist/track-session.js +15 -8
  94. package/dist/track-session.js.map +2 -2
  95. package/dist/wasm-mfu-reassembler.js +1 -7
  96. package/dist/wasm-mfu-reassembler.js.map +4 -4
  97. package/dist/wasm-mmtp-container-parser.js +56 -97
  98. package/dist/wasm-mmtp-container-parser.js.map +4 -4
  99. package/package.json +7 -14
  100. package/schemas/catalog.schema.json +3 -3
  101. package/src/carp-catalog.ts +46 -24
  102. package/src/catalog-fec-params.ts +173 -64
  103. package/src/cmaf-block-assembler.ts +1 -4
  104. package/src/cmcd-producer.ts +0 -6
  105. package/src/delivery-catalog.ts +498 -0
  106. package/src/delta-fallback-config.ts +72 -0
  107. package/src/fec-manager.ts +414 -334
  108. package/src/fec-state.ts +20 -32
  109. package/src/fec-worker-setup.ts +109 -90
  110. package/src/index.ts +32 -0
  111. package/src/media-router.ts +59 -21
  112. package/src/mmtp-container-parser.ts +27 -18
  113. package/src/mmtp-router.ts +146 -313
  114. package/src/multicast-manager.ts +184 -83
  115. package/src/multicast-transport.ts +16 -9
  116. package/src/repair-fetch.ts +46 -27
  117. package/src/repair-layers.ts +28 -8
  118. package/src/track-session.ts +28 -9
  119. package/test-vectors/catalog/README.md +37 -17
  120. package/test-vectors/catalog/negative/fec-block-profile-retired.json +52 -0
  121. package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +1 -1
  122. package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +1 -1
  123. package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +1 -1
  124. package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +1 -1
  125. package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +1 -1
  126. package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +1 -1
  127. package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +1 -1
  128. package/test-vectors/catalog/negative/multicast-endpoint-relay-address.json +39 -0
  129. package/test-vectors/catalog/negative/multicast-network-source-empty.json +1 -1
  130. package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +1 -1
  131. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +1 -1
  132. package/test-vectors/catalog/positive/fec-layered-repair.json +2 -2
  133. package/test-vectors/catalog/positive/fec-multicast.json +2 -1
  134. package/test-vectors/catalog/positive/multicast-amt-manual-relay.json +38 -0
  135. package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +2 -1
  136. package/test-vectors/catalog-timing.json +356 -0
  137. package/test-vectors/catalog-validation.json +40 -5
  138. package/test-vectors/delivery-catalog/README.md +11 -0
  139. package/test-vectors/delivery-catalog/negative/missing-block-profile.json +58 -0
  140. package/test-vectors/delivery-catalog/negative/packet-id-signaling.json +59 -0
  141. package/test-vectors/delivery-catalog/negative/retired-k-padded-block-profile.json +59 -0
  142. package/test-vectors/delivery-catalog/positive/dash.json +56 -0
  143. package/test-vectors/delivery-catalog/positive/mmtp-source-only.json +43 -0
  144. package/node_modules/@blockcast/fec-worker/README.md +0 -223
  145. package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
  146. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts +0 -38
  147. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts.map +0 -1
  148. package/node_modules/@blockcast/fec-worker/dist/block-order.js +0 -78
  149. package/node_modules/@blockcast/fec-worker/dist/block-order.js.map +0 -1
  150. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +0 -93
  151. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +0 -1
  152. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +0 -346
  153. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +0 -1
  154. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +0 -380
  155. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +0 -1
  156. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +0 -11
  157. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +0 -1
  158. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +0 -14
  159. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +0 -1
  160. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +0 -859
  161. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +0 -7
  162. package/node_modules/@blockcast/fec-worker/dist/index.d.ts +0 -10
  163. package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +0 -1
  164. package/node_modules/@blockcast/fec-worker/dist/index.js +0 -8
  165. package/node_modules/@blockcast/fec-worker/dist/index.js.map +0 -1
  166. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +0 -20
  167. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +0 -1
  168. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +0 -370
  169. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +0 -7
  170. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +0 -104
  171. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +0 -1
  172. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +0 -181
  173. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +0 -1
  174. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +0 -194
  175. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +0 -1
  176. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +0 -62
  177. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +0 -1
  178. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +0 -9
  179. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +0 -1
  180. package/node_modules/@blockcast/fec-worker/dist/transfer.js +0 -13
  181. package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +0 -1
  182. package/node_modules/@blockcast/fec-worker/package.json +0 -59
  183. package/node_modules/@blockcast/fec-worker/src/block-order.ts +0 -93
  184. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +0 -392
  185. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +0 -399
  186. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +0 -1502
  187. package/node_modules/@blockcast/fec-worker/src/index.ts +0 -35
  188. package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +0 -589
  189. package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +0 -222
  190. package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +0 -209
  191. package/node_modules/@blockcast/fec-worker/src/transfer.ts +0 -12
  192. package/node_modules/@blockcast/mmt-alta-parse/README.md +0 -181
  193. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  194. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +0 -57
  195. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +0 -1
  196. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +0 -115
  197. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +0 -1
  198. package/node_modules/@blockcast/mmt-alta-parse/package.json +0 -50
  199. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +0 -136
  200. package/node_modules/@blockcast/mmt-base/README.md +0 -134
  201. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  202. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +0 -55
  203. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +0 -1
  204. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +0 -128
  205. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +0 -1
  206. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +0 -7
  207. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +0 -1
  208. package/node_modules/@blockcast/mmt-base/dist/index.js +0 -7
  209. package/node_modules/@blockcast/mmt-base/dist/index.js.map +0 -1
  210. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +0 -292
  211. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +0 -1
  212. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +0 -612
  213. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +0 -1
  214. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +0 -18
  215. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +0 -1
  216. package/node_modules/@blockcast/mmt-base/dist/net-constants.js +0 -18
  217. package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +0 -1
  218. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +0 -47
  219. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +0 -1
  220. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +0 -54
  221. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +0 -1
  222. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +0 -57
  223. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +0 -1
  224. package/node_modules/@blockcast/mmt-base/dist/serial-number.js +0 -98
  225. package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +0 -1
  226. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +0 -312
  227. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +0 -1
  228. package/node_modules/@blockcast/mmt-base/dist/signalling.js +0 -637
  229. package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +0 -1
  230. package/node_modules/@blockcast/mmt-base/package.json +0 -49
  231. package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +0 -145
  232. package/node_modules/@blockcast/mmt-base/src/index.ts +0 -6
  233. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +0 -892
  234. package/node_modules/@blockcast/mmt-base/src/net-constants.ts +0 -17
  235. package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +0 -67
  236. package/node_modules/@blockcast/mmt-base/src/serial-number.ts +0 -107
  237. package/node_modules/@blockcast/mmt-base/src/signalling.ts +0 -862
  238. package/node_modules/@blockcast/mmt-fec/README.md +0 -271
  239. package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
  240. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +0 -82
  241. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +0 -1
  242. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +0 -296
  243. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +0 -1
  244. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +0 -106
  245. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +0 -1
  246. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +0 -288
  247. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +0 -1
  248. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +0 -46
  249. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +0 -1
  250. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +0 -96
  251. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +0 -1
  252. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +0 -33
  253. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +0 -1
  254. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +0 -57
  255. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +0 -1
  256. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +0 -205
  257. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +0 -1
  258. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +0 -240
  259. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +0 -1
  260. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +0 -28
  261. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +0 -1
  262. package/node_modules/@blockcast/mmt-fec/dist/index.js +0 -28
  263. package/node_modules/@blockcast/mmt-fec/dist/index.js.map +0 -1
  264. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +0 -20
  265. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +0 -1
  266. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +0 -28
  267. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +0 -1
  268. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +0 -258
  269. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +0 -1
  270. package/node_modules/@blockcast/mmt-fec/dist/oti.js +0 -382
  271. package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +0 -1
  272. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +0 -41
  273. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +0 -1
  274. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +0 -81
  275. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +0 -1
  276. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts +0 -82
  277. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts.map +0 -1
  278. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js +0 -170
  279. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js.map +0 -1
  280. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +0 -181
  281. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  282. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +0 -57
  283. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +0 -1
  284. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +0 -115
  285. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +0 -1
  286. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +0 -50
  287. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +0 -136
  288. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +0 -134
  289. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  290. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +0 -55
  291. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +0 -1
  292. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +0 -128
  293. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +0 -1
  294. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +0 -7
  295. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +0 -1
  296. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +0 -7
  297. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +0 -1
  298. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +0 -292
  299. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +0 -1
  300. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +0 -612
  301. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +0 -1
  302. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +0 -18
  303. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +0 -1
  304. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js +0 -18
  305. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +0 -1
  306. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +0 -47
  307. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +0 -1
  308. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js +0 -54
  309. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +0 -1
  310. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +0 -57
  311. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +0 -1
  312. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js +0 -98
  313. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +0 -1
  314. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +0 -312
  315. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +0 -1
  316. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js +0 -637
  317. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js.map +0 -1
  318. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +0 -49
  319. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +0 -145
  320. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +0 -6
  321. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +0 -892
  322. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/net-constants.ts +0 -17
  323. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/ntp-short.ts +0 -67
  324. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/serial-number.ts +0 -107
  325. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/signalling.ts +0 -862
  326. package/node_modules/@blockcast/mmt-fec/package.json +0 -79
  327. package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +0 -371
  328. package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +0 -439
  329. package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +0 -127
  330. package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +0 -68
  331. package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +0 -310
  332. package/node_modules/@blockcast/mmt-fec/src/index.ts +0 -94
  333. package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +0 -34
  334. package/node_modules/@blockcast/mmt-fec/src/oti.ts +0 -463
  335. package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +0 -101
  336. package/node_modules/@blockcast/mmt-fec/src/repair-group.ts +0 -195
  337. package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
  338. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +0 -28
  339. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +0 -1
  340. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +0 -128
  341. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +0 -1
  342. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +0 -4
  343. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +0 -1
  344. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +0 -4
  345. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +0 -1
  346. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +0 -37
  347. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +0 -1
  348. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +0 -179
  349. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +0 -1
  350. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +0 -99
  351. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +0 -1
  352. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +0 -492
  353. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +0 -1
  354. package/node_modules/@blockcast/mmt-manifest-verify/package.json +0 -45
  355. package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +0 -171
  356. package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +0 -3
  357. package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +0 -230
  358. package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +0 -669
  359. package/node_modules/@noble/hashes/LICENSE +0 -21
  360. package/node_modules/@noble/hashes/README.md +0 -521
  361. package/node_modules/@noble/hashes/_assert.d.ts +0 -17
  362. package/node_modules/@noble/hashes/_assert.d.ts.map +0 -1
  363. package/node_modules/@noble/hashes/_assert.js +0 -18
  364. package/node_modules/@noble/hashes/_assert.js.map +0 -1
  365. package/node_modules/@noble/hashes/_blake.d.ts +0 -14
  366. package/node_modules/@noble/hashes/_blake.d.ts.map +0 -1
  367. package/node_modules/@noble/hashes/_blake.js +0 -50
  368. package/node_modules/@noble/hashes/_blake.js.map +0 -1
  369. package/node_modules/@noble/hashes/_md.d.ts +0 -51
  370. package/node_modules/@noble/hashes/_md.d.ts.map +0 -1
  371. package/node_modules/@noble/hashes/_md.js +0 -162
  372. package/node_modules/@noble/hashes/_md.js.map +0 -1
  373. package/node_modules/@noble/hashes/_u64.d.ts +0 -55
  374. package/node_modules/@noble/hashes/_u64.d.ts.map +0 -1
  375. package/node_modules/@noble/hashes/_u64.js +0 -90
  376. package/node_modules/@noble/hashes/_u64.js.map +0 -1
  377. package/node_modules/@noble/hashes/argon2.d.ts +0 -32
  378. package/node_modules/@noble/hashes/argon2.d.ts.map +0 -1
  379. package/node_modules/@noble/hashes/argon2.js +0 -401
  380. package/node_modules/@noble/hashes/argon2.js.map +0 -1
  381. package/node_modules/@noble/hashes/blake1.d.ts +0 -106
  382. package/node_modules/@noble/hashes/blake1.d.ts.map +0 -1
  383. package/node_modules/@noble/hashes/blake1.js +0 -459
  384. package/node_modules/@noble/hashes/blake1.js.map +0 -1
  385. package/node_modules/@noble/hashes/blake2.d.ts +0 -116
  386. package/node_modules/@noble/hashes/blake2.d.ts.map +0 -1
  387. package/node_modules/@noble/hashes/blake2.js +0 -420
  388. package/node_modules/@noble/hashes/blake2.js.map +0 -1
  389. package/node_modules/@noble/hashes/blake2b.d.ts +0 -11
  390. package/node_modules/@noble/hashes/blake2b.d.ts.map +0 -1
  391. package/node_modules/@noble/hashes/blake2b.js +0 -14
  392. package/node_modules/@noble/hashes/blake2b.js.map +0 -1
  393. package/node_modules/@noble/hashes/blake2s.d.ts +0 -20
  394. package/node_modules/@noble/hashes/blake2s.d.ts.map +0 -1
  395. package/node_modules/@noble/hashes/blake2s.js +0 -24
  396. package/node_modules/@noble/hashes/blake2s.js.map +0 -1
  397. package/node_modules/@noble/hashes/blake3.d.ts +0 -54
  398. package/node_modules/@noble/hashes/blake3.d.ts.map +0 -1
  399. package/node_modules/@noble/hashes/blake3.js +0 -255
  400. package/node_modules/@noble/hashes/blake3.js.map +0 -1
  401. package/node_modules/@noble/hashes/crypto.d.ts +0 -2
  402. package/node_modules/@noble/hashes/crypto.d.ts.map +0 -1
  403. package/node_modules/@noble/hashes/crypto.js +0 -5
  404. package/node_modules/@noble/hashes/crypto.js.map +0 -1
  405. package/node_modules/@noble/hashes/cryptoNode.d.ts +0 -2
  406. package/node_modules/@noble/hashes/cryptoNode.d.ts.map +0 -1
  407. package/node_modules/@noble/hashes/cryptoNode.js +0 -18
  408. package/node_modules/@noble/hashes/cryptoNode.js.map +0 -1
  409. package/node_modules/@noble/hashes/eskdf.d.ts +0 -47
  410. package/node_modules/@noble/hashes/eskdf.d.ts.map +0 -1
  411. package/node_modules/@noble/hashes/eskdf.js +0 -166
  412. package/node_modules/@noble/hashes/eskdf.js.map +0 -1
  413. package/node_modules/@noble/hashes/esm/_assert.d.ts +0 -17
  414. package/node_modules/@noble/hashes/esm/_assert.d.ts.map +0 -1
  415. package/node_modules/@noble/hashes/esm/_assert.js +0 -15
  416. package/node_modules/@noble/hashes/esm/_assert.js.map +0 -1
  417. package/node_modules/@noble/hashes/esm/_blake.d.ts +0 -14
  418. package/node_modules/@noble/hashes/esm/_blake.d.ts.map +0 -1
  419. package/node_modules/@noble/hashes/esm/_blake.js +0 -45
  420. package/node_modules/@noble/hashes/esm/_blake.js.map +0 -1
  421. package/node_modules/@noble/hashes/esm/_md.d.ts +0 -51
  422. package/node_modules/@noble/hashes/esm/_md.d.ts.map +0 -1
  423. package/node_modules/@noble/hashes/esm/_md.js +0 -155
  424. package/node_modules/@noble/hashes/esm/_md.js.map +0 -1
  425. package/node_modules/@noble/hashes/esm/_u64.d.ts +0 -55
  426. package/node_modules/@noble/hashes/esm/_u64.d.ts.map +0 -1
  427. package/node_modules/@noble/hashes/esm/_u64.js +0 -67
  428. package/node_modules/@noble/hashes/esm/_u64.js.map +0 -1
  429. package/node_modules/@noble/hashes/esm/argon2.d.ts +0 -32
  430. package/node_modules/@noble/hashes/esm/argon2.d.ts.map +0 -1
  431. package/node_modules/@noble/hashes/esm/argon2.js +0 -392
  432. package/node_modules/@noble/hashes/esm/argon2.js.map +0 -1
  433. package/node_modules/@noble/hashes/esm/blake1.d.ts +0 -106
  434. package/node_modules/@noble/hashes/esm/blake1.d.ts.map +0 -1
  435. package/node_modules/@noble/hashes/esm/blake1.js +0 -452
  436. package/node_modules/@noble/hashes/esm/blake1.js.map +0 -1
  437. package/node_modules/@noble/hashes/esm/blake2.d.ts +0 -116
  438. package/node_modules/@noble/hashes/esm/blake2.d.ts.map +0 -1
  439. package/node_modules/@noble/hashes/esm/blake2.js +0 -413
  440. package/node_modules/@noble/hashes/esm/blake2.js.map +0 -1
  441. package/node_modules/@noble/hashes/esm/blake2b.d.ts +0 -11
  442. package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +0 -1
  443. package/node_modules/@noble/hashes/esm/blake2b.js +0 -11
  444. package/node_modules/@noble/hashes/esm/blake2b.js.map +0 -1
  445. package/node_modules/@noble/hashes/esm/blake2s.d.ts +0 -20
  446. package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +0 -1
  447. package/node_modules/@noble/hashes/esm/blake2s.js +0 -21
  448. package/node_modules/@noble/hashes/esm/blake2s.js.map +0 -1
  449. package/node_modules/@noble/hashes/esm/blake3.d.ts +0 -54
  450. package/node_modules/@noble/hashes/esm/blake3.d.ts.map +0 -1
  451. package/node_modules/@noble/hashes/esm/blake3.js +0 -251
  452. package/node_modules/@noble/hashes/esm/blake3.js.map +0 -1
  453. package/node_modules/@noble/hashes/esm/crypto.d.ts +0 -2
  454. package/node_modules/@noble/hashes/esm/crypto.d.ts.map +0 -1
  455. package/node_modules/@noble/hashes/esm/crypto.js +0 -2
  456. package/node_modules/@noble/hashes/esm/crypto.js.map +0 -1
  457. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +0 -2
  458. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +0 -1
  459. package/node_modules/@noble/hashes/esm/cryptoNode.js +0 -15
  460. package/node_modules/@noble/hashes/esm/cryptoNode.js.map +0 -1
  461. package/node_modules/@noble/hashes/esm/eskdf.d.ts +0 -47
  462. package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +0 -1
  463. package/node_modules/@noble/hashes/esm/eskdf.js +0 -160
  464. package/node_modules/@noble/hashes/esm/eskdf.js.map +0 -1
  465. package/node_modules/@noble/hashes/esm/hkdf.d.ts +0 -36
  466. package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +0 -1
  467. package/node_modules/@noble/hashes/esm/hkdf.js +0 -82
  468. package/node_modules/@noble/hashes/esm/hkdf.js.map +0 -1
  469. package/node_modules/@noble/hashes/esm/hmac.d.ts +0 -35
  470. package/node_modules/@noble/hashes/esm/hmac.d.ts.map +0 -1
  471. package/node_modules/@noble/hashes/esm/hmac.js +0 -86
  472. package/node_modules/@noble/hashes/esm/hmac.js.map +0 -1
  473. package/node_modules/@noble/hashes/esm/index.d.ts +0 -2
  474. package/node_modules/@noble/hashes/esm/index.d.ts.map +0 -1
  475. package/node_modules/@noble/hashes/esm/index.js +0 -33
  476. package/node_modules/@noble/hashes/esm/index.js.map +0 -1
  477. package/node_modules/@noble/hashes/esm/legacy.d.ts +0 -71
  478. package/node_modules/@noble/hashes/esm/legacy.d.ts.map +0 -1
  479. package/node_modules/@noble/hashes/esm/legacy.js +0 -281
  480. package/node_modules/@noble/hashes/esm/legacy.js.map +0 -1
  481. package/node_modules/@noble/hashes/esm/package.json +0 -10
  482. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +0 -23
  483. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +0 -1
  484. package/node_modules/@noble/hashes/esm/pbkdf2.js +0 -97
  485. package/node_modules/@noble/hashes/esm/pbkdf2.js.map +0 -1
  486. package/node_modules/@noble/hashes/esm/ripemd160.d.ts +0 -13
  487. package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +0 -1
  488. package/node_modules/@noble/hashes/esm/ripemd160.js +0 -13
  489. package/node_modules/@noble/hashes/esm/ripemd160.js.map +0 -1
  490. package/node_modules/@noble/hashes/esm/scrypt.d.ts +0 -34
  491. package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +0 -1
  492. package/node_modules/@noble/hashes/esm/scrypt.js +0 -228
  493. package/node_modules/@noble/hashes/esm/scrypt.js.map +0 -1
  494. package/node_modules/@noble/hashes/esm/sha1.d.ts +0 -11
  495. package/node_modules/@noble/hashes/esm/sha1.d.ts.map +0 -1
  496. package/node_modules/@noble/hashes/esm/sha1.js +0 -11
  497. package/node_modules/@noble/hashes/esm/sha1.js.map +0 -1
  498. package/node_modules/@noble/hashes/esm/sha2.d.ts +0 -159
  499. package/node_modules/@noble/hashes/esm/sha2.d.ts.map +0 -1
  500. package/node_modules/@noble/hashes/esm/sha2.js +0 -375
  501. package/node_modules/@noble/hashes/esm/sha2.js.map +0 -1
  502. package/node_modules/@noble/hashes/esm/sha256.d.ts +0 -20
  503. package/node_modules/@noble/hashes/esm/sha256.d.ts.map +0 -1
  504. package/node_modules/@noble/hashes/esm/sha256.js +0 -20
  505. package/node_modules/@noble/hashes/esm/sha256.js.map +0 -1
  506. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +0 -142
  507. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +0 -1
  508. package/node_modules/@noble/hashes/esm/sha3-addons.js +0 -393
  509. package/node_modules/@noble/hashes/esm/sha3-addons.js.map +0 -1
  510. package/node_modules/@noble/hashes/esm/sha3.d.ts +0 -53
  511. package/node_modules/@noble/hashes/esm/sha3.d.ts.map +0 -1
  512. package/node_modules/@noble/hashes/esm/sha3.js +0 -234
  513. package/node_modules/@noble/hashes/esm/sha3.js.map +0 -1
  514. package/node_modules/@noble/hashes/esm/sha512.d.ts +0 -26
  515. package/node_modules/@noble/hashes/esm/sha512.d.ts.map +0 -1
  516. package/node_modules/@noble/hashes/esm/sha512.js +0 -26
  517. package/node_modules/@noble/hashes/esm/sha512.js.map +0 -1
  518. package/node_modules/@noble/hashes/esm/utils.d.ts +0 -161
  519. package/node_modules/@noble/hashes/esm/utils.d.ts.map +0 -1
  520. package/node_modules/@noble/hashes/esm/utils.js +0 -281
  521. package/node_modules/@noble/hashes/esm/utils.js.map +0 -1
  522. package/node_modules/@noble/hashes/hkdf.d.ts +0 -36
  523. package/node_modules/@noble/hashes/hkdf.d.ts.map +0 -1
  524. package/node_modules/@noble/hashes/hkdf.js +0 -88
  525. package/node_modules/@noble/hashes/hkdf.js.map +0 -1
  526. package/node_modules/@noble/hashes/hmac.d.ts +0 -35
  527. package/node_modules/@noble/hashes/hmac.d.ts.map +0 -1
  528. package/node_modules/@noble/hashes/hmac.js +0 -91
  529. package/node_modules/@noble/hashes/hmac.js.map +0 -1
  530. package/node_modules/@noble/hashes/index.d.ts +0 -1
  531. package/node_modules/@noble/hashes/index.d.ts.map +0 -1
  532. package/node_modules/@noble/hashes/index.js +0 -33
  533. package/node_modules/@noble/hashes/index.js.map +0 -1
  534. package/node_modules/@noble/hashes/legacy.d.ts +0 -71
  535. package/node_modules/@noble/hashes/legacy.d.ts.map +0 -1
  536. package/node_modules/@noble/hashes/legacy.js +0 -287
  537. package/node_modules/@noble/hashes/legacy.js.map +0 -1
  538. package/node_modules/@noble/hashes/package.json +0 -266
  539. package/node_modules/@noble/hashes/pbkdf2.d.ts +0 -23
  540. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +0 -1
  541. package/node_modules/@noble/hashes/pbkdf2.js +0 -101
  542. package/node_modules/@noble/hashes/pbkdf2.js.map +0 -1
  543. package/node_modules/@noble/hashes/ripemd160.d.ts +0 -13
  544. package/node_modules/@noble/hashes/ripemd160.d.ts.map +0 -1
  545. package/node_modules/@noble/hashes/ripemd160.js +0 -16
  546. package/node_modules/@noble/hashes/ripemd160.js.map +0 -1
  547. package/node_modules/@noble/hashes/scrypt.d.ts +0 -34
  548. package/node_modules/@noble/hashes/scrypt.d.ts.map +0 -1
  549. package/node_modules/@noble/hashes/scrypt.js +0 -232
  550. package/node_modules/@noble/hashes/scrypt.js.map +0 -1
  551. package/node_modules/@noble/hashes/sha1.d.ts +0 -11
  552. package/node_modules/@noble/hashes/sha1.d.ts.map +0 -1
  553. package/node_modules/@noble/hashes/sha1.js +0 -14
  554. package/node_modules/@noble/hashes/sha1.js.map +0 -1
  555. package/node_modules/@noble/hashes/sha2.d.ts +0 -159
  556. package/node_modules/@noble/hashes/sha2.d.ts.map +0 -1
  557. package/node_modules/@noble/hashes/sha2.js +0 -384
  558. package/node_modules/@noble/hashes/sha2.js.map +0 -1
  559. package/node_modules/@noble/hashes/sha256.d.ts +0 -20
  560. package/node_modules/@noble/hashes/sha256.d.ts.map +0 -1
  561. package/node_modules/@noble/hashes/sha256.js +0 -23
  562. package/node_modules/@noble/hashes/sha256.js.map +0 -1
  563. package/node_modules/@noble/hashes/sha3-addons.d.ts +0 -142
  564. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +0 -1
  565. package/node_modules/@noble/hashes/sha3-addons.js +0 -402
  566. package/node_modules/@noble/hashes/sha3-addons.js.map +0 -1
  567. package/node_modules/@noble/hashes/sha3.d.ts +0 -53
  568. package/node_modules/@noble/hashes/sha3.d.ts.map +0 -1
  569. package/node_modules/@noble/hashes/sha3.js +0 -239
  570. package/node_modules/@noble/hashes/sha3.js.map +0 -1
  571. package/node_modules/@noble/hashes/sha512.d.ts +0 -26
  572. package/node_modules/@noble/hashes/sha512.d.ts.map +0 -1
  573. package/node_modules/@noble/hashes/sha512.js +0 -29
  574. package/node_modules/@noble/hashes/sha512.js.map +0 -1
  575. package/node_modules/@noble/hashes/src/_assert.ts +0 -22
  576. package/node_modules/@noble/hashes/src/_blake.ts +0 -50
  577. package/node_modules/@noble/hashes/src/_md.ts +0 -176
  578. package/node_modules/@noble/hashes/src/_u64.ts +0 -91
  579. package/node_modules/@noble/hashes/src/argon2.ts +0 -497
  580. package/node_modules/@noble/hashes/src/blake1.ts +0 -534
  581. package/node_modules/@noble/hashes/src/blake2.ts +0 -486
  582. package/node_modules/@noble/hashes/src/blake2b.ts +0 -10
  583. package/node_modules/@noble/hashes/src/blake2s.ts +0 -20
  584. package/node_modules/@noble/hashes/src/blake3.ts +0 -272
  585. package/node_modules/@noble/hashes/src/crypto.ts +0 -9
  586. package/node_modules/@noble/hashes/src/cryptoNode.ts +0 -15
  587. package/node_modules/@noble/hashes/src/eskdf.ts +0 -187
  588. package/node_modules/@noble/hashes/src/hkdf.ts +0 -88
  589. package/node_modules/@noble/hashes/src/hmac.ts +0 -94
  590. package/node_modules/@noble/hashes/src/index.ts +0 -31
  591. package/node_modules/@noble/hashes/src/legacy.ts +0 -293
  592. package/node_modules/@noble/hashes/src/pbkdf2.ts +0 -122
  593. package/node_modules/@noble/hashes/src/ripemd160.ts +0 -12
  594. package/node_modules/@noble/hashes/src/scrypt.ts +0 -257
  595. package/node_modules/@noble/hashes/src/sha1.ts +0 -10
  596. package/node_modules/@noble/hashes/src/sha2.ts +0 -402
  597. package/node_modules/@noble/hashes/src/sha256.ts +0 -24
  598. package/node_modules/@noble/hashes/src/sha3-addons.ts +0 -499
  599. package/node_modules/@noble/hashes/src/sha3.ts +0 -258
  600. package/node_modules/@noble/hashes/src/sha512.ts +0 -34
  601. package/node_modules/@noble/hashes/src/utils.ts +0 -395
  602. package/node_modules/@noble/hashes/utils.d.ts +0 -161
  603. package/node_modules/@noble/hashes/utils.d.ts.map +0 -1
  604. package/node_modules/@noble/hashes/utils.js +0 -313
  605. package/node_modules/@noble/hashes/utils.js.map +0 -1
@@ -1,78 +1,17 @@
1
- // ../mmt-alta-parse/dist/index.js
1
+ // ../mmt-base/dist/mmtp.js
2
2
  var MMTP_HEADER_SIZE = 12;
3
3
  var MPU_HEADER_SIZE = 8;
4
- var MPU_LENGTH_FIELD_SIZE = 2;
5
- var MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE;
6
- var TIMED_MFU_DU_HEADER_SIZE = 14;
7
- var NONTIMED_MFU_DU_HEADER_SIZE = 4;
8
- var PACKET_TYPE_MPU = 0;
9
- var MMTP_EXT_TYPE_ALTA_AUTH = 61601;
10
- var MMTP_EXT_TRAILER_HEADER_SIZE = 4;
11
- function extractAltaTrailer(mmtpPacket, options) {
12
- if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE)
13
- return null;
14
- const packetType = mmtpPacket[1] & 63;
15
- if (packetType !== PACKET_TYPE_MPU)
16
- return null;
17
- const fecType = mmtpPacket[0] >> 3 & 3;
18
- let headerLength = MMTP_HEADER_SIZE;
19
- if (fecType < 2 && (mmtpPacket[0] & 32) !== 0)
20
- headerLength += 4;
21
- if (fecType < 2 && (mmtpPacket[0] & 2) !== 0) {
22
- if (headerLength + 4 > mmtpPacket.length)
23
- return null;
24
- const extensionLength = new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + headerLength + 2, 2).getUint16(0, false);
25
- headerLength += 4 + extensionLength;
26
- }
27
- if (headerLength + MPU_HEADER_SIZE > mmtpPacket.length)
28
- return null;
29
- const payload = mmtpPacket.subarray(headerLength);
30
- const payloadLength = payload[0] << 8 | payload[1];
31
- const flagsByte = payload[2];
32
- const fragmentType = flagsByte >> 4 & 15;
33
- const timed = (flagsByte >> 3 & 1) === 1;
34
- const fragmentationIndicator = flagsByte >> 1 & 3;
35
- if (fragmentType !== 2)
36
- return null;
37
- const dataUnitHeaderSize = fragmentationIndicator <= 1 ? timed ? TIMED_MFU_DU_HEADER_SIZE : NONTIMED_MFU_DU_HEADER_SIZE : 0;
38
- if (payloadLength <= MPU_HEADER_REMAINDER_SIZE + dataUnitHeaderSize)
39
- return null;
40
- if (fecType > 1)
41
- return null;
42
- const hasFecId = !options?.skipFecId && fecType === 1;
43
- const payloadEnd = hasFecId ? payload.length - 4 : payload.length;
44
- const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength;
45
- if (declaredEnd > payloadEnd)
46
- return null;
47
- if (declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE > payloadEnd)
48
- return null;
49
- const extType = payload[declaredEnd] << 8 | payload[declaredEnd + 1];
50
- if (extType !== MMTP_EXT_TYPE_ALTA_AUTH)
51
- return null;
52
- const extLen = payload[declaredEnd + 2] << 8 | payload[declaredEnd + 3];
53
- const authStart = declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE;
54
- if (authStart + extLen !== payloadEnd)
55
- return null;
56
- return {
57
- auth: payload.subarray(authStart, payloadEnd),
58
- signedEnd: headerLength + declaredEnd
59
- };
60
- }
61
-
62
- // ../mmt-base/dist/mmtp.js
63
- var MMTP_HEADER_SIZE2 = 12;
64
- var MPU_HEADER_SIZE2 = 8;
65
4
  var MFU_DU_HEADER_SIZE_TIMED = 14;
66
5
  var MFU_DU_HEADER_SIZE_NONTIMED = 4;
67
- var MPU_LENGTH_FIELD_SIZE2 = 2;
68
- var MPU_HEADER_REMAINDER_SIZE2 = MPU_HEADER_SIZE2 - MPU_LENGTH_FIELD_SIZE2;
6
+ var MPU_LENGTH_FIELD_SIZE = 2;
7
+ var MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE;
69
8
  var SOURCE_FEC_PAYLOAD_ID_SIZE = 4;
70
- var PACKET_TYPE_MPU2 = 0;
9
+ var PACKET_TYPE_MPU = 0;
71
10
  var PACKET_TYPE_REPAIR = 3;
72
11
  var MMTP_SIGNALING_PACKET_ID = 0;
73
12
  var MMTP_PACKET_ID_MAX = 65535;
74
13
  function parseMMTPHeader(buffer) {
75
- if (buffer.length < MMTP_HEADER_SIZE2) {
14
+ if (buffer.length < MMTP_HEADER_SIZE) {
76
15
  throw new Error(`MMTP packet too small: ${buffer.length} bytes`);
77
16
  }
78
17
  const view2 = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
@@ -128,12 +67,12 @@ function parseMMTPHeader(buffer) {
128
67
  };
129
68
  }
130
69
  function readMMTPPacketId(buffer) {
131
- if (buffer.length < MMTP_HEADER_SIZE2)
70
+ if (buffer.length < MMTP_HEADER_SIZE)
132
71
  return null;
133
72
  const flags = buffer[0];
134
73
  if ((flags >> 6 & 3) !== 0)
135
74
  return null;
136
- let offset = MMTP_HEADER_SIZE2;
75
+ let offset = MMTP_HEADER_SIZE;
137
76
  if ((flags & 32) !== 0) {
138
77
  if (offset + 4 > buffer.length)
139
78
  return null;
@@ -149,7 +88,7 @@ function readMMTPPacketId(buffer) {
149
88
  return buffer[2] << 8 | buffer[3];
150
89
  }
151
90
  function parseMPUHeader(payload) {
152
- if (payload.length < MPU_HEADER_SIZE2) {
91
+ if (payload.length < MPU_HEADER_SIZE) {
153
92
  throw new Error(`Payload too small for MPU header: ${payload.length} bytes`);
154
93
  }
155
94
  const view2 = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
@@ -182,15 +121,15 @@ function parseTimedMfuDuHeader(payload, offset) {
182
121
  };
183
122
  }
184
123
  function resolveMfuPayloadLayout(mpu, fragmentType, payload) {
185
- if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE2) {
124
+ if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE) {
186
125
  return {
187
126
  rejection: {
188
127
  gate: "mpu-payload-length-below-header-remainder",
189
- detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE2}`
128
+ detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE}`
190
129
  }
191
130
  };
192
131
  }
193
- const declaredEnd = MPU_LENGTH_FIELD_SIZE2 + mpu.payloadLength;
132
+ const declaredEnd = MPU_LENGTH_FIELD_SIZE + mpu.payloadLength;
194
133
  if (declaredEnd > payload.length) {
195
134
  return {
196
135
  rejection: {
@@ -201,7 +140,7 @@ function resolveMfuPayloadLayout(mpu, fragmentType, payload) {
201
140
  }
202
141
  const hasHeader = fragmentType === 2 && mpu.fragmentationIndicator <= 1;
203
142
  const mfuHeaderSize = mpu.timedFlag ? MFU_DU_HEADER_SIZE_TIMED : MFU_DU_HEADER_SIZE_NONTIMED;
204
- const mfuOffset = MPU_HEADER_SIZE2 + (hasHeader ? mfuHeaderSize : 0);
143
+ const mfuOffset = MPU_HEADER_SIZE + (hasHeader ? mfuHeaderSize : 0);
205
144
  if (mfuOffset >= declaredEnd) {
206
145
  return {
207
146
  rejection: {
@@ -218,44 +157,46 @@ function resolveMfuPayloadLayout(mpu, fragmentType, payload) {
218
157
  }
219
158
  };
220
159
  }
221
- function isEmptyOrExactAltaTrailer(payload, start, end) {
160
+ var LEGACY_TRAILER_EXT_TYPE = 61601;
161
+ var LEGACY_TRAILER_HEADER_SIZE = 4;
162
+ function isEmptyOrExactLegacyTrailer(payload, start, end) {
222
163
  if (start === end)
223
164
  return true;
224
- if (start < 0 || end > payload.length || start + MMTP_EXT_TRAILER_HEADER_SIZE > end) {
165
+ if (start < 0 || end > payload.length || start + LEGACY_TRAILER_HEADER_SIZE > end) {
225
166
  return false;
226
167
  }
227
168
  const view2 = new DataView(payload.buffer, payload.byteOffset + start, end - start);
228
- if (view2.getUint16(0, false) !== MMTP_EXT_TYPE_ALTA_AUTH)
169
+ if (view2.getUint16(0, false) !== LEGACY_TRAILER_EXT_TYPE)
229
170
  return false;
230
171
  const authLength = view2.getUint16(2, false);
231
- return start + MMTP_EXT_TRAILER_HEADER_SIZE + authLength === end;
172
+ return start + LEGACY_TRAILER_HEADER_SIZE + authLength === end;
232
173
  }
233
174
  function resolveDeclaredTrailers(payload, declaredEnd, fecType, options) {
234
175
  if (declaredEnd > payload.length)
235
176
  return null;
236
177
  if (options?.skipFecId) {
237
- return isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false } : null;
178
+ return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length) ? { hasFecId: false } : null;
238
179
  }
239
180
  if (fecType === 1) {
240
181
  const fecStart = payload.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
241
- if (fecStart < declaredEnd || !isEmptyOrExactAltaTrailer(payload, declaredEnd, fecStart)) {
182
+ if (fecStart < declaredEnd || !isEmptyOrExactLegacyTrailer(payload, declaredEnd, fecStart)) {
242
183
  return null;
243
184
  }
244
185
  return { hasFecId: true };
245
186
  }
246
187
  if (fecType !== 0)
247
188
  return null;
248
- if (isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length)) {
189
+ if (isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)) {
249
190
  return { hasFecId: false };
250
191
  }
251
192
  return null;
252
193
  }
253
194
  function planMfuExtraction(mmtpPacket, options, parsedHeader) {
254
- if (mmtpPacket.length < MMTP_HEADER_SIZE2 + MPU_HEADER_SIZE2) {
195
+ if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {
255
196
  return {
256
197
  rejection: {
257
198
  gate: "packet-shorter-than-mmtp-plus-mpu-header",
258
- detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE2 + MPU_HEADER_SIZE2}`
199
+ detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE + MPU_HEADER_SIZE}`
259
200
  }
260
201
  };
261
202
  }
@@ -274,7 +215,7 @@ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
274
215
  };
275
216
  }
276
217
  }
277
- if (header.packetType !== PACKET_TYPE_MPU2) {
218
+ if (header.packetType !== PACKET_TYPE_MPU) {
278
219
  return {
279
220
  rejection: {
280
221
  gate: "not-an-mpu-packet",
@@ -283,11 +224,11 @@ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
283
224
  };
284
225
  }
285
226
  const payload = mmtpPacket.subarray(header.headerLength);
286
- if (payload.length < MPU_HEADER_SIZE2) {
227
+ if (payload.length < MPU_HEADER_SIZE) {
287
228
  return {
288
229
  rejection: {
289
230
  gate: "payload-shorter-than-mpu-header",
290
- detail: `${payload.length} bytes < ${MPU_HEADER_SIZE2}`
231
+ detail: `${payload.length} bytes < ${MPU_HEADER_SIZE}`
291
232
  }
292
233
  };
293
234
  }
@@ -318,7 +259,7 @@ function planMfuExtraction(mmtpPacket, options, parsedHeader) {
318
259
  return {
319
260
  rejection: {
320
261
  gate: "declared-trailers-unresolved",
321
- detail: `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty nor an exact ALTA trailer (fecType=${header.fecType})`
262
+ detail: `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty nor an exact legacy authenticator trailer (fecType=${header.fecType})`
322
263
  }
323
264
  };
324
265
  }
@@ -334,8 +275,8 @@ function extractPlannedMfu(mmtpPacket, result) {
334
275
  const mfuDuHeaderPresent = layout.hasMfuDuHeader;
335
276
  const mfuOffset = layout.mfuOffset;
336
277
  const dataEnd = layout.dataEnd;
337
- const mfuDuHeader = mfuDuHeaderPresent && mpu.timedFlag === 1 ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE2) : null;
338
- const itemId = mfuDuHeaderPresent && mpu.timedFlag === 0 ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE2, 4).getUint32(0, false) : null;
278
+ const mfuDuHeader = mfuDuHeaderPresent && mpu.timedFlag === 1 ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE) : null;
279
+ const itemId = mfuDuHeaderPresent && mpu.timedFlag === 0 ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false) : null;
339
280
  const data = payload.subarray(mfuOffset, dataEnd);
340
281
  const fecPayloadId = trailerLayout.hasFecId ? {
341
282
  ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE, SOURCE_FEC_PAYLOAD_ID_SIZE).getUint32(0, false)
@@ -343,17 +284,17 @@ function extractPlannedMfu(mmtpPacket, result) {
343
284
  return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId };
344
285
  }
345
286
  function sourceFecPayloadIdOf(mmtpPacket, header) {
346
- if (mmtpPacket.length < MMTP_HEADER_SIZE2 + MPU_HEADER_SIZE2)
287
+ if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE)
347
288
  return null;
348
- if (header.packetType !== PACKET_TYPE_MPU2)
289
+ if (header.packetType !== PACKET_TYPE_MPU)
349
290
  return null;
350
291
  const payload = mmtpPacket.subarray(header.headerLength);
351
- if (payload.length < MPU_HEADER_SIZE2)
292
+ if (payload.length < MPU_HEADER_SIZE)
352
293
  return null;
353
294
  const payloadLength = payload[0] << 8 | payload[1];
354
- if (payloadLength < MPU_HEADER_REMAINDER_SIZE2)
295
+ if (payloadLength < MPU_HEADER_REMAINDER_SIZE)
355
296
  return null;
356
- const declaredEnd = MPU_LENGTH_FIELD_SIZE2 + payloadLength;
297
+ const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength;
357
298
  if (!resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId)
358
299
  return null;
359
300
  const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE;
@@ -361,7 +302,6 @@ function sourceFecPayloadIdOf(mmtpPacket, header) {
361
302
  ssId: (mmtpPacket[off] << 24 | mmtpPacket[off + 1] << 16 | mmtpPacket[off + 2] << 8 | mmtpPacket[off + 3]) >>> 0
362
303
  };
363
304
  }
364
- var extractAltaTrailer2 = extractAltaTrailer;
365
305
  var REPAIR_FEC_PID_SIZE = 13;
366
306
  function readRepairFecPayloadIdFields(mmtpPacket, parsedHeader) {
367
307
  let header;
@@ -441,49 +381,21 @@ function serial32Precedes(a, b) {
441
381
  const distance = serial32Distance(a, b);
442
382
  return distance !== 0 && distance < SERIAL32_HALF;
443
383
  }
444
- function kStrideBlockStart(sbn, k) {
445
- assertUint32(sbn, "sbn");
446
- if (!Number.isInteger(k) || k < 1 || k >= SERIAL32_HALF) {
447
- throw new RangeError(`k must be a positive integer below 2^31 (got ${k})`);
448
- }
449
- const high = Math.floor(sbn / 65536);
450
- const low = sbn % 65536;
451
- return (high * k % SERIAL32_SPAN * 65536 + low * k) % SERIAL32_SPAN;
452
- }
453
- function kStrideBlockPrecedes(a, b, k) {
454
- return serial32Precedes(kStrideBlockStart(a, k), kStrideBlockStart(b, k));
455
- }
456
- function kStrideBlocksBehind(a, b, k) {
457
- const from = kStrideBlockStart(a, k);
458
- const to = kStrideBlockStart(b, k);
459
- if (!serial32Precedes(from, to))
460
- return 0;
461
- return Math.ceil(serial32Distance(from, to) / k);
462
- }
463
- function isKStrideBlock(sbn, k) {
464
- return Number.isInteger(sbn) && sbn >= 0 && sbn < SERIAL32_SPAN && Number.isInteger(k) && k >= 1 && k < SERIAL32_HALF;
465
- }
466
- function kStrideNextBlock(sbn, k) {
467
- assertUint32(sbn, "sbn");
468
- kStrideBlockStart(sbn, k);
469
- return (sbn + 1) * k >= SERIAL32_SPAN ? 0 : sbn + 1;
470
- }
471
384
 
472
385
  // ../mmt-base/dist/fec-block-profile.js
473
386
  var FEC_BLOCK_PROFILES = Object.freeze([
474
- "k-padded-v1",
475
387
  "iso-ssbg1-v1"
476
388
  ]);
477
389
  function isFecBlockProfile(value) {
478
- return value === "k-padded-v1" || value === "iso-ssbg1-v1";
390
+ return value === "iso-ssbg1-v1";
479
391
  }
480
392
  function parseFecBlockProfile(value, where) {
481
393
  if (isFecBlockProfile(value))
482
394
  return value;
483
395
  if (value === void 0) {
484
- throw new Error(`${where} is required: name the FEC block profile ("k-padded-v1" or "iso-ssbg1-v1")`);
396
+ throw new Error(`${where} is required: name the FEC block profile ("iso-ssbg1-v1")`);
485
397
  }
486
- throw new Error(`${where} must be "k-padded-v1" or "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
398
+ throw new Error(`${where} must be "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
487
399
  }
488
400
  var SSBG1_LENGTH_FIELD_BYTES = 2;
489
401
  function ssbg1MaxPacketLength(symbolSize) {
@@ -508,20 +420,22 @@ function isoBlocksBehindLowerBound(a, b, kMax) {
508
420
  // src/track-session.ts
509
421
  import { fecBlockBudgetMs } from "@blockcast/mmt-fec";
510
422
  function computeFecDeliveryWindowMs(config) {
511
- const fecInterleaveMs = config.interleaveDepthMs;
512
423
  const fecK = config.sourceSymbols;
513
- if (!Number.isFinite(fecInterleaveMs) || fecInterleaveMs <= 0) {
424
+ if (!Number.isFinite(fecK) || fecK <= 0) {
514
425
  throw new Error(
515
- `[TrackSession] fecInterleaveMs=${fecInterleaveMs}. Derive a positive FEC interleave window from the stream catalog.`
426
+ `[TrackSession] fecK=${fecK}. Derive a positive FEC source-symbol count from the stream catalog.`
516
427
  );
517
428
  }
518
- if (!Number.isFinite(fecK) || fecK <= 0) {
429
+ return fecSpanDeliveryWindowMs(config.interleaveDepthMs, config.jitterBufferMs);
430
+ }
431
+ function fecSpanDeliveryWindowMs(interleaveDepthMs, jitterBufferMs) {
432
+ if (!Number.isFinite(interleaveDepthMs) || interleaveDepthMs <= 0) {
519
433
  throw new Error(
520
- `[TrackSession] fecK=${fecK}. Derive a positive FEC source-symbol count from the stream catalog.`
434
+ `[TrackSession] fecInterleaveMs=${interleaveDepthMs}. Derive a positive FEC interleave window from the stream catalog.`
521
435
  );
522
436
  }
523
- if (config.jitterBufferMs === void 0) return Math.ceil(fecInterleaveMs);
524
- return fecBlockBudgetMs(fecInterleaveMs, config.jitterBufferMs);
437
+ if (jitterBufferMs === void 0) return Math.ceil(interleaveDepthMs);
438
+ return fecBlockBudgetMs(interleaveDepthMs, jitterBufferMs);
525
439
  }
526
440
 
527
441
  // src/delivery-gap.ts
@@ -613,7 +527,9 @@ var DeliveryGapEstimator = class {
613
527
  import {
614
528
  deriveFecInterleaveDepth as deriveFecInterleaveDepth2,
615
529
  deriveFecRetentionBlocks,
616
- tryDeriveFecRetentionBlocks
530
+ tryDeriveFecRetentionBlocks,
531
+ LOCAL_FEC_RETENTION_BLOCKS,
532
+ LOCAL_FEC_TIMEOUT_MS
617
533
  } from "@blockcast/mmt-fec";
618
534
 
619
535
  // src/iso-block-locator.ts
@@ -847,7 +763,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
847
763
  };
848
764
 
849
765
  // src/fec-state.ts
850
- var MIN_INTERLEAVE_DEPTH_MS = 33;
766
+ var MIN_INTERLEAVE_DEPTH_MS = 1;
851
767
  var FEC_INACTIVE_REASONS = Object.freeze([
852
768
  "no-catalog-fec",
853
769
  "reliable-moq",
@@ -858,6 +774,15 @@ var INACTIVE = Object.freeze({
858
774
  "reliable-moq": Object.freeze({ state: "inactive", reason: "reliable-moq" }),
859
775
  "no-multicast-bridge": Object.freeze({ state: "inactive", reason: "no-multicast-bridge" })
860
776
  });
777
+ function recordOf(value) {
778
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
779
+ }
780
+ function isoBlockSpanUsable(fec) {
781
+ const block = recordOf(fec);
782
+ if (block === void 0 || block["blockProfile"] !== "iso-ssbg1-v1") return true;
783
+ const ms = block["interleaveDepthMs"];
784
+ return ms === void 0 || typeof ms === "number" && Number.isSafeInteger(ms) && ms >= 0;
785
+ }
861
786
 
862
787
  // src/block-policy.ts
863
788
  import { deriveFecInterleaveDepth } from "@blockcast/mmt-fec";
@@ -1366,7 +1291,6 @@ var FECManager = class _FECManager {
1366
1291
  constructor() {
1367
1292
  // Per-track WASM RaptorQ decoders, keyed by physical stream identity.
1368
1293
  this.#fecDecoders = /* @__PURE__ */ new Map();
1369
- /** @deprecated Use setFecDecoder(packetId, ...). Backwards-compat shim. */
1370
1294
  // Per-track Worker-backed FEC decoders, keyed by physical stream identity.
1371
1295
  // Worker-backed tracks use async decode — recovered frames arrive via onFrame callback,
1372
1296
  // not synchronous return from feedSourceSymbol/feedRepairSymbol.
@@ -1381,10 +1305,9 @@ var FECManager = class _FECManager {
1381
1305
  // source/repair symbols do not split across two RaptorQ states.
1382
1306
  this.#syncDecoderBlockOwners = /* @__PURE__ */ new Map();
1383
1307
  this.#decoderBlockOwners = /* @__PURE__ */ new WeakMap();
1384
- this.#fecK = 4;
1385
1308
  this.#fecSymbolSize = 0;
1386
- this.#fecTimeoutMs = 500;
1387
- // default 500ms; refined by setFramerate() when fps known
1309
+ this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
1310
+ // local default; refined by setFramerate() when fps known
1388
1311
  this.#trackConfigs = /* @__PURE__ */ new Map();
1389
1312
  this.#fecStats = newFecCounters();
1390
1313
  this.#fecTrackStats = /* @__PURE__ */ new Map();
@@ -1424,10 +1347,10 @@ var FECManager = class _FECManager {
1424
1347
  this.#blockSourceEsis = /* @__PURE__ */ new Map();
1425
1348
  // Authored SSB_length per block (ISO/IEC 23008-1 §C.5.3), learned from the
1426
1349
  // first repair symbol. The FFmpeg moq_mmt publisher closes a block every D
1427
- // media units, usually before it holds K symbols, and fec-raptorq
1428
- // force_complete() encodes it as a K-symbol RaptorQ block whose ESIs
1429
- // SSB_length..K-1 are zero padding that is never sent. Such a block is clean
1430
- // at SSB_length source arrivals and recoverable from SSB_length received
1350
+ // media units, usually before it holds K symbols; under iso-ssbg1-v1
1351
+ // (ssbg_mode1) that block is K' = SSB_length symbols, its repair ESIs start
1352
+ // at SSB_length, and K is only the maximum. Such a block is clean at
1353
+ // SSB_length source arrivals and recoverable from SSB_length received
1431
1354
  // symbols; waiting for K fails every one of them.
1432
1355
  this.#blockAuthoredLengths = /* @__PURE__ */ new Map();
1433
1356
  this.#ssbLengthConflictCount = 0;
@@ -1544,14 +1467,16 @@ var FECManager = class _FECManager {
1544
1467
  this.#resolvedBlockClasses = /* @__PURE__ */ new Map();
1545
1468
  // Blocks already reported through onBlockRepaired (once per block).
1546
1469
  this.#repairedBlocks = /* @__PURE__ */ new Set();
1547
- // Blocks created without any symbol because every symbol vanished.
1470
+ // Blocks created before any symbol of theirs was tracked: the repair-less
1471
+ // spans placeIsoRepair materializes (onBlockVanished).
1548
1472
  this.#materializedBlocks = /* @__PURE__ */ new Set();
1549
1473
  // packetIds whose ordered-relay horizon became underivable and fell back to
1550
1474
  // the datagram deadline. Bounds the warn in #derivableOrderedHorizon to once
1551
1475
  // per stream. Not only throws: the ordinary out-of-range case is detected by
1552
1476
  // arithmetic and never throws at all.
1553
1477
  this.#orderedHorizonWarnedFor = /* @__PURE__ */ new Set();
1554
- // Highest SBN seen per stream and when, for vanished-block detection.
1478
+ // Newest block start (SS_Start) seen per stream and when: the frontier for
1479
+ // source-discontinuity detection and isoSpanResolution.
1555
1480
  this.#highestSbn = /* @__PURE__ */ new Map();
1556
1481
  // iso-ssbg1-v1 block location per stream (streamKey). A source belongs to a
1557
1482
  // block only once a repair symbol stated the block's (SS_Start, SSB_length).
@@ -1645,7 +1570,10 @@ var FECManager = class _FECManager {
1645
1570
  #releasedWorkerClients;
1646
1571
  #syncDecoderBlockOwners;
1647
1572
  #decoderBlockOwners;
1648
- #fecK;
1573
+ // The stream-global K a configure() call stated, reported as fecStats.k.
1574
+ // It never resolves a packetId: a packetId has only its own track config
1575
+ // (BLO-45953), and K does not feed the delivery-window math.
1576
+ #streamK;
1649
1577
  #fecSymbolSize;
1650
1578
  #fecTimeoutMs;
1651
1579
  #trackConfigs;
@@ -1807,7 +1735,8 @@ var FECManager = class _FECManager {
1807
1735
  this.#completedBlocks.add(key);
1808
1736
  if (clean) this.#cleanlyCompletedBlocks.add(key);
1809
1737
  this.#indexBlockKey(key, sbn, packetId);
1810
- const expiresAtMs = nowMs + this.terminalRetentionMsForPacket(packetId);
1738
+ const config = this.#trackConfig(packetId);
1739
+ const expiresAtMs = nowMs + config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
1811
1740
  this.#terminalExpiryMs.set(key, expiresAtMs);
1812
1741
  let queue = this.#terminalQueues.get(packetId);
1813
1742
  if (!queue) {
@@ -1843,8 +1772,9 @@ var FECManager = class _FECManager {
1843
1772
  * retention the Worker and the WASM decoder are configured with, because it
1844
1773
  * comes from the same function they do (`deriveFecRetentionBlocks`, over the
1845
1774
  * block's delivery window and its span). The decoder retires every block
1846
- * more than this many SBNs behind the newest one it saw (`cleanup_by_sbn`),
1847
- * so settling earlier than this would decide a block the decoder would still
1775
+ * more than this many blocks behind the newest SS_Start it saw, counted
1776
+ * from below by `isoBlocksBehindLowerBound` (`cleanup_by_ss_start`), so
1777
+ * settling earlier than this would decide a block the decoder would still
1848
1778
  * have recovered.
1849
1779
  *
1850
1780
  * This used to call `deriveFecInterleaveDepth`, which returns D -- MEDIA
@@ -1857,6 +1787,14 @@ var FECManager = class _FECManager {
1857
1787
  * derivation; do not re-derive a horizon at either call site.
1858
1788
  */
1859
1789
  #orderedHorizonBlocksFor(packetId, config) {
1790
+ if (config.interleaveDepthMs === 0) {
1791
+ if (Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
1792
+ return LOCAL_FEC_RETENTION_BLOCKS;
1793
+ }
1794
+ throw new Error(
1795
+ `[FECManager] packetId=${packetId}: local FEC cleanup requires a positive timeout (got ${config.timeoutMs})`
1796
+ );
1797
+ }
1860
1798
  if (!_FECManager.#hasOrderedGeometry(config)) {
1861
1799
  throw new Error(
1862
1800
  `[FECManager] packetId=${packetId}: ordered-relay settlement derives the decoder's interleave horizon from the catalog interleaveDepthMs (>= ${MIN_INTERLEAVE_DEPTH_MS}ms), the media-unit rate and the block delivery window; got interleaveDepthMs=${config.interleaveDepthMs} fps=${String(config.fps)} timeoutMs=${config.timeoutMs}.`
@@ -1870,7 +1808,7 @@ var FECManager = class _FECManager {
1870
1808
  * two can never disagree about which streams settle by stream order.
1871
1809
  */
1872
1810
  static #hasOrderedGeometry(config) {
1873
- return config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS && // `fps` is no longer an input to the horizon, but it still gates WHICH
1811
+ return _FECManager.#hasCatalogTiming(config) && // `fps` is no longer an input to the horizon, but it still gates WHICH
1874
1812
  // streams settle by stream order at all: a track with no usable
1875
1813
  // media-unit rate fell back to datagram semantics before this and must
1876
1814
  // keep doing so. Removing this widens ordered-relay settlement to streams
@@ -1910,6 +1848,9 @@ var FECManager = class _FECManager {
1910
1848
  * argument: narrower IS the failing direction.
1911
1849
  */
1912
1850
  #derivableOrderedHorizon(config, packetId) {
1851
+ if (config.interleaveDepthMs === 0) {
1852
+ return Number.isFinite(config.timeoutMs) && config.timeoutMs > 0 ? LOCAL_FEC_RETENTION_BLOCKS : void 0;
1853
+ }
1913
1854
  if (!_FECManager.#hasOrderedGeometry(config)) return void 0;
1914
1855
  try {
1915
1856
  const blocks = tryDeriveFecRetentionBlocks(config.timeoutMs, config.interleaveDepthMs);
@@ -1942,19 +1883,20 @@ var FECManager = class _FECManager {
1942
1883
  /**
1943
1884
  * The stream's interleave horizon, or undefined when its geometry cannot
1944
1885
  * define one. Two different streams reach that case, with different causes:
1945
- * a packetId that was never configured, and so resolves to the manager
1946
- * defaults (explicitly configured tracks are refused an underivable geometry
1886
+ * a packetId that was never configured, which has no geometry at all
1887
+ * (explicitly configured tracks are refused an underivable geometry
1947
1888
  * at config time — #upsertTrackConfig, setMulticastSymbolDelivery); or a
1948
1889
  * configured track whose timeoutMs was later widened past the 255-block
1949
1890
  * ceiling without re-validating, which #derivableOrderedHorizon catches
1950
1891
  * rather than throwing from the packet-feed path. Either way the stream
1951
- * keeps datagram semantics instead of throwing. The manager defaults can
1892
+ * keeps datagram semantics instead of throwing. A track's configuration can
1952
1893
  * also change under a stream that already holds blocks, so a caller cannot
1953
1894
  * assume a horizon that was defined when a block was held is still defined
1954
1895
  * (see #advanceOrderedFrontier).
1955
1896
  */
1956
1897
  #orderedHorizonBlocks(packetId) {
1957
- return this.#derivableOrderedHorizon(this.#resolveTrackConfig(packetId), packetId);
1898
+ const config = this.#trackConfigs.get(packetId);
1899
+ return config === void 0 ? void 0 : this.#derivableOrderedHorizon(config, packetId);
1958
1900
  }
1959
1901
  /** True while a block of an ordered-relay stream can still receive symbols. */
1960
1902
  #withinOrderedHorizon(key) {
@@ -1988,7 +1930,7 @@ var FECManager = class _FECManager {
1988
1930
  */
1989
1931
  #advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName) {
1990
1932
  const previous = this.#orderedFrontiers.get(streamKey);
1991
- const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn, packetId) ? sbn : previous;
1933
+ const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn) ? sbn : previous;
1992
1934
  if (frontier !== previous) this.#orderedFrontiers.set(streamKey, frontier);
1993
1935
  const pending = this.#orderedPendingBlocks.get(streamKey);
1994
1936
  if (!pending) return;
@@ -2023,7 +1965,7 @@ var FECManager = class _FECManager {
2023
1965
  if (this.#completedBlocks.has(key) || this.#pendingWorkerCleanups.has(key) || this.#repairRequestedBlocks.has(key)) return;
2024
1966
  const authoredLength = this.#blockAuthoredLengths.get(key);
2025
1967
  const received = (this.#blockSourceCounts.get(key) ?? 0) + (this.#blockRepairRsIds.get(key)?.size ?? 0);
2026
- if (this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
1968
+ if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
2027
1969
  this.#requestRepair(sbn, packetId, catalogTrackName, true);
2028
1970
  return;
2029
1971
  }
@@ -2179,7 +2121,7 @@ var FECManager = class _FECManager {
2179
2121
  }
2180
2122
  #requestRepair(sbn, packetId, catalogTrackName, settledByOrder = false) {
2181
2123
  const key = this.#blockKey(sbn, packetId, catalogTrackName);
2182
- if (this.#completedBlocks.has(key) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
2124
+ if (this.#completedBlocks.has(key) || !this.#hasCatalogTimingForPacket(packetId) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
2183
2125
  const deadline = this.#repairDeadlines.get(key);
2184
2126
  if (deadline?.handle != null) clearTimeout(deadline.handle);
2185
2127
  this.#repairDeadlines.set(key, null);
@@ -2259,6 +2201,7 @@ var FECManager = class _FECManager {
2259
2201
  }
2260
2202
  /** A block's live class without creating classifier state for the stream. */
2261
2203
  #peekClass(sbn, packetId, catalogTrackName) {
2204
+ if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
2262
2205
  const classifier = this.#classifiers.get(this.#streamKey(packetId, catalogTrackName));
2263
2206
  if (classifier) return classifier.classOf(sbn);
2264
2207
  return this.#geometryFor(packetId, catalogTrackName).trackKind === "audio" ? "audio" : "unknown";
@@ -2280,8 +2223,8 @@ var FECManager = class _FECManager {
2280
2223
  // Deliberately one block tighter than deriveFecRetentionBlocks over the same
2281
2224
  // inputs (that function's `+ 1`). Different question: the retention horizon
2282
2225
  // asks how long the decoder still ACCEPTS symbols for a block, this asks how
2283
- // far back a block that vanished entirely may still be repairable (see
2284
- // #noteSbnSeen).
2226
+ // many blocks a stream can live behind (or ahead of) its frontier before a
2227
+ // source symbol there is a discontinuity rather than loss (see #noteSbnSeen).
2285
2228
  // The extra block of retention is slack for a block still being fed; a block
2286
2229
  // nothing has been heard from does not get it. Not the units defect — but it
2287
2230
  // is the next place the two can drift, so change both or neither.
@@ -2290,27 +2233,28 @@ var FECManager = class _FECManager {
2290
2233
  return Math.ceil(config.timeoutMs / config.interleaveDepthMs);
2291
2234
  }
2292
2235
  /**
2293
- * Record that a symbol for block `sbn` arrived and materialise any blocks
2294
- * that vanished entirely between the stream's highest SBN and this one.
2236
+ * Record that a symbol for block `sbn` (the block's SS_Start) arrived and
2237
+ * advance the stream's frontier, the newest block start seen.
2295
2238
  *
2296
- * The newest ceil(timeoutMs / interleaveDepthMs) vanished blocks may still
2297
- * be repairable, so they get the normal repair and fail deadlines, armed
2298
- * from now. Older ones are already past their fail bound and fail now, as
2299
- * class `unknown`. A SOURCE symbol whose SBN goes back further than one
2300
- * live window, or jumps further than the stream could have produced since
2301
- * its last symbol, is a source discontinuity, not loss. A repair symbol can
2302
- * only advance the frontier: a D-28 fetch legitimately answers for an old
2303
- * block, so it never signals a discontinuity.
2239
+ * A SOURCE symbol whose block goes back further than one live window, or
2240
+ * jumps further than the stream could have produced since its last symbol,
2241
+ * is a source discontinuity, not loss. A repair symbol can only advance the
2242
+ * frontier: a D-28 fetch legitimately answers for an old block, so it never
2243
+ * signals a discontinuity.
2304
2244
  *
2305
2245
  * The producer closes a block at its Dth media-unit boundary OR when K
2306
- * source symbols filled it, so one interleave span can start several
2307
- * blocks (every large keyframe does). What bounds a forward jump is
2308
- * therefore packet evidence, not a block rate: with the MMTP
2309
- * packet_sequence_number (contiguous per packet_id, ISO/IEC 23008-1) of
2310
- * both source symbols, every block strictly between them holds at least one
2311
- * of the packets sent strictly between them, and K-closes can add at most
2312
- * one block per K of those packets on top of the D-closes elapsed time
2313
- * allows. Without it, only the D-close rate is known.
2246
+ * source symbols filled it, so one interleave span can start several blocks
2247
+ * (every large keyframe does). What bounds a forward jump is therefore
2248
+ * packet evidence, not a block rate: SS_ID advances by one per FEC source
2249
+ * packet (ISO/IEC 23008-1 C.5.2) and the MMTP packet_sequence_number by one
2250
+ * per packet of the packet_id, so with both source symbols'
2251
+ * packet_sequence_number a block start can move at most the packets sent
2252
+ * between them plus the anchor's own offset into its block (< K). Without
2253
+ * it, only the D-close rate is known.
2254
+ *
2255
+ * Vanished blocks are not enumerated here: their starts are unknown until a
2256
+ * repair states them. The SS_IDs between two stated blocks become one
2257
+ * repair-less span when the later block's repair arrives (placeIsoRepair).
2314
2258
  */
2315
2259
  #noteSbnSeen(sbn, packetId, catalogTrackName, symbol, packetSequenceNumber) {
2316
2260
  const streamKey = this.#streamKey(packetId, catalogTrackName);
@@ -2319,7 +2263,7 @@ var FECManager = class _FECManager {
2319
2263
  const anchor = this.#sourceAnchors.get(streamKey);
2320
2264
  const psn = symbol === "source" && packetSequenceNumber !== void 0 && Number.isSafeInteger(packetSequenceNumber) && packetSequenceNumber >= 0 && packetSequenceNumber <= 4294967295 ? packetSequenceNumber : void 0;
2321
2265
  const anchorHere = () => {
2322
- if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn, packetId))) {
2266
+ if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn))) {
2323
2267
  this.#sourceAnchors.set(streamKey, { sbn, psn, atMs: now });
2324
2268
  }
2325
2269
  };
@@ -2335,9 +2279,9 @@ var FECManager = class _FECManager {
2335
2279
  anchorHere();
2336
2280
  return;
2337
2281
  }
2338
- const config = this.#resolveTrackConfig(packetId);
2282
+ const config = this.#trackConfig(packetId);
2339
2283
  const window2 = this.#liveWindowBlocks(config);
2340
- if (!this.#blockPrecedes(previous.sbn, sbn, packetId)) {
2284
+ if (!this.#blockPrecedes(previous.sbn, sbn)) {
2341
2285
  if (symbol === "source" && window2 !== null && this.#blocksBehind(sbn, previous.sbn, packetId) > window2) {
2342
2286
  discontinuity();
2343
2287
  return;
@@ -2348,15 +2292,9 @@ var FECManager = class _FECManager {
2348
2292
  const missing = this.#blocksBehind(previous.sbn, sbn, packetId) - 1;
2349
2293
  let plausible = true;
2350
2294
  if (window2 !== null) {
2351
- if (this.#isIso(packetId) && psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn, packetId) && psn !== anchor.psn) {
2295
+ if (psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn) && psn !== anchor.psn) {
2352
2296
  const sent = psn - anchor.psn >>> 0;
2353
2297
  plausible = sent <= 2147483647 && serial32Distance(anchor.sbn, sbn) <= sent + config.k;
2354
- } else if (psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn, packetId) && psn !== anchor.psn) {
2355
- const sent = psn - anchor.psn >>> 0;
2356
- const between = this.#blocksBehind(anchor.sbn, sbn, packetId) - 1;
2357
- const dCloses = window2 + Math.ceil((now - anchor.atMs) / config.interleaveDepthMs);
2358
- const kCloses = config.k > 0 ? Math.ceil(sent / config.k) : sent;
2359
- plausible = sent <= 2147483647 && between <= sent - 1 && between <= dCloses + kCloses;
2360
2298
  } else {
2361
2299
  plausible = missing <= window2 + Math.ceil((now - previous.atMs) / config.interleaveDepthMs);
2362
2300
  }
@@ -2368,48 +2306,31 @@ var FECManager = class _FECManager {
2368
2306
  }
2369
2307
  this.#highestSbn.set(streamKey, { sbn, atMs: now });
2370
2308
  anchorHere();
2371
- if (this.#isIso(packetId)) return;
2372
- if (missing <= 0 || window2 === null) return;
2373
- let vanished = previous.sbn;
2374
- for (let i = 0; i < missing; i++) {
2375
- vanished = this.#nextBlock(vanished, packetId);
2376
- this.#materializeBlock(
2377
- vanished,
2378
- packetId,
2379
- catalogTrackName,
2380
- this.#blocksBehind(vanished, sbn, packetId) <= window2
2381
- );
2382
- }
2383
2309
  }
2384
2310
  /*
2385
- * Block order for one stream (BLO-35597 iso:5). SS_ID is a 32-bit serial
2386
- * number (ISO/IEC 23008-1 C.5.2), so blocks are ordered by RFC 1982
2387
- * comparison of their first SS_ID (sbn * K mod 2^32). That is identical to
2388
- * comparing SBNs with < for every stream that has not wrapped SS_ID; only
2389
- * the order across the wrap changes. A block outside K-stride geometry has
2390
- * no SS_ID position, so it keeps the SBN order it always had.
2311
+ * Block order for one stream (BLO-35597 iso:5). A block is keyed by its
2312
+ * SS_Start and SS_ID is a 32-bit serial number (ISO/IEC 23008-1 C.5.2), so
2313
+ * blocks are ordered by RFC 1982 comparison of their starts. Only configured
2314
+ * streams hold blocks: every entry point refuses an unconfigured packetId.
2391
2315
  */
2392
- #blockPrecedes(a, b, packetId) {
2393
- if (this.#isIso(packetId)) return serial32Precedes(a, b);
2394
- const k = this.kForPacket(packetId);
2395
- return isKStrideBlock(a, k) && isKStrideBlock(b, k) ? kStrideBlockPrecedes(a, b, k) : a < b;
2316
+ #blockPrecedes(a, b) {
2317
+ return serial32Precedes(a, b);
2396
2318
  }
2397
- /** Blocks `a` lies behind `b` (0 when it does not precede it). */
2319
+ /**
2320
+ * Blocks `a` lies behind `b` (0 when it does not precede it). Blocks vary
2321
+ * in length (K' = SSB_length <= K), so only a lower bound is known: the one
2322
+ * metric the decoder and the worker retire by too.
2323
+ */
2398
2324
  #blocksBehind(a, b, packetId) {
2399
- if (this.#isIso(packetId)) return isoBlocksBehindLowerBound(a, b, this.kForPacket(packetId));
2400
- const k = this.kForPacket(packetId);
2401
- return isKStrideBlock(a, k) && isKStrideBlock(b, k) ? kStrideBlocksBehind(a, b, k) : Math.max(0, b - a);
2402
- }
2403
- /** The block after `sbn`, stepping to block 0 where SS_ID wraps. */
2404
- #nextBlock(sbn, packetId) {
2405
- const k = this.kForPacket(packetId);
2406
- return isKStrideBlock(sbn, k) ? kStrideNextBlock(sbn, k) : sbn + 1;
2325
+ return isoBlocksBehindLowerBound(a, b, this.#trackConfig(packetId).k);
2407
2326
  }
2408
2327
  #materializeBlock(sbn, packetId, catalogTrackName, repairable) {
2409
2328
  const key = this.#blockKey(sbn, packetId, catalogTrackName);
2410
2329
  if (this.#blockResolutionFor(key) !== "untracked") return;
2411
2330
  this.#materializedBlocks.add(key);
2412
- this.#classifierFor(packetId, catalogTrackName).touch(sbn);
2331
+ if (this.#hasCatalogTimingForPacket(packetId)) {
2332
+ this.#classifierFor(packetId, catalogTrackName).touch(sbn);
2333
+ }
2413
2334
  this.onBlockVanished?.(sbn, packetId, catalogTrackName);
2414
2335
  if (repairable) {
2415
2336
  this.noteBlockActivity(sbn, packetId, catalogTrackName, true);
@@ -2448,21 +2369,39 @@ var FECManager = class _FECManager {
2448
2369
  });
2449
2370
  if (this.#lifecycleTrace.length > 64) this.#lifecycleTrace.splice(0, 32);
2450
2371
  }
2451
- #defaultTrackConfig() {
2452
- return {
2453
- k: this.#fecK,
2454
- symbolSize: this.#fecSymbolSize,
2455
- interleaveDepthMs: this.#fecInterleaveDepthMs,
2456
- fps: this.#fecFps,
2457
- timeoutMs: this.#fecTimeoutMs
2458
- };
2372
+ /**
2373
+ * A configured packetId's track config. There is no manager-wide default
2374
+ * (BLO-45953): an unconfigured packetId does no FEC work, so every caller
2375
+ * here is reached only behind a configuration gate and this throw marks a
2376
+ * broken invariant, never a packet-path outcome.
2377
+ */
2378
+ #trackConfig(packetId) {
2379
+ const config = this.#trackConfigs.get(packetId);
2380
+ if (config === void 0) {
2381
+ throw new Error(
2382
+ `[FECManager] packetId=${packetId} was never configured; configure the track from its catalog (configureTrack/setFecWorker/setFecDecoder) first`
2383
+ );
2384
+ }
2385
+ return config;
2459
2386
  }
2460
- #resolveTrackConfig(packetId) {
2461
- return this.#trackConfigs.get(packetId) ?? this.#defaultTrackConfig();
2387
+ /**
2388
+ * Whether this track has the catalog's elapsed-time FEC policy. A zero span
2389
+ * is a deliberate no-deadline signal; it still leaves the wire geometry
2390
+ * usable for in-band decode, but it cannot drive classification, repair
2391
+ * fetches or layer adaptation.
2392
+ */
2393
+ static #hasCatalogTiming(config) {
2394
+ return Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS;
2395
+ }
2396
+ #hasCatalogTimingForPacket(packetId) {
2397
+ const config = this.#trackConfigs.get(packetId);
2398
+ return config !== void 0 && _FECManager.#hasCatalogTiming(config);
2462
2399
  }
2463
2400
  #computeTimeout(config) {
2464
- if (config.timeoutMs && config.timeoutMs > 0) return config.timeoutMs;
2465
- if (config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
2401
+ if (typeof config.timeoutMs === "number" && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
2402
+ return config.timeoutMs;
2403
+ }
2404
+ if (Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
2466
2405
  return Math.max(
2467
2406
  this.#fecTimeoutMs,
2468
2407
  computeFecDeliveryWindowMs({
@@ -2487,14 +2426,17 @@ var FECManager = class _FECManager {
2487
2426
  */
2488
2427
  #refreshDeliveryGapWindow() {
2489
2428
  let windowMs;
2490
- const consider = (geometry) => {
2429
+ const consider = (geometry, packetId) => {
2430
+ if (!this.#hasCatalogTimingForPacket(packetId)) return;
2491
2431
  if (geometry?.trackKind !== "video" || geometry.mode !== "classifying") return;
2492
2432
  const { ticks, timescale } = geometry.keyframeInterval;
2493
2433
  const ms = ticks / timescale * 1e3;
2494
2434
  if (Number.isFinite(ms) && ms > 0 && (windowMs === void 0 || ms > windowMs)) windowMs = ms;
2495
2435
  };
2496
- for (const geometry of this.#streamGeometries.values()) consider(geometry);
2497
- for (const config of this.#trackConfigs.values()) consider(config.geometry);
2436
+ for (const [streamKey, geometry] of this.#streamGeometries) {
2437
+ consider(geometry, this.#parseStreamKey(streamKey).packetId);
2438
+ }
2439
+ for (const [packetId, config] of this.#trackConfigs) consider(config.geometry, packetId);
2498
2440
  this.#deliveryGaps.setWindowMs(windowMs);
2499
2441
  }
2500
2442
  /**
@@ -2522,6 +2464,7 @@ var FECManager = class _FECManager {
2522
2464
  return Math.max(config.timeoutMs, Math.ceil(config.interleaveDepthMs + gapMs));
2523
2465
  }
2524
2466
  #repairDeadlineMs(config, waitMs = this.#multicastWaitMs(config)) {
2467
+ if (!_FECManager.#hasCatalogTiming(config)) return waitMs;
2525
2468
  const halfFailBoundMs = Math.ceil(waitMs / 2);
2526
2469
  if (config.k > 0 && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS && config.fps !== void 0 && Number.isFinite(config.fps) && config.fps > 0) {
2527
2470
  const mediaUnitMs = 1e3 / config.fps;
@@ -2536,10 +2479,12 @@ var FECManager = class _FECManager {
2536
2479
  }
2537
2480
  /**
2538
2481
  * One block lifetime: the inactivity fail bound plus the D-28 repair grace,
2539
- * at the widest wait the catalog allows (#maxMulticastWaitMs). Everything
2540
- * derived from it (terminal retention, the release gate's hold ceiling, the
2541
- * duplicate ledgers, the owed-cleanup bound) is compared across time, so it
2542
- * must not follow the live measurement.
2482
+ * at the widest wait the catalog allows (#maxMulticastWaitMs). For a track
2483
+ * without a catalog span this is receiver-local cleanup time only; no repair
2484
+ * grace is added. Everything derived from it (terminal retention, the
2485
+ * release gate's hold ceiling, the duplicate ledgers, the owed-cleanup
2486
+ * bound) is compared across time, so it must not follow the live
2487
+ * measurement.
2543
2488
  */
2544
2489
  #blockLifetimeMs(config) {
2545
2490
  const waitMs = this.#maxMulticastWaitMs(config);
@@ -2555,6 +2500,7 @@ var FECManager = class _FECManager {
2555
2500
  return Math.min(waitMs + this.#repairGraceMs(config, waitMs), this.#blockLifetimeMs(config));
2556
2501
  }
2557
2502
  #repairGraceMs(config, waitMs) {
2503
+ if (!_FECManager.#hasCatalogTiming(config)) return 0;
2558
2504
  return Math.max(1, waitMs - this.#repairDeadlineMs(config, waitMs));
2559
2505
  }
2560
2506
  #armBlockTimeout(sbn, packetId, catalogTrackName, delayMs) {
@@ -2641,51 +2587,72 @@ var FECManager = class _FECManager {
2641
2587
  * stream; every other field stays per packetId.
2642
2588
  */
2643
2589
  #upsertTrackConfig(packetId, config, catalogTrackName) {
2644
- const previous = this.#trackConfigs.get(packetId) ?? this.#defaultTrackConfig();
2590
+ const previousTrack = this.#trackConfigs.get(packetId);
2591
+ const k = config.k ?? previousTrack?.k;
2592
+ if (k === void 0 || !Number.isSafeInteger(k) || k <= 0) {
2593
+ throw new Error(
2594
+ `[FECManager] packetId=${packetId}: fec.k=${String(k)} must be a positive integer; configure the track from its catalog`
2595
+ );
2596
+ }
2597
+ const previous = previousTrack ?? {};
2645
2598
  const streamScopedGeometry = catalogTrackName !== void 0 && config.geometry !== void 0 ? config.geometry : void 0;
2646
2599
  if (streamScopedGeometry !== void 0) {
2647
2600
  const { geometry: _streamGeometry, ...packetLevel } = config;
2648
2601
  config = packetLevel;
2649
2602
  }
2650
2603
  const timingChanged = config.k !== void 0 || config.interleaveDepthMs !== void 0 || config.fps !== void 0;
2604
+ if (config.timeoutMs !== void 0 && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
2605
+ throw new Error(
2606
+ `[FECManager] packetId=${packetId}: timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
2607
+ );
2608
+ }
2651
2609
  if (config.blockProfile !== void 0) {
2652
2610
  parseFecBlockProfile(config.blockProfile, `packetId=${packetId} fec.blockProfile`);
2653
2611
  }
2654
2612
  const blockProfile = config.blockProfile ?? previous.blockProfile;
2655
2613
  if (blockProfile === void 0) {
2656
2614
  throw new Error(
2657
- `[FECManager] packetId=${packetId}: fec.blockProfile is required ("k-padded-v1" or "iso-ssbg1-v1"); configure the track from its catalog`
2615
+ `[FECManager] packetId=${packetId}: fec.blockProfile is required ("iso-ssbg1-v1"); configure the track from its catalog`
2658
2616
  );
2659
2617
  }
2660
- if (blockProfile === "iso-ssbg1-v1") {
2661
- const ownInterleaveDepthMs = config.interleaveDepthMs ?? this.#trackConfigs.get(packetId)?.interleaveDepthMs;
2662
- if (ownInterleaveDepthMs === void 0 || !Number.isFinite(ownInterleaveDepthMs) || ownInterleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
2663
- throw new Error(
2664
- `[FECManager] packetId=${packetId}: iso-ssbg1-v1 requires the catalog fec.interleaveDepthMs (>= ${MIN_INTERLEAVE_DEPTH_MS} ms; got ${String(ownInterleaveDepthMs)}): it bounds how long block location keeps stated blocks and held sources`
2665
- );
2666
- }
2618
+ const ownInterleaveDepthMs = config.interleaveDepthMs ?? previousTrack?.interleaveDepthMs ?? 0;
2619
+ if (!Number.isSafeInteger(ownInterleaveDepthMs) || ownInterleaveDepthMs !== 0 && ownInterleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
2620
+ throw new Error(
2621
+ `[FECManager] packetId=${packetId}: fec.interleaveDepthMs must be absent, 0, or a finite value >= ${MIN_INTERLEAVE_DEPTH_MS} ms (got ${String(ownInterleaveDepthMs)})`
2622
+ );
2667
2623
  }
2624
+ const hasCatalogTiming = _FECManager.#hasCatalogTiming({ interleaveDepthMs: ownInterleaveDepthMs });
2668
2625
  const next = {
2669
2626
  blockProfile,
2670
- k: config.k ?? previous.k,
2671
- symbolSize: config.symbolSize != null && config.symbolSize > 0 ? config.symbolSize : previous.symbolSize,
2672
- interleaveDepthMs: config.interleaveDepthMs ?? previous.interleaveDepthMs,
2673
- fps: config.fps ?? previous.fps,
2674
- timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs),
2627
+ k,
2628
+ symbolSize: config.symbolSize != null && config.symbolSize > 0 ? config.symbolSize : previousTrack?.symbolSize ?? 0,
2629
+ interleaveDepthMs: ownInterleaveDepthMs,
2630
+ fps: config.fps ?? previous.fps ?? this.#fecFps,
2631
+ timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs ?? 0),
2675
2632
  ...(config.trackKind ?? previous.trackKind) === void 0 ? {} : { trackKind: config.trackKind ?? previous.trackKind },
2676
2633
  ...(config.codec ?? previous.codec) === void 0 ? {} : { codec: config.codec ?? previous.codec },
2677
2634
  ...(config.geometry ?? previous.geometry) === void 0 ? {} : { geometry: config.geometry ?? previous.geometry }
2678
2635
  };
2679
- next.timeoutMs = this.#computeTimeout(next);
2636
+ if (!hasCatalogTiming) {
2637
+ next.timeoutMs = config.timeoutMs !== void 0 && config.timeoutMs > 0 ? config.timeoutMs : previousTrack !== void 0 && previousTrack.interleaveDepthMs === 0 ? previousTrack.timeoutMs : LOCAL_FEC_TIMEOUT_MS;
2638
+ } else {
2639
+ next.timeoutMs = this.#computeTimeout(next);
2640
+ }
2680
2641
  if (this.#multicastSymbolDelivery === "ordered-relay") {
2681
2642
  this.#orderedHorizonBlocksFor(packetId, next);
2682
2643
  }
2683
- if (previous.blockProfile !== void 0 && (previous.blockProfile !== blockProfile || blockProfile === "iso-ssbg1-v1" && previous.k !== next.k)) {
2644
+ const timingPolicyChanged = _FECManager.#hasCatalogTiming({ interleaveDepthMs: previous.interleaveDepthMs ?? 0 }) !== hasCatalogTiming;
2645
+ if (timingPolicyChanged && !hasCatalogTiming) {
2646
+ for (const key of [...this.#repairDeadlines.keys()]) {
2647
+ if (this.#belongsToPacket(key, packetId)) this.#clearRepairDeadline(key);
2648
+ }
2649
+ }
2650
+ if (previous.blockProfile !== void 0 && previous.k !== next.k) {
2684
2651
  this.#dropIsoLocators(packetId);
2685
2652
  }
2686
2653
  this.#trackConfigs.set(packetId, next);
2687
2654
  this.#refreshDeliveryGapWindow();
2688
- let policyChanged = config.geometry !== void 0 && JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry) || config.trackKind !== void 0 && config.trackKind !== previous.trackKind;
2655
+ let policyChanged = timingPolicyChanged || config.geometry !== void 0 && JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry) || config.trackKind !== void 0 && config.trackKind !== previous.trackKind;
2689
2656
  if (streamScopedGeometry !== void 0) {
2690
2657
  const streamKey = this.#streamKey(packetId, catalogTrackName);
2691
2658
  if (JSON.stringify(streamScopedGeometry) !== JSON.stringify(this.#streamGeometries.get(streamKey))) {
@@ -2700,20 +2667,14 @@ var FECManager = class _FECManager {
2700
2667
  if (!this.#belongsToPacket(key, packetId)) continue;
2701
2668
  const stream = this.#parseStreamKey(key);
2702
2669
  const geometry = this.#geometryFor(stream.packetId, stream.catalogTrackName);
2703
- if (JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
2670
+ if (hasCatalogTiming && JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
2704
2671
  this.#classifiers.delete(key);
2672
+ if (!hasCatalogTiming) continue;
2705
2673
  this.#applyClassifierChanges(stream.packetId, stream.catalogTrackName, classifier.retire());
2706
2674
  }
2707
2675
  }
2708
2676
  return next;
2709
2677
  }
2710
- #normalizeSymbolData(packetId, data) {
2711
- const symbolSize = this.symbolSizeForPacket(packetId);
2712
- if (symbolSize <= 0 || data.byteLength === symbolSize) return data;
2713
- const normalized = new Uint8Array(symbolSize);
2714
- normalized.set(data.subarray(0, symbolSize));
2715
- return normalized;
2716
- }
2717
2678
  #blockKeysFor(sbn, packetId, catalogTrackName) {
2718
2679
  if (packetId != null && catalogTrackName !== void 0) {
2719
2680
  return [this.#blockKey(sbn, packetId, catalogTrackName)];
@@ -2774,7 +2735,7 @@ var FECManager = class _FECManager {
2774
2735
  ...stats,
2775
2736
  packetId,
2776
2737
  blocksComplete: trackTotal,
2777
- k: this.#resolveTrackConfig(packetId).k,
2738
+ k: this.#trackConfigs.get(packetId)?.k,
2778
2739
  recoveryRate: trackAffected > 0 ? stats.blocksRecovered / trackAffected * 100 : 100,
2779
2740
  packetLossRate: trackTotal > 0 ? trackAffected / trackTotal * 100 : 0
2780
2741
  };
@@ -2786,7 +2747,7 @@ var FECManager = class _FECManager {
2786
2747
  (block) => block.multicastSymbols > 0 && block.moqSymbols > 0
2787
2748
  ).length,
2788
2749
  recentBlockPathCounts: Array.from(this.#blockPathCounts.values()).slice(-24),
2789
- k: this.#fecK,
2750
+ k: this.#streamK,
2790
2751
  // True recovery rate: of blocks that HAD loss, how many were recovered?
2791
2752
  recoveryRate: affected > 0 ? s.blocksRecovered / affected * 100 : 100,
2792
2753
  packetLossRate: total > 0 ? affected / total * 100 : 0,
@@ -2799,7 +2760,6 @@ var FECManager = class _FECManager {
2799
2760
  },
2800
2761
  blocks: stats.blocks.map((block) => ({ ...block })),
2801
2762
  decoder: stats.decoder ? { ...stats.decoder } : void 0,
2802
- altaFailedDetails: stats.altaFailedDetails?.map((detail) => ({ ...detail })),
2803
2763
  packetId
2804
2764
  })),
2805
2765
  repairSymbolSizeMismatches: this.#repairSymbolSizeMismatchCount,
@@ -3081,15 +3041,20 @@ var FECManager = class _FECManager {
3081
3041
  }
3082
3042
  }
3083
3043
  }
3084
- /** Record a transport symbol and reject later duplicates of the same packetId/SBN/ESI. */
3044
+ /**
3045
+ * Record a transport symbol and reject later duplicates of the same
3046
+ * packetId/SBN/ESI. `sbn` is the block's SS_Start. A packetId with no
3047
+ * configured block profile has no block geometry: its symbol is refused
3048
+ * (false) and no block state is created for it.
3049
+ */
3085
3050
  feedSymbolFromSource(packetId, sbn, esi, _data, source, catalogTrackName) {
3051
+ if (!this.#hasBlockProfile(packetId)) return false;
3086
3052
  const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
3087
3053
  const streamKey = this.#streamKey(packetId, catalogTrackName);
3088
3054
  if (source === "multicast" && this.#multicastSymbolDelivery === "ordered-relay") {
3089
3055
  this.#advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName);
3090
3056
  }
3091
- const k = this.kForPacket(packetId);
3092
- const sourceSymbols = this.#isIso(packetId) ? this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? k : k;
3057
+ const sourceSymbols = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? this.#trackConfig(packetId).k;
3093
3058
  const isSourceSymbol = esi >= 0 && esi < sourceSymbols;
3094
3059
  const isStartupPartial = this.#startupPartialBlocks.has(blockKey);
3095
3060
  const isReadyMulticastRepair = this.#startupRequireRepairReady && source === "multicast" && !isSourceSymbol && this.#startupRepairReadyPackets.has(streamKey);
@@ -3207,14 +3172,14 @@ var FECManager = class _FECManager {
3207
3172
  for (const [key] of this.#activeBlocks) {
3208
3173
  const { packetId, sbn } = this.#parseBlockKey(key);
3209
3174
  const count = this.#blockSourceCounts.get(key) ?? 0;
3210
- const k = this.#blockAuthoredLengths.get(key) ?? this.kForPacket(packetId);
3175
+ const k = this.#blockAuthoredLengths.get(key) ?? this.#trackConfig(packetId).k;
3211
3176
  blocks.push({ sbn, packetId, state: "collecting", sourceCount: count, k, progress: k > 0 ? Math.min(count / k, 1) : 0 });
3212
3177
  }
3213
3178
  const completed = [...this.#completedBlocks].slice(-16);
3214
3179
  for (const key of completed) {
3215
3180
  const { packetId, sbn } = this.#parseBlockKey(key);
3216
3181
  if (!this.#activeBlocks.has(key)) {
3217
- const k = this.kForPacket(packetId);
3182
+ const k = this.#trackConfig(packetId).k;
3218
3183
  blocks.push({ sbn, packetId, state: "complete", sourceCount: k, k, progress: 1 });
3219
3184
  }
3220
3185
  }
@@ -3225,25 +3190,23 @@ var FECManager = class _FECManager {
3225
3190
  get wasmDecoder() {
3226
3191
  return this.#fecDecoders.values().next().value?.client ?? null;
3227
3192
  }
3228
- get k() {
3229
- return this.#fecK;
3230
- }
3193
+ /** A configured packetId's K; undefined for an unconfigured one (no FEC work). */
3231
3194
  kForPacket(packetId) {
3232
- return this.#resolveTrackConfig(packetId).k;
3195
+ return this.#trackConfigs.get(packetId)?.k;
3233
3196
  }
3234
3197
  /** FEC symbol size in bytes. */
3235
3198
  get symbolSize() {
3236
3199
  return this.#fecSymbolSize;
3237
3200
  }
3238
3201
  symbolSizeForPacket(packetId) {
3239
- return this.#resolveTrackConfig(packetId).symbolSize;
3202
+ return this.#trackConfigs.get(packetId)?.symbolSize;
3240
3203
  }
3241
3204
  /** FEC block span in milliseconds (0 = no interleaving). */
3242
3205
  get interleaveDepthMs() {
3243
3206
  return this.#fecInterleaveDepthMs;
3244
3207
  }
3245
3208
  interleaveDepthMsForPacket(packetId) {
3246
- return this.#resolveTrackConfig(packetId).interleaveDepthMs;
3209
+ return this.#trackConfigs.get(packetId)?.interleaveDepthMs;
3247
3210
  }
3248
3211
  /**
3249
3212
  * Record one multicast MMTP packet arrival (every packet of the leg, FEC
@@ -3261,7 +3224,8 @@ var FECManager = class _FECManager {
3261
3224
  * plus the larger of the catalog tolerance and the measured delivery gap.
3262
3225
  */
3263
3226
  timeoutMsForPacket(packetId) {
3264
- return this.#multicastWaitMs(this.#resolveTrackConfig(packetId));
3227
+ const config = this.#trackConfigs.get(packetId);
3228
+ return config === void 0 ? void 0 : this.#multicastWaitMs(config);
3265
3229
  }
3266
3230
  /**
3267
3231
  * Largest multicast delivery gap measured over the current window (ms), and
@@ -3317,27 +3281,31 @@ var FECManager = class _FECManager {
3317
3281
  }
3318
3282
  /** Resolved per-track D-28 repair request deadline in milliseconds. */
3319
3283
  repairDeadlineMsForPacket(packetId) {
3320
- return this.#repairDeadlineMs(this.#resolveTrackConfig(packetId));
3284
+ const config = this.#trackConfigs.get(packetId);
3285
+ return config === void 0 ? void 0 : this.#repairDeadlineMs(config);
3321
3286
  }
3322
3287
  /**
3323
3288
  * One block lifetime: the longest a block stays open after its last symbol
3324
3289
  * (inactivity fail bound plus D-28 repair grace).
3325
3290
  */
3326
3291
  blockLifetimeMsForPacket(packetId) {
3327
- return this.#blockLifetimeMs(this.#resolveTrackConfig(packetId));
3292
+ const config = this.#trackConfigs.get(packetId);
3293
+ return config === void 0 ? void 0 : this.#blockLifetimeMs(config);
3328
3294
  }
3329
3295
  /**
3330
3296
  * How long a terminal (clean, recovered or failed) block stays authoritative,
3331
3297
  * for late symbols and for the in-order release gate. It is also the gate's
3332
3298
  * hold ceiling (MmtpRouter's holdLimitMs): a retired block reads back as
3333
3299
  * untracked, which the gate counts as lost, so no frame may still be waiting
3334
- * on a block that can retire. The window is one interleave depth for a gap's
3335
- * last block to finish arriving, one block lifetime to fail it, and one more
3336
- * for an owed Worker cleanup that can keep a failing block pending. Every
3337
- * term comes from the track's catalog geometry; there is no separate cap.
3300
+ * on a block that can retire. With a positive catalog span the window is one
3301
+ * interleave depth for a gap's last block to finish arriving, one block
3302
+ * lifetime to fail it, and one more for an owed Worker cleanup. With an
3303
+ * absent/zero span it is a receiver-local cleanup bound only; it creates no
3304
+ * catalog FEC deadline.
3338
3305
  */
3339
3306
  terminalRetentionMsForPacket(packetId) {
3340
- const config = this.#resolveTrackConfig(packetId);
3307
+ const config = this.#trackConfigs.get(packetId);
3308
+ if (config === void 0) return void 0;
3341
3309
  return config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
3342
3310
  }
3343
3311
  /** Remaining fetch budget for a specific repair-requested block. */
@@ -3348,7 +3316,8 @@ var FECManager = class _FECManager {
3348
3316
  if (deadlineMs !== void 0) {
3349
3317
  return Math.max(1, Math.ceil(deadlineMs - performance.now()));
3350
3318
  }
3351
- const trackConfig = this.#resolveTrackConfig(packetId);
3319
+ const trackConfig = this.#trackConfigs.get(packetId);
3320
+ if (trackConfig === void 0) return void 0;
3352
3321
  return Math.max(1, trackConfig.timeoutMs - this.#repairDeadlineMs(trackConfig));
3353
3322
  }
3354
3323
  /** True once configureTrack/setFecDecoder/setFecWorker configured this packetId. */
@@ -3359,14 +3328,19 @@ var FECManager = class _FECManager {
3359
3328
  blockProfileForPacket(packetId) {
3360
3329
  return this.#trackConfigs.get(packetId)?.blockProfile;
3361
3330
  }
3362
- #isIso(packetId) {
3363
- return this.#trackConfigs.get(packetId)?.blockProfile === "iso-ssbg1-v1";
3331
+ /**
3332
+ * Whether a packetId has a configured block geometry. iso-ssbg1-v1 is the
3333
+ * only profile, so every configured track has it; a packetId never
3334
+ * configured (or released) has none and gets no FEC block tracking.
3335
+ */
3336
+ #hasBlockProfile(packetId) {
3337
+ return this.#trackConfigs.get(packetId)?.blockProfile !== void 0;
3364
3338
  }
3365
3339
  #isoLocatorFor(packetId, catalogTrackName) {
3366
3340
  const streamKey = this.#streamKey(packetId, catalogTrackName);
3367
3341
  let locator = this.#isoLocators.get(streamKey);
3368
3342
  if (!locator) {
3369
- locator = new IsoBlockLocator(this.kForPacket(packetId), this.#isoRetentionBlocks(packetId));
3343
+ locator = new IsoBlockLocator(this.#trackConfig(packetId).k, this.#isoRetentionBlocks(packetId));
3370
3344
  this.#isoLocators.set(streamKey, locator);
3371
3345
  }
3372
3346
  return locator;
@@ -3397,12 +3371,14 @@ var FECManager = class _FECManager {
3397
3371
  */
3398
3372
  #isoRetentionBlocks(packetId) {
3399
3373
  const config = this.#trackConfigs.get(packetId);
3400
- if (config?.blockProfile !== "iso-ssbg1-v1" || !(config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS)) {
3374
+ if (config?.blockProfile === void 0) {
3401
3375
  throw new Error(
3402
- `[FECManager] packetId=${packetId} has no iso-ssbg1-v1 block span to derive block-location retention from`
3376
+ `[FECManager] packetId=${packetId} has no configured iso-ssbg1-v1 block profile`
3403
3377
  );
3404
3378
  }
3405
- const blocks = Math.ceil(this.terminalRetentionMsForPacket(packetId) / config.interleaveDepthMs) + 1;
3379
+ if (!_FECManager.#hasCatalogTiming(config)) return LOCAL_FEC_RETENTION_BLOCKS;
3380
+ const terminalRetentionMs = config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
3381
+ const blocks = Math.ceil(terminalRetentionMs / config.interleaveDepthMs) + 1;
3406
3382
  if (!Number.isSafeInteger(blocks) || blocks <= 0) {
3407
3383
  throw new Error(`[FECManager] packetId=${packetId} iso-ssbg1-v1 retention of ${blocks} blocks is not derivable`);
3408
3384
  }
@@ -3410,7 +3386,8 @@ var FECManager = class _FECManager {
3410
3386
  }
3411
3387
  /** Whether an iso-ssbg1-v1 protected packet fits one ssbg_mode1 symbol (len + 2 <= T). */
3412
3388
  isoSourceFits(packetId, packetLength) {
3413
- return ssbg1FitsSymbol(packetLength, this.symbolSizeForPacket(packetId));
3389
+ const symbolSize = this.#trackConfigs.get(packetId)?.symbolSize;
3390
+ return symbolSize !== void 0 && ssbg1FitsSymbol(packetLength, symbolSize);
3414
3391
  }
3415
3392
  /**
3416
3393
  * Locate an iso-ssbg1-v1 source by range membership in a block a repair
@@ -3471,11 +3448,11 @@ var FECManager = class _FECManager {
3471
3448
  * that could hold them is pending or not yet stated by a repair, else the
3472
3449
  * starts of the blocks that failed or vanished.
3473
3450
  *
3474
- * `untracked` reads exactly as in the k-padded-v1 release gate
3475
- * (MmtpRouter #createFecReleaseGate): a block holding either released
3476
- * frame's SS_ID (`fromSsId` or `toSsId`) delivered that frame, so untracked
3477
- * there means it resolved and retired; a block starting after the stream's frontier (`highestSbn`,
3478
- * an SS_Start here) may still be heard from; a block at or behind the
3451
+ * `untracked` reads as the release gate reads it (MmtpRouter
3452
+ * #createFecReleaseGate): a block holding either released frame's SS_ID
3453
+ * (`fromSsId` or `toSsId`) delivered that frame, so untracked there means it
3454
+ * resolved and retired; a block starting after the stream's frontier
3455
+ * (`highestSbn`, an SS_Start) may still be heard from; a block at or behind the
3479
3456
  * frontier that no symbol of this manager ever reached vanished, and its
3480
3457
  * media is lost.
3481
3458
  */
@@ -3543,6 +3520,7 @@ var FECManager = class _FECManager {
3543
3520
  }
3544
3521
  /** Current classifier mode of a stream. */
3545
3522
  classifierModeFor(packetId, catalogTrackName) {
3523
+ if (!this.#hasCatalogTimingForPacket(packetId)) return { mode: "all-protected", reason: null };
3546
3524
  const classifier = this.#classifierFor(packetId, catalogTrackName);
3547
3525
  return { mode: classifier.mode, reason: classifier.reason };
3548
3526
  }
@@ -3552,10 +3530,12 @@ var FECManager = class _FECManager {
3552
3530
  * it does not. A stream with no classifier yet has shown nothing against it.
3553
3531
  */
3554
3532
  mpuBoundsRapsFor(packetId, catalogTrackName) {
3533
+ if (!this.#hasCatalogTimingForPacket(packetId)) return false;
3555
3534
  return this.#classifiers.get(this.#streamKey(packetId, catalogTrackName))?.mpuBoundsRaps ?? true;
3556
3535
  }
3557
3536
  /** A block's class now, or frozen at its resolution. */
3558
3537
  blockClassFor(sbn, packetId, catalogTrackName) {
3538
+ if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
3559
3539
  const frozen = this.#resolvedBlockClasses.get(this.#blockKey(sbn, packetId, catalogTrackName));
3560
3540
  if (frozen !== void 0) return frozen;
3561
3541
  return this.#peekClass(sbn, packetId, catalogTrackName);
@@ -3577,7 +3557,7 @@ var FECManager = class _FECManager {
3577
3557
  * invariant (consecutive MPUs exactly one keyframe interval apart).
3578
3558
  */
3579
3559
  noteReleasedRap(packetId, catalogTrackName, mpu, presentationTime, mediaTimescale) {
3580
- if (!this.#trackConfigs.has(packetId)) return;
3560
+ if (!this.#trackConfigs.has(packetId) || !this.#hasCatalogTimingForPacket(packetId)) return;
3581
3561
  this.#applyClassifierChanges(
3582
3562
  packetId,
3583
3563
  catalogTrackName,
@@ -3593,27 +3573,34 @@ var FECManager = class _FECManager {
3593
3573
  // -----------------------------------------------------------------------
3594
3574
  /** Configure FEC parameters. */
3595
3575
  configure(config) {
3596
- if (config.k != null) this.#fecK = config.k;
3576
+ if (config.interleaveDepthMs != null && (!Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < 0)) {
3577
+ throw new Error(
3578
+ `[FECManager] interleaveDepthMs=${config.interleaveDepthMs} must be a non-negative safe integer in milliseconds`
3579
+ );
3580
+ }
3581
+ if (config.timeoutMs != null && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
3582
+ throw new Error(
3583
+ `[FECManager] timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
3584
+ );
3585
+ }
3586
+ if (config.k != null && (!Number.isSafeInteger(config.k) || config.k <= 0)) {
3587
+ throw new Error(`[FECManager] k=${config.k} must be a positive integer`);
3588
+ }
3589
+ if (config.k != null) this.#streamK = config.k;
3597
3590
  if (config.symbolSize != null && config.symbolSize > 0) this.#fecSymbolSize = config.symbolSize;
3598
3591
  if (config.interleaveDepthMs != null) this.#fecInterleaveDepthMs = config.interleaveDepthMs;
3599
3592
  if (config.fps != null && Number.isFinite(config.fps) && config.fps > 0) {
3600
3593
  this.#fecFps = config.fps;
3601
3594
  }
3602
- if (config.interleaveDepthMs != null && config.interleaveDepthMs > 0 && config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
3603
- console.warn(`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} looks like frame count, not milliseconds (\xA75.1 requires ms). Expected range: ${MIN_INTERLEAVE_DEPTH_MS}-10000ms.`);
3604
- }
3605
3595
  if (config.timeoutMs != null && config.timeoutMs > 0) {
3606
3596
  this.#fecTimeoutMs = config.timeoutMs;
3607
- } else if (config.interleaveDepthMs != null && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
3608
- this.#fecTimeoutMs = computeFecDeliveryWindowMs({
3609
- interleaveDepthMs: config.interleaveDepthMs,
3610
- sourceSymbols: this.#fecK,
3611
- symbolSize: this.#fecSymbolSize,
3612
- mediaUnitsPerSecond: config.fps
3613
- });
3597
+ } else if (config.interleaveDepthMs === 0) {
3598
+ this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
3599
+ } else if (config.interleaveDepthMs != null && Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
3600
+ this.#fecTimeoutMs = fecSpanDeliveryWindowMs(config.interleaveDepthMs);
3614
3601
  }
3615
3602
  for (const [packetId, track] of this.#trackConfigs) {
3616
- if (track.timeoutMs < this.#fecTimeoutMs) {
3603
+ if (_FECManager.#hasCatalogTiming(track) && track.timeoutMs < this.#fecTimeoutMs) {
3617
3604
  this.#trackConfigs.set(packetId, { ...track, timeoutMs: this.#fecTimeoutMs });
3618
3605
  }
3619
3606
  }
@@ -3672,7 +3659,7 @@ var FECManager = class _FECManager {
3672
3659
  );
3673
3660
  }
3674
3661
  if (config) this.#upsertTrackConfig(packetId, config, catalogTrackName);
3675
- const workerK = this.#resolveTrackConfig(packetId).k;
3662
+ const workerK = this.#trackConfig(packetId).k;
3676
3663
  this.#workerClients.set(streamKey, client);
3677
3664
  this.#releasedWorkerClients.delete(client);
3678
3665
  client.onFrame((data, meta) => {
@@ -3852,7 +3839,7 @@ var FECManager = class _FECManager {
3852
3839
  return;
3853
3840
  }
3854
3841
  this.#settleOwedCleanup(key, "cleanup-ack-timeout");
3855
- }, Math.ceil(this.#liveBlockLifetimeMs(this.#resolveTrackConfig(packetId))))
3842
+ }, Math.ceil(this.#liveBlockLifetimeMs(this.#trackConfig(packetId))))
3856
3843
  };
3857
3844
  this.#pendingWorkerCleanupBounds.set(key, bound);
3858
3845
  }
@@ -3893,16 +3880,11 @@ var FECManager = class _FECManager {
3893
3880
  if (this.#fpsDetected || fps <= 0) return;
3894
3881
  this.#fpsDetected = true;
3895
3882
  this.#fecFps = fps;
3896
- if (this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
3897
- this.#fecTimeoutMs = computeFecDeliveryWindowMs({
3898
- interleaveDepthMs: this.#fecInterleaveDepthMs,
3899
- sourceSymbols: this.#fecK,
3900
- symbolSize: this.#fecSymbolSize,
3901
- mediaUnitsPerSecond: fps
3902
- });
3883
+ if (Number.isSafeInteger(this.#fecInterleaveDepthMs) && this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
3884
+ this.#fecTimeoutMs = fecSpanDeliveryWindowMs(this.#fecInterleaveDepthMs);
3903
3885
  }
3904
3886
  for (const [packetId, config] of this.#trackConfigs) {
3905
- if (config.fps !== void 0 || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
3887
+ if (config.fps !== void 0 || !Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
3906
3888
  this.#trackConfigs.set(packetId, {
3907
3889
  ...config,
3908
3890
  fps,
@@ -3931,51 +3913,33 @@ var FECManager = class _FECManager {
3931
3913
  * Caller is responsible for routing recovered data and updating
3932
3914
  * block completion state via `markBlockCompleted()`.
3933
3915
  */
3934
- feedSourceSymbol(packetId, ssId, symbolData, alta, catalogTrackName) {
3916
+ feedSourceSymbol(packetId, ssId, symbolData, catalogTrackName) {
3935
3917
  this.#fecStats.sourcePackets++;
3936
3918
  this.#statsForPacket(packetId).sourcePackets++;
3937
- let sbn;
3938
- let fecData;
3939
- if (this.#isIso(packetId)) {
3940
- const locator = this.#isoLocatorFor(packetId, catalogTrackName);
3941
- const located = locator.locate(ssId);
3942
- if (located === null) {
3943
- throw new Error(
3944
- `[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; hold it until a repair places it (placeIsoRepair)`
3945
- );
3946
- }
3947
- locator.observe(ssId);
3948
- if (!ssbg1FitsSymbol(symbolData.byteLength, this.symbolSizeForPacket(packetId))) {
3949
- this.#isoSourceOversizeCount++;
3950
- return null;
3951
- }
3952
- sbn = located.block.start;
3953
- fecData = symbolData;
3954
- } else {
3955
- fecData = this.#normalizeSymbolData(packetId, symbolData);
3956
- sbn = Math.floor(ssId / this.kForPacket(packetId));
3919
+ if (!this.#hasBlockProfile(packetId)) return null;
3920
+ const locator = this.#isoLocatorFor(packetId, catalogTrackName);
3921
+ const located = locator.locate(ssId);
3922
+ if (located === null) {
3923
+ throw new Error(
3924
+ `[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; hold it until a repair places it (placeIsoRepair)`
3925
+ );
3957
3926
  }
3927
+ locator.observe(ssId);
3928
+ if (!ssbg1FitsSymbol(symbolData.byteLength, this.#trackConfig(packetId).symbolSize)) {
3929
+ this.#isoSourceOversizeCount++;
3930
+ return null;
3931
+ }
3932
+ const sbn = located.block.start;
3958
3933
  const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
3959
3934
  const syncBlockOwner = this.#syncDecoderBlockOwners.get(blockKey);
3960
3935
  const workerClient = this.#routableWorkerForPacket(packetId, catalogTrackName);
3961
3936
  if (workerClient && !syncBlockOwner) {
3962
3937
  if (!this.#claimDecoderBlock(workerClient, sbn, catalogTrackName)) return null;
3963
- const buf = fecData !== symbolData ? fecData.buffer : fecData.buffer.slice(
3964
- fecData.byteOffset,
3965
- fecData.byteOffset + fecData.byteLength
3938
+ const buf = symbolData.buffer.slice(
3939
+ symbolData.byteOffset,
3940
+ symbolData.byteOffset + symbolData.byteLength
3966
3941
  );
3967
- if (alta) {
3968
- const authBuf = alta.auth.buffer.slice(
3969
- alta.auth.byteOffset,
3970
- alta.auth.byteOffset + alta.auth.byteLength
3971
- );
3972
- workerClient.feedSource(ssId, buf, performance.now(), {
3973
- auth: authBuf,
3974
- payloadEnd: alta.payloadEnd
3975
- });
3976
- } else {
3977
- workerClient.feedSource(ssId, buf, performance.now());
3978
- }
3942
+ workerClient.feedSource(ssId, buf, performance.now());
3979
3943
  return null;
3980
3944
  }
3981
3945
  const entry = this.#decoderForPacket(packetId, catalogTrackName);
@@ -3985,7 +3949,7 @@ var FECManager = class _FECManager {
3985
3949
  this.#syncDecoderBlockOwners.set(blockKey, client);
3986
3950
  let recovered = null;
3987
3951
  try {
3988
- recovered = client.add_source(ssId, fecData, performance.now());
3952
+ recovered = client.add_source(ssId, symbolData, performance.now());
3989
3953
  } catch {
3990
3954
  }
3991
3955
  this.#retireIsoUnplaced(client, packetId, catalogTrackName);
@@ -4004,7 +3968,8 @@ var FECManager = class _FECManager {
4004
3968
  feedRepairSymbol(packetId, ssStart, ssbLength, rsId, repairData, catalogTrackName) {
4005
3969
  this.#fecStats.repairPackets++;
4006
3970
  this.#statsForPacket(packetId).repairPackets++;
4007
- const symbolSize = this.symbolSizeForPacket(packetId);
3971
+ if (!this.#hasBlockProfile(packetId)) return null;
3972
+ const symbolSize = this.#trackConfig(packetId).symbolSize;
4008
3973
  if (symbolSize > 0 && repairData.byteLength !== symbolSize) {
4009
3974
  this.#repairSymbolSizeMismatchCount++;
4010
3975
  if (this.#repairSymbolSizeMismatchCount <= 5 || this.#repairSymbolSizeMismatchCount % 100 === 0) {
@@ -4015,18 +3980,12 @@ var FECManager = class _FECManager {
4015
3980
  return null;
4016
3981
  }
4017
3982
  const fecData = repairData;
4018
- const k = this.kForPacket(packetId);
4019
- let sbn;
4020
- if (this.#isIso(packetId)) {
4021
- const block = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(ssStart);
4022
- if (ssbLength <= 0 || ssbLength > k || block === void 0 || block.span || block.length !== ssbLength) {
4023
- return null;
4024
- }
4025
- sbn = ssStart;
4026
- } else {
4027
- if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
4028
- sbn = Math.floor(ssStart / k);
3983
+ const k = this.#trackConfig(packetId).k;
3984
+ const block = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(ssStart);
3985
+ if (ssbLength <= 0 || ssbLength > k || block === void 0 || block.span || block.length !== ssbLength) {
3986
+ return null;
4029
3987
  }
3988
+ const sbn = ssStart;
4030
3989
  this.#noteSbnSeen(sbn, packetId, catalogTrackName, "repair");
4031
3990
  this.noteBlockActivity(sbn, packetId, catalogTrackName);
4032
3991
  const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
@@ -4064,8 +4023,9 @@ var FECManager = class _FECManager {
4064
4023
  * Record a block's authored SSB_length from a repair symbol. Returns false,
4065
4024
  * leaving the block unchanged, when the symbol contradicts the block: a
4066
4025
  * different SSB_length than an earlier repair symbol declared is dropped,
4067
- * and an SSB_length that places an already-received source ESI inside the
4068
- * zero padding fails the block, since its geometry can no longer be trusted.
4026
+ * and an SSB_length that places an already-received source ESI at or past
4027
+ * the block's end fails the block, since its geometry can no longer be
4028
+ * trusted.
4069
4029
  */
4070
4030
  #noteAuthoredLength(blockKey, sbn, packetId, ssbLength, catalogTrackName) {
4071
4031
  const known = this.#blockAuthoredLengths.get(blockKey);
@@ -4132,7 +4092,7 @@ var FECManager = class _FECManager {
4132
4092
  * a sweep tied to expiry alone would never run there.
4133
4093
  */
4134
4094
  #retireIsoUnplaced(decoder, packetId, catalogTrackName) {
4135
- if (!this.#isIso(packetId)) return;
4095
+ if (!this.#hasBlockProfile(packetId)) return;
4136
4096
  const newest = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newest;
4137
4097
  if (newest === void 0) return;
4138
4098
  try {
@@ -4177,11 +4137,13 @@ var FECManager = class _FECManager {
4177
4137
  this.#failBlock(sbn, packetId, catalogTrackName);
4178
4138
  return;
4179
4139
  }
4180
- this.#applyClassifierChanges(
4181
- packetId,
4182
- catalogTrackName,
4183
- this.#classifierFor(packetId, catalogTrackName).observe(sbn, evidence)
4184
- );
4140
+ if (_FECManager.#hasCatalogTiming(trackConfig)) {
4141
+ this.#applyClassifierChanges(
4142
+ packetId,
4143
+ catalogTrackName,
4144
+ this.#classifierFor(packetId, catalogTrackName).observe(sbn, evidence)
4145
+ );
4146
+ }
4185
4147
  const count = (this.#blockSourceCounts.get(key) ?? 0) + 1;
4186
4148
  this.#blockSourceCounts.set(key, count);
4187
4149
  if (count === 1) this.#indexBlockKey(key, sbn, packetId);
@@ -4225,6 +4187,7 @@ var FECManager = class _FECManager {
4225
4187
  }
4226
4188
  /** Refresh deadlines after accepted block activity. */
4227
4189
  noteBlockActivity(sbn, packetId, catalogTrackName, sourceProgress = false) {
4190
+ if (!this.#trackConfigs.has(packetId)) return;
4228
4191
  const key = this.#blockKey(sbn, packetId, catalogTrackName);
4229
4192
  if (this.#completedBlocks.has(key)) return;
4230
4193
  if (this.#pendingWorkerCleanups.has(key)) return;
@@ -4234,13 +4197,13 @@ var FECManager = class _FECManager {
4234
4197
  this.#holdForOrderedSettlement(key, sbn, packetId, catalogTrackName);
4235
4198
  return;
4236
4199
  }
4237
- const trackConfig = this.#resolveTrackConfig(packetId);
4200
+ const trackConfig = this.#trackConfig(packetId);
4238
4201
  const timeoutMs = this.#multicastWaitMs(trackConfig);
4239
4202
  if (!(timeoutMs > 0)) {
4240
4203
  throw new Error(`[FECManager] packetId=${packetId} has no positive fail bound (timeoutMs=${timeoutMs})`);
4241
4204
  }
4242
4205
  const repairDeadline = this.#repairDeadlines.get(key);
4243
- if (this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
4206
+ if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
4244
4207
  this.#armRepairDeadline(
4245
4208
  key,
4246
4209
  sbn,
@@ -4310,6 +4273,7 @@ var FECManager = class _FECManager {
4310
4273
  }
4311
4274
  /** Mark a block as completed externally (e.g., after routing recovered data). */
4312
4275
  markBlockCompleted(sbn, packetId, catalogTrackName) {
4276
+ if (packetId != null && !this.#trackConfigs.has(packetId)) return;
4313
4277
  for (const key of this.#blockKeysFor(sbn, packetId, catalogTrackName)) {
4314
4278
  this.#resolveCompleted(key);
4315
4279
  }
@@ -8526,7 +8490,6 @@ var CmafBlockAssembler = class {
8526
8490
  failedBlocks: 0
8527
8491
  };
8528
8492
  this.#config = {
8529
- k: config.k,
8530
8493
  timescale: config.timescale ?? 1e6,
8531
8494
  trackId: config.trackId ?? 1,
8532
8495
  trackType: config.trackType,
@@ -10712,6 +10675,21 @@ var FecReleaseSequencer = class {
10712
10675
  this.#streams.delete(key);
10713
10676
  }
10714
10677
  }
10678
+ /**
10679
+ * The packet's FEC ended, so no block will resolve a held gap: release its
10680
+ * frames in order, each gap stalled as a discontinuity, and forget its
10681
+ * streams, so FEC configured on it again starts from a fresh one.
10682
+ */
10683
+ endPacket(packetId) {
10684
+ for (const [key, stream] of this.#streams) {
10685
+ if (stream.packetId !== packetId) continue;
10686
+ while (stream.buf.length > 0 && stream.last !== null) {
10687
+ stream.discontinuity = true;
10688
+ this.#drain(stream);
10689
+ }
10690
+ this.#streams.delete(key);
10691
+ }
10692
+ }
10715
10693
  discardPacket(packetId) {
10716
10694
  for (const [key, stream] of this.#streams) {
10717
10695
  if (stream.packetId !== packetId) continue;
@@ -11094,6 +11072,13 @@ var MmtpContainerParser = class _MmtpContainerParser {
11094
11072
  markFecDiscontinuity(packetId, catalogTrackName) {
11095
11073
  this.#fecSequencer?.markDiscontinuity(this.#streamKey(packetId, catalogTrackName));
11096
11074
  }
11075
+ /**
11076
+ * The FEC of a packet ID ended: release the frames the FEC release gate
11077
+ * holds for it in order, each gap stalled, and forget its gated streams.
11078
+ */
11079
+ endFecRelease(packetId) {
11080
+ this.#fecSequencer?.endPacket(packetId);
11081
+ }
11097
11082
  /**
11098
11083
  * MPU and data-unit identity of one MMTP packet, parsed in place (no copy,
11099
11084
  * no reassembly). Null for non-MPU packets and packets that do not parse.
@@ -11274,7 +11259,7 @@ var MmtpContainerParser = class _MmtpContainerParser {
11274
11259
  const parsed = preParsed !== void 0 && preParsed.skipFecPayloadId === (options?.skipFecPayloadId === true) ? preParsed : this.parsePacket(packet, options);
11275
11260
  if (!parsed) return this.#drop("header-parse-failed");
11276
11261
  const info = parsed.info;
11277
- if (info.packetType !== PACKET_TYPE_MPU2) {
11262
+ if (info.packetType !== PACKET_TYPE_MPU) {
11278
11263
  return {
11279
11264
  kind: info.isRepair ? "repair" : "non-media",
11280
11265
  packet: info,
@@ -11283,10 +11268,8 @@ var MmtpContainerParser = class _MmtpContainerParser {
11283
11268
  }
11284
11269
  if (info.fecPayloadId !== null && this.#sourceSymbolCb) {
11285
11270
  const rawSymbol = packet.subarray(0, packet.length - 4);
11286
- const altaTrailer = extractAltaTrailer2(packet);
11287
- const alta = altaTrailer ? { auth: altaTrailer.auth, payloadEnd: altaTrailer.signedEnd } : void 0;
11288
11271
  try {
11289
- this.#sourceSymbolCb(info.packetId, info.fecPayloadId.ssId, rawSymbol, alta);
11272
+ this.#sourceSymbolCb(info.packetId, info.fecPayloadId.ssId, rawSymbol);
11290
11273
  } catch {
11291
11274
  }
11292
11275
  }
@@ -12426,6 +12409,8 @@ var MmtpRouter = class {
12426
12409
  */
12427
12410
  #onPublisherRestart;
12428
12411
  #publisherRestarts = 0;
12412
+ /** Inits delivered with no onTrackInit handler for their packetId (MOQT-01). */
12413
+ #unmappedInitDeliveries = 0;
12429
12414
  // FEC Payload IDs left unread because their (owner) packetId had no
12430
12415
  // configured fec.blockProfile (see #noteUnreadableFecPayloadId).
12431
12416
  #fecPayloadIdsWithoutProfile = 0;
@@ -12622,15 +12607,24 @@ var MmtpRouter = class {
12622
12607
  return NO_FEC_GEOMETRY_HORIZON;
12623
12608
  }
12624
12609
  const blockSpanMs = fec.interleaveDepthMsForPacket(packetId);
12610
+ const blockLifetimeMs = fec.blockLifetimeMsForPacket(packetId);
12611
+ if (blockSpanMs === void 0 || blockLifetimeMs === void 0) return NO_FEC_GEOMETRY_HORIZON;
12625
12612
  if (blockSpanMs === 0) return NO_BLOCK_SPAN_HORIZON;
12626
12613
  return {
12627
12614
  kind: "fec-horizon",
12628
- retentionMs: duplicateRetentionMs({
12629
- blockSpanMs,
12630
- blockLifetimeMs: fec.blockLifetimeMsForPacket(packetId)
12631
- })
12615
+ retentionMs: duplicateRetentionMs({ blockSpanMs, blockLifetimeMs })
12632
12616
  };
12633
12617
  }
12618
+ /**
12619
+ * Init segments delivered for a packetId with no `onTrackInit` handler
12620
+ * since construction, whether the deprecated aggregate `onInitSegment`
12621
+ * callback then received them or nothing did. A supported deployment reads
12622
+ * 0 here; a non-zero value is the evidence that blocks removing the
12623
+ * aggregate callback (MOQT-01, BLO-21181).
12624
+ */
12625
+ get unmappedInitDeliveryCount() {
12626
+ return this.#unmappedInitDeliveries;
12627
+ }
12634
12628
  /** Publisher restarts detected on the multicast leg since construction. */
12635
12629
  get publisherRestartCount() {
12636
12630
  return this.#publisherRestarts;
@@ -12718,7 +12712,7 @@ var MmtpRouter = class {
12718
12712
  /** Update MFU reassembly timeout. Source and FEC-recovery paths share the
12719
12713
  * parser's single reassembler (BLO-8151), so one update covers both. */
12720
12714
  setMfuTimeoutMs(ms) {
12721
- if (ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
12715
+ if (ms !== null && ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
12722
12716
  this.#parser.setMfuTimeoutMs(ms);
12723
12717
  }
12724
12718
  /** Update parser frame reordering for unordered multicast delivery.
@@ -12788,7 +12782,15 @@ var MmtpRouter = class {
12788
12782
  onTrackPacket(packetId, cb) {
12789
12783
  this.#trackPacketCallbacks.set(packetId, cb);
12790
12784
  }
12791
- /** Register an init segment callback (ftyp+moov from MMTP MPU metadata). */
12785
+ /**
12786
+ * Register an init segment callback (ftyp+moov from MMTP MPU metadata).
12787
+ * Receives an init only for a packetId with no `onTrackInit` handler.
12788
+ *
12789
+ * @deprecated Use onTrackInit(packetId, cb). Still delivered as a fallback;
12790
+ * removed in the 1.0.0 major, no earlier than one minor release after this
12791
+ * deprecation reaches the `latest` dist-tag (BLO-21181). Count fallback
12792
+ * deliveries with `unmappedInitDeliveryCount`.
12793
+ */
12792
12794
  onInitSegment(cb) {
12793
12795
  this.#onInitSegment = cb;
12794
12796
  }
@@ -12802,17 +12804,16 @@ var MmtpRouter = class {
12802
12804
  * When active, source frames are buffered per FEC block and emitted as
12803
12805
  * multi-sample CMAF segments instead of per-frame callbacks. */
12804
12806
  enableMseAssembler(packetId, config, catalogTrackName) {
12805
- const k = this.#kForPacket(packetId);
12806
12807
  const assemblerKey = this.#mseAssemblerKey(packetId, catalogTrackName);
12807
12808
  const previous = this.#mseAssemblers.get(assemblerKey);
12808
12809
  if (previous) {
12809
12810
  previous.flush();
12810
12811
  previous.dispose();
12811
12812
  }
12812
- const assembler = new CmafBlockAssembler({ ...config, k });
12813
+ const assembler = new CmafBlockAssembler(config);
12813
12814
  this.#mseAssemblers.set(assemblerKey, assembler);
12814
12815
  console.log(
12815
- `[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (K=${k}, ${config.trackType})`
12816
+ `[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${config.trackType})`
12816
12817
  );
12817
12818
  return assembler;
12818
12819
  }
@@ -12921,6 +12922,21 @@ var MmtpRouter = class {
12921
12922
  }
12922
12923
  this.#fecMappersByPacketId.set(packetId, mapper);
12923
12924
  }
12925
+ /**
12926
+ * Release the router side of a packetId whose FEC is ending: drop its block
12927
+ * mapper and end its FEC release gating. Frames the gate holds are released
12928
+ * in order, each gap stalled (no block will resolve it), not stranded. Call
12929
+ * before FECManager.releasePacket, while the gate can still read its blocks.
12930
+ */
12931
+ releaseFecPacket(packetId) {
12932
+ this.#fecMappersByPacketId.delete(packetId);
12933
+ this.#withDeferredFecPokes(() => {
12934
+ this.#parser.endFecRelease(packetId);
12935
+ for (const [key, poke] of this.#pendingFecPokes) {
12936
+ if (poke.packetId === packetId) this.#pendingFecPokes.delete(key);
12937
+ }
12938
+ });
12939
+ }
12924
12940
  registerManifestTrack(context) {
12925
12941
  if (!this.#manifestVerifier) throw new Error("manifest verifier is not configured");
12926
12942
  this.#manifestVerifier.registerTrack(context);
@@ -13040,87 +13056,21 @@ var MmtpRouter = class {
13040
13056
  this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
13041
13057
  return;
13042
13058
  }
13043
- if (this.#isIsoPacket(ownerPacketId)) {
13044
- this.#handleIsoRepair(ownerPacketId, catalogTrackName, parsed, "moq");
13045
- return;
13046
- }
13047
- const repairSbn = this.#deriveRepairSbn(parsed.ssStart, parsed.ssbLength, ownerPacketId);
13048
- if (repairSbn === null) return;
13049
- const repairEsi = this.#kForPacket(ownerPacketId) + parsed.rsId;
13050
- if (!this.#feedFecTransportSymbol(
13051
- ownerPacketId,
13052
- repairSbn,
13053
- repairEsi,
13054
- parsed.symbolData,
13055
- "moq",
13056
- catalogTrackName
13057
- )) return;
13058
- const recovered = catalogTrackName === void 0 ? this.#fecManager.feedRepairSymbol(
13059
- ownerPacketId,
13060
- parsed.ssStart,
13061
- parsed.ssbLength,
13062
- parsed.rsId,
13063
- parsed.symbolData
13064
- ) : this.#fecManager.feedRepairSymbol(
13065
- ownerPacketId,
13066
- parsed.ssStart,
13067
- parsed.ssbLength,
13068
- parsed.rsId,
13069
- parsed.symbolData,
13070
- catalogTrackName
13071
- );
13072
- if (recovered) {
13073
- const alreadyCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId) : this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);
13074
- if (catalogTrackName === void 0) {
13075
- this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);
13076
- } else {
13077
- this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId, catalogTrackName);
13078
- }
13079
- if (!alreadyCompleted) {
13080
- this.#fecManager.countRecovery(ownerPacketId, repairSbn, catalogTrackName);
13081
- if (catalogTrackName === void 0) {
13082
- this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);
13083
- } else {
13084
- this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);
13085
- }
13086
- this.#routeRecoveredBlock(
13087
- repairSbn,
13088
- recovered,
13089
- ownerPacketId,
13090
- catalogTrackName
13091
- );
13092
- }
13093
- }
13059
+ this.#handleIsoRepair(ownerPacketId, catalogTrackName, parsed, "moq");
13094
13060
  }
13095
13061
  }
13096
- // ── SBN/ESI derivation ──────────────────────────────────────────
13097
- #kForPacket(packetId) {
13098
- if (packetId != null)
13099
- return this.#fecManager?.kForPacket?.(packetId) ?? this.#fecMappersByPacketId.get(packetId)?.k ?? this.#fecManager?.k ?? 4;
13100
- return this.#defaultFecMapper?.k ?? this.#fecManager?.k ?? 4;
13101
- }
13062
+ // ── Packet FEC geometry and block profile ───────────────────────
13102
13063
  #symbolSizeForPacket(packetId) {
13103
- if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ?? this.#fecManager?.symbolSize ?? 0;
13064
+ if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ?? 0;
13104
13065
  return this.#fecManager?.symbolSize ?? 0;
13105
13066
  }
13106
- /** Derive SBN/ESI from SS_ID using packet-scoped FEC geometry. */
13107
- #deriveSbnEsi(ssId, packetId) {
13108
- const mapper = packetId == null ? this.#defaultFecMapper : this.#fecMappersByPacketId.get(packetId) ?? null;
13109
- if (mapper) return mapper.fromSsId(ssId);
13110
- const k = this.#kForPacket(packetId);
13111
- return { sbn: Math.floor(ssId / k), esi: ssId % k };
13112
- }
13113
- #deriveRepairSbn(ssStart, ssbLength, packetId) {
13114
- const k = this.#kForPacket(packetId);
13115
- if (ssbLength <= 0 || ssbLength > k || ssStart % k !== 0) return null;
13116
- return Math.floor(ssStart / k);
13117
- }
13118
13067
  /**
13119
13068
  * The configured block profile of a packetId, or undefined while it is
13120
13069
  * unconfigured. With an FEC manager its per-packet configuration is the
13121
13070
  * only source. A router without one still recovers symbols through its
13122
13071
  * registered mapper, built with the catalog's explicit profile: the
13123
- * packet's own mapper, else the default one (the #deriveSbnEsi rule).
13072
+ * packet's own mapper, else the default one. A NullBlockMapper (a stream
13073
+ * without an FEC instance) states none.
13124
13074
  */
13125
13075
  #configuredBlockProfile(packetId) {
13126
13076
  if (this.#fecManager) return this.#fecManager.blockProfileForPacket(packetId);
@@ -13128,9 +13078,10 @@ var MmtpRouter = class {
13128
13078
  return mapper?.blockProfile ?? void 0;
13129
13079
  }
13130
13080
  /**
13131
- * The block profile an FEC coordinate of `packetId` is read under. Throws
13132
- * while none is configured: the two profiles carry identical wire tuples
13133
- * with different block math, so there is no default to fall back to.
13081
+ * The block profile an FEC coordinate of `packetId` is read under:
13082
+ * `iso-ssbg1-v1`, the only one (`k-padded-v1` is retired and refused).
13083
+ * Throws while none is configured: such a packet has no FEC geometry, and
13084
+ * nothing is inferred from the wire or defaulted.
13134
13085
  * Packet ingress leaves such a packet's FEC Payload ID unread first
13135
13086
  * (#noteUnreadableFecPayloadId); a throw here is a wiring fault on a path
13136
13087
  * that cannot hold an unconfigured packet.
@@ -13144,8 +13095,16 @@ var MmtpRouter = class {
13144
13095
  }
13145
13096
  return profile;
13146
13097
  }
13147
- #isIsoPacket(packetId) {
13148
- return this.#blockProfileFor(packetId) === "iso-ssbg1-v1";
13098
+ /**
13099
+ * The block coordinates (SS_Start, ESI) of an FEC source: range membership
13100
+ * in a block a repair already stated (ISO/IEC 23008-1 C.5.3). Null while no
13101
+ * repair has stated its block, while its packetId has no configured block
13102
+ * profile, and without an FEC manager: SS_ID alone locates no block, so
13103
+ * nothing is derived from it.
13104
+ */
13105
+ #locateFecSource(packetId, catalogTrackName, ssId) {
13106
+ if (this.#configuredBlockProfile(packetId) === void 0) return null;
13107
+ return this.#fecManager?.locateIsoSource?.(packetId, catalogTrackName, ssId) ?? null;
13149
13108
  }
13150
13109
  /**
13151
13110
  * Explicit refusal to read an FEC Payload ID: its (owner) packetId has no
@@ -13164,21 +13123,20 @@ var MmtpRouter = class {
13164
13123
  }
13165
13124
  }
13166
13125
  /**
13167
- * The release-gate anchor of a recovered symbol: its block (SBN) under
13168
- * k-padded-v1, its own SS_ID under iso-ssbg1-v1, where a frame is gated by
13169
- * the SS_IDs between it and the last released frame.
13126
+ * The release-gate anchor of a recovered symbol: its own SS_ID, SS_Start +
13127
+ * ESI modulo 2^32 (C.5.2), where a frame is gated by the SS_IDs between it
13128
+ * and the last released frame.
13170
13129
  */
13171
- #fecAnchorForRecovered(ownerPacketId, blockId, index) {
13172
- return ownerPacketId !== void 0 && this.#isIsoPacket(ownerPacketId) ? serial32Add(blockId, index) : blockId;
13130
+ #fecAnchorForRecovered(blockStart, index) {
13131
+ return serial32Add(blockStart, index);
13173
13132
  }
13174
13133
  /**
13175
13134
  * One iso-ssbg1-v1 repair symbol (ISO/IEC 23008-1 C.5.3). It states the
13176
13135
  * block `[SS_Start, SS_Start + SSB_length)`; repair ESI = SSB_length +
13177
13136
  * RS_ID (K' = SSB_length). The held sources it placed (and those of a
13178
13137
  * repair-less span in front of it) are fed as first-arrival sources, then
13179
- * the repair, the order they had on the wire, so a loss-free block is clean
13180
- * on its repair exactly as under k-padded-v1. A repair contradicting a known
13181
- * block is dropped.
13138
+ * the repair, the order they had on the wire, so a loss-free block completes
13139
+ * clean on its repair. A repair contradicting a known block is dropped.
13182
13140
  */
13183
13141
  #handleIsoRepair(ownerPacketId, catalogTrackName, repair, source) {
13184
13142
  const fec = this.#fecManager;
@@ -13245,7 +13203,7 @@ var MmtpRouter = class {
13245
13203
  }
13246
13204
  if (payload.oversized) continue;
13247
13205
  const alreadyCompleted = fec.isBlockCompleted(blockStart, ownerPacketId, catalogTrackName);
13248
- const recovered = fec.feedSourceSymbol(ownerPacketId, ssId, payload.symbolData, void 0, catalogTrackName);
13206
+ const recovered = fec.feedSourceSymbol(ownerPacketId, ssId, payload.symbolData, catalogTrackName);
13249
13207
  if (!recovered || alreadyCompleted) continue;
13250
13208
  fec.cancelBlockTimeout(blockStart, ownerPacketId, catalogTrackName);
13251
13209
  fec.countRecovery(ownerPacketId, blockStart, catalogTrackName);
@@ -13270,7 +13228,8 @@ var MmtpRouter = class {
13270
13228
  return null;
13271
13229
  }
13272
13230
  /** Route a recovered FEC block through the parser + dedup pipeline.
13273
- * Sync WASM decoders return K fixed-width source symbols concatenated. */
13231
+ * Sync WASM decoders return the block's K' = SSB_length fixed-width
13232
+ * ssbg_mode1 source symbols concatenated. */
13274
13233
  routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
13275
13234
  this.#routeRecoveredBlock(blockId, data, packetId, catalogTrackName);
13276
13235
  }
@@ -13738,29 +13697,19 @@ var MmtpRouter = class {
13738
13697
  resolvedLoss: (packetId, catalogTrackName, fromSbn, toSbn, releasedSbn) => {
13739
13698
  const fec = this.#fecManager;
13740
13699
  if (!fec?.blockResolution) return [];
13741
- if (this.#isIsoPacket(packetId)) {
13742
- const head = fromSbn === releasedSbn ? toSbn : fromSbn;
13743
- return fec.isoSpanResolution?.(packetId, catalogTrackName, releasedSbn, head) ?? null;
13744
- }
13745
- const highest = fec.highestSbn?.(packetId, catalogTrackName);
13746
- const lost = [];
13747
- for (let sbn = fromSbn; sbn <= toSbn; sbn++) {
13748
- const resolution = fec.blockResolution(sbn, packetId, catalogTrackName);
13749
- if (resolution === "pending") return null;
13750
- if (resolution === "failed") {
13751
- lost.push(sbn);
13752
- } else if (resolution === "untracked") {
13753
- if (sbn === releasedSbn) continue;
13754
- if (highest === void 0 || sbn > highest) return null;
13755
- lost.push(sbn);
13756
- }
13757
- }
13758
- return lost;
13700
+ const head = fromSbn === releasedSbn ? toSbn : fromSbn;
13701
+ return fec.isoSpanResolution?.(packetId, catalogTrackName, releasedSbn, head) ?? null;
13759
13702
  },
13760
13703
  // The retention window itself: a frame is never still held when a block
13761
13704
  // it waits on retires. Only consulted while resolvedLoss holds a gap,
13762
13705
  // which needs blockResolution, so the check above guarantees the method.
13763
- holdLimitMs: (packetId) => this.#fecManager?.terminalRetentionMsForPacket?.(packetId) ?? Infinity,
13706
+ // A packetId the manager no longer has configured (released) has no
13707
+ // block left to wait on: release its frames now (BLO-45953).
13708
+ holdLimitMs: (packetId) => {
13709
+ const fec = this.#fecManager;
13710
+ if (!fec?.terminalRetentionMsForPacket) return Infinity;
13711
+ return fec.terminalRetentionMsForPacket(packetId) ?? 0;
13712
+ },
13764
13713
  decide: (before, after, lostBlocks, discontinuity) => {
13765
13714
  const decision = this.#decideGap(before, after, lostBlocks, discontinuity);
13766
13715
  this.#onFrameGap?.(decision);
@@ -13822,9 +13771,10 @@ var MmtpRouter = class {
13822
13771
  }
13823
13772
  /**
13824
13773
  * The most media units a gap between two released frames can hold: the
13825
- * FEC blocks from `before`'s SBN to `after`'s, times D, the media units one
13826
- * block touches (ceil(interleaveDepthMs / unit duration), §5.1). Null when
13827
- * either SBN, the unit duration or the catalog interleave is unknown.
13774
+ * FEC blocks between `before`'s anchor and `after`'s, times D, the media
13775
+ * units one block touches (ceil(interleaveDepthMs / unit duration), §5.1).
13776
+ * Null when either anchor, the unit duration or the catalog interleave is
13777
+ * unknown.
13828
13778
  */
13829
13779
  #maxMissingUnits(before, after) {
13830
13780
  const from = before.fecSbn;
@@ -13837,7 +13787,7 @@ var MmtpRouter = class {
13837
13787
  const denominator = BigInt(timing.durationTicks) * 1000n;
13838
13788
  const unitsPerBlock = (numerator + denominator - 1n) / denominator;
13839
13789
  const blocks = BigInt(
13840
- (this.#isIsoPacket(after.packetId) ? serial32Precedes(from, to) ? serial32Distance(from, to) : serial32Distance(to, from) : Math.abs(to - from)) + 1
13790
+ (serial32Precedes(from, to) ? serial32Distance(from, to) : serial32Distance(to, from)) + 1
13841
13791
  );
13842
13792
  const bound = unitsPerBlock * blocks;
13843
13793
  return bound > BigInt(Number.MAX_SAFE_INTEGER) ? null : Number(bound);
@@ -13887,8 +13837,9 @@ var MmtpRouter = class {
13887
13837
  const cb = this.#trackInitCallbacks.get(packetId);
13888
13838
  if (cb) {
13889
13839
  cb(data);
13890
- } else if (this.#onInitSegment) {
13891
- this.#onInitSegment(data);
13840
+ } else {
13841
+ this.#unmappedInitDeliveries++;
13842
+ this.#onInitSegment?.(data);
13892
13843
  }
13893
13844
  console.log(
13894
13845
  `[MmtpRouter] Init delivered for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${data.byteLength}B)`
@@ -13930,9 +13881,9 @@ var MmtpRouter = class {
13930
13881
  if (!info.isRepair && this.#manifestVerifier && !manifestAdmitted) {
13931
13882
  let identity = manifestIdentity;
13932
13883
  if (!identity && info.fecPayloadId && !fecUnreadable && (this.#resolveManifestIdentityForRoute || this.#resolveManifestIdentity)) {
13933
- const coordinates = this.#deriveSbnEsi(info.fecPayloadId.ssId, info.packetId);
13934
13884
  const catalogTrackName = catalogTrackNameForPacket(ingress, info.packetId);
13935
- identity = (this.#resolveManifestIdentityForRoute ? this.#resolveManifestIdentityForRoute({
13885
+ const coordinates = this.#locateFecSource(info.packetId, catalogTrackName, info.fecPayloadId.ssId);
13886
+ identity = coordinates === null ? void 0 : (this.#resolveManifestIdentityForRoute ? this.#resolveManifestIdentityForRoute({
13936
13887
  packetId: info.packetId,
13937
13888
  sbn: coordinates.sbn,
13938
13889
  esi: coordinates.esi,
@@ -13984,79 +13935,7 @@ var MmtpRouter = class {
13984
13935
  this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
13985
13936
  return;
13986
13937
  }
13987
- if (this.#isIsoPacket(ownerPacketId)) {
13988
- this.#handleIsoRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair, source);
13989
- return;
13990
- }
13991
- const repairSbn = this.#deriveRepairSbn(repair.ssStart, repair.ssbLength, ownerPacketId);
13992
- if (repairSbn === null) return;
13993
- const repairEsi = this.#kForPacket(ownerPacketId) + repair.rsId;
13994
- if (!this.#feedFecTransportSymbol(
13995
- ownerPacketId,
13996
- repairSbn,
13997
- repairEsi,
13998
- repair.symbolData,
13999
- source,
14000
- catalogTrackNameForPacket(ingress, ownerPacketId)
14001
- )) return;
14002
- const repairCatalogTrackName = catalogTrackNameForPacket(ingress, ownerPacketId);
14003
- if (repairCatalogTrackName === void 0) {
14004
- this.#fecManager.noteBlockActivity?.(repairSbn, ownerPacketId);
14005
- } else {
14006
- this.#fecManager.noteBlockActivity?.(
14007
- repairSbn,
14008
- ownerPacketId,
14009
- repairCatalogTrackName
14010
- );
14011
- }
14012
- const recovered = repairCatalogTrackName === void 0 ? this.#fecManager.feedRepairSymbol(
14013
- ownerPacketId,
14014
- repair.ssStart,
14015
- repair.ssbLength,
14016
- repair.rsId,
14017
- repair.symbolData
14018
- ) : this.#fecManager.feedRepairSymbol(
14019
- ownerPacketId,
14020
- repair.ssStart,
14021
- repair.ssbLength,
14022
- repair.rsId,
14023
- repair.symbolData,
14024
- repairCatalogTrackName
14025
- );
14026
- if (recovered) {
14027
- const alreadyCompleted = repairCatalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId) : this.#fecManager.isBlockCompleted(
14028
- repairSbn,
14029
- ownerPacketId,
14030
- repairCatalogTrackName
14031
- );
14032
- if (repairCatalogTrackName === void 0) {
14033
- this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);
14034
- } else {
14035
- this.#fecManager.cancelBlockTimeout(
14036
- repairSbn,
14037
- ownerPacketId,
14038
- repairCatalogTrackName
14039
- );
14040
- }
14041
- if (!alreadyCompleted) {
14042
- this.#fecManager.countRecovery(ownerPacketId, repairSbn, repairCatalogTrackName);
14043
- if (repairCatalogTrackName === void 0) {
14044
- this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);
14045
- } else {
14046
- this.#fecManager.markBlockCompleted(
14047
- repairSbn,
14048
- ownerPacketId,
14049
- repairCatalogTrackName
14050
- );
14051
- }
14052
- this.#routeRecoveredBlock(
14053
- repairSbn,
14054
- recovered,
14055
- ownerPacketId,
14056
- catalogTrackNameForPacket(ingress, ownerPacketId)
14057
- );
14058
- }
14059
- }
13938
+ this.#handleIsoRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair, source);
14060
13939
  }
14061
13940
  } else {
14062
13941
  if (this.#classifyPacketId(info.packetId) === null) return;
@@ -14080,8 +13959,8 @@ var MmtpRouter = class {
14080
13959
  }
14081
13960
  const duplicates = this.#stream(info.packetId, catalogTrackName).duplicates;
14082
13961
  const fecPayloadId = fecUnreadable ? null : info.fecPayloadId;
14083
- const isoSource = this.#fecManager !== null && fecPayloadId != null && this.#isIsoPacket(info.packetId);
14084
- const coords = this.#fecManager && fecPayloadId ? isoSource ? this.#fecManager.locateIsoSource?.(info.packetId, catalogTrackName, fecPayloadId.ssId) ?? void 0 : this.#deriveSbnEsi(fecPayloadId.ssId, info.packetId) : void 0;
13962
+ const fecSource = this.#fecManager !== null && fecPayloadId != null;
13963
+ const coords = fecSource && fecPayloadId ? this.#locateFecSource(info.packetId, catalogTrackName, fecPayloadId.ssId) ?? void 0 : void 0;
14085
13964
  duplicates.advance(coords?.sbn);
14086
13965
  if (source === "multicast" && !overlapCopy && !releasedFromQuarantine && catalogTrackName !== void 0 && duplicates.predatesHorizon(info.sequenceNumber, fecPayloadId?.ssId)) {
14087
13966
  this.#publisherRestarts++;
@@ -14096,9 +13975,8 @@ var MmtpRouter = class {
14096
13975
  }
14097
13976
  if (this.#fecManager && fecPayloadId && coords) {
14098
13977
  const fecId = fecPayloadId;
14099
- const symbolSize = this.#symbolSizeForPacket(info.packetId);
14100
13978
  const rawSymbol = packet.subarray(0, packet.length - 4);
14101
- const oversized = isoSource ? !(this.#fecManager.isoSourceFits?.(info.packetId, rawSymbol.length) ?? false) : symbolSize > 0 && rawSymbol.length > symbolSize;
13979
+ const oversized = !(this.#fecManager.isoSourceFits?.(info.packetId, rawSymbol.length) ?? false);
14102
13980
  let overlapBlockCompleted = false;
14103
13981
  if (overlapCopy) {
14104
13982
  overlapBlockCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(coords.sbn, info.packetId) : this.#fecManager.isBlockCompleted(
@@ -14133,7 +14011,7 @@ var MmtpRouter = class {
14133
14011
  }
14134
14012
  parseSourceSymbol = (overlapCopy ? !overlapBlockCompleted : sourceSymbolAccepted) && !duplicates.parsedSymbols.has(fecId.ssId);
14135
14013
  fecInfo = { sbn: coords.sbn, esi: coords.esi, oversized };
14136
- } else if (isoSource && this.#fecManager && fecPayloadId) {
14014
+ } else if (fecSource && this.#fecManager && fecPayloadId) {
14137
14015
  const ssId = fecPayloadId.ssId;
14138
14016
  if (overlapCopy) {
14139
14017
  parseSourceSymbol = !duplicates.parsedSymbols.has(ssId);
@@ -14167,12 +14045,13 @@ var MmtpRouter = class {
14167
14045
  this.#feedViaParser(packet, info, fecInfo, {
14168
14046
  source,
14169
14047
  catalogTrackName,
14170
- ...isoSource && info.fecPayloadId ? { fecSbn: info.fecPayloadId.ssId } : fecInfo === void 0 ? {} : { fecSbn: fecInfo.sbn }
14048
+ // An FEC source frame is anchored by its own SS_ID (C.5.2).
14049
+ ...fecSource && fecPayloadId ? { fecSbn: fecPayloadId.ssId } : {}
14171
14050
  }, parsed);
14172
14051
  }
14173
14052
  if (this.#classifyPacketId(info.packetId) === null) return;
14174
- if (this.#fecManager && fecPayloadId && sourceSymbolAccepted && sourceSymbolTrackable) {
14175
- const { sbn, esi } = isoSource && coords ? coords : this.#deriveSbnEsi(fecPayloadId.ssId, info.packetId);
14053
+ if (this.#fecManager && fecPayloadId && coords && sourceSymbolAccepted && sourceSymbolTrackable) {
14054
+ const { sbn, esi } = coords;
14176
14055
  if (this.#classifyPacketId(info.packetId) !== null && this.#fecManager.isTrackConfigured?.(info.packetId) !== false) {
14177
14056
  this.#fecManager.trackBlock(
14178
14057
  sbn,
@@ -14193,13 +14072,11 @@ var MmtpRouter = class {
14193
14072
  const recovered = catalogTrackName === void 0 ? this.#fecManager.feedSourceSymbol(
14194
14073
  info.packetId,
14195
14074
  sourceFec.ssId,
14196
- sourceFec.symbolData,
14197
- void 0
14075
+ sourceFec.symbolData
14198
14076
  ) : this.#fecManager.feedSourceSymbol(
14199
14077
  info.packetId,
14200
14078
  sourceFec.ssId,
14201
14079
  sourceFec.symbolData,
14202
- void 0,
14203
14080
  catalogTrackName
14204
14081
  );
14205
14082
  if (recovered) {
@@ -14606,7 +14483,6 @@ var MmtpRouter = class {
14606
14483
  // ── Private: FEC recovery routing ───────────────────────────────
14607
14484
  /** Route WASM-recovered block through appropriate path based on container kind. */
14608
14485
  #routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName) {
14609
- const k = this.#kForPacket(ownerPacketId);
14610
14486
  const packetSymbolSize = this.#symbolSizeForPacket(ownerPacketId);
14611
14487
  if (ownerPacketId === void 0) {
14612
14488
  throw new Error("[MmtpRouter] a recovered FEC block needs its owner packetId to read its fec.blockProfile");
@@ -14619,7 +14495,6 @@ var MmtpRouter = class {
14619
14495
  }
14620
14496
  const symbolSize = packetSymbolSize;
14621
14497
  const result = processFecBlock(recovered, {
14622
- ...blockProfile === "k-padded-v1" ? { k } : {},
14623
14498
  symbolSize,
14624
14499
  ownerPacketId,
14625
14500
  blockProfile
@@ -14699,8 +14574,7 @@ var MmtpRouter = class {
14699
14574
  const admission = verifier.admitObject(
14700
14575
  {
14701
14576
  ...identity,
14702
- packetId: ownerPacketId,
14703
- ...ownerPacketId !== void 0 && this.#isIsoPacket(ownerPacketId) ? {} : { ssId: blockId * this.#kForPacket(ownerPacketId) + symbol.index }
14577
+ packetId: ownerPacketId
14704
14578
  },
14705
14579
  symbol.rawSymbol,
14706
14580
  () => {
@@ -14803,7 +14677,7 @@ var MmtpRouter = class {
14803
14677
  rapFlag: sym.fragment.rapFlag,
14804
14678
  recovered: true,
14805
14679
  deliveryProvenance,
14806
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
14680
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
14807
14681
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
14808
14682
  });
14809
14683
  continue;
@@ -14915,7 +14789,7 @@ var MmtpRouter = class {
14915
14789
  rapFlag: assembled.rapFlag,
14916
14790
  recovered: true,
14917
14791
  ...assembled.deliveryProvenance === void 0 ? {} : { deliveryProvenance: assembled.deliveryProvenance },
14918
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
14792
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
14919
14793
  ...recoveredCatalogTrackName === void 0 ? {} : { catalogTrackName: recoveredCatalogTrackName }
14920
14794
  });
14921
14795
  continue;
@@ -15052,7 +14926,7 @@ var MmtpRouter = class {
15052
14926
  rapFlag: sym.fragment.rapFlag,
15053
14927
  recovered: true,
15054
14928
  deliveryProvenance,
15055
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
14929
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
15056
14930
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
15057
14931
  });
15058
14932
  } else {
@@ -15131,7 +15005,7 @@ var MmtpRouter = class {
15131
15005
  rapFlag: assembled.rapFlag,
15132
15006
  recovered: true,
15133
15007
  deliveryProvenance,
15134
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
15008
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
15135
15009
  ...assembled.catalogTrackName === void 0 ? {} : { catalogTrackName: assembled.catalogTrackName }
15136
15010
  });
15137
15011
  }
@@ -15166,7 +15040,7 @@ var MmtpRouter = class {
15166
15040
  ...sym.fragment.sampleNumber === void 0 ? {} : { sampleNumber: sym.fragment.sampleNumber },
15167
15041
  recovered: true,
15168
15042
  deliveryProvenance,
15169
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
15043
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
15170
15044
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
15171
15045
  });
15172
15046
  continue;
@@ -15217,22 +15091,17 @@ var MoqFecAlgorithm;
15217
15091
  MoqFecAlgorithm2[MoqFecAlgorithm2["RaptorQ"] = 2] = "RaptorQ";
15218
15092
  })(MoqFecAlgorithm || (MoqFecAlgorithm = {}));
15219
15093
 
15220
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-alta-parse/dist/index.js
15221
- var MPU_HEADER_SIZE3 = 8;
15222
- var MPU_LENGTH_FIELD_SIZE3 = 2;
15223
- var MPU_HEADER_REMAINDER_SIZE3 = MPU_HEADER_SIZE3 - MPU_LENGTH_FIELD_SIZE3;
15224
-
15225
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/mmtp.js
15226
- var MPU_HEADER_SIZE4 = 8;
15227
- var MPU_LENGTH_FIELD_SIZE4 = 2;
15228
- var MPU_HEADER_REMAINDER_SIZE4 = MPU_HEADER_SIZE4 - MPU_LENGTH_FIELD_SIZE4;
15094
+ // node_modules/@blockcast/mmt-base/dist/mmtp.js
15095
+ var MPU_HEADER_SIZE2 = 8;
15096
+ var MPU_LENGTH_FIELD_SIZE2 = 2;
15097
+ var MPU_HEADER_REMAINDER_SIZE2 = MPU_HEADER_SIZE2 - MPU_LENGTH_FIELD_SIZE2;
15229
15098
  var MMTP_SIGNALING_PACKET_ID2 = 0;
15230
15099
  var MMTP_PACKET_ID_MAX2 = 65535;
15231
15100
 
15232
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/net-constants.js
15101
+ // node_modules/@blockcast/mmt-base/dist/net-constants.js
15233
15102
  var DEFAULT_AMT_RELAY_PORT2 = 2268;
15234
15103
 
15235
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/ntp-short.js
15104
+ // node_modules/@blockcast/mmt-base/dist/ntp-short.js
15236
15105
  var NTP_SHORT_BITS2 = 32;
15237
15106
  var NTP_SHORT_MODULUS2 = 2 ** NTP_SHORT_BITS2;
15238
15107
  var NTP_SHORT_HALF_RANGE2 = 2 ** (NTP_SHORT_BITS2 - 1);
@@ -15253,9 +15122,8 @@ function compareNtpShort(a, b) {
15253
15122
  return "unordered";
15254
15123
  }
15255
15124
 
15256
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js
15125
+ // node_modules/@blockcast/mmt-base/dist/fec-block-profile.js
15257
15126
  var FEC_BLOCK_PROFILES2 = Object.freeze([
15258
- "k-padded-v1",
15259
15127
  "iso-ssbg1-v1"
15260
15128
  ]);
15261
15129
 
@@ -15647,10 +15515,12 @@ var MulticastOrchestrator = class {
15647
15515
  }
15648
15516
  onGoodput(sample) {
15649
15517
  const bitrate = sample.bitrate;
15650
- if (!this.#current || this.options.adaptive === false)
15518
+ if (!this.#current)
15651
15519
  return void 0;
15652
15520
  if (this.#state === "joining")
15653
15521
  return this.poll();
15522
+ if (this.options.adaptive === false)
15523
+ return void 0;
15654
15524
  if (bitrate === void 0 || !Number.isFinite(bitrate))
15655
15525
  return void 0;
15656
15526
  this.#estimate = bitrate;
@@ -15717,6 +15587,27 @@ var MulticastOrchestrator = class {
15717
15587
  this.#favored = void 0;
15718
15588
  return { kind: "join-target", current: this.#current, target: desired };
15719
15589
  }
15590
+ /**
15591
+ * Join `target` because the viewer selected it, not because goodput or
15592
+ * headroom ranked it. It runs whether or not the orchestrator adapts, and
15593
+ * takes the path an adaptive join takes: commit on the target's RAP at or
15594
+ * beyond the high-water mark, abandon at the join deadline. While it is
15595
+ * pending nothing re-ranks the ladder; goodput only advances the deadline.
15596
+ *
15597
+ * Returns undefined, changing nothing, while another join is pending (cancel
15598
+ * it first), when `target` is the current tier, or when it carries no media.
15599
+ */
15600
+ select(target) {
15601
+ if (!this.#current || this.#state === "joining" || !carriesMedia(target))
15602
+ return void 0;
15603
+ if (endpointKey(target) === endpointKey(this.#current))
15604
+ return void 0;
15605
+ this.#state = "joining";
15606
+ this.#target = target;
15607
+ this.#joinStartedAt = this.#now();
15608
+ this.#favored = void 0;
15609
+ return { kind: "join-target", current: this.#current, target };
15610
+ }
15720
15611
  /**
15721
15612
  * Feed a packet from the active tier (to advance the high-water mark) or
15722
15613
  * from the in-flight target (to find a committable RAP).
@@ -16756,7 +16647,7 @@ var MulticastTransport = class _MulticastTransport {
16756
16647
  // inside the window, instead of a clear+set pair on every packet.
16757
16648
  #watchdog = null;
16758
16649
  #lastPacketAtMs = 0;
16759
- #failbackWindowMs = null;
16650
+ #failbackWindowMs;
16760
16651
  #onTakeover = null;
16761
16652
  #onFailback = null;
16762
16653
  // Data-plane-live edge: the first packet this join accepted. Everything
@@ -17061,14 +16952,20 @@ var MulticastTransport = class _MulticastTransport {
17061
16952
  return this.#dataPlaneLive;
17062
16953
  }
17063
16954
  setFailbackWindowMs(windowMs) {
17064
- if (!Number.isFinite(windowMs) || windowMs <= 0) {
16955
+ if (windowMs !== null && (!Number.isFinite(windowMs) || windowMs <= 0)) {
17065
16956
  throw new Error(
17066
16957
  `[MulticastTransport] failbackWindowMs=${windowMs}. Derive a positive failback window from the stream catalog.`
17067
16958
  );
17068
16959
  }
17069
16960
  this.#failbackWindowMs = windowMs;
17070
- if (this.#watchdog !== null) {
17071
- clearTimeout(this.#watchdog);
16961
+ if (windowMs === null) {
16962
+ if (this.#watchdog !== null) clearTimeout(this.#watchdog);
16963
+ this.#watchdog = null;
16964
+ return;
16965
+ }
16966
+ if (this.#watchdog !== null) clearTimeout(this.#watchdog);
16967
+ this.#watchdog = null;
16968
+ if (this.#dataPlaneLive && this.#onFailback) {
17072
16969
  const silentMs = performance.now() - this.#lastPacketAtMs;
17073
16970
  this.#armWatchdog(Math.max(0, Math.ceil(windowMs - silentMs)));
17074
16971
  }
@@ -17394,7 +17291,8 @@ var MulticastTransport = class _MulticastTransport {
17394
17291
  }
17395
17292
  if (this.#watchdog !== null) return;
17396
17293
  if (!this.#onFailback) return;
17397
- if (this.#failbackWindowMs === null) {
17294
+ if (this.#failbackWindowMs === null) return;
17295
+ if (this.#failbackWindowMs === void 0) {
17398
17296
  throw new Error(
17399
17297
  "[MulticastTransport] Failback watchdog requires a catalog-derived window"
17400
17298
  );
@@ -17404,7 +17302,7 @@ var MulticastTransport = class _MulticastTransport {
17404
17302
  #armWatchdog(delayMs) {
17405
17303
  this.#watchdog = setTimeout(() => {
17406
17304
  this.#watchdog = null;
17407
- if (this.#disposed || !this.#onFailback || this.#failbackWindowMs === null) return;
17305
+ if (this.#disposed || !this.#onFailback || this.#failbackWindowMs == null) return;
17408
17306
  const silentMs = performance.now() - this.#lastPacketAtMs;
17409
17307
  if (silentMs < this.#failbackWindowMs) {
17410
17308
  this.#armWatchdog(Math.ceil(this.#failbackWindowMs - silentMs));
@@ -17438,6 +17336,35 @@ var BASE64URL_DECODE_TABLE = (() => {
17438
17336
  return table;
17439
17337
  })();
17440
17338
 
17339
+ // src/repair-layers.ts
17340
+ var RepairContractError = class extends Error {
17341
+ constructor(code, path, message) {
17342
+ super(`[repair-layers:${code}] ${path}: ${message}`);
17343
+ this.code = code;
17344
+ this.path = path;
17345
+ this.name = "RepairContractError";
17346
+ }
17347
+ };
17348
+ var throwRepairContractError = (code, path, message) => {
17349
+ throw new RepairContractError(code, path, message);
17350
+ };
17351
+ function readFecBlockProfile(value, path, declaresFecInstance, fail) {
17352
+ if (value === void 0 && !declaresFecInstance) return void 0;
17353
+ if (isFecBlockProfile(value)) return value;
17354
+ if (typeof value === "string" && value.length > 0) {
17355
+ return fail(
17356
+ "track_fec_block_profile_unimplemented",
17357
+ path,
17358
+ `${JSON.stringify(value)} is not an implemented FEC block profile; only "iso-ssbg1-v1" is implemented`
17359
+ );
17360
+ }
17361
+ try {
17362
+ return parseFecBlockProfile(value, "blockProfile");
17363
+ } catch (error) {
17364
+ return fail("track_fec_block_profile", path, error.message);
17365
+ }
17366
+ }
17367
+
17441
17368
  // src/carp-catalog.ts
17442
17369
  var VALID_ROLE_SUFFIXES = /* @__PURE__ */ new Set(["base", "delta", "repair"]);
17443
17370
  function parseTrackRole(trackName) {
@@ -17477,12 +17404,49 @@ function hasSwitchingSetRole(names) {
17477
17404
  return suffix === "base" || suffix === "delta";
17478
17405
  });
17479
17406
  }
17480
- function isFecCatalogConfig(obj) {
17407
+ function isFecCatalogGeometry(obj) {
17481
17408
  if (typeof obj !== "object" || obj === null) return false;
17482
17409
  const o = obj;
17483
17410
  const algorithm = o["algorithm"];
17484
17411
  const symbolSize = o["symbolSize"];
17485
- return typeof algorithm === "string" && (algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") && typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 && typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 && typeof symbolSize === "number" && symbolSize >= 1 && (algorithm !== "raptorq" || symbolSize % 8 === 0) && typeof o["repairTrack"] === "string" && o["repairTrack"].length > 0 && (isFecBlockProfile(o["blockProfile"]) || algorithm === "none" && o["blockProfile"] === void 0);
17412
+ return typeof algorithm === "string" && (algorithm === "none" || algorithm === "raptorq" || algorithm === "reed-solomon") && typeof o["sourceSymbols"] === "number" && o["sourceSymbols"] >= 1 && typeof o["repairSymbols"] === "number" && o["repairSymbols"] >= 1 && typeof symbolSize === "number" && symbolSize >= 1 && (algorithm !== "raptorq" || symbolSize % 8 === 0) && typeof o["repairTrack"] === "string" && o["repairTrack"].length > 0;
17413
+ }
17414
+ function isFecCatalogConfig(obj) {
17415
+ if (!isFecCatalogGeometry(obj)) return false;
17416
+ const o = obj;
17417
+ return isFecBlockProfile(o["blockProfile"]) || o["algorithm"] === "none" && o["blockProfile"] === void 0;
17418
+ }
17419
+
17420
+ // src/catalog-fec-params.ts
17421
+ import { catalogFecInterleaveMs } from "@blockcast/mmt-fec";
17422
+ var MAX_SAFE_TICKS_PER_HZ = Number.MAX_SAFE_INTEGER / 1e3;
17423
+ function recordOf2(value) {
17424
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
17425
+ }
17426
+ function blockProfileUnavailable(value, path, declaresFecInstance) {
17427
+ try {
17428
+ readFecBlockProfile(value, path, declaresFecInstance, throwRepairContractError);
17429
+ return void 0;
17430
+ } catch (error) {
17431
+ if (!(error instanceof RepairContractError) || error.code !== "track_fec_block_profile" && error.code !== "track_fec_block_profile_unimplemented") {
17432
+ throw error;
17433
+ }
17434
+ return Object.freeze({ code: error.code, path: error.path, message: error.message });
17435
+ }
17436
+ }
17437
+ function isoSpanUnavailable(fec, path, declaresFecInstance) {
17438
+ if (!declaresFecInstance || isoBlockSpanUsable(fec)) return void 0;
17439
+ return Object.freeze({
17440
+ code: "track_fec",
17441
+ path,
17442
+ message: `[repair-layers:track_fec] ${path}: must be a non-negative safe integer in milliseconds`
17443
+ });
17444
+ }
17445
+ function catalogFecUnavailable(fec, fecPath) {
17446
+ const block = recordOf2(fec);
17447
+ if (block === void 0) return void 0;
17448
+ const declaresFecInstance = block["algorithm"] !== "none";
17449
+ return blockProfileUnavailable(block["blockProfile"], `${fecPath}.blockProfile`, declaresFecInstance) ?? isoSpanUnavailable(block, `${fecPath}.interleaveDepthMs`, declaresFecInstance);
17486
17450
  }
17487
17451
 
17488
17452
  // src/multicast-manager.ts
@@ -17728,7 +17692,10 @@ function projectFecDiagnosticBlock(block) {
17728
17692
  lastSymbolTs: block.lastSymbolTs,
17729
17693
  ...block.completedTs === void 0 ? {} : { completedTs: block.completedTs },
17730
17694
  deadlineTs: block.deadlineTs,
17731
- altaVerified: block.altaVerified,
17695
+ // ponytail: the per-packet authenticator is gone, so this is always 0. It
17696
+ // stays only because pim-multicast-gateway's normalizeSubscriptionDiagnostics
17697
+ // still requires a number here; drop it once that validator stops asking.
17698
+ altaVerified: 0,
17732
17699
  relayBlockSigValid: block.relayBlockSigValid
17733
17700
  };
17734
17701
  }
@@ -17748,7 +17715,6 @@ function projectFecDiagnosticConfig(config) {
17748
17715
  interleaveMs: config.interleaveMs,
17749
17716
  fecMode: config.fecMode,
17750
17717
  ...config.repairTrack === void 0 ? {} : { repairTrack: config.repairTrack },
17751
- altaEnabled: config.altaEnabled,
17752
17718
  relayBlockSigEnabled: config.relayBlockSigEnabled
17753
17719
  };
17754
17720
  }
@@ -17795,9 +17761,8 @@ function repairTrackNamesBySource(tracks) {
17795
17761
  bySource.set(source, names);
17796
17762
  };
17797
17763
  for (const track of tracks) {
17798
- if (track.fec !== void 0 && isFecCatalogConfig(track.fec) && track.fec.repairTrack) {
17799
- add(track.name, track.fec.repairTrack);
17800
- }
17764
+ const repairTrack = typeof track.fec === "object" && track.fec !== null ? track.fec.repairTrack : void 0;
17765
+ if (typeof repairTrack === "string") add(track.name, repairTrack);
17801
17766
  if (track.packaging === "fec-repair" && Array.isArray(track.depends) && track.depends.length === 1) {
17802
17767
  add(track.depends[0], track.name);
17803
17768
  }
@@ -17807,6 +17772,7 @@ function repairTrackNamesBySource(tracks) {
17807
17772
  var MulticastManager = class {
17808
17773
  #fec;
17809
17774
  #router;
17775
+ #hostMfuTimeoutMs;
17810
17776
  #transport;
17811
17777
  #reportedFecDiagnosticsFailure = false;
17812
17778
  // ── Stream-global FEC timing reduction ──
@@ -17905,6 +17871,7 @@ var MulticastManager = class {
17905
17871
  this.#emitRepairLifecycle(decision);
17906
17872
  }
17907
17873
  });
17874
+ this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
17908
17875
  this.#transport = new MulticastTransport((packet, ingress) => {
17909
17876
  this.#router.feedPacket(packet, "multicast", void 0, ingress);
17910
17877
  });
@@ -18121,7 +18088,6 @@ var MulticastManager = class {
18121
18088
  ...this.#streamFecBaseline ? [this.#streamFecBaseline] : [],
18122
18089
  ...this.#trackFecTiming.values()
18123
18090
  ];
18124
- if (contributions.length === 0) return;
18125
18091
  let deliveryWindowMs;
18126
18092
  let minimumFailbackWindowMs;
18127
18093
  let mfuTimeoutMs;
@@ -18153,15 +18119,9 @@ var MulticastManager = class {
18153
18119
  }
18154
18120
  }
18155
18121
  const failbackWindowMs = deliveryWindowMs === void 0 ? minimumFailbackWindowMs : Math.max(deliveryWindowMs, minimumFailbackWindowMs ?? 0);
18156
- if (failbackWindowMs !== void 0) {
18157
- this.#transport.setFailbackWindowMs(failbackWindowMs);
18158
- }
18159
- if (mfuTimeoutMs !== void 0) {
18160
- this.#router.setMfuTimeoutMs(mfuTimeoutMs);
18161
- }
18162
- if (reorderWindowFrames !== void 0) {
18163
- this.#router.setReorderWindow(reorderWindowFrames);
18164
- }
18122
+ this.#transport.setFailbackWindowMs(failbackWindowMs ?? null);
18123
+ this.#router.setMfuTimeoutMs(mfuTimeoutMs ?? this.#hostMfuTimeoutMs);
18124
+ this.#router.setReorderWindow(reorderWindowFrames ?? 0);
18165
18125
  }
18166
18126
  /**
18167
18127
  * Configure one track's FEC geometry, block mapper, and block-inactivity
@@ -18188,9 +18148,9 @@ var MulticastManager = class {
18188
18148
  `[MulticastManager] configureTrackFec(${packetId}) catalogTrackName must be non-empty when given`
18189
18149
  );
18190
18150
  }
18191
- if (config.fecK === void 0 || config.fecInterleaveDepthMs === void 0) {
18151
+ if (config.fecK === void 0) {
18192
18152
  throw new Error(
18193
- `[MulticastManager] configureTrackFec(${packetId}) requires fecK and fecInterleaveDepthMs. Derive the per-track FEC geometry from the stream catalog.`
18153
+ `[MulticastManager] configureTrackFec(${packetId}) requires fecK. Derive the per-track FEC geometry from the stream catalog.`
18194
18154
  );
18195
18155
  }
18196
18156
  const blockProfile = parseFecBlockProfile(
@@ -18198,7 +18158,10 @@ var MulticastManager = class {
18198
18158
  `[MulticastManager] configureTrackFec(${packetId}) fecBlockProfile`
18199
18159
  );
18200
18160
  assertPositiveGeometry("fecK", config.fecK);
18201
- assertPositiveGeometry("fecInterleaveDepthMs", config.fecInterleaveDepthMs);
18161
+ const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
18162
+ if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
18163
+ throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
18164
+ }
18202
18165
  assertPositiveGeometry("fecSymbolSize", config.fecSymbolSize);
18203
18166
  assertPositiveGeometry("fps", config.fps);
18204
18167
  assertPositiveGeometry("bandwidth", config.bandwidth);
@@ -18214,8 +18177,8 @@ var MulticastManager = class {
18214
18177
  "fecReorderToleranceMs",
18215
18178
  config.fecReorderToleranceMs
18216
18179
  );
18217
- const deliveryWindowMs = config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
18218
- interleaveDepthMs: config.fecInterleaveDepthMs,
18180
+ const deliveryWindowMs = interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
18181
+ interleaveDepthMs,
18219
18182
  sourceSymbols: config.fecK,
18220
18183
  repairSymbols: config.fecRepairSymbols,
18221
18184
  symbolSize: config.fecSymbolSize,
@@ -18224,10 +18187,10 @@ var MulticastManager = class {
18224
18187
  jitterBufferMs: config.fecReorderToleranceMs
18225
18188
  }) : void 0;
18226
18189
  const trackKind = config.trackKind ?? this.#router.trackKindFor(packetId);
18227
- const geometry = trackKind === void 0 ? void 0 : deriveBlockClassifierGeometry({
18190
+ const geometry = trackKind === void 0 || interleaveDepthMs === 0 ? void 0 : deriveBlockClassifierGeometry({
18228
18191
  trackKind,
18229
18192
  framerate: config.fps,
18230
- interleaveDepthMs: config.fecInterleaveDepthMs,
18193
+ interleaveDepthMs,
18231
18194
  timescale: config.timescale,
18232
18195
  keyframeIntervalTicks: config.keyframeIntervalTicks,
18233
18196
  keyframeIntervalMs: config.keyframeIntervalMs
@@ -18236,7 +18199,7 @@ var MulticastManager = class {
18236
18199
  blockProfile,
18237
18200
  k: config.fecK,
18238
18201
  symbolSize: config.fecSymbolSize,
18239
- interleaveDepthMs: config.fecInterleaveDepthMs,
18202
+ interleaveDepthMs,
18240
18203
  fps: config.fps,
18241
18204
  timeoutMs: deliveryWindowMs,
18242
18205
  ...trackKind === void 0 ? {} : { trackKind },
@@ -18247,7 +18210,7 @@ var MulticastManager = class {
18247
18210
  this.#configureTrackMapper(packetId, fecConfig);
18248
18211
  this.#trackFecTiming.set(packetId, {
18249
18212
  deliveryWindowMs,
18250
- interleaveDepthMs: config.fecInterleaveDepthMs,
18213
+ interleaveDepthMs,
18251
18214
  fps: config.fps,
18252
18215
  k: config.fecK,
18253
18216
  repairSymbols: config.fecRepairSymbols,
@@ -18256,6 +18219,10 @@ var MulticastManager = class {
18256
18219
  this.#applyStreamGlobalFecTiming();
18257
18220
  }
18258
18221
  configureFec(config) {
18222
+ const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
18223
+ if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
18224
+ throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
18225
+ }
18259
18226
  const minimumFailbackWindowMs = config.minimumFailbackWindowMs;
18260
18227
  if (minimumFailbackWindowMs !== void 0 && (!Number.isFinite(minimumFailbackWindowMs) || minimumFailbackWindowMs <= 0)) {
18261
18228
  throw new Error(
@@ -18267,7 +18234,7 @@ var MulticastManager = class {
18267
18234
  config.fecReorderToleranceMs
18268
18235
  );
18269
18236
  const deliveryWindowMs = config.fecK && config.fecK > 0 && config.fecInterleaveDepthMs && config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
18270
- interleaveDepthMs: config.fecInterleaveDepthMs,
18237
+ interleaveDepthMs,
18271
18238
  sourceSymbols: config.fecK,
18272
18239
  repairSymbols: config.fecRepairSymbols,
18273
18240
  symbolSize: config.fecSymbolSize,
@@ -18278,13 +18245,13 @@ var MulticastManager = class {
18278
18245
  this.#fec.configure({
18279
18246
  k: config.fecK,
18280
18247
  symbolSize: config.fecSymbolSize,
18281
- interleaveDepthMs: config.fecInterleaveDepthMs,
18248
+ interleaveDepthMs,
18282
18249
  fps: config.fps,
18283
18250
  timeoutMs: deliveryWindowMs
18284
18251
  });
18285
18252
  this.#streamFecBaseline = {
18286
18253
  deliveryWindowMs,
18287
- interleaveDepthMs: config.fecInterleaveDepthMs,
18254
+ interleaveDepthMs,
18288
18255
  fps: config.fps,
18289
18256
  k: config.fecK,
18290
18257
  repairSymbols: config.fecRepairSymbols,
@@ -18333,6 +18300,7 @@ var MulticastManager = class {
18333
18300
  }
18334
18301
  setMfuTimeoutMs(ms) {
18335
18302
  this.#router.setMfuTimeoutMs(ms);
18303
+ this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
18336
18304
  }
18337
18305
  setTrackCodec(packetId, codec) {
18338
18306
  this.#router.setTrackCodec(packetId, codec);
@@ -18355,6 +18323,7 @@ var MulticastManager = class {
18355
18323
  onTrackPacket(packetId, cb) {
18356
18324
  this.#router.onTrackPacket(packetId, cb);
18357
18325
  }
18326
+ /** @deprecated Use onTrackInit(packetId, cb); see MmtpRouter.onInitSegment. */
18358
18327
  onInitSegment(cb) {
18359
18328
  this.#router.onInitSegment(cb);
18360
18329
  }
@@ -18427,7 +18396,7 @@ var MulticastManager = class {
18427
18396
  )
18428
18397
  )
18429
18398
  };
18430
- const amtRelay = config.amtRelay ?? config.relayAddress;
18399
+ const amtRelay = config.amtRelay;
18431
18400
  const transportConfig = {
18432
18401
  sourceAddress: config.sourceAddress,
18433
18402
  groupAddress: config.groupAddress,
@@ -18440,9 +18409,8 @@ var MulticastManager = class {
18440
18409
  // endpoint-config.ts) deliberately has no implicit relay port — it throws
18441
18410
  // and `MulticastTransport.join` resolves false — so the default is
18442
18411
  // resolved HERE and arrives there explicit, exactly as the two catalog
18443
- // readers do it. This is the last of the three readers: the `relayAddress`
18444
- // half cannot supply a port at all (the pre-migration wire shape has no
18445
- // port field), and `resolveNetworkSource` emits the same port-less shape.
18412
+ // readers do it. This is the last of the three readers: `resolveNetworkSource`
18413
+ // emits a port-less relay shape.
18446
18414
  // Keyed on the resolved relay, so a DRIAD entry — which pins no relay —
18447
18415
  // never has a port manufactured for it. Blank counts as no relay, the
18448
18416
  // same test `catalogAmtNetworkSource` applies (it trims, then skips) and
@@ -18666,7 +18634,10 @@ var MulticastManager = class {
18666
18634
  console.warn("[MulticastManager] rendition switch onCancel failed", error);
18667
18635
  }
18668
18636
  }
18669
- /** Release what the manager and FEC hold for a stream whose packet ID the router retired. */
18637
+ /**
18638
+ * Release what the manager and FEC hold for a stream whose packet ID the
18639
+ * router retired, or whose FEC a catalog reconfiguration no longer configures.
18640
+ */
18670
18641
  #releaseRetiredStream(stream) {
18671
18642
  for (const observer of this.#trackResetObservers) {
18672
18643
  try {
@@ -18685,7 +18656,10 @@ var MulticastManager = class {
18685
18656
  for (const [key, stats] of [...this.#blockPolicyStats]) {
18686
18657
  if (stats.packetId === stream.packetId) this.#blockPolicyStats.delete(key);
18687
18658
  }
18688
- this.#trackFecTiming.delete(stream.packetId);
18659
+ if (this.#trackFecTiming.delete(stream.packetId)) {
18660
+ this.#applyStreamGlobalFecTiming();
18661
+ }
18662
+ this.#multiTrackFecTracks.delete(stream.packetId);
18689
18663
  this.#fec.releasePacket(stream.packetId);
18690
18664
  }
18691
18665
  /** Feed a raw packet from an external source (AMT tunnel, ASM subscription).
@@ -18730,12 +18704,53 @@ var MulticastManager = class {
18730
18704
  * D-17: Builds altGroup → track-name index for getSwitchingSet().
18731
18705
  * D-26: Registers packetId → TrackRole on MmtpRouter for block-complete fanout.
18732
18706
  *
18707
+ * FEC availability is per track (draft-ramadan-moq-fec §5.1). A media track
18708
+ * whose `fec` names no implemented `blockProfile` (only `iso-ssbg1-v1` is
18709
+ * implemented; the retired `k-padded-v1` is not), or an `iso-ssbg1-v1` span
18710
+ * it cannot use, is consumed without FEC: it gets no decoder, and none of
18711
+ * its repair tracks (layer 0 by `fec.repairTrack`, further layers and the
18712
+ * overlay by `depends`) is given an owner, so the router drops their packets
18713
+ * before any decoder. Those legs keep the `repair` role and their owner's
18714
+ * switching set. Neither the call nor any other track's FEC fails on that
18715
+ * account; `trackFecUnavailable(name)` reports the reason. FEC a previous
18716
+ * call configured on a packetId this call does not is released
18717
+ * (FECManager.releasePacket); frames its release gate held are delivered in
18718
+ * order, each gap stalled. FEC wired by hand (setFecDecoder) is left alone.
18719
+ * Geometry a decoder could not configure (K, P, T, `repairTrack`,
18720
+ * algorithm) still throws.
18721
+ *
18733
18722
  * @param catalog - Multi-track catalog (tracks array with name, altGroup, fec, packetId).
18734
18723
  * @param wasmDecoderSource - One WASM FEC decoder for a single FEC stream, or
18735
18724
  * a factory returning a distinct decoder per stream.
18736
18725
  */
18737
18726
  configureMultiTrack(catalog, wasmDecoderSource) {
18738
18727
  const repairNamesBySource = repairTrackNamesBySource(catalog.tracks);
18728
+ const declaredRepairNames = /* @__PURE__ */ new Set();
18729
+ for (const names of repairNamesBySource.values()) {
18730
+ for (const name of names) declaredRepairNames.add(name);
18731
+ }
18732
+ const fecUnavailableByTrack = /* @__PURE__ */ new Map();
18733
+ for (let i = 0; i < catalog.tracks.length; i++) {
18734
+ const track = catalog.tracks[i];
18735
+ if (track.fec === void 0 || track.packaging === "fec-repair" || declaredRepairNames.has(track.name)) {
18736
+ continue;
18737
+ }
18738
+ const fecUnavailable = catalogFecUnavailable(track.fec, `$.tracks[${i}].fec`);
18739
+ if (fecUnavailable === void 0) continue;
18740
+ fecUnavailableByTrack.set(track.name, fecUnavailable);
18741
+ const layerZero = track.fec.repairTrack;
18742
+ if (typeof layerZero === "string" && layerZero.length > 0) {
18743
+ const names = repairNamesBySource.get(track.name) ?? /* @__PURE__ */ new Set();
18744
+ names.add(layerZero);
18745
+ repairNamesBySource.set(track.name, names);
18746
+ }
18747
+ }
18748
+ const unownedRepairNames = /* @__PURE__ */ new Set();
18749
+ for (const sourceName of fecUnavailableByTrack.keys()) {
18750
+ for (const repairName of repairNamesBySource.get(sourceName) ?? []) {
18751
+ unownedRepairNames.add(repairName);
18752
+ }
18753
+ }
18739
18754
  const repairOwnerGroup = /* @__PURE__ */ new Map();
18740
18755
  const repairOwnerAltGroup = /* @__PURE__ */ new Map();
18741
18756
  for (const track of catalog.tracks) {
@@ -18788,11 +18803,13 @@ var MulticastManager = class {
18788
18803
  for (let i = 0; i < catalog.tracks.length; i++) {
18789
18804
  const track = catalog.tracks[i];
18790
18805
  if (resolvedRoles[i] === "repair" || track.fec === void 0) continue;
18791
- if (!isFecCatalogConfig(track.fec)) {
18806
+ const unavailable = fecUnavailableByTrack.has(track.name);
18807
+ if (!(unavailable ? isFecCatalogGeometry(track.fec) : isFecCatalogConfig(track.fec))) {
18792
18808
  throw new Error(
18793
18809
  `[MulticastManager] invalid fec config for track '${track.name}'`
18794
18810
  );
18795
18811
  }
18812
+ if (unavailable) continue;
18796
18813
  if (track.packetId !== void 0) fecTrackIndexes.push(i);
18797
18814
  }
18798
18815
  const fecDecoders = /* @__PURE__ */ new Map();
@@ -18821,6 +18838,20 @@ var MulticastManager = class {
18821
18838
  this.#switchingSets.clear();
18822
18839
  this.#repairPacketIds.clear();
18823
18840
  this.#router.clearRepairPacketIdOwners();
18841
+ const fecPacketIds = new Set([...fecDecoders.keys()].map((i) => catalog.tracks[i].packetId));
18842
+ for (const [packetId, catalogTrackName] of this.#multiTrackFecTracks) {
18843
+ if (fecPacketIds.has(packetId)) continue;
18844
+ if (!this.#fec.isTrackConfigured(packetId)) {
18845
+ this.#multiTrackFecTracks.delete(packetId);
18846
+ continue;
18847
+ }
18848
+ this.#router.releaseFecPacket(packetId);
18849
+ this.#releaseRetiredStream({ packetId, catalogTrackName });
18850
+ }
18851
+ this.#fecUnavailableByTrack.clear();
18852
+ for (const [name, reason] of fecUnavailableByTrack) {
18853
+ this.#fecUnavailableByTrack.set(name, reason);
18854
+ }
18824
18855
  const catalogTracksByName = new Map(
18825
18856
  catalog.tracks.map((track) => [track.name, track])
18826
18857
  );
@@ -18842,10 +18873,11 @@ var MulticastManager = class {
18842
18873
  }
18843
18874
  continue;
18844
18875
  }
18845
- if (track.fec !== void 0) {
18876
+ if (track.fec !== void 0 && !fecUnavailableByTrack.has(track.name)) {
18846
18877
  const fecCfg = track.fec;
18847
18878
  if (track.packetId !== void 0) {
18848
18879
  for (const repairName of repairNamesBySource.get(track.name) ?? []) {
18880
+ if (unownedRepairNames.has(repairName)) continue;
18849
18881
  const repairTrack = catalogTracksByName.get(repairName);
18850
18882
  if (repairTrack?.packetId !== void 0) {
18851
18883
  this.#router.setRepairPacketIdOwner(repairTrack.packetId, track.packetId);
@@ -18866,17 +18898,18 @@ var MulticastManager = class {
18866
18898
  blockProfile,
18867
18899
  k: fecCfg.sourceSymbols,
18868
18900
  symbolSize: fecCfg.symbolSize,
18869
- interleaveDepthMs: fecCfg.interleaveDepthMs,
18901
+ interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
18870
18902
  fps: track.framerate,
18871
18903
  trackKind: "video"
18872
18904
  },
18873
18905
  track.name
18874
18906
  );
18907
+ this.#multiTrackFecTracks.set(track.packetId, track.name);
18875
18908
  this.#configureTrackMapper(track.packetId, {
18876
18909
  blockProfile,
18877
18910
  k: fecCfg.sourceSymbols,
18878
18911
  symbolSize: fecCfg.symbolSize,
18879
- interleaveDepthMs: fecCfg.interleaveDepthMs,
18912
+ interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
18880
18913
  fps: track.framerate
18881
18914
  });
18882
18915
  this.#router.setPacketIdType(track.packetId, "video");
@@ -18884,6 +18917,24 @@ var MulticastManager = class {
18884
18917
  }
18885
18918
  }
18886
18919
  }
18920
+ /**
18921
+ * Why `configureMultiTrack` consumes the named catalog track without FEC
18922
+ * although its `fec` block declares one (draft-ramadan-moq-fec §5.1): no
18923
+ * implemented `blockProfile` (`track_fec_block_profile_unimplemented`, e.g.
18924
+ * the retired `k-padded-v1`; `track_fec_block_profile` when absent), or an
18925
+ * invalid `iso-ssbg1-v1` span (`track_fec`). Absent/zero spans keep in-band
18926
+ * decoding enabled. The same reason the hang
18927
+ * resolver reports (`catalogFecUnavailable`). Undefined for every other
18928
+ * track, and for every track before the first call; each call replaces the
18929
+ * previous call's answers.
18930
+ */
18931
+ trackFecUnavailable(trackName) {
18932
+ return this.#fecUnavailableByTrack.get(trackName);
18933
+ }
18934
+ /** Per-track FEC availability from the last `configureMultiTrack` call. */
18935
+ #fecUnavailableByTrack = /* @__PURE__ */ new Map();
18936
+ /** packetId → catalog track name of the FEC the last `configureMultiTrack` installed. */
18937
+ #multiTrackFecTracks = /* @__PURE__ */ new Map();
18887
18938
  /**
18888
18939
  * Return the track names belonging to the given altGroup switching set.
18889
18940
  * D-17: altGroup groups base+delta+repair into one switching set.