@blockcast/mmt-container 0.3.0 → 0.4.0-main.d6f2737828fc

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 (588) hide show
  1. package/README.md +22 -8
  2. package/dist/browser-runtime.js +1185 -1049
  3. package/dist/browser-runtime.js.map +4 -4
  4. package/dist/carp-catalog.d.ts +24 -8
  5. package/dist/carp-catalog.d.ts.map +1 -1
  6. package/dist/carp-catalog.js +21 -8
  7. package/dist/carp-catalog.js.map +2 -2
  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 +59 -17
  11. package/dist/catalog-fec-params.js.map +3 -3
  12. package/dist/catalog-network-source.js +0 -1
  13. package/dist/catalog-network-source.js.map +2 -2
  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/delta-fallback-config.d.ts +42 -0
  19. package/dist/delta-fallback-config.d.ts.map +1 -0
  20. package/dist/delta-fallback-config.js +59 -0
  21. package/dist/delta-fallback-config.js.map +7 -0
  22. package/dist/fec-manager.d.ts +35 -26
  23. package/dist/fec-manager.d.ts.map +1 -1
  24. package/dist/fec-manager.js +300 -273
  25. package/dist/fec-manager.js.map +2 -2
  26. package/dist/fec-state.d.ts +19 -31
  27. package/dist/fec-state.d.ts.map +1 -1
  28. package/dist/fec-state.js +3 -4
  29. package/dist/fec-state.js.map +2 -2
  30. package/dist/fec-worker-setup.d.ts +16 -6
  31. package/dist/fec-worker-setup.d.ts.map +1 -1
  32. package/dist/fec-worker-setup.js +72 -34
  33. package/dist/fec-worker-setup.js.map +2 -2
  34. package/dist/index.d.ts +4 -3
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +12 -0
  37. package/dist/index.js.map +2 -2
  38. package/dist/iso-block-locator.js +0 -1
  39. package/dist/iso-block-locator.js.map +2 -2
  40. package/dist/media-router.d.ts +20 -8
  41. package/dist/media-router.d.ts.map +1 -1
  42. package/dist/media-router.js +46 -21
  43. package/dist/media-router.js.map +2 -2
  44. package/dist/mfu-reassembler.js +0 -1
  45. package/dist/mfu-reassembler.js.map +2 -2
  46. package/dist/mmtp-container-parser.d.ts +5 -0
  47. package/dist/mmtp-container-parser.d.ts.map +1 -1
  48. package/dist/mmtp-container-parser.js +22 -1
  49. package/dist/mmtp-container-parser.js.map +2 -2
  50. package/dist/mmtp-container-parser.no-modules.js +37 -9
  51. package/dist/mmtp-container-parser.no-modules.js.map +2 -2
  52. package/dist/mmtp-decode-pipeline.js +22 -1
  53. package/dist/mmtp-decode-pipeline.js.map +2 -2
  54. package/dist/mmtp-router.d.ts +36 -16
  55. package/dist/mmtp-router.d.ts.map +1 -1
  56. package/dist/mmtp-router.js +126 -213
  57. package/dist/mmtp-router.js.map +2 -2
  58. package/dist/multicast-input.js +0 -1
  59. package/dist/multicast-input.js.map +2 -2
  60. package/dist/multicast-manager.d.ts +33 -3
  61. package/dist/multicast-manager.d.ts.map +1 -1
  62. package/dist/multicast-manager.js +660 -529
  63. package/dist/multicast-manager.js.map +4 -4
  64. package/dist/multicast-transport.d.ts +1 -1
  65. package/dist/multicast-transport.d.ts.map +1 -1
  66. package/dist/multicast-transport.js +42 -14
  67. package/dist/multicast-transport.js.map +3 -3
  68. package/dist/multicast-unwrap.js +0 -1
  69. package/dist/multicast-unwrap.js.map +2 -2
  70. package/dist/no-modules.js +36 -8
  71. package/dist/no-modules.js.map +2 -2
  72. package/dist/repair-fetch.d.ts +30 -19
  73. package/dist/repair-fetch.d.ts.map +1 -1
  74. package/dist/repair-fetch.js +32 -18
  75. package/dist/repair-fetch.js.map +3 -3
  76. package/dist/repair-layers.d.ts +18 -7
  77. package/dist/repair-layers.d.ts.map +1 -1
  78. package/dist/repair-layers.js +11 -4
  79. package/dist/repair-layers.js.map +2 -2
  80. package/dist/signaling.js +0 -4
  81. package/dist/signaling.js.map +2 -2
  82. package/dist/timestamp-codec.js +0 -1
  83. package/dist/timestamp-codec.js.map +2 -2
  84. package/dist/track-session.d.ts +7 -0
  85. package/dist/track-session.d.ts.map +1 -1
  86. package/dist/track-session.js +15 -8
  87. package/dist/track-session.js.map +2 -2
  88. package/dist/wasm-mfu-reassembler.js +0 -1
  89. package/dist/wasm-mfu-reassembler.js.map +2 -2
  90. package/dist/wasm-mmtp-container-parser.js +22 -1
  91. package/dist/wasm-mmtp-container-parser.js.map +2 -2
  92. package/package.json +8 -14
  93. package/schemas/catalog.schema.json +3 -3
  94. package/src/carp-catalog.ts +35 -13
  95. package/src/catalog-fec-params.ts +173 -64
  96. package/src/cmaf-block-assembler.ts +1 -4
  97. package/src/delta-fallback-config.ts +72 -0
  98. package/src/fec-manager.ts +413 -317
  99. package/src/fec-state.ts +20 -32
  100. package/src/fec-worker-setup.ts +105 -55
  101. package/src/index.ts +10 -0
  102. package/src/media-router.ts +59 -21
  103. package/src/mmtp-container-parser.ts +26 -0
  104. package/src/mmtp-router.ts +145 -303
  105. package/src/multicast-manager.ts +172 -60
  106. package/src/multicast-transport.ts +16 -9
  107. package/src/repair-fetch.ts +46 -27
  108. package/src/repair-layers.ts +28 -8
  109. package/src/track-session.ts +28 -9
  110. package/test-vectors/catalog/README.md +34 -17
  111. package/test-vectors/catalog/negative/fec-block-profile-retired.json +52 -0
  112. package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +1 -1
  113. package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +1 -1
  114. package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +1 -1
  115. package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +1 -1
  116. package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +1 -1
  117. package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +1 -1
  118. package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +1 -1
  119. package/test-vectors/catalog/negative/multicast-network-source-empty.json +1 -1
  120. package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +1 -1
  121. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +1 -1
  122. package/test-vectors/catalog/positive/fec-layered-repair.json +2 -2
  123. package/test-vectors/catalog/positive/fec-multicast.json +2 -1
  124. package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +2 -1
  125. package/test-vectors/catalog-timing.json +356 -0
  126. package/test-vectors/catalog-validation.json +40 -5
  127. package/node_modules/@blockcast/fec-worker/README.md +0 -223
  128. package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
  129. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts +0 -38
  130. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts.map +0 -1
  131. package/node_modules/@blockcast/fec-worker/dist/block-order.js +0 -78
  132. package/node_modules/@blockcast/fec-worker/dist/block-order.js.map +0 -1
  133. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +0 -93
  134. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +0 -1
  135. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +0 -346
  136. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +0 -1
  137. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +0 -380
  138. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +0 -1
  139. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +0 -11
  140. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +0 -1
  141. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +0 -14
  142. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +0 -1
  143. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +0 -859
  144. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +0 -7
  145. package/node_modules/@blockcast/fec-worker/dist/index.d.ts +0 -10
  146. package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +0 -1
  147. package/node_modules/@blockcast/fec-worker/dist/index.js +0 -8
  148. package/node_modules/@blockcast/fec-worker/dist/index.js.map +0 -1
  149. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +0 -20
  150. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +0 -1
  151. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +0 -370
  152. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +0 -7
  153. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +0 -104
  154. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +0 -1
  155. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +0 -181
  156. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +0 -1
  157. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +0 -194
  158. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +0 -1
  159. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +0 -62
  160. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +0 -1
  161. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +0 -9
  162. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +0 -1
  163. package/node_modules/@blockcast/fec-worker/dist/transfer.js +0 -13
  164. package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +0 -1
  165. package/node_modules/@blockcast/fec-worker/package.json +0 -59
  166. package/node_modules/@blockcast/fec-worker/src/block-order.ts +0 -93
  167. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +0 -392
  168. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +0 -399
  169. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +0 -1502
  170. package/node_modules/@blockcast/fec-worker/src/index.ts +0 -35
  171. package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +0 -589
  172. package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +0 -222
  173. package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +0 -209
  174. package/node_modules/@blockcast/fec-worker/src/transfer.ts +0 -12
  175. package/node_modules/@blockcast/mmt-alta-parse/README.md +0 -181
  176. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  177. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +0 -57
  178. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +0 -1
  179. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +0 -115
  180. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +0 -1
  181. package/node_modules/@blockcast/mmt-alta-parse/package.json +0 -50
  182. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +0 -136
  183. package/node_modules/@blockcast/mmt-base/README.md +0 -134
  184. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  185. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +0 -55
  186. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +0 -1
  187. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +0 -128
  188. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +0 -1
  189. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +0 -7
  190. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +0 -1
  191. package/node_modules/@blockcast/mmt-base/dist/index.js +0 -7
  192. package/node_modules/@blockcast/mmt-base/dist/index.js.map +0 -1
  193. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +0 -292
  194. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +0 -1
  195. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +0 -612
  196. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +0 -1
  197. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +0 -18
  198. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +0 -1
  199. package/node_modules/@blockcast/mmt-base/dist/net-constants.js +0 -18
  200. package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +0 -1
  201. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +0 -47
  202. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +0 -1
  203. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +0 -54
  204. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +0 -1
  205. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +0 -57
  206. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +0 -1
  207. package/node_modules/@blockcast/mmt-base/dist/serial-number.js +0 -98
  208. package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +0 -1
  209. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +0 -312
  210. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +0 -1
  211. package/node_modules/@blockcast/mmt-base/dist/signalling.js +0 -637
  212. package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +0 -1
  213. package/node_modules/@blockcast/mmt-base/package.json +0 -49
  214. package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +0 -145
  215. package/node_modules/@blockcast/mmt-base/src/index.ts +0 -6
  216. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +0 -892
  217. package/node_modules/@blockcast/mmt-base/src/net-constants.ts +0 -17
  218. package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +0 -67
  219. package/node_modules/@blockcast/mmt-base/src/serial-number.ts +0 -107
  220. package/node_modules/@blockcast/mmt-base/src/signalling.ts +0 -862
  221. package/node_modules/@blockcast/mmt-fec/README.md +0 -271
  222. package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
  223. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +0 -82
  224. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +0 -1
  225. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +0 -296
  226. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +0 -1
  227. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +0 -106
  228. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +0 -1
  229. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +0 -288
  230. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +0 -1
  231. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +0 -46
  232. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +0 -1
  233. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +0 -96
  234. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +0 -1
  235. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +0 -33
  236. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +0 -1
  237. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +0 -57
  238. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +0 -1
  239. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +0 -205
  240. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +0 -1
  241. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +0 -240
  242. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +0 -1
  243. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +0 -28
  244. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +0 -1
  245. package/node_modules/@blockcast/mmt-fec/dist/index.js +0 -28
  246. package/node_modules/@blockcast/mmt-fec/dist/index.js.map +0 -1
  247. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +0 -20
  248. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +0 -1
  249. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +0 -28
  250. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +0 -1
  251. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +0 -258
  252. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +0 -1
  253. package/node_modules/@blockcast/mmt-fec/dist/oti.js +0 -382
  254. package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +0 -1
  255. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +0 -41
  256. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +0 -1
  257. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +0 -81
  258. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +0 -1
  259. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts +0 -82
  260. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts.map +0 -1
  261. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js +0 -170
  262. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js.map +0 -1
  263. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +0 -181
  264. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  265. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +0 -57
  266. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +0 -1
  267. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +0 -115
  268. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +0 -1
  269. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +0 -50
  270. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +0 -136
  271. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +0 -134
  272. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  273. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +0 -55
  274. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +0 -1
  275. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +0 -128
  276. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +0 -1
  277. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +0 -7
  278. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +0 -1
  279. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +0 -7
  280. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +0 -1
  281. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +0 -292
  282. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +0 -1
  283. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +0 -612
  284. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +0 -1
  285. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +0 -18
  286. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +0 -1
  287. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js +0 -18
  288. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +0 -1
  289. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +0 -47
  290. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +0 -1
  291. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js +0 -54
  292. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +0 -1
  293. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +0 -57
  294. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +0 -1
  295. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js +0 -98
  296. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +0 -1
  297. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +0 -312
  298. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +0 -1
  299. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js +0 -637
  300. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js.map +0 -1
  301. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +0 -49
  302. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +0 -145
  303. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +0 -6
  304. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +0 -892
  305. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/net-constants.ts +0 -17
  306. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/ntp-short.ts +0 -67
  307. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/serial-number.ts +0 -107
  308. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/signalling.ts +0 -862
  309. package/node_modules/@blockcast/mmt-fec/package.json +0 -79
  310. package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +0 -371
  311. package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +0 -439
  312. package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +0 -127
  313. package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +0 -68
  314. package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +0 -310
  315. package/node_modules/@blockcast/mmt-fec/src/index.ts +0 -94
  316. package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +0 -34
  317. package/node_modules/@blockcast/mmt-fec/src/oti.ts +0 -463
  318. package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +0 -101
  319. package/node_modules/@blockcast/mmt-fec/src/repair-group.ts +0 -195
  320. package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
  321. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +0 -28
  322. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +0 -1
  323. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +0 -128
  324. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +0 -1
  325. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +0 -4
  326. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +0 -1
  327. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +0 -4
  328. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +0 -1
  329. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +0 -37
  330. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +0 -1
  331. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +0 -179
  332. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +0 -1
  333. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +0 -99
  334. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +0 -1
  335. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +0 -492
  336. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +0 -1
  337. package/node_modules/@blockcast/mmt-manifest-verify/package.json +0 -45
  338. package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +0 -171
  339. package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +0 -3
  340. package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +0 -230
  341. package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +0 -669
  342. package/node_modules/@noble/hashes/LICENSE +0 -21
  343. package/node_modules/@noble/hashes/README.md +0 -521
  344. package/node_modules/@noble/hashes/_assert.d.ts +0 -17
  345. package/node_modules/@noble/hashes/_assert.d.ts.map +0 -1
  346. package/node_modules/@noble/hashes/_assert.js +0 -18
  347. package/node_modules/@noble/hashes/_assert.js.map +0 -1
  348. package/node_modules/@noble/hashes/_blake.d.ts +0 -14
  349. package/node_modules/@noble/hashes/_blake.d.ts.map +0 -1
  350. package/node_modules/@noble/hashes/_blake.js +0 -50
  351. package/node_modules/@noble/hashes/_blake.js.map +0 -1
  352. package/node_modules/@noble/hashes/_md.d.ts +0 -51
  353. package/node_modules/@noble/hashes/_md.d.ts.map +0 -1
  354. package/node_modules/@noble/hashes/_md.js +0 -162
  355. package/node_modules/@noble/hashes/_md.js.map +0 -1
  356. package/node_modules/@noble/hashes/_u64.d.ts +0 -55
  357. package/node_modules/@noble/hashes/_u64.d.ts.map +0 -1
  358. package/node_modules/@noble/hashes/_u64.js +0 -90
  359. package/node_modules/@noble/hashes/_u64.js.map +0 -1
  360. package/node_modules/@noble/hashes/argon2.d.ts +0 -32
  361. package/node_modules/@noble/hashes/argon2.d.ts.map +0 -1
  362. package/node_modules/@noble/hashes/argon2.js +0 -401
  363. package/node_modules/@noble/hashes/argon2.js.map +0 -1
  364. package/node_modules/@noble/hashes/blake1.d.ts +0 -106
  365. package/node_modules/@noble/hashes/blake1.d.ts.map +0 -1
  366. package/node_modules/@noble/hashes/blake1.js +0 -459
  367. package/node_modules/@noble/hashes/blake1.js.map +0 -1
  368. package/node_modules/@noble/hashes/blake2.d.ts +0 -116
  369. package/node_modules/@noble/hashes/blake2.d.ts.map +0 -1
  370. package/node_modules/@noble/hashes/blake2.js +0 -420
  371. package/node_modules/@noble/hashes/blake2.js.map +0 -1
  372. package/node_modules/@noble/hashes/blake2b.d.ts +0 -11
  373. package/node_modules/@noble/hashes/blake2b.d.ts.map +0 -1
  374. package/node_modules/@noble/hashes/blake2b.js +0 -14
  375. package/node_modules/@noble/hashes/blake2b.js.map +0 -1
  376. package/node_modules/@noble/hashes/blake2s.d.ts +0 -20
  377. package/node_modules/@noble/hashes/blake2s.d.ts.map +0 -1
  378. package/node_modules/@noble/hashes/blake2s.js +0 -24
  379. package/node_modules/@noble/hashes/blake2s.js.map +0 -1
  380. package/node_modules/@noble/hashes/blake3.d.ts +0 -54
  381. package/node_modules/@noble/hashes/blake3.d.ts.map +0 -1
  382. package/node_modules/@noble/hashes/blake3.js +0 -255
  383. package/node_modules/@noble/hashes/blake3.js.map +0 -1
  384. package/node_modules/@noble/hashes/crypto.d.ts +0 -2
  385. package/node_modules/@noble/hashes/crypto.d.ts.map +0 -1
  386. package/node_modules/@noble/hashes/crypto.js +0 -5
  387. package/node_modules/@noble/hashes/crypto.js.map +0 -1
  388. package/node_modules/@noble/hashes/cryptoNode.d.ts +0 -2
  389. package/node_modules/@noble/hashes/cryptoNode.d.ts.map +0 -1
  390. package/node_modules/@noble/hashes/cryptoNode.js +0 -18
  391. package/node_modules/@noble/hashes/cryptoNode.js.map +0 -1
  392. package/node_modules/@noble/hashes/eskdf.d.ts +0 -47
  393. package/node_modules/@noble/hashes/eskdf.d.ts.map +0 -1
  394. package/node_modules/@noble/hashes/eskdf.js +0 -166
  395. package/node_modules/@noble/hashes/eskdf.js.map +0 -1
  396. package/node_modules/@noble/hashes/esm/_assert.d.ts +0 -17
  397. package/node_modules/@noble/hashes/esm/_assert.d.ts.map +0 -1
  398. package/node_modules/@noble/hashes/esm/_assert.js +0 -15
  399. package/node_modules/@noble/hashes/esm/_assert.js.map +0 -1
  400. package/node_modules/@noble/hashes/esm/_blake.d.ts +0 -14
  401. package/node_modules/@noble/hashes/esm/_blake.d.ts.map +0 -1
  402. package/node_modules/@noble/hashes/esm/_blake.js +0 -45
  403. package/node_modules/@noble/hashes/esm/_blake.js.map +0 -1
  404. package/node_modules/@noble/hashes/esm/_md.d.ts +0 -51
  405. package/node_modules/@noble/hashes/esm/_md.d.ts.map +0 -1
  406. package/node_modules/@noble/hashes/esm/_md.js +0 -155
  407. package/node_modules/@noble/hashes/esm/_md.js.map +0 -1
  408. package/node_modules/@noble/hashes/esm/_u64.d.ts +0 -55
  409. package/node_modules/@noble/hashes/esm/_u64.d.ts.map +0 -1
  410. package/node_modules/@noble/hashes/esm/_u64.js +0 -67
  411. package/node_modules/@noble/hashes/esm/_u64.js.map +0 -1
  412. package/node_modules/@noble/hashes/esm/argon2.d.ts +0 -32
  413. package/node_modules/@noble/hashes/esm/argon2.d.ts.map +0 -1
  414. package/node_modules/@noble/hashes/esm/argon2.js +0 -392
  415. package/node_modules/@noble/hashes/esm/argon2.js.map +0 -1
  416. package/node_modules/@noble/hashes/esm/blake1.d.ts +0 -106
  417. package/node_modules/@noble/hashes/esm/blake1.d.ts.map +0 -1
  418. package/node_modules/@noble/hashes/esm/blake1.js +0 -452
  419. package/node_modules/@noble/hashes/esm/blake1.js.map +0 -1
  420. package/node_modules/@noble/hashes/esm/blake2.d.ts +0 -116
  421. package/node_modules/@noble/hashes/esm/blake2.d.ts.map +0 -1
  422. package/node_modules/@noble/hashes/esm/blake2.js +0 -413
  423. package/node_modules/@noble/hashes/esm/blake2.js.map +0 -1
  424. package/node_modules/@noble/hashes/esm/blake2b.d.ts +0 -11
  425. package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +0 -1
  426. package/node_modules/@noble/hashes/esm/blake2b.js +0 -11
  427. package/node_modules/@noble/hashes/esm/blake2b.js.map +0 -1
  428. package/node_modules/@noble/hashes/esm/blake2s.d.ts +0 -20
  429. package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +0 -1
  430. package/node_modules/@noble/hashes/esm/blake2s.js +0 -21
  431. package/node_modules/@noble/hashes/esm/blake2s.js.map +0 -1
  432. package/node_modules/@noble/hashes/esm/blake3.d.ts +0 -54
  433. package/node_modules/@noble/hashes/esm/blake3.d.ts.map +0 -1
  434. package/node_modules/@noble/hashes/esm/blake3.js +0 -251
  435. package/node_modules/@noble/hashes/esm/blake3.js.map +0 -1
  436. package/node_modules/@noble/hashes/esm/crypto.d.ts +0 -2
  437. package/node_modules/@noble/hashes/esm/crypto.d.ts.map +0 -1
  438. package/node_modules/@noble/hashes/esm/crypto.js +0 -2
  439. package/node_modules/@noble/hashes/esm/crypto.js.map +0 -1
  440. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +0 -2
  441. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +0 -1
  442. package/node_modules/@noble/hashes/esm/cryptoNode.js +0 -15
  443. package/node_modules/@noble/hashes/esm/cryptoNode.js.map +0 -1
  444. package/node_modules/@noble/hashes/esm/eskdf.d.ts +0 -47
  445. package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +0 -1
  446. package/node_modules/@noble/hashes/esm/eskdf.js +0 -160
  447. package/node_modules/@noble/hashes/esm/eskdf.js.map +0 -1
  448. package/node_modules/@noble/hashes/esm/hkdf.d.ts +0 -36
  449. package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +0 -1
  450. package/node_modules/@noble/hashes/esm/hkdf.js +0 -82
  451. package/node_modules/@noble/hashes/esm/hkdf.js.map +0 -1
  452. package/node_modules/@noble/hashes/esm/hmac.d.ts +0 -35
  453. package/node_modules/@noble/hashes/esm/hmac.d.ts.map +0 -1
  454. package/node_modules/@noble/hashes/esm/hmac.js +0 -86
  455. package/node_modules/@noble/hashes/esm/hmac.js.map +0 -1
  456. package/node_modules/@noble/hashes/esm/index.d.ts +0 -2
  457. package/node_modules/@noble/hashes/esm/index.d.ts.map +0 -1
  458. package/node_modules/@noble/hashes/esm/index.js +0 -33
  459. package/node_modules/@noble/hashes/esm/index.js.map +0 -1
  460. package/node_modules/@noble/hashes/esm/legacy.d.ts +0 -71
  461. package/node_modules/@noble/hashes/esm/legacy.d.ts.map +0 -1
  462. package/node_modules/@noble/hashes/esm/legacy.js +0 -281
  463. package/node_modules/@noble/hashes/esm/legacy.js.map +0 -1
  464. package/node_modules/@noble/hashes/esm/package.json +0 -10
  465. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +0 -23
  466. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +0 -1
  467. package/node_modules/@noble/hashes/esm/pbkdf2.js +0 -97
  468. package/node_modules/@noble/hashes/esm/pbkdf2.js.map +0 -1
  469. package/node_modules/@noble/hashes/esm/ripemd160.d.ts +0 -13
  470. package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +0 -1
  471. package/node_modules/@noble/hashes/esm/ripemd160.js +0 -13
  472. package/node_modules/@noble/hashes/esm/ripemd160.js.map +0 -1
  473. package/node_modules/@noble/hashes/esm/scrypt.d.ts +0 -34
  474. package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +0 -1
  475. package/node_modules/@noble/hashes/esm/scrypt.js +0 -228
  476. package/node_modules/@noble/hashes/esm/scrypt.js.map +0 -1
  477. package/node_modules/@noble/hashes/esm/sha1.d.ts +0 -11
  478. package/node_modules/@noble/hashes/esm/sha1.d.ts.map +0 -1
  479. package/node_modules/@noble/hashes/esm/sha1.js +0 -11
  480. package/node_modules/@noble/hashes/esm/sha1.js.map +0 -1
  481. package/node_modules/@noble/hashes/esm/sha2.d.ts +0 -159
  482. package/node_modules/@noble/hashes/esm/sha2.d.ts.map +0 -1
  483. package/node_modules/@noble/hashes/esm/sha2.js +0 -375
  484. package/node_modules/@noble/hashes/esm/sha2.js.map +0 -1
  485. package/node_modules/@noble/hashes/esm/sha256.d.ts +0 -20
  486. package/node_modules/@noble/hashes/esm/sha256.d.ts.map +0 -1
  487. package/node_modules/@noble/hashes/esm/sha256.js +0 -20
  488. package/node_modules/@noble/hashes/esm/sha256.js.map +0 -1
  489. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +0 -142
  490. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +0 -1
  491. package/node_modules/@noble/hashes/esm/sha3-addons.js +0 -393
  492. package/node_modules/@noble/hashes/esm/sha3-addons.js.map +0 -1
  493. package/node_modules/@noble/hashes/esm/sha3.d.ts +0 -53
  494. package/node_modules/@noble/hashes/esm/sha3.d.ts.map +0 -1
  495. package/node_modules/@noble/hashes/esm/sha3.js +0 -234
  496. package/node_modules/@noble/hashes/esm/sha3.js.map +0 -1
  497. package/node_modules/@noble/hashes/esm/sha512.d.ts +0 -26
  498. package/node_modules/@noble/hashes/esm/sha512.d.ts.map +0 -1
  499. package/node_modules/@noble/hashes/esm/sha512.js +0 -26
  500. package/node_modules/@noble/hashes/esm/sha512.js.map +0 -1
  501. package/node_modules/@noble/hashes/esm/utils.d.ts +0 -161
  502. package/node_modules/@noble/hashes/esm/utils.d.ts.map +0 -1
  503. package/node_modules/@noble/hashes/esm/utils.js +0 -281
  504. package/node_modules/@noble/hashes/esm/utils.js.map +0 -1
  505. package/node_modules/@noble/hashes/hkdf.d.ts +0 -36
  506. package/node_modules/@noble/hashes/hkdf.d.ts.map +0 -1
  507. package/node_modules/@noble/hashes/hkdf.js +0 -88
  508. package/node_modules/@noble/hashes/hkdf.js.map +0 -1
  509. package/node_modules/@noble/hashes/hmac.d.ts +0 -35
  510. package/node_modules/@noble/hashes/hmac.d.ts.map +0 -1
  511. package/node_modules/@noble/hashes/hmac.js +0 -91
  512. package/node_modules/@noble/hashes/hmac.js.map +0 -1
  513. package/node_modules/@noble/hashes/index.d.ts +0 -1
  514. package/node_modules/@noble/hashes/index.d.ts.map +0 -1
  515. package/node_modules/@noble/hashes/index.js +0 -33
  516. package/node_modules/@noble/hashes/index.js.map +0 -1
  517. package/node_modules/@noble/hashes/legacy.d.ts +0 -71
  518. package/node_modules/@noble/hashes/legacy.d.ts.map +0 -1
  519. package/node_modules/@noble/hashes/legacy.js +0 -287
  520. package/node_modules/@noble/hashes/legacy.js.map +0 -1
  521. package/node_modules/@noble/hashes/package.json +0 -266
  522. package/node_modules/@noble/hashes/pbkdf2.d.ts +0 -23
  523. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +0 -1
  524. package/node_modules/@noble/hashes/pbkdf2.js +0 -101
  525. package/node_modules/@noble/hashes/pbkdf2.js.map +0 -1
  526. package/node_modules/@noble/hashes/ripemd160.d.ts +0 -13
  527. package/node_modules/@noble/hashes/ripemd160.d.ts.map +0 -1
  528. package/node_modules/@noble/hashes/ripemd160.js +0 -16
  529. package/node_modules/@noble/hashes/ripemd160.js.map +0 -1
  530. package/node_modules/@noble/hashes/scrypt.d.ts +0 -34
  531. package/node_modules/@noble/hashes/scrypt.d.ts.map +0 -1
  532. package/node_modules/@noble/hashes/scrypt.js +0 -232
  533. package/node_modules/@noble/hashes/scrypt.js.map +0 -1
  534. package/node_modules/@noble/hashes/sha1.d.ts +0 -11
  535. package/node_modules/@noble/hashes/sha1.d.ts.map +0 -1
  536. package/node_modules/@noble/hashes/sha1.js +0 -14
  537. package/node_modules/@noble/hashes/sha1.js.map +0 -1
  538. package/node_modules/@noble/hashes/sha2.d.ts +0 -159
  539. package/node_modules/@noble/hashes/sha2.d.ts.map +0 -1
  540. package/node_modules/@noble/hashes/sha2.js +0 -384
  541. package/node_modules/@noble/hashes/sha2.js.map +0 -1
  542. package/node_modules/@noble/hashes/sha256.d.ts +0 -20
  543. package/node_modules/@noble/hashes/sha256.d.ts.map +0 -1
  544. package/node_modules/@noble/hashes/sha256.js +0 -23
  545. package/node_modules/@noble/hashes/sha256.js.map +0 -1
  546. package/node_modules/@noble/hashes/sha3-addons.d.ts +0 -142
  547. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +0 -1
  548. package/node_modules/@noble/hashes/sha3-addons.js +0 -402
  549. package/node_modules/@noble/hashes/sha3-addons.js.map +0 -1
  550. package/node_modules/@noble/hashes/sha3.d.ts +0 -53
  551. package/node_modules/@noble/hashes/sha3.d.ts.map +0 -1
  552. package/node_modules/@noble/hashes/sha3.js +0 -239
  553. package/node_modules/@noble/hashes/sha3.js.map +0 -1
  554. package/node_modules/@noble/hashes/sha512.d.ts +0 -26
  555. package/node_modules/@noble/hashes/sha512.d.ts.map +0 -1
  556. package/node_modules/@noble/hashes/sha512.js +0 -29
  557. package/node_modules/@noble/hashes/sha512.js.map +0 -1
  558. package/node_modules/@noble/hashes/src/_assert.ts +0 -22
  559. package/node_modules/@noble/hashes/src/_blake.ts +0 -50
  560. package/node_modules/@noble/hashes/src/_md.ts +0 -176
  561. package/node_modules/@noble/hashes/src/_u64.ts +0 -91
  562. package/node_modules/@noble/hashes/src/argon2.ts +0 -497
  563. package/node_modules/@noble/hashes/src/blake1.ts +0 -534
  564. package/node_modules/@noble/hashes/src/blake2.ts +0 -486
  565. package/node_modules/@noble/hashes/src/blake2b.ts +0 -10
  566. package/node_modules/@noble/hashes/src/blake2s.ts +0 -20
  567. package/node_modules/@noble/hashes/src/blake3.ts +0 -272
  568. package/node_modules/@noble/hashes/src/crypto.ts +0 -9
  569. package/node_modules/@noble/hashes/src/cryptoNode.ts +0 -15
  570. package/node_modules/@noble/hashes/src/eskdf.ts +0 -187
  571. package/node_modules/@noble/hashes/src/hkdf.ts +0 -88
  572. package/node_modules/@noble/hashes/src/hmac.ts +0 -94
  573. package/node_modules/@noble/hashes/src/index.ts +0 -31
  574. package/node_modules/@noble/hashes/src/legacy.ts +0 -293
  575. package/node_modules/@noble/hashes/src/pbkdf2.ts +0 -122
  576. package/node_modules/@noble/hashes/src/ripemd160.ts +0 -12
  577. package/node_modules/@noble/hashes/src/scrypt.ts +0 -257
  578. package/node_modules/@noble/hashes/src/sha1.ts +0 -10
  579. package/node_modules/@noble/hashes/src/sha2.ts +0 -402
  580. package/node_modules/@noble/hashes/src/sha256.ts +0 -24
  581. package/node_modules/@noble/hashes/src/sha3-addons.ts +0 -499
  582. package/node_modules/@noble/hashes/src/sha3.ts +0 -258
  583. package/node_modules/@noble/hashes/src/sha512.ts +0 -34
  584. package/node_modules/@noble/hashes/src/utils.ts +0 -395
  585. package/node_modules/@noble/hashes/utils.d.ts +0 -161
  586. package/node_modules/@noble/hashes/utils.d.ts.map +0 -1
  587. package/node_modules/@noble/hashes/utils.js +0 -313
  588. package/node_modules/@noble/hashes/utils.js.map +0 -1
@@ -441,49 +441,21 @@ function serial32Precedes(a, b) {
441
441
  const distance = serial32Distance(a, b);
442
442
  return distance !== 0 && distance < SERIAL32_HALF;
443
443
  }
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
444
 
472
445
  // ../mmt-base/dist/fec-block-profile.js
473
446
  var FEC_BLOCK_PROFILES = Object.freeze([
474
- "k-padded-v1",
475
447
  "iso-ssbg1-v1"
476
448
  ]);
477
449
  function isFecBlockProfile(value) {
478
- return value === "k-padded-v1" || value === "iso-ssbg1-v1";
450
+ return value === "iso-ssbg1-v1";
479
451
  }
480
452
  function parseFecBlockProfile(value, where) {
481
453
  if (isFecBlockProfile(value))
482
454
  return value;
483
455
  if (value === void 0) {
484
- throw new Error(`${where} is required: name the FEC block profile ("k-padded-v1" or "iso-ssbg1-v1")`);
456
+ throw new Error(`${where} is required: name the FEC block profile ("iso-ssbg1-v1")`);
485
457
  }
486
- throw new Error(`${where} must be "k-padded-v1" or "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
458
+ throw new Error(`${where} must be "iso-ssbg1-v1" (got ${JSON.stringify(value)})`);
487
459
  }
488
460
  var SSBG1_LENGTH_FIELD_BYTES = 2;
489
461
  function ssbg1MaxPacketLength(symbolSize) {
@@ -508,20 +480,22 @@ function isoBlocksBehindLowerBound(a, b, kMax) {
508
480
  // src/track-session.ts
509
481
  import { fecBlockBudgetMs } from "@blockcast/mmt-fec";
510
482
  function computeFecDeliveryWindowMs(config) {
511
- const fecInterleaveMs = config.interleaveDepthMs;
512
483
  const fecK = config.sourceSymbols;
513
- if (!Number.isFinite(fecInterleaveMs) || fecInterleaveMs <= 0) {
484
+ if (!Number.isFinite(fecK) || fecK <= 0) {
514
485
  throw new Error(
515
- `[TrackSession] fecInterleaveMs=${fecInterleaveMs}. Derive a positive FEC interleave window from the stream catalog.`
486
+ `[TrackSession] fecK=${fecK}. Derive a positive FEC source-symbol count from the stream catalog.`
516
487
  );
517
488
  }
518
- if (!Number.isFinite(fecK) || fecK <= 0) {
489
+ return fecSpanDeliveryWindowMs(config.interleaveDepthMs, config.jitterBufferMs);
490
+ }
491
+ function fecSpanDeliveryWindowMs(interleaveDepthMs, jitterBufferMs) {
492
+ if (!Number.isFinite(interleaveDepthMs) || interleaveDepthMs <= 0) {
519
493
  throw new Error(
520
- `[TrackSession] fecK=${fecK}. Derive a positive FEC source-symbol count from the stream catalog.`
494
+ `[TrackSession] fecInterleaveMs=${interleaveDepthMs}. Derive a positive FEC interleave window from the stream catalog.`
521
495
  );
522
496
  }
523
- if (config.jitterBufferMs === void 0) return Math.ceil(fecInterleaveMs);
524
- return fecBlockBudgetMs(fecInterleaveMs, config.jitterBufferMs);
497
+ if (jitterBufferMs === void 0) return Math.ceil(interleaveDepthMs);
498
+ return fecBlockBudgetMs(interleaveDepthMs, jitterBufferMs);
525
499
  }
526
500
 
527
501
  // src/delivery-gap.ts
@@ -613,7 +587,9 @@ var DeliveryGapEstimator = class {
613
587
  import {
614
588
  deriveFecInterleaveDepth as deriveFecInterleaveDepth2,
615
589
  deriveFecRetentionBlocks,
616
- tryDeriveFecRetentionBlocks
590
+ tryDeriveFecRetentionBlocks,
591
+ LOCAL_FEC_RETENTION_BLOCKS,
592
+ LOCAL_FEC_TIMEOUT_MS
617
593
  } from "@blockcast/mmt-fec";
618
594
 
619
595
  // src/iso-block-locator.ts
@@ -847,7 +823,7 @@ var IsoBlockLocator = class _IsoBlockLocator {
847
823
  };
848
824
 
849
825
  // src/fec-state.ts
850
- var MIN_INTERLEAVE_DEPTH_MS = 33;
826
+ var MIN_INTERLEAVE_DEPTH_MS = 1;
851
827
  var FEC_INACTIVE_REASONS = Object.freeze([
852
828
  "no-catalog-fec",
853
829
  "reliable-moq",
@@ -858,6 +834,15 @@ var INACTIVE = Object.freeze({
858
834
  "reliable-moq": Object.freeze({ state: "inactive", reason: "reliable-moq" }),
859
835
  "no-multicast-bridge": Object.freeze({ state: "inactive", reason: "no-multicast-bridge" })
860
836
  });
837
+ function recordOf(value) {
838
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
839
+ }
840
+ function isoBlockSpanUsable(fec) {
841
+ const block = recordOf(fec);
842
+ if (block === void 0 || block["blockProfile"] !== "iso-ssbg1-v1") return true;
843
+ const ms = block["interleaveDepthMs"];
844
+ return ms === void 0 || typeof ms === "number" && Number.isSafeInteger(ms) && ms >= 0;
845
+ }
861
846
 
862
847
  // src/block-policy.ts
863
848
  import { deriveFecInterleaveDepth } from "@blockcast/mmt-fec";
@@ -1366,7 +1351,6 @@ var FECManager = class _FECManager {
1366
1351
  constructor() {
1367
1352
  // Per-track WASM RaptorQ decoders, keyed by physical stream identity.
1368
1353
  this.#fecDecoders = /* @__PURE__ */ new Map();
1369
- /** @deprecated Use setFecDecoder(packetId, ...). Backwards-compat shim. */
1370
1354
  // Per-track Worker-backed FEC decoders, keyed by physical stream identity.
1371
1355
  // Worker-backed tracks use async decode — recovered frames arrive via onFrame callback,
1372
1356
  // not synchronous return from feedSourceSymbol/feedRepairSymbol.
@@ -1381,10 +1365,9 @@ var FECManager = class _FECManager {
1381
1365
  // source/repair symbols do not split across two RaptorQ states.
1382
1366
  this.#syncDecoderBlockOwners = /* @__PURE__ */ new Map();
1383
1367
  this.#decoderBlockOwners = /* @__PURE__ */ new WeakMap();
1384
- this.#fecK = 4;
1385
1368
  this.#fecSymbolSize = 0;
1386
- this.#fecTimeoutMs = 500;
1387
- // default 500ms; refined by setFramerate() when fps known
1369
+ this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
1370
+ // local default; refined by setFramerate() when fps known
1388
1371
  this.#trackConfigs = /* @__PURE__ */ new Map();
1389
1372
  this.#fecStats = newFecCounters();
1390
1373
  this.#fecTrackStats = /* @__PURE__ */ new Map();
@@ -1424,10 +1407,10 @@ var FECManager = class _FECManager {
1424
1407
  this.#blockSourceEsis = /* @__PURE__ */ new Map();
1425
1408
  // Authored SSB_length per block (ISO/IEC 23008-1 §C.5.3), learned from the
1426
1409
  // 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
1410
+ // media units, usually before it holds K symbols; under iso-ssbg1-v1
1411
+ // (ssbg_mode1) that block is K' = SSB_length symbols, its repair ESIs start
1412
+ // at SSB_length, and K is only the maximum. Such a block is clean at
1413
+ // SSB_length source arrivals and recoverable from SSB_length received
1431
1414
  // symbols; waiting for K fails every one of them.
1432
1415
  this.#blockAuthoredLengths = /* @__PURE__ */ new Map();
1433
1416
  this.#ssbLengthConflictCount = 0;
@@ -1544,14 +1527,16 @@ var FECManager = class _FECManager {
1544
1527
  this.#resolvedBlockClasses = /* @__PURE__ */ new Map();
1545
1528
  // Blocks already reported through onBlockRepaired (once per block).
1546
1529
  this.#repairedBlocks = /* @__PURE__ */ new Set();
1547
- // Blocks created without any symbol because every symbol vanished.
1530
+ // Blocks created before any symbol of theirs was tracked: the repair-less
1531
+ // spans placeIsoRepair materializes (onBlockVanished).
1548
1532
  this.#materializedBlocks = /* @__PURE__ */ new Set();
1549
1533
  // packetIds whose ordered-relay horizon became underivable and fell back to
1550
1534
  // the datagram deadline. Bounds the warn in #derivableOrderedHorizon to once
1551
1535
  // per stream. Not only throws: the ordinary out-of-range case is detected by
1552
1536
  // arithmetic and never throws at all.
1553
1537
  this.#orderedHorizonWarnedFor = /* @__PURE__ */ new Set();
1554
- // Highest SBN seen per stream and when, for vanished-block detection.
1538
+ // Newest block start (SS_Start) seen per stream and when: the frontier for
1539
+ // source-discontinuity detection and isoSpanResolution.
1555
1540
  this.#highestSbn = /* @__PURE__ */ new Map();
1556
1541
  // iso-ssbg1-v1 block location per stream (streamKey). A source belongs to a
1557
1542
  // block only once a repair symbol stated the block's (SS_Start, SSB_length).
@@ -1645,7 +1630,10 @@ var FECManager = class _FECManager {
1645
1630
  #releasedWorkerClients;
1646
1631
  #syncDecoderBlockOwners;
1647
1632
  #decoderBlockOwners;
1648
- #fecK;
1633
+ // The stream-global K a configure() call stated, reported as fecStats.k.
1634
+ // It never resolves a packetId: a packetId has only its own track config
1635
+ // (BLO-45953), and K does not feed the delivery-window math.
1636
+ #streamK;
1649
1637
  #fecSymbolSize;
1650
1638
  #fecTimeoutMs;
1651
1639
  #trackConfigs;
@@ -1807,7 +1795,8 @@ var FECManager = class _FECManager {
1807
1795
  this.#completedBlocks.add(key);
1808
1796
  if (clean) this.#cleanlyCompletedBlocks.add(key);
1809
1797
  this.#indexBlockKey(key, sbn, packetId);
1810
- const expiresAtMs = nowMs + this.terminalRetentionMsForPacket(packetId);
1798
+ const config = this.#trackConfig(packetId);
1799
+ const expiresAtMs = nowMs + config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
1811
1800
  this.#terminalExpiryMs.set(key, expiresAtMs);
1812
1801
  let queue = this.#terminalQueues.get(packetId);
1813
1802
  if (!queue) {
@@ -1843,8 +1832,9 @@ var FECManager = class _FECManager {
1843
1832
  * retention the Worker and the WASM decoder are configured with, because it
1844
1833
  * comes from the same function they do (`deriveFecRetentionBlocks`, over the
1845
1834
  * 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
1835
+ * more than this many blocks behind the newest SS_Start it saw, counted
1836
+ * from below by `isoBlocksBehindLowerBound` (`cleanup_by_ss_start`), so
1837
+ * settling earlier than this would decide a block the decoder would still
1848
1838
  * have recovered.
1849
1839
  *
1850
1840
  * This used to call `deriveFecInterleaveDepth`, which returns D -- MEDIA
@@ -1857,6 +1847,14 @@ var FECManager = class _FECManager {
1857
1847
  * derivation; do not re-derive a horizon at either call site.
1858
1848
  */
1859
1849
  #orderedHorizonBlocksFor(packetId, config) {
1850
+ if (config.interleaveDepthMs === 0) {
1851
+ if (Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
1852
+ return LOCAL_FEC_RETENTION_BLOCKS;
1853
+ }
1854
+ throw new Error(
1855
+ `[FECManager] packetId=${packetId}: local FEC cleanup requires a positive timeout (got ${config.timeoutMs})`
1856
+ );
1857
+ }
1860
1858
  if (!_FECManager.#hasOrderedGeometry(config)) {
1861
1859
  throw new Error(
1862
1860
  `[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 +1868,7 @@ var FECManager = class _FECManager {
1870
1868
  * two can never disagree about which streams settle by stream order.
1871
1869
  */
1872
1870
  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
1871
+ return _FECManager.#hasCatalogTiming(config) && // `fps` is no longer an input to the horizon, but it still gates WHICH
1874
1872
  // streams settle by stream order at all: a track with no usable
1875
1873
  // media-unit rate fell back to datagram semantics before this and must
1876
1874
  // keep doing so. Removing this widens ordered-relay settlement to streams
@@ -1910,6 +1908,9 @@ var FECManager = class _FECManager {
1910
1908
  * argument: narrower IS the failing direction.
1911
1909
  */
1912
1910
  #derivableOrderedHorizon(config, packetId) {
1911
+ if (config.interleaveDepthMs === 0) {
1912
+ return Number.isFinite(config.timeoutMs) && config.timeoutMs > 0 ? LOCAL_FEC_RETENTION_BLOCKS : void 0;
1913
+ }
1913
1914
  if (!_FECManager.#hasOrderedGeometry(config)) return void 0;
1914
1915
  try {
1915
1916
  const blocks = tryDeriveFecRetentionBlocks(config.timeoutMs, config.interleaveDepthMs);
@@ -1942,19 +1943,20 @@ var FECManager = class _FECManager {
1942
1943
  /**
1943
1944
  * The stream's interleave horizon, or undefined when its geometry cannot
1944
1945
  * 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
1946
+ * a packetId that was never configured, which has no geometry at all
1947
+ * (explicitly configured tracks are refused an underivable geometry
1947
1948
  * at config time — #upsertTrackConfig, setMulticastSymbolDelivery); or a
1948
1949
  * configured track whose timeoutMs was later widened past the 255-block
1949
1950
  * ceiling without re-validating, which #derivableOrderedHorizon catches
1950
1951
  * rather than throwing from the packet-feed path. Either way the stream
1951
- * keeps datagram semantics instead of throwing. The manager defaults can
1952
+ * keeps datagram semantics instead of throwing. A track's configuration can
1952
1953
  * also change under a stream that already holds blocks, so a caller cannot
1953
1954
  * assume a horizon that was defined when a block was held is still defined
1954
1955
  * (see #advanceOrderedFrontier).
1955
1956
  */
1956
1957
  #orderedHorizonBlocks(packetId) {
1957
- return this.#derivableOrderedHorizon(this.#resolveTrackConfig(packetId), packetId);
1958
+ const config = this.#trackConfigs.get(packetId);
1959
+ return config === void 0 ? void 0 : this.#derivableOrderedHorizon(config, packetId);
1958
1960
  }
1959
1961
  /** True while a block of an ordered-relay stream can still receive symbols. */
1960
1962
  #withinOrderedHorizon(key) {
@@ -1988,7 +1990,7 @@ var FECManager = class _FECManager {
1988
1990
  */
1989
1991
  #advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName) {
1990
1992
  const previous = this.#orderedFrontiers.get(streamKey);
1991
- const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn, packetId) ? sbn : previous;
1993
+ const frontier = previous === void 0 || this.#blockPrecedes(previous, sbn) ? sbn : previous;
1992
1994
  if (frontier !== previous) this.#orderedFrontiers.set(streamKey, frontier);
1993
1995
  const pending = this.#orderedPendingBlocks.get(streamKey);
1994
1996
  if (!pending) return;
@@ -2023,7 +2025,7 @@ var FECManager = class _FECManager {
2023
2025
  if (this.#completedBlocks.has(key) || this.#pendingWorkerCleanups.has(key) || this.#repairRequestedBlocks.has(key)) return;
2024
2026
  const authoredLength = this.#blockAuthoredLengths.get(key);
2025
2027
  const received = (this.#blockSourceCounts.get(key) ?? 0) + (this.#blockRepairRsIds.get(key)?.size ?? 0);
2026
- if (this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
2028
+ if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && (authoredLength === void 0 || received < authoredLength)) {
2027
2029
  this.#requestRepair(sbn, packetId, catalogTrackName, true);
2028
2030
  return;
2029
2031
  }
@@ -2179,7 +2181,7 @@ var FECManager = class _FECManager {
2179
2181
  }
2180
2182
  #requestRepair(sbn, packetId, catalogTrackName, settledByOrder = false) {
2181
2183
  const key = this.#blockKey(sbn, packetId, catalogTrackName);
2182
- if (this.#completedBlocks.has(key) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
2184
+ if (this.#completedBlocks.has(key) || !this.#hasCatalogTimingForPacket(packetId) || !this.onRepairNeeded || this.#repairRequestedBlocks.has(key)) return;
2183
2185
  const deadline = this.#repairDeadlines.get(key);
2184
2186
  if (deadline?.handle != null) clearTimeout(deadline.handle);
2185
2187
  this.#repairDeadlines.set(key, null);
@@ -2259,6 +2261,7 @@ var FECManager = class _FECManager {
2259
2261
  }
2260
2262
  /** A block's live class without creating classifier state for the stream. */
2261
2263
  #peekClass(sbn, packetId, catalogTrackName) {
2264
+ if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
2262
2265
  const classifier = this.#classifiers.get(this.#streamKey(packetId, catalogTrackName));
2263
2266
  if (classifier) return classifier.classOf(sbn);
2264
2267
  return this.#geometryFor(packetId, catalogTrackName).trackKind === "audio" ? "audio" : "unknown";
@@ -2280,8 +2283,8 @@ var FECManager = class _FECManager {
2280
2283
  // Deliberately one block tighter than deriveFecRetentionBlocks over the same
2281
2284
  // inputs (that function's `+ 1`). Different question: the retention horizon
2282
2285
  // 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).
2286
+ // many blocks a stream can live behind (or ahead of) its frontier before a
2287
+ // source symbol there is a discontinuity rather than loss (see #noteSbnSeen).
2285
2288
  // The extra block of retention is slack for a block still being fed; a block
2286
2289
  // nothing has been heard from does not get it. Not the units defect — but it
2287
2290
  // is the next place the two can drift, so change both or neither.
@@ -2290,27 +2293,28 @@ var FECManager = class _FECManager {
2290
2293
  return Math.ceil(config.timeoutMs / config.interleaveDepthMs);
2291
2294
  }
2292
2295
  /**
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.
2296
+ * Record that a symbol for block `sbn` (the block's SS_Start) arrived and
2297
+ * advance the stream's frontier, the newest block start seen.
2295
2298
  *
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.
2299
+ * A SOURCE symbol whose block goes back further than one live window, or
2300
+ * jumps further than the stream could have produced since its last symbol,
2301
+ * is a source discontinuity, not loss. A repair symbol can only advance the
2302
+ * frontier: a D-28 fetch legitimately answers for an old block, so it never
2303
+ * signals a discontinuity.
2304
2304
  *
2305
2305
  * 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.
2306
+ * source symbols filled it, so one interleave span can start several blocks
2307
+ * (every large keyframe does). What bounds a forward jump is therefore
2308
+ * packet evidence, not a block rate: SS_ID advances by one per FEC source
2309
+ * packet (ISO/IEC 23008-1 C.5.2) and the MMTP packet_sequence_number by one
2310
+ * per packet of the packet_id, so with both source symbols'
2311
+ * packet_sequence_number a block start can move at most the packets sent
2312
+ * between them plus the anchor's own offset into its block (< K). Without
2313
+ * it, only the D-close rate is known.
2314
+ *
2315
+ * Vanished blocks are not enumerated here: their starts are unknown until a
2316
+ * repair states them. The SS_IDs between two stated blocks become one
2317
+ * repair-less span when the later block's repair arrives (placeIsoRepair).
2314
2318
  */
2315
2319
  #noteSbnSeen(sbn, packetId, catalogTrackName, symbol, packetSequenceNumber) {
2316
2320
  const streamKey = this.#streamKey(packetId, catalogTrackName);
@@ -2319,7 +2323,7 @@ var FECManager = class _FECManager {
2319
2323
  const anchor = this.#sourceAnchors.get(streamKey);
2320
2324
  const psn = symbol === "source" && packetSequenceNumber !== void 0 && Number.isSafeInteger(packetSequenceNumber) && packetSequenceNumber >= 0 && packetSequenceNumber <= 4294967295 ? packetSequenceNumber : void 0;
2321
2325
  const anchorHere = () => {
2322
- if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn, packetId))) {
2326
+ if (psn !== void 0 && (anchor === void 0 || !this.#blockPrecedes(sbn, anchor.sbn))) {
2323
2327
  this.#sourceAnchors.set(streamKey, { sbn, psn, atMs: now });
2324
2328
  }
2325
2329
  };
@@ -2335,9 +2339,9 @@ var FECManager = class _FECManager {
2335
2339
  anchorHere();
2336
2340
  return;
2337
2341
  }
2338
- const config = this.#resolveTrackConfig(packetId);
2342
+ const config = this.#trackConfig(packetId);
2339
2343
  const window2 = this.#liveWindowBlocks(config);
2340
- if (!this.#blockPrecedes(previous.sbn, sbn, packetId)) {
2344
+ if (!this.#blockPrecedes(previous.sbn, sbn)) {
2341
2345
  if (symbol === "source" && window2 !== null && this.#blocksBehind(sbn, previous.sbn, packetId) > window2) {
2342
2346
  discontinuity();
2343
2347
  return;
@@ -2348,15 +2352,9 @@ var FECManager = class _FECManager {
2348
2352
  const missing = this.#blocksBehind(previous.sbn, sbn, packetId) - 1;
2349
2353
  let plausible = true;
2350
2354
  if (window2 !== null) {
2351
- if (this.#isIso(packetId) && psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn, packetId) && psn !== anchor.psn) {
2355
+ if (psn !== void 0 && anchor !== void 0 && this.#blockPrecedes(anchor.sbn, sbn) && psn !== anchor.psn) {
2352
2356
  const sent = psn - anchor.psn >>> 0;
2353
2357
  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
2358
  } else {
2361
2359
  plausible = missing <= window2 + Math.ceil((now - previous.atMs) / config.interleaveDepthMs);
2362
2360
  }
@@ -2368,48 +2366,31 @@ var FECManager = class _FECManager {
2368
2366
  }
2369
2367
  this.#highestSbn.set(streamKey, { sbn, atMs: now });
2370
2368
  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
2369
  }
2384
2370
  /*
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.
2371
+ * Block order for one stream (BLO-35597 iso:5). A block is keyed by its
2372
+ * SS_Start and SS_ID is a 32-bit serial number (ISO/IEC 23008-1 C.5.2), so
2373
+ * blocks are ordered by RFC 1982 comparison of their starts. Only configured
2374
+ * streams hold blocks: every entry point refuses an unconfigured packetId.
2391
2375
  */
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;
2376
+ #blockPrecedes(a, b) {
2377
+ return serial32Precedes(a, b);
2396
2378
  }
2397
- /** Blocks `a` lies behind `b` (0 when it does not precede it). */
2379
+ /**
2380
+ * Blocks `a` lies behind `b` (0 when it does not precede it). Blocks vary
2381
+ * in length (K' = SSB_length <= K), so only a lower bound is known: the one
2382
+ * metric the decoder and the worker retire by too.
2383
+ */
2398
2384
  #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;
2385
+ return isoBlocksBehindLowerBound(a, b, this.#trackConfig(packetId).k);
2407
2386
  }
2408
2387
  #materializeBlock(sbn, packetId, catalogTrackName, repairable) {
2409
2388
  const key = this.#blockKey(sbn, packetId, catalogTrackName);
2410
2389
  if (this.#blockResolutionFor(key) !== "untracked") return;
2411
2390
  this.#materializedBlocks.add(key);
2412
- this.#classifierFor(packetId, catalogTrackName).touch(sbn);
2391
+ if (this.#hasCatalogTimingForPacket(packetId)) {
2392
+ this.#classifierFor(packetId, catalogTrackName).touch(sbn);
2393
+ }
2413
2394
  this.onBlockVanished?.(sbn, packetId, catalogTrackName);
2414
2395
  if (repairable) {
2415
2396
  this.noteBlockActivity(sbn, packetId, catalogTrackName, true);
@@ -2448,21 +2429,39 @@ var FECManager = class _FECManager {
2448
2429
  });
2449
2430
  if (this.#lifecycleTrace.length > 64) this.#lifecycleTrace.splice(0, 32);
2450
2431
  }
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
- };
2432
+ /**
2433
+ * A configured packetId's track config. There is no manager-wide default
2434
+ * (BLO-45953): an unconfigured packetId does no FEC work, so every caller
2435
+ * here is reached only behind a configuration gate and this throw marks a
2436
+ * broken invariant, never a packet-path outcome.
2437
+ */
2438
+ #trackConfig(packetId) {
2439
+ const config = this.#trackConfigs.get(packetId);
2440
+ if (config === void 0) {
2441
+ throw new Error(
2442
+ `[FECManager] packetId=${packetId} was never configured; configure the track from its catalog (configureTrack/setFecWorker/setFecDecoder) first`
2443
+ );
2444
+ }
2445
+ return config;
2446
+ }
2447
+ /**
2448
+ * Whether this track has the catalog's elapsed-time FEC policy. A zero span
2449
+ * is a deliberate no-deadline signal; it still leaves the wire geometry
2450
+ * usable for in-band decode, but it cannot drive classification, repair
2451
+ * fetches or layer adaptation.
2452
+ */
2453
+ static #hasCatalogTiming(config) {
2454
+ return Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS;
2459
2455
  }
2460
- #resolveTrackConfig(packetId) {
2461
- return this.#trackConfigs.get(packetId) ?? this.#defaultTrackConfig();
2456
+ #hasCatalogTimingForPacket(packetId) {
2457
+ const config = this.#trackConfigs.get(packetId);
2458
+ return config !== void 0 && _FECManager.#hasCatalogTiming(config);
2462
2459
  }
2463
2460
  #computeTimeout(config) {
2464
- if (config.timeoutMs && config.timeoutMs > 0) return config.timeoutMs;
2465
- if (config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
2461
+ if (typeof config.timeoutMs === "number" && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0) {
2462
+ return config.timeoutMs;
2463
+ }
2464
+ if (Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
2466
2465
  return Math.max(
2467
2466
  this.#fecTimeoutMs,
2468
2467
  computeFecDeliveryWindowMs({
@@ -2487,14 +2486,17 @@ var FECManager = class _FECManager {
2487
2486
  */
2488
2487
  #refreshDeliveryGapWindow() {
2489
2488
  let windowMs;
2490
- const consider = (geometry) => {
2489
+ const consider = (geometry, packetId) => {
2490
+ if (!this.#hasCatalogTimingForPacket(packetId)) return;
2491
2491
  if (geometry?.trackKind !== "video" || geometry.mode !== "classifying") return;
2492
2492
  const { ticks, timescale } = geometry.keyframeInterval;
2493
2493
  const ms = ticks / timescale * 1e3;
2494
2494
  if (Number.isFinite(ms) && ms > 0 && (windowMs === void 0 || ms > windowMs)) windowMs = ms;
2495
2495
  };
2496
- for (const geometry of this.#streamGeometries.values()) consider(geometry);
2497
- for (const config of this.#trackConfigs.values()) consider(config.geometry);
2496
+ for (const [streamKey, geometry] of this.#streamGeometries) {
2497
+ consider(geometry, this.#parseStreamKey(streamKey).packetId);
2498
+ }
2499
+ for (const [packetId, config] of this.#trackConfigs) consider(config.geometry, packetId);
2498
2500
  this.#deliveryGaps.setWindowMs(windowMs);
2499
2501
  }
2500
2502
  /**
@@ -2522,6 +2524,7 @@ var FECManager = class _FECManager {
2522
2524
  return Math.max(config.timeoutMs, Math.ceil(config.interleaveDepthMs + gapMs));
2523
2525
  }
2524
2526
  #repairDeadlineMs(config, waitMs = this.#multicastWaitMs(config)) {
2527
+ if (!_FECManager.#hasCatalogTiming(config)) return waitMs;
2525
2528
  const halfFailBoundMs = Math.ceil(waitMs / 2);
2526
2529
  if (config.k > 0 && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS && config.fps !== void 0 && Number.isFinite(config.fps) && config.fps > 0) {
2527
2530
  const mediaUnitMs = 1e3 / config.fps;
@@ -2536,10 +2539,12 @@ var FECManager = class _FECManager {
2536
2539
  }
2537
2540
  /**
2538
2541
  * 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.
2542
+ * at the widest wait the catalog allows (#maxMulticastWaitMs). For a track
2543
+ * without a catalog span this is receiver-local cleanup time only; no repair
2544
+ * grace is added. Everything derived from it (terminal retention, the
2545
+ * release gate's hold ceiling, the duplicate ledgers, the owed-cleanup
2546
+ * bound) is compared across time, so it must not follow the live
2547
+ * measurement.
2543
2548
  */
2544
2549
  #blockLifetimeMs(config) {
2545
2550
  const waitMs = this.#maxMulticastWaitMs(config);
@@ -2555,6 +2560,7 @@ var FECManager = class _FECManager {
2555
2560
  return Math.min(waitMs + this.#repairGraceMs(config, waitMs), this.#blockLifetimeMs(config));
2556
2561
  }
2557
2562
  #repairGraceMs(config, waitMs) {
2563
+ if (!_FECManager.#hasCatalogTiming(config)) return 0;
2558
2564
  return Math.max(1, waitMs - this.#repairDeadlineMs(config, waitMs));
2559
2565
  }
2560
2566
  #armBlockTimeout(sbn, packetId, catalogTrackName, delayMs) {
@@ -2641,51 +2647,72 @@ var FECManager = class _FECManager {
2641
2647
  * stream; every other field stays per packetId.
2642
2648
  */
2643
2649
  #upsertTrackConfig(packetId, config, catalogTrackName) {
2644
- const previous = this.#trackConfigs.get(packetId) ?? this.#defaultTrackConfig();
2650
+ const previousTrack = this.#trackConfigs.get(packetId);
2651
+ const k = config.k ?? previousTrack?.k;
2652
+ if (k === void 0 || !Number.isSafeInteger(k) || k <= 0) {
2653
+ throw new Error(
2654
+ `[FECManager] packetId=${packetId}: fec.k=${String(k)} must be a positive integer; configure the track from its catalog`
2655
+ );
2656
+ }
2657
+ const previous = previousTrack ?? {};
2645
2658
  const streamScopedGeometry = catalogTrackName !== void 0 && config.geometry !== void 0 ? config.geometry : void 0;
2646
2659
  if (streamScopedGeometry !== void 0) {
2647
2660
  const { geometry: _streamGeometry, ...packetLevel } = config;
2648
2661
  config = packetLevel;
2649
2662
  }
2650
2663
  const timingChanged = config.k !== void 0 || config.interleaveDepthMs !== void 0 || config.fps !== void 0;
2664
+ if (config.timeoutMs !== void 0 && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
2665
+ throw new Error(
2666
+ `[FECManager] packetId=${packetId}: timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
2667
+ );
2668
+ }
2651
2669
  if (config.blockProfile !== void 0) {
2652
2670
  parseFecBlockProfile(config.blockProfile, `packetId=${packetId} fec.blockProfile`);
2653
2671
  }
2654
2672
  const blockProfile = config.blockProfile ?? previous.blockProfile;
2655
2673
  if (blockProfile === void 0) {
2656
2674
  throw new Error(
2657
- `[FECManager] packetId=${packetId}: fec.blockProfile is required ("k-padded-v1" or "iso-ssbg1-v1"); configure the track from its catalog`
2675
+ `[FECManager] packetId=${packetId}: fec.blockProfile is required ("iso-ssbg1-v1"); configure the track from its catalog`
2658
2676
  );
2659
2677
  }
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
- }
2678
+ const ownInterleaveDepthMs = config.interleaveDepthMs ?? previousTrack?.interleaveDepthMs ?? 0;
2679
+ if (!Number.isSafeInteger(ownInterleaveDepthMs) || ownInterleaveDepthMs !== 0 && ownInterleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {
2680
+ throw new Error(
2681
+ `[FECManager] packetId=${packetId}: fec.interleaveDepthMs must be absent, 0, or a finite value >= ${MIN_INTERLEAVE_DEPTH_MS} ms (got ${String(ownInterleaveDepthMs)})`
2682
+ );
2667
2683
  }
2684
+ const hasCatalogTiming = _FECManager.#hasCatalogTiming({ interleaveDepthMs: ownInterleaveDepthMs });
2668
2685
  const next = {
2669
2686
  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),
2687
+ k,
2688
+ symbolSize: config.symbolSize != null && config.symbolSize > 0 ? config.symbolSize : previousTrack?.symbolSize ?? 0,
2689
+ interleaveDepthMs: ownInterleaveDepthMs,
2690
+ fps: config.fps ?? previous.fps ?? this.#fecFps,
2691
+ timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs ?? 0),
2675
2692
  ...(config.trackKind ?? previous.trackKind) === void 0 ? {} : { trackKind: config.trackKind ?? previous.trackKind },
2676
2693
  ...(config.codec ?? previous.codec) === void 0 ? {} : { codec: config.codec ?? previous.codec },
2677
2694
  ...(config.geometry ?? previous.geometry) === void 0 ? {} : { geometry: config.geometry ?? previous.geometry }
2678
2695
  };
2679
- next.timeoutMs = this.#computeTimeout(next);
2696
+ if (!hasCatalogTiming) {
2697
+ next.timeoutMs = config.timeoutMs !== void 0 && config.timeoutMs > 0 ? config.timeoutMs : previousTrack !== void 0 && previousTrack.interleaveDepthMs === 0 ? previousTrack.timeoutMs : LOCAL_FEC_TIMEOUT_MS;
2698
+ } else {
2699
+ next.timeoutMs = this.#computeTimeout(next);
2700
+ }
2680
2701
  if (this.#multicastSymbolDelivery === "ordered-relay") {
2681
2702
  this.#orderedHorizonBlocksFor(packetId, next);
2682
2703
  }
2683
- if (previous.blockProfile !== void 0 && (previous.blockProfile !== blockProfile || blockProfile === "iso-ssbg1-v1" && previous.k !== next.k)) {
2704
+ const timingPolicyChanged = _FECManager.#hasCatalogTiming({ interleaveDepthMs: previous.interleaveDepthMs ?? 0 }) !== hasCatalogTiming;
2705
+ if (timingPolicyChanged && !hasCatalogTiming) {
2706
+ for (const key of [...this.#repairDeadlines.keys()]) {
2707
+ if (this.#belongsToPacket(key, packetId)) this.#clearRepairDeadline(key);
2708
+ }
2709
+ }
2710
+ if (previous.blockProfile !== void 0 && previous.k !== next.k) {
2684
2711
  this.#dropIsoLocators(packetId);
2685
2712
  }
2686
2713
  this.#trackConfigs.set(packetId, next);
2687
2714
  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;
2715
+ let policyChanged = timingPolicyChanged || config.geometry !== void 0 && JSON.stringify(config.geometry) !== JSON.stringify(previous.geometry) || config.trackKind !== void 0 && config.trackKind !== previous.trackKind;
2689
2716
  if (streamScopedGeometry !== void 0) {
2690
2717
  const streamKey = this.#streamKey(packetId, catalogTrackName);
2691
2718
  if (JSON.stringify(streamScopedGeometry) !== JSON.stringify(this.#streamGeometries.get(streamKey))) {
@@ -2700,20 +2727,14 @@ var FECManager = class _FECManager {
2700
2727
  if (!this.#belongsToPacket(key, packetId)) continue;
2701
2728
  const stream = this.#parseStreamKey(key);
2702
2729
  const geometry = this.#geometryFor(stream.packetId, stream.catalogTrackName);
2703
- if (JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
2730
+ if (hasCatalogTiming && JSON.stringify(geometry) === JSON.stringify(classifier.geometry)) continue;
2704
2731
  this.#classifiers.delete(key);
2732
+ if (!hasCatalogTiming) continue;
2705
2733
  this.#applyClassifierChanges(stream.packetId, stream.catalogTrackName, classifier.retire());
2706
2734
  }
2707
2735
  }
2708
2736
  return next;
2709
2737
  }
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
2738
  #blockKeysFor(sbn, packetId, catalogTrackName) {
2718
2739
  if (packetId != null && catalogTrackName !== void 0) {
2719
2740
  return [this.#blockKey(sbn, packetId, catalogTrackName)];
@@ -2774,7 +2795,7 @@ var FECManager = class _FECManager {
2774
2795
  ...stats,
2775
2796
  packetId,
2776
2797
  blocksComplete: trackTotal,
2777
- k: this.#resolveTrackConfig(packetId).k,
2798
+ k: this.#trackConfigs.get(packetId)?.k,
2778
2799
  recoveryRate: trackAffected > 0 ? stats.blocksRecovered / trackAffected * 100 : 100,
2779
2800
  packetLossRate: trackTotal > 0 ? trackAffected / trackTotal * 100 : 0
2780
2801
  };
@@ -2786,7 +2807,7 @@ var FECManager = class _FECManager {
2786
2807
  (block) => block.multicastSymbols > 0 && block.moqSymbols > 0
2787
2808
  ).length,
2788
2809
  recentBlockPathCounts: Array.from(this.#blockPathCounts.values()).slice(-24),
2789
- k: this.#fecK,
2810
+ k: this.#streamK,
2790
2811
  // True recovery rate: of blocks that HAD loss, how many were recovered?
2791
2812
  recoveryRate: affected > 0 ? s.blocksRecovered / affected * 100 : 100,
2792
2813
  packetLossRate: total > 0 ? affected / total * 100 : 0,
@@ -3081,15 +3102,20 @@ var FECManager = class _FECManager {
3081
3102
  }
3082
3103
  }
3083
3104
  }
3084
- /** Record a transport symbol and reject later duplicates of the same packetId/SBN/ESI. */
3105
+ /**
3106
+ * Record a transport symbol and reject later duplicates of the same
3107
+ * packetId/SBN/ESI. `sbn` is the block's SS_Start. A packetId with no
3108
+ * configured block profile has no block geometry: its symbol is refused
3109
+ * (false) and no block state is created for it.
3110
+ */
3085
3111
  feedSymbolFromSource(packetId, sbn, esi, _data, source, catalogTrackName) {
3112
+ if (!this.#hasBlockProfile(packetId)) return false;
3086
3113
  const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
3087
3114
  const streamKey = this.#streamKey(packetId, catalogTrackName);
3088
3115
  if (source === "multicast" && this.#multicastSymbolDelivery === "ordered-relay") {
3089
3116
  this.#advanceOrderedFrontier(streamKey, sbn, packetId, catalogTrackName);
3090
3117
  }
3091
- const k = this.kForPacket(packetId);
3092
- const sourceSymbols = this.#isIso(packetId) ? this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? k : k;
3118
+ const sourceSymbols = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(sbn)?.length ?? this.#trackConfig(packetId).k;
3093
3119
  const isSourceSymbol = esi >= 0 && esi < sourceSymbols;
3094
3120
  const isStartupPartial = this.#startupPartialBlocks.has(blockKey);
3095
3121
  const isReadyMulticastRepair = this.#startupRequireRepairReady && source === "multicast" && !isSourceSymbol && this.#startupRepairReadyPackets.has(streamKey);
@@ -3207,14 +3233,14 @@ var FECManager = class _FECManager {
3207
3233
  for (const [key] of this.#activeBlocks) {
3208
3234
  const { packetId, sbn } = this.#parseBlockKey(key);
3209
3235
  const count = this.#blockSourceCounts.get(key) ?? 0;
3210
- const k = this.#blockAuthoredLengths.get(key) ?? this.kForPacket(packetId);
3236
+ const k = this.#blockAuthoredLengths.get(key) ?? this.#trackConfig(packetId).k;
3211
3237
  blocks.push({ sbn, packetId, state: "collecting", sourceCount: count, k, progress: k > 0 ? Math.min(count / k, 1) : 0 });
3212
3238
  }
3213
3239
  const completed = [...this.#completedBlocks].slice(-16);
3214
3240
  for (const key of completed) {
3215
3241
  const { packetId, sbn } = this.#parseBlockKey(key);
3216
3242
  if (!this.#activeBlocks.has(key)) {
3217
- const k = this.kForPacket(packetId);
3243
+ const k = this.#trackConfig(packetId).k;
3218
3244
  blocks.push({ sbn, packetId, state: "complete", sourceCount: k, k, progress: 1 });
3219
3245
  }
3220
3246
  }
@@ -3225,25 +3251,23 @@ var FECManager = class _FECManager {
3225
3251
  get wasmDecoder() {
3226
3252
  return this.#fecDecoders.values().next().value?.client ?? null;
3227
3253
  }
3228
- get k() {
3229
- return this.#fecK;
3230
- }
3254
+ /** A configured packetId's K; undefined for an unconfigured one (no FEC work). */
3231
3255
  kForPacket(packetId) {
3232
- return this.#resolveTrackConfig(packetId).k;
3256
+ return this.#trackConfigs.get(packetId)?.k;
3233
3257
  }
3234
3258
  /** FEC symbol size in bytes. */
3235
3259
  get symbolSize() {
3236
3260
  return this.#fecSymbolSize;
3237
3261
  }
3238
3262
  symbolSizeForPacket(packetId) {
3239
- return this.#resolveTrackConfig(packetId).symbolSize;
3263
+ return this.#trackConfigs.get(packetId)?.symbolSize;
3240
3264
  }
3241
3265
  /** FEC block span in milliseconds (0 = no interleaving). */
3242
3266
  get interleaveDepthMs() {
3243
3267
  return this.#fecInterleaveDepthMs;
3244
3268
  }
3245
3269
  interleaveDepthMsForPacket(packetId) {
3246
- return this.#resolveTrackConfig(packetId).interleaveDepthMs;
3270
+ return this.#trackConfigs.get(packetId)?.interleaveDepthMs;
3247
3271
  }
3248
3272
  /**
3249
3273
  * Record one multicast MMTP packet arrival (every packet of the leg, FEC
@@ -3261,7 +3285,8 @@ var FECManager = class _FECManager {
3261
3285
  * plus the larger of the catalog tolerance and the measured delivery gap.
3262
3286
  */
3263
3287
  timeoutMsForPacket(packetId) {
3264
- return this.#multicastWaitMs(this.#resolveTrackConfig(packetId));
3288
+ const config = this.#trackConfigs.get(packetId);
3289
+ return config === void 0 ? void 0 : this.#multicastWaitMs(config);
3265
3290
  }
3266
3291
  /**
3267
3292
  * Largest multicast delivery gap measured over the current window (ms), and
@@ -3317,27 +3342,31 @@ var FECManager = class _FECManager {
3317
3342
  }
3318
3343
  /** Resolved per-track D-28 repair request deadline in milliseconds. */
3319
3344
  repairDeadlineMsForPacket(packetId) {
3320
- return this.#repairDeadlineMs(this.#resolveTrackConfig(packetId));
3345
+ const config = this.#trackConfigs.get(packetId);
3346
+ return config === void 0 ? void 0 : this.#repairDeadlineMs(config);
3321
3347
  }
3322
3348
  /**
3323
3349
  * One block lifetime: the longest a block stays open after its last symbol
3324
3350
  * (inactivity fail bound plus D-28 repair grace).
3325
3351
  */
3326
3352
  blockLifetimeMsForPacket(packetId) {
3327
- return this.#blockLifetimeMs(this.#resolveTrackConfig(packetId));
3353
+ const config = this.#trackConfigs.get(packetId);
3354
+ return config === void 0 ? void 0 : this.#blockLifetimeMs(config);
3328
3355
  }
3329
3356
  /**
3330
3357
  * How long a terminal (clean, recovered or failed) block stays authoritative,
3331
3358
  * for late symbols and for the in-order release gate. It is also the gate's
3332
3359
  * hold ceiling (MmtpRouter's holdLimitMs): a retired block reads back as
3333
3360
  * 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.
3361
+ * on a block that can retire. With a positive catalog span the window is one
3362
+ * interleave depth for a gap's last block to finish arriving, one block
3363
+ * lifetime to fail it, and one more for an owed Worker cleanup. With an
3364
+ * absent/zero span it is a receiver-local cleanup bound only; it creates no
3365
+ * catalog FEC deadline.
3338
3366
  */
3339
3367
  terminalRetentionMsForPacket(packetId) {
3340
- const config = this.#resolveTrackConfig(packetId);
3368
+ const config = this.#trackConfigs.get(packetId);
3369
+ if (config === void 0) return void 0;
3341
3370
  return config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
3342
3371
  }
3343
3372
  /** Remaining fetch budget for a specific repair-requested block. */
@@ -3348,7 +3377,8 @@ var FECManager = class _FECManager {
3348
3377
  if (deadlineMs !== void 0) {
3349
3378
  return Math.max(1, Math.ceil(deadlineMs - performance.now()));
3350
3379
  }
3351
- const trackConfig = this.#resolveTrackConfig(packetId);
3380
+ const trackConfig = this.#trackConfigs.get(packetId);
3381
+ if (trackConfig === void 0) return void 0;
3352
3382
  return Math.max(1, trackConfig.timeoutMs - this.#repairDeadlineMs(trackConfig));
3353
3383
  }
3354
3384
  /** True once configureTrack/setFecDecoder/setFecWorker configured this packetId. */
@@ -3359,14 +3389,19 @@ var FECManager = class _FECManager {
3359
3389
  blockProfileForPacket(packetId) {
3360
3390
  return this.#trackConfigs.get(packetId)?.blockProfile;
3361
3391
  }
3362
- #isIso(packetId) {
3363
- return this.#trackConfigs.get(packetId)?.blockProfile === "iso-ssbg1-v1";
3392
+ /**
3393
+ * Whether a packetId has a configured block geometry. iso-ssbg1-v1 is the
3394
+ * only profile, so every configured track has it; a packetId never
3395
+ * configured (or released) has none and gets no FEC block tracking.
3396
+ */
3397
+ #hasBlockProfile(packetId) {
3398
+ return this.#trackConfigs.get(packetId)?.blockProfile !== void 0;
3364
3399
  }
3365
3400
  #isoLocatorFor(packetId, catalogTrackName) {
3366
3401
  const streamKey = this.#streamKey(packetId, catalogTrackName);
3367
3402
  let locator = this.#isoLocators.get(streamKey);
3368
3403
  if (!locator) {
3369
- locator = new IsoBlockLocator(this.kForPacket(packetId), this.#isoRetentionBlocks(packetId));
3404
+ locator = new IsoBlockLocator(this.#trackConfig(packetId).k, this.#isoRetentionBlocks(packetId));
3370
3405
  this.#isoLocators.set(streamKey, locator);
3371
3406
  }
3372
3407
  return locator;
@@ -3397,12 +3432,14 @@ var FECManager = class _FECManager {
3397
3432
  */
3398
3433
  #isoRetentionBlocks(packetId) {
3399
3434
  const config = this.#trackConfigs.get(packetId);
3400
- if (config?.blockProfile !== "iso-ssbg1-v1" || !(config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS)) {
3435
+ if (config?.blockProfile === void 0) {
3401
3436
  throw new Error(
3402
- `[FECManager] packetId=${packetId} has no iso-ssbg1-v1 block span to derive block-location retention from`
3437
+ `[FECManager] packetId=${packetId} has no configured iso-ssbg1-v1 block profile`
3403
3438
  );
3404
3439
  }
3405
- const blocks = Math.ceil(this.terminalRetentionMsForPacket(packetId) / config.interleaveDepthMs) + 1;
3440
+ if (!_FECManager.#hasCatalogTiming(config)) return LOCAL_FEC_RETENTION_BLOCKS;
3441
+ const terminalRetentionMs = config.interleaveDepthMs + 2 * this.#blockLifetimeMs(config);
3442
+ const blocks = Math.ceil(terminalRetentionMs / config.interleaveDepthMs) + 1;
3406
3443
  if (!Number.isSafeInteger(blocks) || blocks <= 0) {
3407
3444
  throw new Error(`[FECManager] packetId=${packetId} iso-ssbg1-v1 retention of ${blocks} blocks is not derivable`);
3408
3445
  }
@@ -3410,7 +3447,8 @@ var FECManager = class _FECManager {
3410
3447
  }
3411
3448
  /** Whether an iso-ssbg1-v1 protected packet fits one ssbg_mode1 symbol (len + 2 <= T). */
3412
3449
  isoSourceFits(packetId, packetLength) {
3413
- return ssbg1FitsSymbol(packetLength, this.symbolSizeForPacket(packetId));
3450
+ const symbolSize = this.#trackConfigs.get(packetId)?.symbolSize;
3451
+ return symbolSize !== void 0 && ssbg1FitsSymbol(packetLength, symbolSize);
3414
3452
  }
3415
3453
  /**
3416
3454
  * Locate an iso-ssbg1-v1 source by range membership in a block a repair
@@ -3471,11 +3509,11 @@ var FECManager = class _FECManager {
3471
3509
  * that could hold them is pending or not yet stated by a repair, else the
3472
3510
  * starts of the blocks that failed or vanished.
3473
3511
  *
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
3512
+ * `untracked` reads as the release gate reads it (MmtpRouter
3513
+ * #createFecReleaseGate): a block holding either released frame's SS_ID
3514
+ * (`fromSsId` or `toSsId`) delivered that frame, so untracked there means it
3515
+ * resolved and retired; a block starting after the stream's frontier
3516
+ * (`highestSbn`, an SS_Start) may still be heard from; a block at or behind the
3479
3517
  * frontier that no symbol of this manager ever reached vanished, and its
3480
3518
  * media is lost.
3481
3519
  */
@@ -3543,6 +3581,7 @@ var FECManager = class _FECManager {
3543
3581
  }
3544
3582
  /** Current classifier mode of a stream. */
3545
3583
  classifierModeFor(packetId, catalogTrackName) {
3584
+ if (!this.#hasCatalogTimingForPacket(packetId)) return { mode: "all-protected", reason: null };
3546
3585
  const classifier = this.#classifierFor(packetId, catalogTrackName);
3547
3586
  return { mode: classifier.mode, reason: classifier.reason };
3548
3587
  }
@@ -3552,10 +3591,12 @@ var FECManager = class _FECManager {
3552
3591
  * it does not. A stream with no classifier yet has shown nothing against it.
3553
3592
  */
3554
3593
  mpuBoundsRapsFor(packetId, catalogTrackName) {
3594
+ if (!this.#hasCatalogTimingForPacket(packetId)) return false;
3555
3595
  return this.#classifiers.get(this.#streamKey(packetId, catalogTrackName))?.mpuBoundsRaps ?? true;
3556
3596
  }
3557
3597
  /** A block's class now, or frozen at its resolution. */
3558
3598
  blockClassFor(sbn, packetId, catalogTrackName) {
3599
+ if (!this.#hasCatalogTimingForPacket(packetId)) return "unknown";
3559
3600
  const frozen = this.#resolvedBlockClasses.get(this.#blockKey(sbn, packetId, catalogTrackName));
3560
3601
  if (frozen !== void 0) return frozen;
3561
3602
  return this.#peekClass(sbn, packetId, catalogTrackName);
@@ -3577,7 +3618,7 @@ var FECManager = class _FECManager {
3577
3618
  * invariant (consecutive MPUs exactly one keyframe interval apart).
3578
3619
  */
3579
3620
  noteReleasedRap(packetId, catalogTrackName, mpu, presentationTime, mediaTimescale) {
3580
- if (!this.#trackConfigs.has(packetId)) return;
3621
+ if (!this.#trackConfigs.has(packetId) || !this.#hasCatalogTimingForPacket(packetId)) return;
3581
3622
  this.#applyClassifierChanges(
3582
3623
  packetId,
3583
3624
  catalogTrackName,
@@ -3593,27 +3634,34 @@ var FECManager = class _FECManager {
3593
3634
  // -----------------------------------------------------------------------
3594
3635
  /** Configure FEC parameters. */
3595
3636
  configure(config) {
3596
- if (config.k != null) this.#fecK = config.k;
3637
+ if (config.interleaveDepthMs != null && (!Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < 0)) {
3638
+ throw new Error(
3639
+ `[FECManager] interleaveDepthMs=${config.interleaveDepthMs} must be a non-negative safe integer in milliseconds`
3640
+ );
3641
+ }
3642
+ if (config.timeoutMs != null && (!Number.isFinite(config.timeoutMs) || config.timeoutMs < 0)) {
3643
+ throw new Error(
3644
+ `[FECManager] timeoutMs=${config.timeoutMs} must be a finite non-negative cleanup budget`
3645
+ );
3646
+ }
3647
+ if (config.k != null && (!Number.isSafeInteger(config.k) || config.k <= 0)) {
3648
+ throw new Error(`[FECManager] k=${config.k} must be a positive integer`);
3649
+ }
3650
+ if (config.k != null) this.#streamK = config.k;
3597
3651
  if (config.symbolSize != null && config.symbolSize > 0) this.#fecSymbolSize = config.symbolSize;
3598
3652
  if (config.interleaveDepthMs != null) this.#fecInterleaveDepthMs = config.interleaveDepthMs;
3599
3653
  if (config.fps != null && Number.isFinite(config.fps) && config.fps > 0) {
3600
3654
  this.#fecFps = config.fps;
3601
3655
  }
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
3656
  if (config.timeoutMs != null && config.timeoutMs > 0) {
3606
3657
  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
- });
3658
+ } else if (config.interleaveDepthMs === 0) {
3659
+ this.#fecTimeoutMs = LOCAL_FEC_TIMEOUT_MS;
3660
+ } else if (config.interleaveDepthMs != null && Number.isSafeInteger(config.interleaveDepthMs) && config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
3661
+ this.#fecTimeoutMs = fecSpanDeliveryWindowMs(config.interleaveDepthMs);
3614
3662
  }
3615
3663
  for (const [packetId, track] of this.#trackConfigs) {
3616
- if (track.timeoutMs < this.#fecTimeoutMs) {
3664
+ if (_FECManager.#hasCatalogTiming(track) && track.timeoutMs < this.#fecTimeoutMs) {
3617
3665
  this.#trackConfigs.set(packetId, { ...track, timeoutMs: this.#fecTimeoutMs });
3618
3666
  }
3619
3667
  }
@@ -3672,7 +3720,7 @@ var FECManager = class _FECManager {
3672
3720
  );
3673
3721
  }
3674
3722
  if (config) this.#upsertTrackConfig(packetId, config, catalogTrackName);
3675
- const workerK = this.#resolveTrackConfig(packetId).k;
3723
+ const workerK = this.#trackConfig(packetId).k;
3676
3724
  this.#workerClients.set(streamKey, client);
3677
3725
  this.#releasedWorkerClients.delete(client);
3678
3726
  client.onFrame((data, meta) => {
@@ -3852,7 +3900,7 @@ var FECManager = class _FECManager {
3852
3900
  return;
3853
3901
  }
3854
3902
  this.#settleOwedCleanup(key, "cleanup-ack-timeout");
3855
- }, Math.ceil(this.#liveBlockLifetimeMs(this.#resolveTrackConfig(packetId))))
3903
+ }, Math.ceil(this.#liveBlockLifetimeMs(this.#trackConfig(packetId))))
3856
3904
  };
3857
3905
  this.#pendingWorkerCleanupBounds.set(key, bound);
3858
3906
  }
@@ -3893,16 +3941,11 @@ var FECManager = class _FECManager {
3893
3941
  if (this.#fpsDetected || fps <= 0) return;
3894
3942
  this.#fpsDetected = true;
3895
3943
  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
- });
3944
+ if (Number.isSafeInteger(this.#fecInterleaveDepthMs) && this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {
3945
+ this.#fecTimeoutMs = fecSpanDeliveryWindowMs(this.#fecInterleaveDepthMs);
3903
3946
  }
3904
3947
  for (const [packetId, config] of this.#trackConfigs) {
3905
- if (config.fps !== void 0 || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
3948
+ if (config.fps !== void 0 || !Number.isSafeInteger(config.interleaveDepthMs) || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue;
3906
3949
  this.#trackConfigs.set(packetId, {
3907
3950
  ...config,
3908
3951
  fps,
@@ -3934,35 +3977,28 @@ var FECManager = class _FECManager {
3934
3977
  feedSourceSymbol(packetId, ssId, symbolData, alta, catalogTrackName) {
3935
3978
  this.#fecStats.sourcePackets++;
3936
3979
  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));
3980
+ if (!this.#hasBlockProfile(packetId)) return null;
3981
+ const locator = this.#isoLocatorFor(packetId, catalogTrackName);
3982
+ const located = locator.locate(ssId);
3983
+ if (located === null) {
3984
+ throw new Error(
3985
+ `[FECManager] iso-ssbg1-v1 source SS_ID=${ssId} (packetId=${packetId}) has no stated block; hold it until a repair places it (placeIsoRepair)`
3986
+ );
3957
3987
  }
3988
+ locator.observe(ssId);
3989
+ if (!ssbg1FitsSymbol(symbolData.byteLength, this.#trackConfig(packetId).symbolSize)) {
3990
+ this.#isoSourceOversizeCount++;
3991
+ return null;
3992
+ }
3993
+ const sbn = located.block.start;
3958
3994
  const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
3959
3995
  const syncBlockOwner = this.#syncDecoderBlockOwners.get(blockKey);
3960
3996
  const workerClient = this.#routableWorkerForPacket(packetId, catalogTrackName);
3961
3997
  if (workerClient && !syncBlockOwner) {
3962
3998
  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
3999
+ const buf = symbolData.buffer.slice(
4000
+ symbolData.byteOffset,
4001
+ symbolData.byteOffset + symbolData.byteLength
3966
4002
  );
3967
4003
  if (alta) {
3968
4004
  const authBuf = alta.auth.buffer.slice(
@@ -3985,7 +4021,7 @@ var FECManager = class _FECManager {
3985
4021
  this.#syncDecoderBlockOwners.set(blockKey, client);
3986
4022
  let recovered = null;
3987
4023
  try {
3988
- recovered = client.add_source(ssId, fecData, performance.now());
4024
+ recovered = client.add_source(ssId, symbolData, performance.now());
3989
4025
  } catch {
3990
4026
  }
3991
4027
  this.#retireIsoUnplaced(client, packetId, catalogTrackName);
@@ -4004,7 +4040,8 @@ var FECManager = class _FECManager {
4004
4040
  feedRepairSymbol(packetId, ssStart, ssbLength, rsId, repairData, catalogTrackName) {
4005
4041
  this.#fecStats.repairPackets++;
4006
4042
  this.#statsForPacket(packetId).repairPackets++;
4007
- const symbolSize = this.symbolSizeForPacket(packetId);
4043
+ if (!this.#hasBlockProfile(packetId)) return null;
4044
+ const symbolSize = this.#trackConfig(packetId).symbolSize;
4008
4045
  if (symbolSize > 0 && repairData.byteLength !== symbolSize) {
4009
4046
  this.#repairSymbolSizeMismatchCount++;
4010
4047
  if (this.#repairSymbolSizeMismatchCount <= 5 || this.#repairSymbolSizeMismatchCount % 100 === 0) {
@@ -4015,18 +4052,12 @@ var FECManager = class _FECManager {
4015
4052
  return null;
4016
4053
  }
4017
4054
  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);
4055
+ const k = this.#trackConfig(packetId).k;
4056
+ const block = this.#isoLocatorFor(packetId, catalogTrackName).blockAt(ssStart);
4057
+ if (ssbLength <= 0 || ssbLength > k || block === void 0 || block.span || block.length !== ssbLength) {
4058
+ return null;
4029
4059
  }
4060
+ const sbn = ssStart;
4030
4061
  this.#noteSbnSeen(sbn, packetId, catalogTrackName, "repair");
4031
4062
  this.noteBlockActivity(sbn, packetId, catalogTrackName);
4032
4063
  const blockKey = this.#blockKey(sbn, packetId, catalogTrackName);
@@ -4064,8 +4095,9 @@ var FECManager = class _FECManager {
4064
4095
  * Record a block's authored SSB_length from a repair symbol. Returns false,
4065
4096
  * leaving the block unchanged, when the symbol contradicts the block: a
4066
4097
  * 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.
4098
+ * and an SSB_length that places an already-received source ESI at or past
4099
+ * the block's end fails the block, since its geometry can no longer be
4100
+ * trusted.
4069
4101
  */
4070
4102
  #noteAuthoredLength(blockKey, sbn, packetId, ssbLength, catalogTrackName) {
4071
4103
  const known = this.#blockAuthoredLengths.get(blockKey);
@@ -4132,7 +4164,7 @@ var FECManager = class _FECManager {
4132
4164
  * a sweep tied to expiry alone would never run there.
4133
4165
  */
4134
4166
  #retireIsoUnplaced(decoder, packetId, catalogTrackName) {
4135
- if (!this.#isIso(packetId)) return;
4167
+ if (!this.#hasBlockProfile(packetId)) return;
4136
4168
  const newest = this.#isoLocators.get(this.#streamKey(packetId, catalogTrackName))?.newest;
4137
4169
  if (newest === void 0) return;
4138
4170
  try {
@@ -4177,11 +4209,13 @@ var FECManager = class _FECManager {
4177
4209
  this.#failBlock(sbn, packetId, catalogTrackName);
4178
4210
  return;
4179
4211
  }
4180
- this.#applyClassifierChanges(
4181
- packetId,
4182
- catalogTrackName,
4183
- this.#classifierFor(packetId, catalogTrackName).observe(sbn, evidence)
4184
- );
4212
+ if (_FECManager.#hasCatalogTiming(trackConfig)) {
4213
+ this.#applyClassifierChanges(
4214
+ packetId,
4215
+ catalogTrackName,
4216
+ this.#classifierFor(packetId, catalogTrackName).observe(sbn, evidence)
4217
+ );
4218
+ }
4185
4219
  const count = (this.#blockSourceCounts.get(key) ?? 0) + 1;
4186
4220
  this.#blockSourceCounts.set(key, count);
4187
4221
  if (count === 1) this.#indexBlockKey(key, sbn, packetId);
@@ -4225,6 +4259,7 @@ var FECManager = class _FECManager {
4225
4259
  }
4226
4260
  /** Refresh deadlines after accepted block activity. */
4227
4261
  noteBlockActivity(sbn, packetId, catalogTrackName, sourceProgress = false) {
4262
+ if (!this.#trackConfigs.has(packetId)) return;
4228
4263
  const key = this.#blockKey(sbn, packetId, catalogTrackName);
4229
4264
  if (this.#completedBlocks.has(key)) return;
4230
4265
  if (this.#pendingWorkerCleanups.has(key)) return;
@@ -4234,13 +4269,13 @@ var FECManager = class _FECManager {
4234
4269
  this.#holdForOrderedSettlement(key, sbn, packetId, catalogTrackName);
4235
4270
  return;
4236
4271
  }
4237
- const trackConfig = this.#resolveTrackConfig(packetId);
4272
+ const trackConfig = this.#trackConfig(packetId);
4238
4273
  const timeoutMs = this.#multicastWaitMs(trackConfig);
4239
4274
  if (!(timeoutMs > 0)) {
4240
4275
  throw new Error(`[FECManager] packetId=${packetId} has no positive fail bound (timeoutMs=${timeoutMs})`);
4241
4276
  }
4242
4277
  const repairDeadline = this.#repairDeadlines.get(key);
4243
- if (this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
4278
+ if (this.#hasCatalogTimingForPacket(packetId) && this.onRepairNeeded && !repairRequested && (repairDeadline === void 0 || sourceProgress)) {
4244
4279
  this.#armRepairDeadline(
4245
4280
  key,
4246
4281
  sbn,
@@ -4310,6 +4345,7 @@ var FECManager = class _FECManager {
4310
4345
  }
4311
4346
  /** Mark a block as completed externally (e.g., after routing recovered data). */
4312
4347
  markBlockCompleted(sbn, packetId, catalogTrackName) {
4348
+ if (packetId != null && !this.#trackConfigs.has(packetId)) return;
4313
4349
  for (const key of this.#blockKeysFor(sbn, packetId, catalogTrackName)) {
4314
4350
  this.#resolveCompleted(key);
4315
4351
  }
@@ -8526,7 +8562,6 @@ var CmafBlockAssembler = class {
8526
8562
  failedBlocks: 0
8527
8563
  };
8528
8564
  this.#config = {
8529
- k: config.k,
8530
8565
  timescale: config.timescale ?? 1e6,
8531
8566
  trackId: config.trackId ?? 1,
8532
8567
  trackType: config.trackType,
@@ -10712,6 +10747,21 @@ var FecReleaseSequencer = class {
10712
10747
  this.#streams.delete(key);
10713
10748
  }
10714
10749
  }
10750
+ /**
10751
+ * The packet's FEC ended, so no block will resolve a held gap: release its
10752
+ * frames in order, each gap stalled as a discontinuity, and forget its
10753
+ * streams, so FEC configured on it again starts from a fresh one.
10754
+ */
10755
+ endPacket(packetId) {
10756
+ for (const [key, stream] of this.#streams) {
10757
+ if (stream.packetId !== packetId) continue;
10758
+ while (stream.buf.length > 0 && stream.last !== null) {
10759
+ stream.discontinuity = true;
10760
+ this.#drain(stream);
10761
+ }
10762
+ this.#streams.delete(key);
10763
+ }
10764
+ }
10715
10765
  discardPacket(packetId) {
10716
10766
  for (const [key, stream] of this.#streams) {
10717
10767
  if (stream.packetId !== packetId) continue;
@@ -11094,6 +11144,13 @@ var MmtpContainerParser = class _MmtpContainerParser {
11094
11144
  markFecDiscontinuity(packetId, catalogTrackName) {
11095
11145
  this.#fecSequencer?.markDiscontinuity(this.#streamKey(packetId, catalogTrackName));
11096
11146
  }
11147
+ /**
11148
+ * The FEC of a packet ID ended: release the frames the FEC release gate
11149
+ * holds for it in order, each gap stalled, and forget its gated streams.
11150
+ */
11151
+ endFecRelease(packetId) {
11152
+ this.#fecSequencer?.endPacket(packetId);
11153
+ }
11097
11154
  /**
11098
11155
  * MPU and data-unit identity of one MMTP packet, parsed in place (no copy,
11099
11156
  * no reassembly). Null for non-MPU packets and packets that do not parse.
@@ -12426,6 +12483,8 @@ var MmtpRouter = class {
12426
12483
  */
12427
12484
  #onPublisherRestart;
12428
12485
  #publisherRestarts = 0;
12486
+ /** Inits delivered with no onTrackInit handler for their packetId (MOQT-01). */
12487
+ #unmappedInitDeliveries = 0;
12429
12488
  // FEC Payload IDs left unread because their (owner) packetId had no
12430
12489
  // configured fec.blockProfile (see #noteUnreadableFecPayloadId).
12431
12490
  #fecPayloadIdsWithoutProfile = 0;
@@ -12622,15 +12681,24 @@ var MmtpRouter = class {
12622
12681
  return NO_FEC_GEOMETRY_HORIZON;
12623
12682
  }
12624
12683
  const blockSpanMs = fec.interleaveDepthMsForPacket(packetId);
12684
+ const blockLifetimeMs = fec.blockLifetimeMsForPacket(packetId);
12685
+ if (blockSpanMs === void 0 || blockLifetimeMs === void 0) return NO_FEC_GEOMETRY_HORIZON;
12625
12686
  if (blockSpanMs === 0) return NO_BLOCK_SPAN_HORIZON;
12626
12687
  return {
12627
12688
  kind: "fec-horizon",
12628
- retentionMs: duplicateRetentionMs({
12629
- blockSpanMs,
12630
- blockLifetimeMs: fec.blockLifetimeMsForPacket(packetId)
12631
- })
12689
+ retentionMs: duplicateRetentionMs({ blockSpanMs, blockLifetimeMs })
12632
12690
  };
12633
12691
  }
12692
+ /**
12693
+ * Init segments delivered for a packetId with no `onTrackInit` handler
12694
+ * since construction, whether the deprecated aggregate `onInitSegment`
12695
+ * callback then received them or nothing did. A supported deployment reads
12696
+ * 0 here; a non-zero value is the evidence that blocks removing the
12697
+ * aggregate callback (MOQT-01, BLO-21181).
12698
+ */
12699
+ get unmappedInitDeliveryCount() {
12700
+ return this.#unmappedInitDeliveries;
12701
+ }
12634
12702
  /** Publisher restarts detected on the multicast leg since construction. */
12635
12703
  get publisherRestartCount() {
12636
12704
  return this.#publisherRestarts;
@@ -12718,7 +12786,7 @@ var MmtpRouter = class {
12718
12786
  /** Update MFU reassembly timeout. Source and FEC-recovery paths share the
12719
12787
  * parser's single reassembler (BLO-8151), so one update covers both. */
12720
12788
  setMfuTimeoutMs(ms) {
12721
- if (ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
12789
+ if (ms !== null && ms <= 0) throw new Error("[MmtpRouter] mfuTimeoutMs must be > 0");
12722
12790
  this.#parser.setMfuTimeoutMs(ms);
12723
12791
  }
12724
12792
  /** Update parser frame reordering for unordered multicast delivery.
@@ -12788,7 +12856,15 @@ var MmtpRouter = class {
12788
12856
  onTrackPacket(packetId, cb) {
12789
12857
  this.#trackPacketCallbacks.set(packetId, cb);
12790
12858
  }
12791
- /** Register an init segment callback (ftyp+moov from MMTP MPU metadata). */
12859
+ /**
12860
+ * Register an init segment callback (ftyp+moov from MMTP MPU metadata).
12861
+ * Receives an init only for a packetId with no `onTrackInit` handler.
12862
+ *
12863
+ * @deprecated Use onTrackInit(packetId, cb). Still delivered as a fallback;
12864
+ * removed in the 1.0.0 major, no earlier than one minor release after this
12865
+ * deprecation reaches the `latest` dist-tag (BLO-21181). Count fallback
12866
+ * deliveries with `unmappedInitDeliveryCount`.
12867
+ */
12792
12868
  onInitSegment(cb) {
12793
12869
  this.#onInitSegment = cb;
12794
12870
  }
@@ -12802,17 +12878,16 @@ var MmtpRouter = class {
12802
12878
  * When active, source frames are buffered per FEC block and emitted as
12803
12879
  * multi-sample CMAF segments instead of per-frame callbacks. */
12804
12880
  enableMseAssembler(packetId, config, catalogTrackName) {
12805
- const k = this.#kForPacket(packetId);
12806
12881
  const assemblerKey = this.#mseAssemblerKey(packetId, catalogTrackName);
12807
12882
  const previous = this.#mseAssemblers.get(assemblerKey);
12808
12883
  if (previous) {
12809
12884
  previous.flush();
12810
12885
  previous.dispose();
12811
12886
  }
12812
- const assembler = new CmafBlockAssembler({ ...config, k });
12887
+ const assembler = new CmafBlockAssembler(config);
12813
12888
  this.#mseAssemblers.set(assemblerKey, assembler);
12814
12889
  console.log(
12815
- `[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (K=${k}, ${config.trackType})`
12890
+ `[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${config.trackType})`
12816
12891
  );
12817
12892
  return assembler;
12818
12893
  }
@@ -12921,6 +12996,21 @@ var MmtpRouter = class {
12921
12996
  }
12922
12997
  this.#fecMappersByPacketId.set(packetId, mapper);
12923
12998
  }
12999
+ /**
13000
+ * Release the router side of a packetId whose FEC is ending: drop its block
13001
+ * mapper and end its FEC release gating. Frames the gate holds are released
13002
+ * in order, each gap stalled (no block will resolve it), not stranded. Call
13003
+ * before FECManager.releasePacket, while the gate can still read its blocks.
13004
+ */
13005
+ releaseFecPacket(packetId) {
13006
+ this.#fecMappersByPacketId.delete(packetId);
13007
+ this.#withDeferredFecPokes(() => {
13008
+ this.#parser.endFecRelease(packetId);
13009
+ for (const [key, poke] of this.#pendingFecPokes) {
13010
+ if (poke.packetId === packetId) this.#pendingFecPokes.delete(key);
13011
+ }
13012
+ });
13013
+ }
12924
13014
  registerManifestTrack(context) {
12925
13015
  if (!this.#manifestVerifier) throw new Error("manifest verifier is not configured");
12926
13016
  this.#manifestVerifier.registerTrack(context);
@@ -13040,87 +13130,21 @@ var MmtpRouter = class {
13040
13130
  this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
13041
13131
  return;
13042
13132
  }
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
- }
13133
+ this.#handleIsoRepair(ownerPacketId, catalogTrackName, parsed, "moq");
13094
13134
  }
13095
13135
  }
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
- }
13136
+ // ── Packet FEC geometry and block profile ───────────────────────
13102
13137
  #symbolSizeForPacket(packetId) {
13103
- if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ?? this.#fecManager?.symbolSize ?? 0;
13138
+ if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ?? 0;
13104
13139
  return this.#fecManager?.symbolSize ?? 0;
13105
13140
  }
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
13141
  /**
13119
13142
  * The configured block profile of a packetId, or undefined while it is
13120
13143
  * unconfigured. With an FEC manager its per-packet configuration is the
13121
13144
  * only source. A router without one still recovers symbols through its
13122
13145
  * registered mapper, built with the catalog's explicit profile: the
13123
- * packet's own mapper, else the default one (the #deriveSbnEsi rule).
13146
+ * packet's own mapper, else the default one. A NullBlockMapper (a stream
13147
+ * without an FEC instance) states none.
13124
13148
  */
13125
13149
  #configuredBlockProfile(packetId) {
13126
13150
  if (this.#fecManager) return this.#fecManager.blockProfileForPacket(packetId);
@@ -13128,9 +13152,10 @@ var MmtpRouter = class {
13128
13152
  return mapper?.blockProfile ?? void 0;
13129
13153
  }
13130
13154
  /**
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.
13155
+ * The block profile an FEC coordinate of `packetId` is read under:
13156
+ * `iso-ssbg1-v1`, the only one (`k-padded-v1` is retired and refused).
13157
+ * Throws while none is configured: such a packet has no FEC geometry, and
13158
+ * nothing is inferred from the wire or defaulted.
13134
13159
  * Packet ingress leaves such a packet's FEC Payload ID unread first
13135
13160
  * (#noteUnreadableFecPayloadId); a throw here is a wiring fault on a path
13136
13161
  * that cannot hold an unconfigured packet.
@@ -13144,8 +13169,16 @@ var MmtpRouter = class {
13144
13169
  }
13145
13170
  return profile;
13146
13171
  }
13147
- #isIsoPacket(packetId) {
13148
- return this.#blockProfileFor(packetId) === "iso-ssbg1-v1";
13172
+ /**
13173
+ * The block coordinates (SS_Start, ESI) of an FEC source: range membership
13174
+ * in a block a repair already stated (ISO/IEC 23008-1 C.5.3). Null while no
13175
+ * repair has stated its block, while its packetId has no configured block
13176
+ * profile, and without an FEC manager: SS_ID alone locates no block, so
13177
+ * nothing is derived from it.
13178
+ */
13179
+ #locateFecSource(packetId, catalogTrackName, ssId) {
13180
+ if (this.#configuredBlockProfile(packetId) === void 0) return null;
13181
+ return this.#fecManager?.locateIsoSource?.(packetId, catalogTrackName, ssId) ?? null;
13149
13182
  }
13150
13183
  /**
13151
13184
  * Explicit refusal to read an FEC Payload ID: its (owner) packetId has no
@@ -13164,21 +13197,20 @@ var MmtpRouter = class {
13164
13197
  }
13165
13198
  }
13166
13199
  /**
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.
13200
+ * The release-gate anchor of a recovered symbol: its own SS_ID, SS_Start +
13201
+ * ESI modulo 2^32 (C.5.2), where a frame is gated by the SS_IDs between it
13202
+ * and the last released frame.
13170
13203
  */
13171
- #fecAnchorForRecovered(ownerPacketId, blockId, index) {
13172
- return ownerPacketId !== void 0 && this.#isIsoPacket(ownerPacketId) ? serial32Add(blockId, index) : blockId;
13204
+ #fecAnchorForRecovered(blockStart, index) {
13205
+ return serial32Add(blockStart, index);
13173
13206
  }
13174
13207
  /**
13175
13208
  * One iso-ssbg1-v1 repair symbol (ISO/IEC 23008-1 C.5.3). It states the
13176
13209
  * block `[SS_Start, SS_Start + SSB_length)`; repair ESI = SSB_length +
13177
13210
  * RS_ID (K' = SSB_length). The held sources it placed (and those of a
13178
13211
  * 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.
13212
+ * the repair, the order they had on the wire, so a loss-free block completes
13213
+ * clean on its repair. A repair contradicting a known block is dropped.
13182
13214
  */
13183
13215
  #handleIsoRepair(ownerPacketId, catalogTrackName, repair, source) {
13184
13216
  const fec = this.#fecManager;
@@ -13270,7 +13302,8 @@ var MmtpRouter = class {
13270
13302
  return null;
13271
13303
  }
13272
13304
  /** Route a recovered FEC block through the parser + dedup pipeline.
13273
- * Sync WASM decoders return K fixed-width source symbols concatenated. */
13305
+ * Sync WASM decoders return the block's K' = SSB_length fixed-width
13306
+ * ssbg_mode1 source symbols concatenated. */
13274
13307
  routeRecoveredBlock(blockId, data, packetId, catalogTrackName) {
13275
13308
  this.#routeRecoveredBlock(blockId, data, packetId, catalogTrackName);
13276
13309
  }
@@ -13738,29 +13771,19 @@ var MmtpRouter = class {
13738
13771
  resolvedLoss: (packetId, catalogTrackName, fromSbn, toSbn, releasedSbn) => {
13739
13772
  const fec = this.#fecManager;
13740
13773
  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;
13774
+ const head = fromSbn === releasedSbn ? toSbn : fromSbn;
13775
+ return fec.isoSpanResolution?.(packetId, catalogTrackName, releasedSbn, head) ?? null;
13759
13776
  },
13760
13777
  // The retention window itself: a frame is never still held when a block
13761
13778
  // it waits on retires. Only consulted while resolvedLoss holds a gap,
13762
13779
  // which needs blockResolution, so the check above guarantees the method.
13763
- holdLimitMs: (packetId) => this.#fecManager?.terminalRetentionMsForPacket?.(packetId) ?? Infinity,
13780
+ // A packetId the manager no longer has configured (released) has no
13781
+ // block left to wait on: release its frames now (BLO-45953).
13782
+ holdLimitMs: (packetId) => {
13783
+ const fec = this.#fecManager;
13784
+ if (!fec?.terminalRetentionMsForPacket) return Infinity;
13785
+ return fec.terminalRetentionMsForPacket(packetId) ?? 0;
13786
+ },
13764
13787
  decide: (before, after, lostBlocks, discontinuity) => {
13765
13788
  const decision = this.#decideGap(before, after, lostBlocks, discontinuity);
13766
13789
  this.#onFrameGap?.(decision);
@@ -13822,9 +13845,10 @@ var MmtpRouter = class {
13822
13845
  }
13823
13846
  /**
13824
13847
  * 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.
13848
+ * FEC blocks between `before`'s anchor and `after`'s, times D, the media
13849
+ * units one block touches (ceil(interleaveDepthMs / unit duration), §5.1).
13850
+ * Null when either anchor, the unit duration or the catalog interleave is
13851
+ * unknown.
13828
13852
  */
13829
13853
  #maxMissingUnits(before, after) {
13830
13854
  const from = before.fecSbn;
@@ -13837,7 +13861,7 @@ var MmtpRouter = class {
13837
13861
  const denominator = BigInt(timing.durationTicks) * 1000n;
13838
13862
  const unitsPerBlock = (numerator + denominator - 1n) / denominator;
13839
13863
  const blocks = BigInt(
13840
- (this.#isIsoPacket(after.packetId) ? serial32Precedes(from, to) ? serial32Distance(from, to) : serial32Distance(to, from) : Math.abs(to - from)) + 1
13864
+ (serial32Precedes(from, to) ? serial32Distance(from, to) : serial32Distance(to, from)) + 1
13841
13865
  );
13842
13866
  const bound = unitsPerBlock * blocks;
13843
13867
  return bound > BigInt(Number.MAX_SAFE_INTEGER) ? null : Number(bound);
@@ -13887,8 +13911,9 @@ var MmtpRouter = class {
13887
13911
  const cb = this.#trackInitCallbacks.get(packetId);
13888
13912
  if (cb) {
13889
13913
  cb(data);
13890
- } else if (this.#onInitSegment) {
13891
- this.#onInitSegment(data);
13914
+ } else {
13915
+ this.#unmappedInitDeliveries++;
13916
+ this.#onInitSegment?.(data);
13892
13917
  }
13893
13918
  console.log(
13894
13919
  `[MmtpRouter] Init delivered for packetId=${packetId}${catalogTrackName === void 0 ? "" : ` track=${catalogTrackName}`} (${data.byteLength}B)`
@@ -13930,9 +13955,9 @@ var MmtpRouter = class {
13930
13955
  if (!info.isRepair && this.#manifestVerifier && !manifestAdmitted) {
13931
13956
  let identity = manifestIdentity;
13932
13957
  if (!identity && info.fecPayloadId && !fecUnreadable && (this.#resolveManifestIdentityForRoute || this.#resolveManifestIdentity)) {
13933
- const coordinates = this.#deriveSbnEsi(info.fecPayloadId.ssId, info.packetId);
13934
13958
  const catalogTrackName = catalogTrackNameForPacket(ingress, info.packetId);
13935
- identity = (this.#resolveManifestIdentityForRoute ? this.#resolveManifestIdentityForRoute({
13959
+ const coordinates = this.#locateFecSource(info.packetId, catalogTrackName, info.fecPayloadId.ssId);
13960
+ identity = coordinates === null ? void 0 : (this.#resolveManifestIdentityForRoute ? this.#resolveManifestIdentityForRoute({
13936
13961
  packetId: info.packetId,
13937
13962
  sbn: coordinates.sbn,
13938
13963
  esi: coordinates.esi,
@@ -13984,79 +14009,7 @@ var MmtpRouter = class {
13984
14009
  this.#noteUnreadableFecPayloadId(ownerPacketId, "repair");
13985
14010
  return;
13986
14011
  }
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
- }
14012
+ this.#handleIsoRepair(ownerPacketId, catalogTrackNameForPacket(ingress, ownerPacketId), repair, source);
14060
14013
  }
14061
14014
  } else {
14062
14015
  if (this.#classifyPacketId(info.packetId) === null) return;
@@ -14080,8 +14033,8 @@ var MmtpRouter = class {
14080
14033
  }
14081
14034
  const duplicates = this.#stream(info.packetId, catalogTrackName).duplicates;
14082
14035
  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;
14036
+ const fecSource = this.#fecManager !== null && fecPayloadId != null;
14037
+ const coords = fecSource && fecPayloadId ? this.#locateFecSource(info.packetId, catalogTrackName, fecPayloadId.ssId) ?? void 0 : void 0;
14085
14038
  duplicates.advance(coords?.sbn);
14086
14039
  if (source === "multicast" && !overlapCopy && !releasedFromQuarantine && catalogTrackName !== void 0 && duplicates.predatesHorizon(info.sequenceNumber, fecPayloadId?.ssId)) {
14087
14040
  this.#publisherRestarts++;
@@ -14096,9 +14049,8 @@ var MmtpRouter = class {
14096
14049
  }
14097
14050
  if (this.#fecManager && fecPayloadId && coords) {
14098
14051
  const fecId = fecPayloadId;
14099
- const symbolSize = this.#symbolSizeForPacket(info.packetId);
14100
14052
  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;
14053
+ const oversized = !(this.#fecManager.isoSourceFits?.(info.packetId, rawSymbol.length) ?? false);
14102
14054
  let overlapBlockCompleted = false;
14103
14055
  if (overlapCopy) {
14104
14056
  overlapBlockCompleted = catalogTrackName === void 0 ? this.#fecManager.isBlockCompleted(coords.sbn, info.packetId) : this.#fecManager.isBlockCompleted(
@@ -14133,7 +14085,7 @@ var MmtpRouter = class {
14133
14085
  }
14134
14086
  parseSourceSymbol = (overlapCopy ? !overlapBlockCompleted : sourceSymbolAccepted) && !duplicates.parsedSymbols.has(fecId.ssId);
14135
14087
  fecInfo = { sbn: coords.sbn, esi: coords.esi, oversized };
14136
- } else if (isoSource && this.#fecManager && fecPayloadId) {
14088
+ } else if (fecSource && this.#fecManager && fecPayloadId) {
14137
14089
  const ssId = fecPayloadId.ssId;
14138
14090
  if (overlapCopy) {
14139
14091
  parseSourceSymbol = !duplicates.parsedSymbols.has(ssId);
@@ -14167,12 +14119,13 @@ var MmtpRouter = class {
14167
14119
  this.#feedViaParser(packet, info, fecInfo, {
14168
14120
  source,
14169
14121
  catalogTrackName,
14170
- ...isoSource && info.fecPayloadId ? { fecSbn: info.fecPayloadId.ssId } : fecInfo === void 0 ? {} : { fecSbn: fecInfo.sbn }
14122
+ // An FEC source frame is anchored by its own SS_ID (C.5.2).
14123
+ ...fecSource && fecPayloadId ? { fecSbn: fecPayloadId.ssId } : {}
14171
14124
  }, parsed);
14172
14125
  }
14173
14126
  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);
14127
+ if (this.#fecManager && fecPayloadId && coords && sourceSymbolAccepted && sourceSymbolTrackable) {
14128
+ const { sbn, esi } = coords;
14176
14129
  if (this.#classifyPacketId(info.packetId) !== null && this.#fecManager.isTrackConfigured?.(info.packetId) !== false) {
14177
14130
  this.#fecManager.trackBlock(
14178
14131
  sbn,
@@ -14606,7 +14559,6 @@ var MmtpRouter = class {
14606
14559
  // ── Private: FEC recovery routing ───────────────────────────────
14607
14560
  /** Route WASM-recovered block through appropriate path based on container kind. */
14608
14561
  #routeRecoveredBlock(blockId, recovered, ownerPacketId, catalogTrackName) {
14609
- const k = this.#kForPacket(ownerPacketId);
14610
14562
  const packetSymbolSize = this.#symbolSizeForPacket(ownerPacketId);
14611
14563
  if (ownerPacketId === void 0) {
14612
14564
  throw new Error("[MmtpRouter] a recovered FEC block needs its owner packetId to read its fec.blockProfile");
@@ -14619,7 +14571,6 @@ var MmtpRouter = class {
14619
14571
  }
14620
14572
  const symbolSize = packetSymbolSize;
14621
14573
  const result = processFecBlock(recovered, {
14622
- ...blockProfile === "k-padded-v1" ? { k } : {},
14623
14574
  symbolSize,
14624
14575
  ownerPacketId,
14625
14576
  blockProfile
@@ -14699,8 +14650,7 @@ var MmtpRouter = class {
14699
14650
  const admission = verifier.admitObject(
14700
14651
  {
14701
14652
  ...identity,
14702
- packetId: ownerPacketId,
14703
- ...ownerPacketId !== void 0 && this.#isIsoPacket(ownerPacketId) ? {} : { ssId: blockId * this.#kForPacket(ownerPacketId) + symbol.index }
14653
+ packetId: ownerPacketId
14704
14654
  },
14705
14655
  symbol.rawSymbol,
14706
14656
  () => {
@@ -14803,7 +14753,7 @@ var MmtpRouter = class {
14803
14753
  rapFlag: sym.fragment.rapFlag,
14804
14754
  recovered: true,
14805
14755
  deliveryProvenance,
14806
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
14756
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
14807
14757
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
14808
14758
  });
14809
14759
  continue;
@@ -14915,7 +14865,7 @@ var MmtpRouter = class {
14915
14865
  rapFlag: assembled.rapFlag,
14916
14866
  recovered: true,
14917
14867
  ...assembled.deliveryProvenance === void 0 ? {} : { deliveryProvenance: assembled.deliveryProvenance },
14918
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
14868
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
14919
14869
  ...recoveredCatalogTrackName === void 0 ? {} : { catalogTrackName: recoveredCatalogTrackName }
14920
14870
  });
14921
14871
  continue;
@@ -15052,7 +15002,7 @@ var MmtpRouter = class {
15052
15002
  rapFlag: sym.fragment.rapFlag,
15053
15003
  recovered: true,
15054
15004
  deliveryProvenance,
15055
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
15005
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
15056
15006
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
15057
15007
  });
15058
15008
  } else {
@@ -15131,7 +15081,7 @@ var MmtpRouter = class {
15131
15081
  rapFlag: assembled.rapFlag,
15132
15082
  recovered: true,
15133
15083
  deliveryProvenance,
15134
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
15084
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
15135
15085
  ...assembled.catalogTrackName === void 0 ? {} : { catalogTrackName: assembled.catalogTrackName }
15136
15086
  });
15137
15087
  }
@@ -15166,7 +15116,7 @@ var MmtpRouter = class {
15166
15116
  ...sym.fragment.sampleNumber === void 0 ? {} : { sampleNumber: sym.fragment.sampleNumber },
15167
15117
  recovered: true,
15168
15118
  deliveryProvenance,
15169
- fecSbn: this.#fecAnchorForRecovered(ownerPacketId, blockId, sym.index),
15119
+ fecSbn: this.#fecAnchorForRecovered(blockId, sym.index),
15170
15120
  ...catalogTrackName === void 0 ? {} : { catalogTrackName }
15171
15121
  });
15172
15122
  continue;
@@ -15217,22 +15167,22 @@ var MoqFecAlgorithm;
15217
15167
  MoqFecAlgorithm2[MoqFecAlgorithm2["RaptorQ"] = 2] = "RaptorQ";
15218
15168
  })(MoqFecAlgorithm || (MoqFecAlgorithm = {}));
15219
15169
 
15220
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-alta-parse/dist/index.js
15170
+ // node_modules/@blockcast/mmt-alta-parse/dist/index.js
15221
15171
  var MPU_HEADER_SIZE3 = 8;
15222
15172
  var MPU_LENGTH_FIELD_SIZE3 = 2;
15223
15173
  var MPU_HEADER_REMAINDER_SIZE3 = MPU_HEADER_SIZE3 - MPU_LENGTH_FIELD_SIZE3;
15224
15174
 
15225
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/mmtp.js
15175
+ // node_modules/@blockcast/mmt-base/dist/mmtp.js
15226
15176
  var MPU_HEADER_SIZE4 = 8;
15227
15177
  var MPU_LENGTH_FIELD_SIZE4 = 2;
15228
15178
  var MPU_HEADER_REMAINDER_SIZE4 = MPU_HEADER_SIZE4 - MPU_LENGTH_FIELD_SIZE4;
15229
15179
  var MMTP_SIGNALING_PACKET_ID2 = 0;
15230
15180
  var MMTP_PACKET_ID_MAX2 = 65535;
15231
15181
 
15232
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/net-constants.js
15182
+ // node_modules/@blockcast/mmt-base/dist/net-constants.js
15233
15183
  var DEFAULT_AMT_RELAY_PORT2 = 2268;
15234
15184
 
15235
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/ntp-short.js
15185
+ // node_modules/@blockcast/mmt-base/dist/ntp-short.js
15236
15186
  var NTP_SHORT_BITS2 = 32;
15237
15187
  var NTP_SHORT_MODULUS2 = 2 ** NTP_SHORT_BITS2;
15238
15188
  var NTP_SHORT_HALF_RANGE2 = 2 ** (NTP_SHORT_BITS2 - 1);
@@ -15253,9 +15203,8 @@ function compareNtpShort(a, b) {
15253
15203
  return "unordered";
15254
15204
  }
15255
15205
 
15256
- // node_modules/@blockcast/mmt-transport/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js
15206
+ // node_modules/@blockcast/mmt-base/dist/fec-block-profile.js
15257
15207
  var FEC_BLOCK_PROFILES2 = Object.freeze([
15258
- "k-padded-v1",
15259
15208
  "iso-ssbg1-v1"
15260
15209
  ]);
15261
15210
 
@@ -15647,10 +15596,12 @@ var MulticastOrchestrator = class {
15647
15596
  }
15648
15597
  onGoodput(sample) {
15649
15598
  const bitrate = sample.bitrate;
15650
- if (!this.#current || this.options.adaptive === false)
15599
+ if (!this.#current)
15651
15600
  return void 0;
15652
15601
  if (this.#state === "joining")
15653
15602
  return this.poll();
15603
+ if (this.options.adaptive === false)
15604
+ return void 0;
15654
15605
  if (bitrate === void 0 || !Number.isFinite(bitrate))
15655
15606
  return void 0;
15656
15607
  this.#estimate = bitrate;
@@ -15717,6 +15668,27 @@ var MulticastOrchestrator = class {
15717
15668
  this.#favored = void 0;
15718
15669
  return { kind: "join-target", current: this.#current, target: desired };
15719
15670
  }
15671
+ /**
15672
+ * Join `target` because the viewer selected it, not because goodput or
15673
+ * headroom ranked it. It runs whether or not the orchestrator adapts, and
15674
+ * takes the path an adaptive join takes: commit on the target's RAP at or
15675
+ * beyond the high-water mark, abandon at the join deadline. While it is
15676
+ * pending nothing re-ranks the ladder; goodput only advances the deadline.
15677
+ *
15678
+ * Returns undefined, changing nothing, while another join is pending (cancel
15679
+ * it first), when `target` is the current tier, or when it carries no media.
15680
+ */
15681
+ select(target) {
15682
+ if (!this.#current || this.#state === "joining" || !carriesMedia(target))
15683
+ return void 0;
15684
+ if (endpointKey(target) === endpointKey(this.#current))
15685
+ return void 0;
15686
+ this.#state = "joining";
15687
+ this.#target = target;
15688
+ this.#joinStartedAt = this.#now();
15689
+ this.#favored = void 0;
15690
+ return { kind: "join-target", current: this.#current, target };
15691
+ }
15720
15692
  /**
15721
15693
  * Feed a packet from the active tier (to advance the high-water mark) or
15722
15694
  * from the in-flight target (to find a committable RAP).
@@ -16756,7 +16728,7 @@ var MulticastTransport = class _MulticastTransport {
16756
16728
  // inside the window, instead of a clear+set pair on every packet.
16757
16729
  #watchdog = null;
16758
16730
  #lastPacketAtMs = 0;
16759
- #failbackWindowMs = null;
16731
+ #failbackWindowMs;
16760
16732
  #onTakeover = null;
16761
16733
  #onFailback = null;
16762
16734
  // Data-plane-live edge: the first packet this join accepted. Everything
@@ -17061,14 +17033,20 @@ var MulticastTransport = class _MulticastTransport {
17061
17033
  return this.#dataPlaneLive;
17062
17034
  }
17063
17035
  setFailbackWindowMs(windowMs) {
17064
- if (!Number.isFinite(windowMs) || windowMs <= 0) {
17036
+ if (windowMs !== null && (!Number.isFinite(windowMs) || windowMs <= 0)) {
17065
17037
  throw new Error(
17066
17038
  `[MulticastTransport] failbackWindowMs=${windowMs}. Derive a positive failback window from the stream catalog.`
17067
17039
  );
17068
17040
  }
17069
17041
  this.#failbackWindowMs = windowMs;
17070
- if (this.#watchdog !== null) {
17071
- clearTimeout(this.#watchdog);
17042
+ if (windowMs === null) {
17043
+ if (this.#watchdog !== null) clearTimeout(this.#watchdog);
17044
+ this.#watchdog = null;
17045
+ return;
17046
+ }
17047
+ if (this.#watchdog !== null) clearTimeout(this.#watchdog);
17048
+ this.#watchdog = null;
17049
+ if (this.#dataPlaneLive && this.#onFailback) {
17072
17050
  const silentMs = performance.now() - this.#lastPacketAtMs;
17073
17051
  this.#armWatchdog(Math.max(0, Math.ceil(windowMs - silentMs)));
17074
17052
  }
@@ -17394,7 +17372,8 @@ var MulticastTransport = class _MulticastTransport {
17394
17372
  }
17395
17373
  if (this.#watchdog !== null) return;
17396
17374
  if (!this.#onFailback) return;
17397
- if (this.#failbackWindowMs === null) {
17375
+ if (this.#failbackWindowMs === null) return;
17376
+ if (this.#failbackWindowMs === void 0) {
17398
17377
  throw new Error(
17399
17378
  "[MulticastTransport] Failback watchdog requires a catalog-derived window"
17400
17379
  );
@@ -17404,7 +17383,7 @@ var MulticastTransport = class _MulticastTransport {
17404
17383
  #armWatchdog(delayMs) {
17405
17384
  this.#watchdog = setTimeout(() => {
17406
17385
  this.#watchdog = null;
17407
- if (this.#disposed || !this.#onFailback || this.#failbackWindowMs === null) return;
17386
+ if (this.#disposed || !this.#onFailback || this.#failbackWindowMs == null) return;
17408
17387
  const silentMs = performance.now() - this.#lastPacketAtMs;
17409
17388
  if (silentMs < this.#failbackWindowMs) {
17410
17389
  this.#armWatchdog(Math.ceil(this.#failbackWindowMs - silentMs));
@@ -17438,6 +17417,35 @@ var BASE64URL_DECODE_TABLE = (() => {
17438
17417
  return table;
17439
17418
  })();
17440
17419
 
17420
+ // src/repair-layers.ts
17421
+ var RepairContractError = class extends Error {
17422
+ constructor(code, path, message) {
17423
+ super(`[repair-layers:${code}] ${path}: ${message}`);
17424
+ this.code = code;
17425
+ this.path = path;
17426
+ this.name = "RepairContractError";
17427
+ }
17428
+ };
17429
+ var throwRepairContractError = (code, path, message) => {
17430
+ throw new RepairContractError(code, path, message);
17431
+ };
17432
+ function readFecBlockProfile(value, path, declaresFecInstance, fail) {
17433
+ if (value === void 0 && !declaresFecInstance) return void 0;
17434
+ if (isFecBlockProfile(value)) return value;
17435
+ if (typeof value === "string" && value.length > 0) {
17436
+ return fail(
17437
+ "track_fec_block_profile_unimplemented",
17438
+ path,
17439
+ `${JSON.stringify(value)} is not an implemented FEC block profile; only "iso-ssbg1-v1" is implemented`
17440
+ );
17441
+ }
17442
+ try {
17443
+ return parseFecBlockProfile(value, "blockProfile");
17444
+ } catch (error) {
17445
+ return fail("track_fec_block_profile", path, error.message);
17446
+ }
17447
+ }
17448
+
17441
17449
  // src/carp-catalog.ts
17442
17450
  var VALID_ROLE_SUFFIXES = /* @__PURE__ */ new Set(["base", "delta", "repair"]);
17443
17451
  function parseTrackRole(trackName) {
@@ -17477,12 +17485,49 @@ function hasSwitchingSetRole(names) {
17477
17485
  return suffix === "base" || suffix === "delta";
17478
17486
  });
17479
17487
  }
17480
- function isFecCatalogConfig(obj) {
17488
+ function isFecCatalogGeometry(obj) {
17481
17489
  if (typeof obj !== "object" || obj === null) return false;
17482
17490
  const o = obj;
17483
17491
  const algorithm = o["algorithm"];
17484
17492
  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);
17493
+ 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;
17494
+ }
17495
+ function isFecCatalogConfig(obj) {
17496
+ if (!isFecCatalogGeometry(obj)) return false;
17497
+ const o = obj;
17498
+ return isFecBlockProfile(o["blockProfile"]) || o["algorithm"] === "none" && o["blockProfile"] === void 0;
17499
+ }
17500
+
17501
+ // src/catalog-fec-params.ts
17502
+ import { catalogFecInterleaveMs } from "@blockcast/mmt-fec";
17503
+ var MAX_SAFE_TICKS_PER_HZ = Number.MAX_SAFE_INTEGER / 1e3;
17504
+ function recordOf2(value) {
17505
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
17506
+ }
17507
+ function blockProfileUnavailable(value, path, declaresFecInstance) {
17508
+ try {
17509
+ readFecBlockProfile(value, path, declaresFecInstance, throwRepairContractError);
17510
+ return void 0;
17511
+ } catch (error) {
17512
+ if (!(error instanceof RepairContractError) || error.code !== "track_fec_block_profile" && error.code !== "track_fec_block_profile_unimplemented") {
17513
+ throw error;
17514
+ }
17515
+ return Object.freeze({ code: error.code, path: error.path, message: error.message });
17516
+ }
17517
+ }
17518
+ function isoSpanUnavailable(fec, path, declaresFecInstance) {
17519
+ if (!declaresFecInstance || isoBlockSpanUsable(fec)) return void 0;
17520
+ return Object.freeze({
17521
+ code: "track_fec",
17522
+ path,
17523
+ message: `[repair-layers:track_fec] ${path}: must be a non-negative safe integer in milliseconds`
17524
+ });
17525
+ }
17526
+ function catalogFecUnavailable(fec, fecPath) {
17527
+ const block = recordOf2(fec);
17528
+ if (block === void 0) return void 0;
17529
+ const declaresFecInstance = block["algorithm"] !== "none";
17530
+ return blockProfileUnavailable(block["blockProfile"], `${fecPath}.blockProfile`, declaresFecInstance) ?? isoSpanUnavailable(block, `${fecPath}.interleaveDepthMs`, declaresFecInstance);
17486
17531
  }
17487
17532
 
17488
17533
  // src/multicast-manager.ts
@@ -17795,9 +17840,8 @@ function repairTrackNamesBySource(tracks) {
17795
17840
  bySource.set(source, names);
17796
17841
  };
17797
17842
  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
- }
17843
+ const repairTrack = typeof track.fec === "object" && track.fec !== null ? track.fec.repairTrack : void 0;
17844
+ if (typeof repairTrack === "string") add(track.name, repairTrack);
17801
17845
  if (track.packaging === "fec-repair" && Array.isArray(track.depends) && track.depends.length === 1) {
17802
17846
  add(track.depends[0], track.name);
17803
17847
  }
@@ -17807,6 +17851,7 @@ function repairTrackNamesBySource(tracks) {
17807
17851
  var MulticastManager = class {
17808
17852
  #fec;
17809
17853
  #router;
17854
+ #hostMfuTimeoutMs;
17810
17855
  #transport;
17811
17856
  #reportedFecDiagnosticsFailure = false;
17812
17857
  // ── Stream-global FEC timing reduction ──
@@ -17905,6 +17950,7 @@ var MulticastManager = class {
17905
17950
  this.#emitRepairLifecycle(decision);
17906
17951
  }
17907
17952
  });
17953
+ this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
17908
17954
  this.#transport = new MulticastTransport((packet, ingress) => {
17909
17955
  this.#router.feedPacket(packet, "multicast", void 0, ingress);
17910
17956
  });
@@ -18121,7 +18167,6 @@ var MulticastManager = class {
18121
18167
  ...this.#streamFecBaseline ? [this.#streamFecBaseline] : [],
18122
18168
  ...this.#trackFecTiming.values()
18123
18169
  ];
18124
- if (contributions.length === 0) return;
18125
18170
  let deliveryWindowMs;
18126
18171
  let minimumFailbackWindowMs;
18127
18172
  let mfuTimeoutMs;
@@ -18153,15 +18198,9 @@ var MulticastManager = class {
18153
18198
  }
18154
18199
  }
18155
18200
  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
- }
18201
+ this.#transport.setFailbackWindowMs(failbackWindowMs ?? null);
18202
+ this.#router.setMfuTimeoutMs(mfuTimeoutMs ?? this.#hostMfuTimeoutMs);
18203
+ this.#router.setReorderWindow(reorderWindowFrames ?? 0);
18165
18204
  }
18166
18205
  /**
18167
18206
  * Configure one track's FEC geometry, block mapper, and block-inactivity
@@ -18188,9 +18227,9 @@ var MulticastManager = class {
18188
18227
  `[MulticastManager] configureTrackFec(${packetId}) catalogTrackName must be non-empty when given`
18189
18228
  );
18190
18229
  }
18191
- if (config.fecK === void 0 || config.fecInterleaveDepthMs === void 0) {
18230
+ if (config.fecK === void 0) {
18192
18231
  throw new Error(
18193
- `[MulticastManager] configureTrackFec(${packetId}) requires fecK and fecInterleaveDepthMs. Derive the per-track FEC geometry from the stream catalog.`
18232
+ `[MulticastManager] configureTrackFec(${packetId}) requires fecK. Derive the per-track FEC geometry from the stream catalog.`
18194
18233
  );
18195
18234
  }
18196
18235
  const blockProfile = parseFecBlockProfile(
@@ -18198,7 +18237,10 @@ var MulticastManager = class {
18198
18237
  `[MulticastManager] configureTrackFec(${packetId}) fecBlockProfile`
18199
18238
  );
18200
18239
  assertPositiveGeometry("fecK", config.fecK);
18201
- assertPositiveGeometry("fecInterleaveDepthMs", config.fecInterleaveDepthMs);
18240
+ const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
18241
+ if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
18242
+ throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
18243
+ }
18202
18244
  assertPositiveGeometry("fecSymbolSize", config.fecSymbolSize);
18203
18245
  assertPositiveGeometry("fps", config.fps);
18204
18246
  assertPositiveGeometry("bandwidth", config.bandwidth);
@@ -18214,8 +18256,8 @@ var MulticastManager = class {
18214
18256
  "fecReorderToleranceMs",
18215
18257
  config.fecReorderToleranceMs
18216
18258
  );
18217
- const deliveryWindowMs = config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
18218
- interleaveDepthMs: config.fecInterleaveDepthMs,
18259
+ const deliveryWindowMs = interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
18260
+ interleaveDepthMs,
18219
18261
  sourceSymbols: config.fecK,
18220
18262
  repairSymbols: config.fecRepairSymbols,
18221
18263
  symbolSize: config.fecSymbolSize,
@@ -18224,10 +18266,10 @@ var MulticastManager = class {
18224
18266
  jitterBufferMs: config.fecReorderToleranceMs
18225
18267
  }) : void 0;
18226
18268
  const trackKind = config.trackKind ?? this.#router.trackKindFor(packetId);
18227
- const geometry = trackKind === void 0 ? void 0 : deriveBlockClassifierGeometry({
18269
+ const geometry = trackKind === void 0 || interleaveDepthMs === 0 ? void 0 : deriveBlockClassifierGeometry({
18228
18270
  trackKind,
18229
18271
  framerate: config.fps,
18230
- interleaveDepthMs: config.fecInterleaveDepthMs,
18272
+ interleaveDepthMs,
18231
18273
  timescale: config.timescale,
18232
18274
  keyframeIntervalTicks: config.keyframeIntervalTicks,
18233
18275
  keyframeIntervalMs: config.keyframeIntervalMs
@@ -18236,7 +18278,7 @@ var MulticastManager = class {
18236
18278
  blockProfile,
18237
18279
  k: config.fecK,
18238
18280
  symbolSize: config.fecSymbolSize,
18239
- interleaveDepthMs: config.fecInterleaveDepthMs,
18281
+ interleaveDepthMs,
18240
18282
  fps: config.fps,
18241
18283
  timeoutMs: deliveryWindowMs,
18242
18284
  ...trackKind === void 0 ? {} : { trackKind },
@@ -18247,7 +18289,7 @@ var MulticastManager = class {
18247
18289
  this.#configureTrackMapper(packetId, fecConfig);
18248
18290
  this.#trackFecTiming.set(packetId, {
18249
18291
  deliveryWindowMs,
18250
- interleaveDepthMs: config.fecInterleaveDepthMs,
18292
+ interleaveDepthMs,
18251
18293
  fps: config.fps,
18252
18294
  k: config.fecK,
18253
18295
  repairSymbols: config.fecRepairSymbols,
@@ -18256,6 +18298,10 @@ var MulticastManager = class {
18256
18298
  this.#applyStreamGlobalFecTiming();
18257
18299
  }
18258
18300
  configureFec(config) {
18301
+ const interleaveDepthMs = config.fecInterleaveDepthMs ?? 0;
18302
+ if (!Number.isSafeInteger(interleaveDepthMs) || interleaveDepthMs < 0) {
18303
+ throw new Error("[MulticastManager] fecInterleaveDepthMs must be a non-negative safe integer");
18304
+ }
18259
18305
  const minimumFailbackWindowMs = config.minimumFailbackWindowMs;
18260
18306
  if (minimumFailbackWindowMs !== void 0 && (!Number.isFinite(minimumFailbackWindowMs) || minimumFailbackWindowMs <= 0)) {
18261
18307
  throw new Error(
@@ -18267,7 +18313,7 @@ var MulticastManager = class {
18267
18313
  config.fecReorderToleranceMs
18268
18314
  );
18269
18315
  const deliveryWindowMs = config.fecK && config.fecK > 0 && config.fecInterleaveDepthMs && config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS ? computeFecDeliveryWindowMs({
18270
- interleaveDepthMs: config.fecInterleaveDepthMs,
18316
+ interleaveDepthMs,
18271
18317
  sourceSymbols: config.fecK,
18272
18318
  repairSymbols: config.fecRepairSymbols,
18273
18319
  symbolSize: config.fecSymbolSize,
@@ -18278,13 +18324,13 @@ var MulticastManager = class {
18278
18324
  this.#fec.configure({
18279
18325
  k: config.fecK,
18280
18326
  symbolSize: config.fecSymbolSize,
18281
- interleaveDepthMs: config.fecInterleaveDepthMs,
18327
+ interleaveDepthMs,
18282
18328
  fps: config.fps,
18283
18329
  timeoutMs: deliveryWindowMs
18284
18330
  });
18285
18331
  this.#streamFecBaseline = {
18286
18332
  deliveryWindowMs,
18287
- interleaveDepthMs: config.fecInterleaveDepthMs,
18333
+ interleaveDepthMs,
18288
18334
  fps: config.fps,
18289
18335
  k: config.fecK,
18290
18336
  repairSymbols: config.fecRepairSymbols,
@@ -18333,6 +18379,7 @@ var MulticastManager = class {
18333
18379
  }
18334
18380
  setMfuTimeoutMs(ms) {
18335
18381
  this.#router.setMfuTimeoutMs(ms);
18382
+ this.#hostMfuTimeoutMs = this.#router.reassembler.timeoutMs;
18336
18383
  }
18337
18384
  setTrackCodec(packetId, codec) {
18338
18385
  this.#router.setTrackCodec(packetId, codec);
@@ -18355,6 +18402,7 @@ var MulticastManager = class {
18355
18402
  onTrackPacket(packetId, cb) {
18356
18403
  this.#router.onTrackPacket(packetId, cb);
18357
18404
  }
18405
+ /** @deprecated Use onTrackInit(packetId, cb); see MmtpRouter.onInitSegment. */
18358
18406
  onInitSegment(cb) {
18359
18407
  this.#router.onInitSegment(cb);
18360
18408
  }
@@ -18666,7 +18714,10 @@ var MulticastManager = class {
18666
18714
  console.warn("[MulticastManager] rendition switch onCancel failed", error);
18667
18715
  }
18668
18716
  }
18669
- /** Release what the manager and FEC hold for a stream whose packet ID the router retired. */
18717
+ /**
18718
+ * Release what the manager and FEC hold for a stream whose packet ID the
18719
+ * router retired, or whose FEC a catalog reconfiguration no longer configures.
18720
+ */
18670
18721
  #releaseRetiredStream(stream) {
18671
18722
  for (const observer of this.#trackResetObservers) {
18672
18723
  try {
@@ -18685,7 +18736,10 @@ var MulticastManager = class {
18685
18736
  for (const [key, stats] of [...this.#blockPolicyStats]) {
18686
18737
  if (stats.packetId === stream.packetId) this.#blockPolicyStats.delete(key);
18687
18738
  }
18688
- this.#trackFecTiming.delete(stream.packetId);
18739
+ if (this.#trackFecTiming.delete(stream.packetId)) {
18740
+ this.#applyStreamGlobalFecTiming();
18741
+ }
18742
+ this.#multiTrackFecTracks.delete(stream.packetId);
18689
18743
  this.#fec.releasePacket(stream.packetId);
18690
18744
  }
18691
18745
  /** Feed a raw packet from an external source (AMT tunnel, ASM subscription).
@@ -18730,12 +18784,53 @@ var MulticastManager = class {
18730
18784
  * D-17: Builds altGroup → track-name index for getSwitchingSet().
18731
18785
  * D-26: Registers packetId → TrackRole on MmtpRouter for block-complete fanout.
18732
18786
  *
18787
+ * FEC availability is per track (draft-ramadan-moq-fec §5.1). A media track
18788
+ * whose `fec` names no implemented `blockProfile` (only `iso-ssbg1-v1` is
18789
+ * implemented; the retired `k-padded-v1` is not), or an `iso-ssbg1-v1` span
18790
+ * it cannot use, is consumed without FEC: it gets no decoder, and none of
18791
+ * its repair tracks (layer 0 by `fec.repairTrack`, further layers and the
18792
+ * overlay by `depends`) is given an owner, so the router drops their packets
18793
+ * before any decoder. Those legs keep the `repair` role and their owner's
18794
+ * switching set. Neither the call nor any other track's FEC fails on that
18795
+ * account; `trackFecUnavailable(name)` reports the reason. FEC a previous
18796
+ * call configured on a packetId this call does not is released
18797
+ * (FECManager.releasePacket); frames its release gate held are delivered in
18798
+ * order, each gap stalled. FEC wired by hand (setFecDecoder) is left alone.
18799
+ * Geometry a decoder could not configure (K, P, T, `repairTrack`,
18800
+ * algorithm) still throws.
18801
+ *
18733
18802
  * @param catalog - Multi-track catalog (tracks array with name, altGroup, fec, packetId).
18734
18803
  * @param wasmDecoderSource - One WASM FEC decoder for a single FEC stream, or
18735
18804
  * a factory returning a distinct decoder per stream.
18736
18805
  */
18737
18806
  configureMultiTrack(catalog, wasmDecoderSource) {
18738
18807
  const repairNamesBySource = repairTrackNamesBySource(catalog.tracks);
18808
+ const declaredRepairNames = /* @__PURE__ */ new Set();
18809
+ for (const names of repairNamesBySource.values()) {
18810
+ for (const name of names) declaredRepairNames.add(name);
18811
+ }
18812
+ const fecUnavailableByTrack = /* @__PURE__ */ new Map();
18813
+ for (let i = 0; i < catalog.tracks.length; i++) {
18814
+ const track = catalog.tracks[i];
18815
+ if (track.fec === void 0 || track.packaging === "fec-repair" || declaredRepairNames.has(track.name)) {
18816
+ continue;
18817
+ }
18818
+ const fecUnavailable = catalogFecUnavailable(track.fec, `$.tracks[${i}].fec`);
18819
+ if (fecUnavailable === void 0) continue;
18820
+ fecUnavailableByTrack.set(track.name, fecUnavailable);
18821
+ const layerZero = track.fec.repairTrack;
18822
+ if (typeof layerZero === "string" && layerZero.length > 0) {
18823
+ const names = repairNamesBySource.get(track.name) ?? /* @__PURE__ */ new Set();
18824
+ names.add(layerZero);
18825
+ repairNamesBySource.set(track.name, names);
18826
+ }
18827
+ }
18828
+ const unownedRepairNames = /* @__PURE__ */ new Set();
18829
+ for (const sourceName of fecUnavailableByTrack.keys()) {
18830
+ for (const repairName of repairNamesBySource.get(sourceName) ?? []) {
18831
+ unownedRepairNames.add(repairName);
18832
+ }
18833
+ }
18739
18834
  const repairOwnerGroup = /* @__PURE__ */ new Map();
18740
18835
  const repairOwnerAltGroup = /* @__PURE__ */ new Map();
18741
18836
  for (const track of catalog.tracks) {
@@ -18788,11 +18883,13 @@ var MulticastManager = class {
18788
18883
  for (let i = 0; i < catalog.tracks.length; i++) {
18789
18884
  const track = catalog.tracks[i];
18790
18885
  if (resolvedRoles[i] === "repair" || track.fec === void 0) continue;
18791
- if (!isFecCatalogConfig(track.fec)) {
18886
+ const unavailable = fecUnavailableByTrack.has(track.name);
18887
+ if (!(unavailable ? isFecCatalogGeometry(track.fec) : isFecCatalogConfig(track.fec))) {
18792
18888
  throw new Error(
18793
18889
  `[MulticastManager] invalid fec config for track '${track.name}'`
18794
18890
  );
18795
18891
  }
18892
+ if (unavailable) continue;
18796
18893
  if (track.packetId !== void 0) fecTrackIndexes.push(i);
18797
18894
  }
18798
18895
  const fecDecoders = /* @__PURE__ */ new Map();
@@ -18821,6 +18918,20 @@ var MulticastManager = class {
18821
18918
  this.#switchingSets.clear();
18822
18919
  this.#repairPacketIds.clear();
18823
18920
  this.#router.clearRepairPacketIdOwners();
18921
+ const fecPacketIds = new Set([...fecDecoders.keys()].map((i) => catalog.tracks[i].packetId));
18922
+ for (const [packetId, catalogTrackName] of this.#multiTrackFecTracks) {
18923
+ if (fecPacketIds.has(packetId)) continue;
18924
+ if (!this.#fec.isTrackConfigured(packetId)) {
18925
+ this.#multiTrackFecTracks.delete(packetId);
18926
+ continue;
18927
+ }
18928
+ this.#router.releaseFecPacket(packetId);
18929
+ this.#releaseRetiredStream({ packetId, catalogTrackName });
18930
+ }
18931
+ this.#fecUnavailableByTrack.clear();
18932
+ for (const [name, reason] of fecUnavailableByTrack) {
18933
+ this.#fecUnavailableByTrack.set(name, reason);
18934
+ }
18824
18935
  const catalogTracksByName = new Map(
18825
18936
  catalog.tracks.map((track) => [track.name, track])
18826
18937
  );
@@ -18842,10 +18953,11 @@ var MulticastManager = class {
18842
18953
  }
18843
18954
  continue;
18844
18955
  }
18845
- if (track.fec !== void 0) {
18956
+ if (track.fec !== void 0 && !fecUnavailableByTrack.has(track.name)) {
18846
18957
  const fecCfg = track.fec;
18847
18958
  if (track.packetId !== void 0) {
18848
18959
  for (const repairName of repairNamesBySource.get(track.name) ?? []) {
18960
+ if (unownedRepairNames.has(repairName)) continue;
18849
18961
  const repairTrack = catalogTracksByName.get(repairName);
18850
18962
  if (repairTrack?.packetId !== void 0) {
18851
18963
  this.#router.setRepairPacketIdOwner(repairTrack.packetId, track.packetId);
@@ -18866,17 +18978,18 @@ var MulticastManager = class {
18866
18978
  blockProfile,
18867
18979
  k: fecCfg.sourceSymbols,
18868
18980
  symbolSize: fecCfg.symbolSize,
18869
- interleaveDepthMs: fecCfg.interleaveDepthMs,
18981
+ interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
18870
18982
  fps: track.framerate,
18871
18983
  trackKind: "video"
18872
18984
  },
18873
18985
  track.name
18874
18986
  );
18987
+ this.#multiTrackFecTracks.set(track.packetId, track.name);
18875
18988
  this.#configureTrackMapper(track.packetId, {
18876
18989
  blockProfile,
18877
18990
  k: fecCfg.sourceSymbols,
18878
18991
  symbolSize: fecCfg.symbolSize,
18879
- interleaveDepthMs: fecCfg.interleaveDepthMs,
18992
+ interleaveDepthMs: fecCfg.interleaveDepthMs ?? 0,
18880
18993
  fps: track.framerate
18881
18994
  });
18882
18995
  this.#router.setPacketIdType(track.packetId, "video");
@@ -18884,6 +18997,24 @@ var MulticastManager = class {
18884
18997
  }
18885
18998
  }
18886
18999
  }
19000
+ /**
19001
+ * Why `configureMultiTrack` consumes the named catalog track without FEC
19002
+ * although its `fec` block declares one (draft-ramadan-moq-fec §5.1): no
19003
+ * implemented `blockProfile` (`track_fec_block_profile_unimplemented`, e.g.
19004
+ * the retired `k-padded-v1`; `track_fec_block_profile` when absent), or an
19005
+ * invalid `iso-ssbg1-v1` span (`track_fec`). Absent/zero spans keep in-band
19006
+ * decoding enabled. The same reason the hang
19007
+ * resolver reports (`catalogFecUnavailable`). Undefined for every other
19008
+ * track, and for every track before the first call; each call replaces the
19009
+ * previous call's answers.
19010
+ */
19011
+ trackFecUnavailable(trackName) {
19012
+ return this.#fecUnavailableByTrack.get(trackName);
19013
+ }
19014
+ /** Per-track FEC availability from the last `configureMultiTrack` call. */
19015
+ #fecUnavailableByTrack = /* @__PURE__ */ new Map();
19016
+ /** packetId → catalog track name of the FEC the last `configureMultiTrack` installed. */
19017
+ #multiTrackFecTracks = /* @__PURE__ */ new Map();
18887
19018
  /**
18888
19019
  * Return the track names belonging to the given altGroup switching set.
18889
19020
  * D-17: altGroup groups base+delta+repair into one switching set.