@blockcast/mmt-container 0.1.0-main.149f78a4adae

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 (678) hide show
  1. package/README.md +432 -0
  2. package/dist/.build-stamp +0 -0
  3. package/dist/__tests__/test-utils.d.ts +71 -0
  4. package/dist/__tests__/test-utils.d.ts.map +1 -0
  5. package/dist/align.d.ts +148 -0
  6. package/dist/align.d.ts.map +1 -0
  7. package/dist/align.js +88 -0
  8. package/dist/align.js.map +7 -0
  9. package/dist/audio-init-parser.d.ts +66 -0
  10. package/dist/audio-init-parser.d.ts.map +1 -0
  11. package/dist/audio-init-parser.js +118 -0
  12. package/dist/audio-init-parser.js.map +7 -0
  13. package/dist/audio-ring-buffer.d.ts +50 -0
  14. package/dist/audio-ring-buffer.d.ts.map +1 -0
  15. package/dist/audio-ring-buffer.js +93 -0
  16. package/dist/audio-ring-buffer.js.map +7 -0
  17. package/dist/authored-timing.d.ts +32 -0
  18. package/dist/authored-timing.d.ts.map +1 -0
  19. package/dist/authored-timing.js +32 -0
  20. package/dist/authored-timing.js.map +7 -0
  21. package/dist/browser-runtime.js +17109 -0
  22. package/dist/browser-runtime.js.map +7 -0
  23. package/dist/carp-catalog.d.ts +318 -0
  24. package/dist/carp-catalog.d.ts.map +1 -0
  25. package/dist/carp-catalog.js +549 -0
  26. package/dist/carp-catalog.js.map +7 -0
  27. package/dist/catalog-fec-params.d.ts +82 -0
  28. package/dist/catalog-fec-params.d.ts.map +1 -0
  29. package/dist/catalog-fec-params.js +66 -0
  30. package/dist/catalog-fec-params.js.map +7 -0
  31. package/dist/catalog-network-source.d.ts +19 -0
  32. package/dist/catalog-network-source.d.ts.map +1 -0
  33. package/dist/catalog-network-source.js +22 -0
  34. package/dist/catalog-network-source.js.map +7 -0
  35. package/dist/catalog-validator.d.ts +18 -0
  36. package/dist/catalog-validator.d.ts.map +1 -0
  37. package/dist/catalog-validator.js +7747 -0
  38. package/dist/catalog-validator.js.map +7 -0
  39. package/dist/cmaf-block-assembler.d.ts +168 -0
  40. package/dist/cmaf-block-assembler.d.ts.map +1 -0
  41. package/dist/cmaf-block-assembler.js +1755 -0
  42. package/dist/cmaf-block-assembler.js.map +7 -0
  43. package/dist/cmaf-group-batcher.d.ts +38 -0
  44. package/dist/cmaf-group-batcher.d.ts.map +1 -0
  45. package/dist/cmaf-group-batcher.js +195 -0
  46. package/dist/cmaf-group-batcher.js.map +7 -0
  47. package/dist/cmcd-producer.d.ts +104 -0
  48. package/dist/cmcd-producer.d.ts.map +1 -0
  49. package/dist/cmcd-producer.js +207 -0
  50. package/dist/cmcd-producer.js.map +7 -0
  51. package/dist/codec-utils.d.ts +235 -0
  52. package/dist/codec-utils.d.ts.map +1 -0
  53. package/dist/codec-utils.js +1225 -0
  54. package/dist/codec-utils.js.map +7 -0
  55. package/dist/fec-codec.d.ts +32 -0
  56. package/dist/fec-codec.d.ts.map +1 -0
  57. package/dist/fec-codec.js +47 -0
  58. package/dist/fec-codec.js.map +7 -0
  59. package/dist/fec-manager.d.ts +266 -0
  60. package/dist/fec-manager.d.ts.map +1 -0
  61. package/dist/fec-manager.js +1447 -0
  62. package/dist/fec-manager.js.map +7 -0
  63. package/dist/fec-starvation-detector.d.ts +59 -0
  64. package/dist/fec-starvation-detector.d.ts.map +1 -0
  65. package/dist/fec-starvation-detector.js +75 -0
  66. package/dist/fec-starvation-detector.js.map +7 -0
  67. package/dist/fec-worker-setup.d.ts +76 -0
  68. package/dist/fec-worker-setup.d.ts.map +1 -0
  69. package/dist/fec-worker-setup.js +603 -0
  70. package/dist/fec-worker-setup.js.map +7 -0
  71. package/dist/fmp4-completion.d.ts +30 -0
  72. package/dist/fmp4-completion.d.ts.map +1 -0
  73. package/dist/fmp4-completion.js +21 -0
  74. package/dist/fmp4-completion.js.map +7 -0
  75. package/dist/fmp4-reader.d.ts +37 -0
  76. package/dist/fmp4-reader.d.ts.map +1 -0
  77. package/dist/fmp4-reader.js +100 -0
  78. package/dist/fmp4-reader.js.map +7 -0
  79. package/dist/index.d.ts +47 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +330 -0
  82. package/dist/index.js.map +7 -0
  83. package/dist/iso-bmff-timing.d.ts +47 -0
  84. package/dist/iso-bmff-timing.d.ts.map +1 -0
  85. package/dist/iso-bmff-timing.js +526 -0
  86. package/dist/iso-bmff-timing.js.map +7 -0
  87. package/dist/loc.d.ts +68 -0
  88. package/dist/loc.d.ts.map +1 -0
  89. package/dist/loc.js +143 -0
  90. package/dist/loc.js.map +7 -0
  91. package/dist/manifest-auth-plan.d.ts +30 -0
  92. package/dist/manifest-auth-plan.d.ts.map +1 -0
  93. package/dist/manifest-auth-plan.js +1267 -0
  94. package/dist/manifest-auth-plan.js.map +7 -0
  95. package/dist/media-router.d.ts +338 -0
  96. package/dist/media-router.d.ts.map +1 -0
  97. package/dist/media-router.js +802 -0
  98. package/dist/media-router.js.map +7 -0
  99. package/dist/mfu-reassembler.d.ts +264 -0
  100. package/dist/mfu-reassembler.d.ts.map +1 -0
  101. package/dist/mfu-reassembler.js +2668 -0
  102. package/dist/mfu-reassembler.js.map +7 -0
  103. package/dist/mfu-reassembly-policy.d.ts +24 -0
  104. package/dist/mfu-reassembly-policy.d.ts.map +1 -0
  105. package/dist/mfu-reassembly-policy.js +99 -0
  106. package/dist/mfu-reassembly-policy.js.map +7 -0
  107. package/dist/mmtp-container-parser.d.ts +304 -0
  108. package/dist/mmtp-container-parser.d.ts.map +1 -0
  109. package/dist/mmtp-container-parser.js +5378 -0
  110. package/dist/mmtp-container-parser.js.map +7 -0
  111. package/dist/mmtp-container-parser.no-modules.js +7853 -0
  112. package/dist/mmtp-container-parser.no-modules.js.map +7 -0
  113. package/dist/mmtp-decode-pipeline.d.ts +96 -0
  114. package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
  115. package/dist/mmtp-decode-pipeline.js +5589 -0
  116. package/dist/mmtp-decode-pipeline.js.map +7 -0
  117. package/dist/mmtp-router.d.ts +383 -0
  118. package/dist/mmtp-router.d.ts.map +1 -0
  119. package/dist/mmtp-router.js +8384 -0
  120. package/dist/mmtp-router.js.map +7 -0
  121. package/dist/mmtp-to-cmaf.d.ts +166 -0
  122. package/dist/mmtp-to-cmaf.d.ts.map +1 -0
  123. package/dist/mmtp-to-cmaf.js +781 -0
  124. package/dist/mmtp-to-cmaf.js.map +7 -0
  125. package/dist/multicast-auth-catalog.d.ts +115 -0
  126. package/dist/multicast-auth-catalog.d.ts.map +1 -0
  127. package/dist/multicast-auth-catalog.js +202 -0
  128. package/dist/multicast-auth-catalog.js.map +7 -0
  129. package/dist/multicast-input.d.ts +107 -0
  130. package/dist/multicast-input.d.ts.map +1 -0
  131. package/dist/multicast-input.js +256 -0
  132. package/dist/multicast-input.js.map +7 -0
  133. package/dist/multicast-manager.d.ts +330 -0
  134. package/dist/multicast-manager.d.ts.map +1 -0
  135. package/dist/multicast-manager.js +11032 -0
  136. package/dist/multicast-manager.js.map +7 -0
  137. package/dist/multicast-transport.d.ts +62 -0
  138. package/dist/multicast-transport.d.ts.map +1 -0
  139. package/dist/multicast-transport.js +338 -0
  140. package/dist/multicast-transport.js.map +7 -0
  141. package/dist/multicast-unwrap.d.ts +57 -0
  142. package/dist/multicast-unwrap.d.ts.map +1 -0
  143. package/dist/multicast-unwrap.js +384 -0
  144. package/dist/multicast-unwrap.js.map +7 -0
  145. package/dist/no-modules.d.ts +9 -0
  146. package/dist/no-modules.d.ts.map +1 -0
  147. package/dist/no-modules.js +7819 -0
  148. package/dist/no-modules.js.map +7 -0
  149. package/dist/phase14-catalog-override.d.ts +126 -0
  150. package/dist/phase14-catalog-override.d.ts.map +1 -0
  151. package/dist/phase14-catalog-override.js +155 -0
  152. package/dist/phase14-catalog-override.js.map +7 -0
  153. package/dist/repair-fetch.d.ts +58 -0
  154. package/dist/repair-fetch.d.ts.map +1 -0
  155. package/dist/repair-fetch.js +117 -0
  156. package/dist/repair-fetch.js.map +7 -0
  157. package/dist/sample-combiner.d.ts +182 -0
  158. package/dist/sample-combiner.d.ts.map +1 -0
  159. package/dist/sample-combiner.js +571 -0
  160. package/dist/sample-combiner.js.map +7 -0
  161. package/dist/signaling.d.ts +29 -0
  162. package/dist/signaling.d.ts.map +1 -0
  163. package/dist/signaling.js +232 -0
  164. package/dist/signaling.js.map +7 -0
  165. package/dist/source-auth.d.ts +2 -0
  166. package/dist/source-auth.d.ts.map +1 -0
  167. package/dist/source-auth.js +1220 -0
  168. package/dist/source-auth.js.map +7 -0
  169. package/dist/switching-set-alignment.d.ts +61 -0
  170. package/dist/switching-set-alignment.d.ts.map +1 -0
  171. package/dist/switching-set-alignment.js +85 -0
  172. package/dist/switching-set-alignment.js.map +7 -0
  173. package/dist/timestamp-codec.d.ts +34 -0
  174. package/dist/timestamp-codec.d.ts.map +1 -0
  175. package/dist/timestamp-codec.js +456 -0
  176. package/dist/timestamp-codec.js.map +7 -0
  177. package/dist/track-pipeline-setup.d.ts +72 -0
  178. package/dist/track-pipeline-setup.d.ts.map +1 -0
  179. package/dist/track-pipeline-setup.js +306 -0
  180. package/dist/track-pipeline-setup.js.map +7 -0
  181. package/dist/track-pipeline.d.ts +121 -0
  182. package/dist/track-pipeline.d.ts.map +1 -0
  183. package/dist/track-pipeline.js +229 -0
  184. package/dist/track-pipeline.js.map +7 -0
  185. package/dist/track-session.d.ts +269 -0
  186. package/dist/track-session.d.ts.map +1 -0
  187. package/dist/track-session.js +597 -0
  188. package/dist/track-session.js.map +7 -0
  189. package/dist/transport-pool.d.ts +69 -0
  190. package/dist/transport-pool.d.ts.map +1 -0
  191. package/dist/transport-pool.js +88 -0
  192. package/dist/transport-pool.js.map +7 -0
  193. package/dist/wasm-mfu-reassembler.d.ts +81 -0
  194. package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
  195. package/dist/wasm-mfu-reassembler.js +1915 -0
  196. package/dist/wasm-mfu-reassembler.js.map +7 -0
  197. package/dist/wasm-mmtp-container-parser.d.ts +10 -0
  198. package/dist/wasm-mmtp-container-parser.d.ts.map +1 -0
  199. package/dist/wasm-mmtp-container-parser.js +5635 -0
  200. package/dist/wasm-mmtp-container-parser.js.map +7 -0
  201. package/node_modules/@blockcast/fec-worker/README.md +213 -0
  202. package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
  203. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +80 -0
  204. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -0
  205. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +270 -0
  206. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -0
  207. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +252 -0
  208. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -0
  209. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +11 -0
  210. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +1 -0
  211. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +14 -0
  212. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +1 -0
  213. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +565 -0
  214. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +7 -0
  215. package/node_modules/@blockcast/fec-worker/dist/index.d.ts +10 -0
  216. package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +1 -0
  217. package/node_modules/@blockcast/fec-worker/dist/index.js +8 -0
  218. package/node_modules/@blockcast/fec-worker/dist/index.js.map +1 -0
  219. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +20 -0
  220. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +1 -0
  221. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +349 -0
  222. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +7 -0
  223. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +104 -0
  224. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +1 -0
  225. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +181 -0
  226. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +1 -0
  227. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +179 -0
  228. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +1 -0
  229. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +47 -0
  230. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +1 -0
  231. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +9 -0
  232. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +1 -0
  233. package/node_modules/@blockcast/fec-worker/dist/transfer.js +13 -0
  234. package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +1 -0
  235. package/node_modules/@blockcast/fec-worker/package.json +59 -0
  236. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +309 -0
  237. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +268 -0
  238. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1048 -0
  239. package/node_modules/@blockcast/fec-worker/src/index.ts +32 -0
  240. package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +558 -0
  241. package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +222 -0
  242. package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +194 -0
  243. package/node_modules/@blockcast/fec-worker/src/transfer.ts +12 -0
  244. package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  245. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  246. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  247. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  248. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  249. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  250. package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  251. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  252. package/node_modules/@blockcast/mmt-base/README.md +120 -0
  253. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  254. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
  255. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  256. package/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
  257. package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  258. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +275 -0
  259. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  260. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +644 -0
  261. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  262. package/node_modules/@blockcast/mmt-base/package.json +49 -0
  263. package/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
  264. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +898 -0
  265. package/node_modules/@blockcast/mmt-fec/README.md +270 -0
  266. package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
  267. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +80 -0
  268. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +1 -0
  269. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +294 -0
  270. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +1 -0
  271. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +72 -0
  272. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +1 -0
  273. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +192 -0
  274. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +1 -0
  275. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +46 -0
  276. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +1 -0
  277. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +96 -0
  278. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +1 -0
  279. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +33 -0
  280. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +1 -0
  281. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +57 -0
  282. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +1 -0
  283. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +185 -0
  284. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +1 -0
  285. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +273 -0
  286. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +1 -0
  287. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +22 -0
  288. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
  289. package/node_modules/@blockcast/mmt-fec/dist/index.js +22 -0
  290. package/node_modules/@blockcast/mmt-fec/dist/index.js.map +1 -0
  291. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +20 -0
  292. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +1 -0
  293. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +28 -0
  294. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +1 -0
  295. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +174 -0
  296. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
  297. package/node_modules/@blockcast/mmt-fec/dist/oti.js +256 -0
  298. package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +1 -0
  299. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +41 -0
  300. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +1 -0
  301. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +81 -0
  302. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +1 -0
  303. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  304. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  305. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  306. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  307. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  308. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  309. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  310. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  311. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +120 -0
  312. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  313. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
  314. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  315. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
  316. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  317. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +275 -0
  318. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  319. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +644 -0
  320. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  321. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +49 -0
  322. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
  323. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +898 -0
  324. package/node_modules/@blockcast/mmt-fec/package.json +79 -0
  325. package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +369 -0
  326. package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +277 -0
  327. package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +127 -0
  328. package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +68 -0
  329. package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +332 -0
  330. package/node_modules/@blockcast/mmt-fec/src/index.ts +77 -0
  331. package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
  332. package/node_modules/@blockcast/mmt-fec/src/oti.ts +319 -0
  333. package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -0
  334. package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
  335. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +28 -0
  336. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +1 -0
  337. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +128 -0
  338. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +1 -0
  339. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +4 -0
  340. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +1 -0
  341. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +4 -0
  342. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +1 -0
  343. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +37 -0
  344. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +1 -0
  345. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +179 -0
  346. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +1 -0
  347. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +90 -0
  348. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +1 -0
  349. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +462 -0
  350. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +1 -0
  351. package/node_modules/@blockcast/mmt-manifest-verify/package.json +45 -0
  352. package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +171 -0
  353. package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +3 -0
  354. package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +230 -0
  355. package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +636 -0
  356. package/node_modules/@noble/hashes/LICENSE +21 -0
  357. package/node_modules/@noble/hashes/README.md +521 -0
  358. package/node_modules/@noble/hashes/_assert.d.ts +17 -0
  359. package/node_modules/@noble/hashes/_assert.d.ts.map +1 -0
  360. package/node_modules/@noble/hashes/_assert.js +18 -0
  361. package/node_modules/@noble/hashes/_assert.js.map +1 -0
  362. package/node_modules/@noble/hashes/_blake.d.ts +14 -0
  363. package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
  364. package/node_modules/@noble/hashes/_blake.js +50 -0
  365. package/node_modules/@noble/hashes/_blake.js.map +1 -0
  366. package/node_modules/@noble/hashes/_md.d.ts +51 -0
  367. package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
  368. package/node_modules/@noble/hashes/_md.js +162 -0
  369. package/node_modules/@noble/hashes/_md.js.map +1 -0
  370. package/node_modules/@noble/hashes/_u64.d.ts +55 -0
  371. package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
  372. package/node_modules/@noble/hashes/_u64.js +90 -0
  373. package/node_modules/@noble/hashes/_u64.js.map +1 -0
  374. package/node_modules/@noble/hashes/argon2.d.ts +32 -0
  375. package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
  376. package/node_modules/@noble/hashes/argon2.js +401 -0
  377. package/node_modules/@noble/hashes/argon2.js.map +1 -0
  378. package/node_modules/@noble/hashes/blake1.d.ts +106 -0
  379. package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
  380. package/node_modules/@noble/hashes/blake1.js +459 -0
  381. package/node_modules/@noble/hashes/blake1.js.map +1 -0
  382. package/node_modules/@noble/hashes/blake2.d.ts +116 -0
  383. package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
  384. package/node_modules/@noble/hashes/blake2.js +420 -0
  385. package/node_modules/@noble/hashes/blake2.js.map +1 -0
  386. package/node_modules/@noble/hashes/blake2b.d.ts +11 -0
  387. package/node_modules/@noble/hashes/blake2b.d.ts.map +1 -0
  388. package/node_modules/@noble/hashes/blake2b.js +14 -0
  389. package/node_modules/@noble/hashes/blake2b.js.map +1 -0
  390. package/node_modules/@noble/hashes/blake2s.d.ts +20 -0
  391. package/node_modules/@noble/hashes/blake2s.d.ts.map +1 -0
  392. package/node_modules/@noble/hashes/blake2s.js +24 -0
  393. package/node_modules/@noble/hashes/blake2s.js.map +1 -0
  394. package/node_modules/@noble/hashes/blake3.d.ts +54 -0
  395. package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
  396. package/node_modules/@noble/hashes/blake3.js +255 -0
  397. package/node_modules/@noble/hashes/blake3.js.map +1 -0
  398. package/node_modules/@noble/hashes/crypto.d.ts +2 -0
  399. package/node_modules/@noble/hashes/crypto.d.ts.map +1 -0
  400. package/node_modules/@noble/hashes/crypto.js +5 -0
  401. package/node_modules/@noble/hashes/crypto.js.map +1 -0
  402. package/node_modules/@noble/hashes/cryptoNode.d.ts +2 -0
  403. package/node_modules/@noble/hashes/cryptoNode.d.ts.map +1 -0
  404. package/node_modules/@noble/hashes/cryptoNode.js +18 -0
  405. package/node_modules/@noble/hashes/cryptoNode.js.map +1 -0
  406. package/node_modules/@noble/hashes/eskdf.d.ts +47 -0
  407. package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
  408. package/node_modules/@noble/hashes/eskdf.js +166 -0
  409. package/node_modules/@noble/hashes/eskdf.js.map +1 -0
  410. package/node_modules/@noble/hashes/esm/_assert.d.ts +17 -0
  411. package/node_modules/@noble/hashes/esm/_assert.d.ts.map +1 -0
  412. package/node_modules/@noble/hashes/esm/_assert.js +15 -0
  413. package/node_modules/@noble/hashes/esm/_assert.js.map +1 -0
  414. package/node_modules/@noble/hashes/esm/_blake.d.ts +14 -0
  415. package/node_modules/@noble/hashes/esm/_blake.d.ts.map +1 -0
  416. package/node_modules/@noble/hashes/esm/_blake.js +45 -0
  417. package/node_modules/@noble/hashes/esm/_blake.js.map +1 -0
  418. package/node_modules/@noble/hashes/esm/_md.d.ts +51 -0
  419. package/node_modules/@noble/hashes/esm/_md.d.ts.map +1 -0
  420. package/node_modules/@noble/hashes/esm/_md.js +155 -0
  421. package/node_modules/@noble/hashes/esm/_md.js.map +1 -0
  422. package/node_modules/@noble/hashes/esm/_u64.d.ts +55 -0
  423. package/node_modules/@noble/hashes/esm/_u64.d.ts.map +1 -0
  424. package/node_modules/@noble/hashes/esm/_u64.js +67 -0
  425. package/node_modules/@noble/hashes/esm/_u64.js.map +1 -0
  426. package/node_modules/@noble/hashes/esm/argon2.d.ts +32 -0
  427. package/node_modules/@noble/hashes/esm/argon2.d.ts.map +1 -0
  428. package/node_modules/@noble/hashes/esm/argon2.js +392 -0
  429. package/node_modules/@noble/hashes/esm/argon2.js.map +1 -0
  430. package/node_modules/@noble/hashes/esm/blake1.d.ts +106 -0
  431. package/node_modules/@noble/hashes/esm/blake1.d.ts.map +1 -0
  432. package/node_modules/@noble/hashes/esm/blake1.js +452 -0
  433. package/node_modules/@noble/hashes/esm/blake1.js.map +1 -0
  434. package/node_modules/@noble/hashes/esm/blake2.d.ts +116 -0
  435. package/node_modules/@noble/hashes/esm/blake2.d.ts.map +1 -0
  436. package/node_modules/@noble/hashes/esm/blake2.js +413 -0
  437. package/node_modules/@noble/hashes/esm/blake2.js.map +1 -0
  438. package/node_modules/@noble/hashes/esm/blake2b.d.ts +11 -0
  439. package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +1 -0
  440. package/node_modules/@noble/hashes/esm/blake2b.js +11 -0
  441. package/node_modules/@noble/hashes/esm/blake2b.js.map +1 -0
  442. package/node_modules/@noble/hashes/esm/blake2s.d.ts +20 -0
  443. package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +1 -0
  444. package/node_modules/@noble/hashes/esm/blake2s.js +21 -0
  445. package/node_modules/@noble/hashes/esm/blake2s.js.map +1 -0
  446. package/node_modules/@noble/hashes/esm/blake3.d.ts +54 -0
  447. package/node_modules/@noble/hashes/esm/blake3.d.ts.map +1 -0
  448. package/node_modules/@noble/hashes/esm/blake3.js +251 -0
  449. package/node_modules/@noble/hashes/esm/blake3.js.map +1 -0
  450. package/node_modules/@noble/hashes/esm/crypto.d.ts +2 -0
  451. package/node_modules/@noble/hashes/esm/crypto.d.ts.map +1 -0
  452. package/node_modules/@noble/hashes/esm/crypto.js +2 -0
  453. package/node_modules/@noble/hashes/esm/crypto.js.map +1 -0
  454. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +2 -0
  455. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +1 -0
  456. package/node_modules/@noble/hashes/esm/cryptoNode.js +15 -0
  457. package/node_modules/@noble/hashes/esm/cryptoNode.js.map +1 -0
  458. package/node_modules/@noble/hashes/esm/eskdf.d.ts +47 -0
  459. package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +1 -0
  460. package/node_modules/@noble/hashes/esm/eskdf.js +160 -0
  461. package/node_modules/@noble/hashes/esm/eskdf.js.map +1 -0
  462. package/node_modules/@noble/hashes/esm/hkdf.d.ts +36 -0
  463. package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +1 -0
  464. package/node_modules/@noble/hashes/esm/hkdf.js +82 -0
  465. package/node_modules/@noble/hashes/esm/hkdf.js.map +1 -0
  466. package/node_modules/@noble/hashes/esm/hmac.d.ts +35 -0
  467. package/node_modules/@noble/hashes/esm/hmac.d.ts.map +1 -0
  468. package/node_modules/@noble/hashes/esm/hmac.js +86 -0
  469. package/node_modules/@noble/hashes/esm/hmac.js.map +1 -0
  470. package/node_modules/@noble/hashes/esm/index.d.ts +2 -0
  471. package/node_modules/@noble/hashes/esm/index.d.ts.map +1 -0
  472. package/node_modules/@noble/hashes/esm/index.js +33 -0
  473. package/node_modules/@noble/hashes/esm/index.js.map +1 -0
  474. package/node_modules/@noble/hashes/esm/legacy.d.ts +71 -0
  475. package/node_modules/@noble/hashes/esm/legacy.d.ts.map +1 -0
  476. package/node_modules/@noble/hashes/esm/legacy.js +281 -0
  477. package/node_modules/@noble/hashes/esm/legacy.js.map +1 -0
  478. package/node_modules/@noble/hashes/esm/package.json +10 -0
  479. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +23 -0
  480. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +1 -0
  481. package/node_modules/@noble/hashes/esm/pbkdf2.js +97 -0
  482. package/node_modules/@noble/hashes/esm/pbkdf2.js.map +1 -0
  483. package/node_modules/@noble/hashes/esm/ripemd160.d.ts +13 -0
  484. package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +1 -0
  485. package/node_modules/@noble/hashes/esm/ripemd160.js +13 -0
  486. package/node_modules/@noble/hashes/esm/ripemd160.js.map +1 -0
  487. package/node_modules/@noble/hashes/esm/scrypt.d.ts +34 -0
  488. package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +1 -0
  489. package/node_modules/@noble/hashes/esm/scrypt.js +228 -0
  490. package/node_modules/@noble/hashes/esm/scrypt.js.map +1 -0
  491. package/node_modules/@noble/hashes/esm/sha1.d.ts +11 -0
  492. package/node_modules/@noble/hashes/esm/sha1.d.ts.map +1 -0
  493. package/node_modules/@noble/hashes/esm/sha1.js +11 -0
  494. package/node_modules/@noble/hashes/esm/sha1.js.map +1 -0
  495. package/node_modules/@noble/hashes/esm/sha2.d.ts +159 -0
  496. package/node_modules/@noble/hashes/esm/sha2.d.ts.map +1 -0
  497. package/node_modules/@noble/hashes/esm/sha2.js +375 -0
  498. package/node_modules/@noble/hashes/esm/sha2.js.map +1 -0
  499. package/node_modules/@noble/hashes/esm/sha256.d.ts +20 -0
  500. package/node_modules/@noble/hashes/esm/sha256.d.ts.map +1 -0
  501. package/node_modules/@noble/hashes/esm/sha256.js +20 -0
  502. package/node_modules/@noble/hashes/esm/sha256.js.map +1 -0
  503. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +142 -0
  504. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +1 -0
  505. package/node_modules/@noble/hashes/esm/sha3-addons.js +393 -0
  506. package/node_modules/@noble/hashes/esm/sha3-addons.js.map +1 -0
  507. package/node_modules/@noble/hashes/esm/sha3.d.ts +53 -0
  508. package/node_modules/@noble/hashes/esm/sha3.d.ts.map +1 -0
  509. package/node_modules/@noble/hashes/esm/sha3.js +234 -0
  510. package/node_modules/@noble/hashes/esm/sha3.js.map +1 -0
  511. package/node_modules/@noble/hashes/esm/sha512.d.ts +26 -0
  512. package/node_modules/@noble/hashes/esm/sha512.d.ts.map +1 -0
  513. package/node_modules/@noble/hashes/esm/sha512.js +26 -0
  514. package/node_modules/@noble/hashes/esm/sha512.js.map +1 -0
  515. package/node_modules/@noble/hashes/esm/utils.d.ts +161 -0
  516. package/node_modules/@noble/hashes/esm/utils.d.ts.map +1 -0
  517. package/node_modules/@noble/hashes/esm/utils.js +281 -0
  518. package/node_modules/@noble/hashes/esm/utils.js.map +1 -0
  519. package/node_modules/@noble/hashes/hkdf.d.ts +36 -0
  520. package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
  521. package/node_modules/@noble/hashes/hkdf.js +88 -0
  522. package/node_modules/@noble/hashes/hkdf.js.map +1 -0
  523. package/node_modules/@noble/hashes/hmac.d.ts +35 -0
  524. package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
  525. package/node_modules/@noble/hashes/hmac.js +91 -0
  526. package/node_modules/@noble/hashes/hmac.js.map +1 -0
  527. package/node_modules/@noble/hashes/index.d.ts +1 -0
  528. package/node_modules/@noble/hashes/index.d.ts.map +1 -0
  529. package/node_modules/@noble/hashes/index.js +33 -0
  530. package/node_modules/@noble/hashes/index.js.map +1 -0
  531. package/node_modules/@noble/hashes/legacy.d.ts +71 -0
  532. package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
  533. package/node_modules/@noble/hashes/legacy.js +287 -0
  534. package/node_modules/@noble/hashes/legacy.js.map +1 -0
  535. package/node_modules/@noble/hashes/package.json +266 -0
  536. package/node_modules/@noble/hashes/pbkdf2.d.ts +23 -0
  537. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
  538. package/node_modules/@noble/hashes/pbkdf2.js +101 -0
  539. package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
  540. package/node_modules/@noble/hashes/ripemd160.d.ts +13 -0
  541. package/node_modules/@noble/hashes/ripemd160.d.ts.map +1 -0
  542. package/node_modules/@noble/hashes/ripemd160.js +16 -0
  543. package/node_modules/@noble/hashes/ripemd160.js.map +1 -0
  544. package/node_modules/@noble/hashes/scrypt.d.ts +34 -0
  545. package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
  546. package/node_modules/@noble/hashes/scrypt.js +232 -0
  547. package/node_modules/@noble/hashes/scrypt.js.map +1 -0
  548. package/node_modules/@noble/hashes/sha1.d.ts +11 -0
  549. package/node_modules/@noble/hashes/sha1.d.ts.map +1 -0
  550. package/node_modules/@noble/hashes/sha1.js +14 -0
  551. package/node_modules/@noble/hashes/sha1.js.map +1 -0
  552. package/node_modules/@noble/hashes/sha2.d.ts +159 -0
  553. package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
  554. package/node_modules/@noble/hashes/sha2.js +384 -0
  555. package/node_modules/@noble/hashes/sha2.js.map +1 -0
  556. package/node_modules/@noble/hashes/sha256.d.ts +20 -0
  557. package/node_modules/@noble/hashes/sha256.d.ts.map +1 -0
  558. package/node_modules/@noble/hashes/sha256.js +23 -0
  559. package/node_modules/@noble/hashes/sha256.js.map +1 -0
  560. package/node_modules/@noble/hashes/sha3-addons.d.ts +142 -0
  561. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
  562. package/node_modules/@noble/hashes/sha3-addons.js +402 -0
  563. package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
  564. package/node_modules/@noble/hashes/sha3.d.ts +53 -0
  565. package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
  566. package/node_modules/@noble/hashes/sha3.js +239 -0
  567. package/node_modules/@noble/hashes/sha3.js.map +1 -0
  568. package/node_modules/@noble/hashes/sha512.d.ts +26 -0
  569. package/node_modules/@noble/hashes/sha512.d.ts.map +1 -0
  570. package/node_modules/@noble/hashes/sha512.js +29 -0
  571. package/node_modules/@noble/hashes/sha512.js.map +1 -0
  572. package/node_modules/@noble/hashes/src/_assert.ts +22 -0
  573. package/node_modules/@noble/hashes/src/_blake.ts +50 -0
  574. package/node_modules/@noble/hashes/src/_md.ts +176 -0
  575. package/node_modules/@noble/hashes/src/_u64.ts +91 -0
  576. package/node_modules/@noble/hashes/src/argon2.ts +497 -0
  577. package/node_modules/@noble/hashes/src/blake1.ts +534 -0
  578. package/node_modules/@noble/hashes/src/blake2.ts +486 -0
  579. package/node_modules/@noble/hashes/src/blake2b.ts +10 -0
  580. package/node_modules/@noble/hashes/src/blake2s.ts +20 -0
  581. package/node_modules/@noble/hashes/src/blake3.ts +272 -0
  582. package/node_modules/@noble/hashes/src/crypto.ts +9 -0
  583. package/node_modules/@noble/hashes/src/cryptoNode.ts +15 -0
  584. package/node_modules/@noble/hashes/src/eskdf.ts +187 -0
  585. package/node_modules/@noble/hashes/src/hkdf.ts +88 -0
  586. package/node_modules/@noble/hashes/src/hmac.ts +94 -0
  587. package/node_modules/@noble/hashes/src/index.ts +31 -0
  588. package/node_modules/@noble/hashes/src/legacy.ts +293 -0
  589. package/node_modules/@noble/hashes/src/pbkdf2.ts +122 -0
  590. package/node_modules/@noble/hashes/src/ripemd160.ts +12 -0
  591. package/node_modules/@noble/hashes/src/scrypt.ts +257 -0
  592. package/node_modules/@noble/hashes/src/sha1.ts +10 -0
  593. package/node_modules/@noble/hashes/src/sha2.ts +402 -0
  594. package/node_modules/@noble/hashes/src/sha256.ts +24 -0
  595. package/node_modules/@noble/hashes/src/sha3-addons.ts +499 -0
  596. package/node_modules/@noble/hashes/src/sha3.ts +258 -0
  597. package/node_modules/@noble/hashes/src/sha512.ts +34 -0
  598. package/node_modules/@noble/hashes/src/utils.ts +395 -0
  599. package/node_modules/@noble/hashes/utils.d.ts +161 -0
  600. package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
  601. package/node_modules/@noble/hashes/utils.js +313 -0
  602. package/node_modules/@noble/hashes/utils.js.map +1 -0
  603. package/package.json +203 -0
  604. package/schemas/catalog.schema.json +283 -0
  605. package/src/align.ts +235 -0
  606. package/src/audio-init-parser.ts +189 -0
  607. package/src/audio-ring-buffer.ts +120 -0
  608. package/src/authored-timing.ts +63 -0
  609. package/src/carp-catalog.ts +776 -0
  610. package/src/catalog-fec-params.ts +167 -0
  611. package/src/catalog-network-source.ts +49 -0
  612. package/src/catalog-validator.ts +34 -0
  613. package/src/cmaf-block-assembler.ts +1058 -0
  614. package/src/cmaf-group-batcher.ts +104 -0
  615. package/src/cmcd-producer.ts +329 -0
  616. package/src/codec-utils.ts +1539 -0
  617. package/src/fec-codec.ts +61 -0
  618. package/src/fec-manager.ts +1939 -0
  619. package/src/fec-starvation-detector.ts +128 -0
  620. package/src/fec-worker-setup.ts +177 -0
  621. package/src/fmp4-completion.ts +47 -0
  622. package/src/fmp4-reader.ts +112 -0
  623. package/src/index.ts +443 -0
  624. package/src/iso-bmff-timing.ts +688 -0
  625. package/src/loc.ts +181 -0
  626. package/src/manifest-auth-plan.ts +107 -0
  627. package/src/media-router.ts +1185 -0
  628. package/src/mfu-reassembler.ts +2842 -0
  629. package/src/mfu-reassembly-policy.ts +134 -0
  630. package/src/mmtp-container-parser.ts +1498 -0
  631. package/src/mmtp-decode-pipeline.ts +315 -0
  632. package/src/mmtp-router.ts +3027 -0
  633. package/src/mmtp-to-cmaf.ts +956 -0
  634. package/src/multicast-auth-catalog.ts +340 -0
  635. package/src/multicast-input.ts +439 -0
  636. package/src/multicast-manager.ts +1503 -0
  637. package/src/multicast-transport.ts +320 -0
  638. package/src/multicast-unwrap.ts +132 -0
  639. package/src/no-modules.ts +20 -0
  640. package/src/phase14-catalog-override.ts +336 -0
  641. package/src/repair-fetch.ts +217 -0
  642. package/src/sample-combiner.ts +846 -0
  643. package/src/signaling.ts +78 -0
  644. package/src/source-auth.ts +1 -0
  645. package/src/switching-set-alignment.ts +109 -0
  646. package/src/timestamp-codec.ts +265 -0
  647. package/src/track-pipeline-setup.ts +189 -0
  648. package/src/track-pipeline.ts +332 -0
  649. package/src/track-session.ts +778 -0
  650. package/src/transport-pool.ts +128 -0
  651. package/src/wasm-mfu-reassembler.ts +372 -0
  652. package/src/wasm-mmtp-container-parser.ts +23 -0
  653. package/test-vectors/align.json +199 -0
  654. package/test-vectors/catalog/README.md +25 -0
  655. package/test-vectors/catalog/negative/legacy-selection-params.json +15 -0
  656. package/test-vectors/catalog/negative/missing-mmtp-fields.json +12 -0
  657. package/test-vectors/catalog/negative/multicast-auth-padded-key.json +37 -0
  658. package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +53 -0
  659. package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +37 -0
  660. package/test-vectors/catalog/negative/multicast-endpoint-missing-protocol-and-source.json +24 -0
  661. package/test-vectors/catalog/negative/network-source-object.json +25 -0
  662. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +26 -0
  663. package/test-vectors/catalog/negative/repair-track-legacy-shape.json +18 -0
  664. package/test-vectors/catalog/negative/track-role-empty.json +16 -0
  665. package/test-vectors/catalog/negative/track-role-non-string.json +16 -0
  666. package/test-vectors/catalog/positive/fec-multicast.json +51 -0
  667. package/test-vectors/catalog/positive/flat-av.json +30 -0
  668. package/test-vectors/catalog/positive/multicast-auth-rotation.json +55 -0
  669. package/test-vectors/catalog/positive/ticks-and-mixed-timescales.json +39 -0
  670. package/test-vectors/catalog/positive/track-role.json +49 -0
  671. package/test-vectors/catalog/releases/moqcast-native-v16-v1/catalog.json +65 -0
  672. package/test-vectors/catalog/releases/moqcast-native-v16-v1.archive.sha256 +1 -0
  673. package/test-vectors/catalog-multicast-auth-v1.json +26 -0
  674. package/test-vectors/catalog-validation.json +148 -0
  675. package/test-vectors/catalog-validator-public-api.mjs +25 -0
  676. package/test-vectors/generate-source-auth-fixture.mjs +114 -0
  677. package/test-vectors/source-auth-v1.json +61 -0
  678. package/test-vectors/subsample-placement-v1.json +230 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../fec-worker/src/fec-worker-client.ts", "../../fec-worker/src/shred-worker-client.ts", "../../fec-worker/src/index.ts", "../../mmt-alta-parse/src/index.ts", "../../mmt-base/src/mmtp.ts", "../src/mfu-reassembler.ts", "../src/sample-combiner.ts", "../src/fmp4-completion.ts", "../src/codec-utils.ts", "../src/loc.ts", "../src/multicast-auth-catalog.ts", "../src/carp-catalog.ts", "../src/multicast-input.ts", "../src/phase14-catalog-override.ts", "../src/multicast-unwrap.ts", "../src/timestamp-codec.ts", "../src/mmtp-to-cmaf.ts", "../src/signaling.ts", "../src/authored-timing.ts", "../src/cmaf-block-assembler.ts", "../src/cmaf-group-batcher.ts", "../src/transport-pool.ts", "../../fec/src/block-processor.ts", "../../fec/src/oti.ts", "../../fec/src/fec-block-mapper.ts", "../src/track-session.ts", "../src/fec-manager.ts", "../src/mfu-reassembly-policy.ts", "../src/iso-bmff-timing.ts", "../src/mmtp-container-parser.ts", "../src/mmtp-router.ts", "../src/wasm-mfu-reassembler.ts", "../src/wasm-mmtp-container-parser.ts", "../src/fec-codec.ts", "../src/multicast-transport.ts", "../src/multicast-manager.ts", "../src/fec-worker-setup.ts", "../src/align.ts", "../src/media-router.ts", "../src/audio-ring-buffer.ts", "../src/mmtp-decode-pipeline.ts", "../src/track-pipeline.ts", "../src/track-pipeline-setup.ts", "../src/audio-init-parser.ts", "../src/fec-starvation-detector.ts", "../src/cmcd-producer.ts", "../src/catalog-fec-params.ts", "../src/catalog-network-source.ts", "../src/repair-fetch.ts"],
4
+ "sourcesContent": ["/**\n * @blockcast/fec-worker \u2014 Main-Thread Client\n *\n * Wraps one Worker per track instance. Provides typed API for the\n * FecWorkerCommand/FecWorkerEvent protocol with zero-copy ArrayBuffer transfers.\n *\n * Constructor accepts either a URL (creates Worker internally) or a pre-created\n * Worker instance (for testing via constructor injection).\n */\n\nimport type {\n\tFecWorkerCommand,\n\tFecWorkerEvent,\n\tFecTrackConfig,\n\tFecTrackStats,\n\tFecBlockSnapshot,\n\tFrameMeta,\n\tFecWorkerErrorCode,\n} from \"./fec-worker-types.js\";\n\nexport class FecWorkerClient {\n\treadonly #worker: Worker;\n\t#onFrame: ((data: ArrayBuffer, meta: FrameMeta) => void) | null = null;\n\t#onSnapshot: ((stats: FecTrackStats) => void) | null = null;\n\t#onBlockUpdate: ((block: FecBlockSnapshot) => void) | null = null;\n\t#onGreenFill: ((sbn: number, data: ArrayBuffer) => void) | null = null;\n\t#onCleanupComplete: ((sbn: number) => void) | null = null;\n\t#onError: ((message: string, code?: FecWorkerErrorCode) => void) | null = null;\n\t#cleanupInFlight: Set<number> | null = null;\n\t#queuedCleanupSbns = new Set<number>();\n\t#pendingResets = 0;\n\t#resetFailed = false;\n\t#disposed = false;\n\n\t/**\n\t * Create a FecWorkerClient.\n\t * @param workerOrUrl - Either a Worker instance (for testing) or a URL to create one\n\t */\n\tconstructor(workerOrUrl: Worker | URL) {\n\t\tif (workerOrUrl instanceof URL) {\n\t\t\tthis.#worker = new Worker(workerOrUrl, { type: \"module\" });\n\t\t} else {\n\t\t\tthis.#worker = workerOrUrl;\n\t\t}\n\t\tthis.#worker.onmessage = (e: MessageEvent<FecWorkerEvent>) =>\n\t\t\tthis.#handleEvent(e.data);\n\t\tthis.#worker.onerror = (e) => {\n\t\t\tthis.#onError?.(formatWorkerError(e), \"worker-runtime\");\n\t\t};\n\t\tthis.#worker.onmessageerror = (e) => {\n\t\t\tthis.#onError?.(formatWorkerMessageError(e), \"worker-message\");\n\t\t};\n\t}\n\n\t/**\n\t * Configure the Worker with FEC parameters and WASM module.\n\t * wasmModule is transferred (not neutered \u2014 WebAssembly.Module is shareable).\n\t * altaPublicKey is PKCS#8 SPKI DER bytes (self-describing \u2014 algorithm detected from OID).\n\t * Transferred (detached from sender).\n\t * altaWasmModule is structured-cloneable (NOT transferable \u2014 do not add to transfer list).\n\t */\n\tconfigure(\n\t\tconfig: FecTrackConfig,\n\t\twasmModule: WebAssembly.Module,\n\t\taltaPublicKey?: ArrayBuffer,\n\t\taltaWasmModule?: WebAssembly.Module,\n\t): void {\n\t\tif (this.#disposed) throw new Error(\"[FecWorkerClient] disposed\");\n\t\tconst cmd: FecWorkerCommand = {\n\t\t\ttype: \"configure\",\n\t\t\tconfig,\n\t\t\twasmModule,\n\t\t\taltaPublicKey,\n\t\t\taltaWasmModule,\n\t\t};\n\t\t// WebAssembly.Module is structured-cloneable, NOT transferable.\n\t\t// Pass it in the message body (Chrome clones it automatically).\n\t\t// Only ArrayBuffers (like altaPublicKey) go in the transfer list.\n\t\tconst transferList: Transferable[] = [];\n\t\tif (altaPublicKey) transferList.push(altaPublicKey);\n\t\t// altaWasmModule is structured-cloneable \u2014 NOT added to transferList\n\t\tthis.#worker.postMessage(cmd, transferList);\n\t}\n\n\t/**\n\t * Feed a source symbol to the Worker. data ArrayBuffer is transferred (zero-copy).\n\t * Caller must not access data after this call \u2014 it is detached.\n\t *\n\t * Optional `alta` carries the detached ALTA authenticator bytes and the\n\t * byte offset in `data` where the signed payload ends. The auth buffer is\n\t * transferred zero-copy alongside `data`. Use {@link getMmtpAltaTrailer}\n\t * upstream (mmtp-router) to extract it from the wire packet.\n\t */\n\tfeedSource(\n\t\tssId: number,\n\t\tdata: ArrayBuffer,\n\t\tts: number,\n\t\talta?: { auth: ArrayBuffer; payloadEnd: number },\n\t): void {\n\t\tif (this.#disposed) return;\n\t\tconst cmd: FecWorkerCommand = alta\n\t\t\t? {\n\t\t\t\t\ttype: \"feedSource\",\n\t\t\t\t\tssId,\n\t\t\t\t\tdata,\n\t\t\t\t\tts,\n\t\t\t\t\taltaAuth: alta.auth,\n\t\t\t\t\taltaPayloadEnd: alta.payloadEnd,\n\t\t\t\t}\n\t\t\t: { type: \"feedSource\", ssId, data, ts };\n\t\tconst transfer: Transferable[] = alta ? [data, alta.auth] : [data];\n\t\tthis.#worker.postMessage(cmd, transfer);\n\t}\n\n\t/**\n\t * Feed a repair symbol to the Worker. data ArrayBuffer is transferred (zero-copy).\n\t * Caller must not access data after this call \u2014 it is detached.\n\t *\n\t * Carries the wire-level Repair FEC Payload ID fields (ISO 23008-1 \u00A7C.5.3):\n\t * - ssStart: SS_ID of first source symbol in the block (= SBN * SSB_length)\n\t * - ssbLength: K for this block\n\t * - rsId: repair symbol index within the block\n\t */\n\tfeedRepair(\n\t\tssStart: number,\n\t\tssbLength: number,\n\t\trsId: number,\n\t\tdata: ArrayBuffer,\n\t\tts: number,\n\t): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#worker.postMessage(\n\t\t\t{\n\t\t\t\ttype: \"feedRepair\",\n\t\t\t\tssStart,\n\t\t\t\tssbLength,\n\t\t\t\trsId,\n\t\t\t\tdata,\n\t\t\t\tts,\n\t\t\t} satisfies FecWorkerCommand,\n\t\t\t[data],\n\t\t);\n\t}\n\n\t/**\n\t * Submit relay block signature for verification. sig and hash are transferred.\n\t */\n\trelayBlockSig(sbn: number, sig: ArrayBuffer, hash: ArrayBuffer): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#worker.postMessage(\n\t\t\t{ type: \"relayBlockSig\", sbn, sig, hash } satisfies FecWorkerCommand,\n\t\t\t[sig, hash],\n\t\t);\n\t}\n\n\t/**\n\t * Trigger cleanup after a block's inactivity deadline.\n\t *\n\t * While one queue-drain barrier is in flight, later SBNs are coalesced into\n\t * the next command without losing their individual acknowledgement identity.\n\t */\n\tcleanup(currentSbn: number): void {\n\t\tif (this.#disposed) return;\n\t\tif (\n\t\t\t!Number.isInteger(currentSbn) ||\n\t\t\tcurrentSbn < 0 ||\n\t\t\tcurrentSbn > 0xffff_ffff\n\t\t) {\n\t\t\tthrow new Error(\"[FecWorkerClient] cleanup: SBN must be a uint32\");\n\t\t}\n\t\tif (this.#cleanupInFlight) {\n\t\t\tthis.#queuedCleanupSbns.add(currentSbn);\n\t\t\treturn;\n\t\t}\n\t\tthis.#postCleanup(new Set([currentSbn]));\n\t}\n\n\t/** Reset this track's decoder epoch while preserving Worker configuration. */\n\treset(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#pendingResets++;\n\t\tthis.#cleanupInFlight = null;\n\t\tthis.#queuedCleanupSbns.clear();\n\t\tthis.#worker.postMessage({ type: \"reset\" } satisfies FecWorkerCommand);\n\t}\n\n\t#postCleanup(sbns: Set<number>): void {\n\t\tthis.#cleanupInFlight = sbns;\n\t\tconst orderedSbns = [...sbns].sort((a, b) => a - b);\n\t\tthis.#worker.postMessage(\n\t\t\t{ type: \"cleanup\", sbns: orderedSbns } satisfies FecWorkerCommand,\n\t\t);\n\t}\n\n\t/** Register callback for decoded/recovered frames (FEC-03). */\n\tonFrame(cb: (data: ArrayBuffer, meta: FrameMeta) => void): void {\n\t\tthis.#onFrame = cb;\n\t}\n\n\t/** Register callback for periodic stats snapshots (FEC-04). */\n\tonSnapshot(cb: (stats: FecTrackStats) => void): void {\n\t\tthis.#onSnapshot = cb;\n\t}\n\n\t/** Register callback for individual block state changes. */\n\tonBlockUpdate(cb: (block: FecBlockSnapshot) => void): void {\n\t\tthis.#onBlockUpdate = cb;\n\t}\n\n\t/** Register callback for green-fill frames. */\n\tonGreenFill(cb: (sbn: number, data: ArrayBuffer) => void): void {\n\t\tthis.#onGreenFill = cb;\n\t}\n\n\t/** Register callback fired after the Worker drains all commands queued before cleanup. */\n\tonCleanupComplete(cb: (sbn: number) => void): void {\n\t\tthis.#onCleanupComplete = cb;\n\t}\n\n\t/** Register callback for Worker errors. */\n\tonError(cb: (message: string, code?: FecWorkerErrorCode) => void): void {\n\t\tthis.#onError = cb;\n\t}\n\n\t/**\n\t * Dispose the Worker. Sends dispose command then terminates.\n\t * Idempotent \u2014 safe to call multiple times.\n\t */\n\tdispose(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\tthis.#cleanupInFlight = null;\n\t\tthis.#queuedCleanupSbns.clear();\n\t\tthis.#worker.postMessage({ type: \"dispose\" } satisfies FecWorkerCommand);\n\t\tthis.#worker.terminate();\n\t}\n\n\t#handleEvent(event: FecWorkerEvent): void {\n\t\tif (event.type === \"resetComplete\") {\n\t\t\tthis.#pendingResets = Math.max(0, this.#pendingResets - 1);\n\t\t\tthis.#resetFailed = false;\n\t\t\treturn;\n\t\t}\n\t\tif (event.type === \"error\") {\n\t\t\tif (event.code === \"reset-failed\") {\n\t\t\t\tthis.#pendingResets = Math.max(0, this.#pendingResets - 1);\n\t\t\t\tthis.#resetFailed = true;\n\t\t\t}\n\t\t\tif (event.code) this.#onError?.(event.message, event.code);\n\t\t\telse this.#onError?.(event.message);\n\t\t\treturn;\n\t\t}\n\t\tif (this.#pendingResets > 0 || this.#resetFailed) return;\n\t\tswitch (event.type) {\n\t\t\tcase \"frame\":\n\t\t\t\tthis.#onFrame?.(event.data, event.meta);\n\t\t\t\tbreak;\n\t\t\tcase \"snapshot\":\n\t\t\t\tthis.#onSnapshot?.(event.stats);\n\t\t\t\tbreak;\n\t\t\tcase \"blockUpdate\":\n\t\t\t\tthis.#onBlockUpdate?.(event.block);\n\t\t\t\tbreak;\n\t\t\tcase \"greenFill\":\n\t\t\t\tthis.#onGreenFill?.(event.sbn, event.data);\n\t\t\t\tbreak;\n\t\t\tcase \"cleanupComplete\":\n\t\t\t\tfor (const sbn of event.sbns) {\n\t\t\t\t\tthis.#onCleanupComplete?.(sbn);\n\t\t\t\t}\n\t\t\t\tthis.#cleanupInFlight = null;\n\t\t\t\tif (this.#queuedCleanupSbns.size > 0) {\n\t\t\t\t\tconst next = this.#queuedCleanupSbns;\n\t\t\t\t\tthis.#queuedCleanupSbns = new Set();\n\t\t\t\t\tthis.#postCleanup(next);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction formatWorkerError(event: ErrorEvent): string {\n\tconst parts = [\"Worker error\"];\n\tif (event.message) parts.push(event.message);\n\tif (event.filename) {\n\t\tconst location = [event.filename];\n\t\tif (event.lineno) location.push(String(event.lineno));\n\t\tif (event.colno) location.push(String(event.colno));\n\t\tparts.push(`at ${location.join(\":\")}`);\n\t}\n\tif (event.error instanceof Error) {\n\t\tif (event.error.message && event.error.message !== event.message) {\n\t\t\tparts.push(event.error.message);\n\t\t}\n\t\tif (event.error.stack) parts.push(event.error.stack);\n\t} else if (event.error != null) {\n\t\tparts.push(String(event.error));\n\t}\n\treturn parts.join(\": \");\n}\n\nfunction formatWorkerMessageError(event: MessageEvent): string {\n\tconst data = event.data;\n\tconst detail =\n\t\tdata == null ? \"\" :\n\t\t\tdata instanceof Error ? `: ${data.message}` :\n\t\t\t\t`: ${Object.prototype.toString.call(data)}`;\n\treturn `Worker messageerror: failed to deserialize worker message${detail}`;\n}\n", "/**\n * @blockcast/fec-worker \u2014 Shred Worker Main-Thread Client\n *\n * Thin wrapper over the shred (Reed-Solomon) worker, mirroring\n * {@link FecWorkerClient}'s shape. Spawns one Worker, speaks the\n * {@link ShredWorkerCommand}/{@link ShredWorkerEvent} protocol, and keeps the\n * datagram read loop + wire parse + WASM FEC off the main thread.\n *\n * Typical wiring for a WebTransport datagram source (fully off-thread):\n *\n * const client = new ShredWorkerClient(new URL(\"./shred-fec-worker.js\", import.meta.url));\n * client.onRecovered((data, meta) => renderRecoveredSet(data, meta));\n * client.configure({ maxSlots: 5000, symbolSize: 512, wasmUrl: \"/vendor/fec/mmt_wasm.js\" });\n * client.attachStream(wt.datagrams.readable); // transferred; main does no read() per packet\n *\n * For sources that demux on the main thread (moqtail OBJECT_DATAGRAM callback,\n * IWA multicast bridge), push each raw frame with {@link feedShred} instead.\n *\n * {@link feedShred} is a fire-and-forget `postMessage` with no native\n * backpressure, so a consumer that decodes slower than the ingest rate (a\n * backgrounded/throttled tab, WASM RS falling behind a ~4k shred/s feed) would\n * grow the worker's inbound message queue without limit \u2014 the queue lives in the\n * renderer process and has been observed to reach multiple GB, GC-thrashing the\n * page into a wedge. The client bounds it with an in-flight window keyed off the\n * worker's own periodic stats heartbeat (no per-frame ack round-trip); see\n * {@link ShredWorkerClientOptions.maxInflight}.\n */\n\nimport type {\n\tShredWorkerCommand,\n\tShredWorkerConfig,\n\tShredWorkerEvent,\n\tShredWorkerStats,\n\tShredWorkerErrorCode,\n\tRecoveredMeta,\n} from \"./shred-worker-types.js\";\n\n/** Construction-time options for {@link ShredWorkerClient}. */\nexport interface ShredWorkerClientOptions {\n\t/**\n\t * Cap on frames posted via {@link ShredWorkerClient.feedShred} but not yet\n\t * drained by the worker, before further frames are dropped (\"skip-to-live\" at\n\t * the worker boundary). Bounds the worker's inbound message queue \u2014 the sole\n\t * unbounded structure on the main-thread-demux (`feedShred`) path.\n\t *\n\t * The drain signal is the worker's periodic `stats` heartbeat: the monotonic\n\t * total of frames it has taken off the queue (`shredsRx + malformed +\n\t * versionMismatch`). No per-frame acknowledgement crosses the boundary, so the\n\t * bound is heartbeat-granular \u2014 size it above one heartbeat's worth of ingest\n\t * (default 250ms heartbeat \u00D7 ~4k frames/s \u2248 ~1k frames).\n\t *\n\t * Default {@link DEFAULT_MAX_INFLIGHT} (8192 frames \u2248 ~10 MB at ~1.3 KB/frame).\n\t * Set to `0` (or any value `<= 0`) to disable the bound entirely \u2014 only do so\n\t * for a source with its own backpressure (e.g. a bounded {@link\n\t * ShredWorkerClient.attachStream} producer). Note the bound assumes `feedShred`\n\t * is the sole ingest path; mixing it with `attachStream` (whose frames also\n\t * advance the heartbeat total) makes the window under-count and benignly\n\t * disables the drop.\n\t */\n\tmaxInflight?: number;\n}\n\n/** Default {@link ShredWorkerClientOptions.maxInflight} (~10 MB of queued frames). */\nexport const DEFAULT_MAX_INFLIGHT = 8192;\n\nexport class ShredWorkerClient {\n\treadonly #worker: Worker;\n\t#onRecovered: ((data: ArrayBuffer, meta: RecoveredMeta) => void) | null = null;\n\t#onStats: ((stats: ShredWorkerStats) => void) | null = null;\n\t#onError: ((message: string, code?: ShredWorkerErrorCode) => void) | null = null;\n\t#disposed = false;\n\n\t/** feedShred in-flight backpressure accounting (see ShredWorkerClientOptions.maxInflight). */\n\treadonly #maxInflight: number;\n\t#sent = 0; // frames posted via feedShred (monotonic)\n\t#processed = 0; // worker-consumed total from the last stats heartbeat (monotonic)\n\t#droppedBackpressure = 0; // frames dropped by the in-flight guard (monotonic)\n\n\t/**\n\t * @param workerOrUrl - A URL (a Worker is created internally, `type: module`)\n\t * or a pre-created Worker (for testing via constructor injection).\n\t * @param opts - Optional {@link ShredWorkerClientOptions}.\n\t */\n\tconstructor(workerOrUrl: Worker | URL, opts: ShredWorkerClientOptions = {}) {\n\t\tthis.#maxInflight = opts.maxInflight ?? DEFAULT_MAX_INFLIGHT;\n\t\tthis.#worker =\n\t\t\tworkerOrUrl instanceof URL\n\t\t\t\t? new Worker(workerOrUrl, { type: \"module\" })\n\t\t\t\t: workerOrUrl;\n\t\tthis.#worker.onmessage = (e: MessageEvent<ShredWorkerEvent>) =>\n\t\t\tthis.#handleEvent(e.data);\n\t\tthis.#worker.onerror = (e) =>\n\t\t\tthis.#onError?.(formatWorkerError(e), \"worker-runtime\");\n\t\tthis.#worker.onmessageerror = () =>\n\t\t\tthis.#onError?.(\"failed to deserialize worker message\", \"worker-message\");\n\t}\n\n\t/** Configure the decoder (WASM load + ShredFecBuffer construction happen in the worker). */\n\tconfigure(config: ShredWorkerConfig): void {\n\t\tif (this.#disposed) throw new Error(\"[ShredWorkerClient] disposed\");\n\t\tthis.#post({ type: \"configure\", config });\n\t}\n\n\t/**\n\t * Hand the worker a stream of raw shred frames to batch-drain. The stream is\n\t * transferred \u2014 the caller must not use it afterward. One active stream at a\n\t * time; attaching a new one cancels the previous.\n\t */\n\tattachStream(readable: ReadableStream<Uint8Array>): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#post({ type: \"attachStream\", readable }, [readable]);\n\t}\n\n\t/**\n\t * Feed one already-demuxed raw shred frame. `frame` is transferred zero-copy;\n\t * the caller must not access it afterward.\n\t *\n\t * Dropped (frame NOT transferred, so the caller's buffer stays intact) when the\n\t * in-flight window is full \u2014 i.e. the worker is more than `maxInflight` frames\n\t * behind. This is the memory bound: a slow/wedged consumer costs bounded\n\t * skip-to-live drops (see {@link droppedBackpressure}), never an unbounded\n\t * renderer-heap blow-up.\n\t */\n\tfeedShred(frame: ArrayBuffer): void {\n\t\tif (this.#disposed) return;\n\t\tif (this.#maxInflight > 0 && this.#sent - this.#processed >= this.#maxInflight) {\n\t\t\tthis.#droppedBackpressure++;\n\t\t\treturn;\n\t\t}\n\t\tthis.#sent++;\n\t\tthis.#post({ type: \"feedShred\", frame }, [frame]);\n\t}\n\n\t/** Explicitly drive loss recovery (in addition to the worker's internal timer). */\n\trecoverAll(nowMs: number = Date.now()): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#post({ type: \"recoverAll\", nowMs });\n\t}\n\n\t/** Register callback for recovered/completed FEC sets. `data` is a transferred buffer. */\n\tonRecovered(cb: (data: ArrayBuffer, meta: RecoveredMeta) => void): void {\n\t\tthis.#onRecovered = cb;\n\t}\n\n\t/** Register callback for the coalesced stats heartbeat. */\n\tonStats(cb: (stats: ShredWorkerStats) => void): void {\n\t\tthis.#onStats = cb;\n\t}\n\n\t/** Register callback for worker errors. */\n\tonError(cb: (message: string, code?: ShredWorkerErrorCode) => void): void {\n\t\tthis.#onError = cb;\n\t}\n\n\t/**\n\t * Frames dropped by the in-flight backpressure guard since construction\n\t * (monotonic). Surface this in the UI so sustained loss is visible rather than\n\t * silent \u2014 a rising counter means the consumer can't keep pace with ingest.\n\t */\n\tget droppedBackpressure(): number {\n\t\treturn this.#droppedBackpressure;\n\t}\n\n\t/**\n\t * Frames posted via {@link feedShred} but not yet reported drained by the\n\t * worker's last stats heartbeat (best-effort, heartbeat-granular; never < 0).\n\t */\n\tget inflight(): number {\n\t\treturn Math.max(0, this.#sent - this.#processed);\n\t}\n\n\t/** Dispose the worker. Idempotent. */\n\tdispose(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\ttry {\n\t\t\tthis.#worker.postMessage({ type: \"dispose\" } satisfies ShredWorkerCommand);\n\t\t} catch {\n\t\t\t/* worker may already be gone */\n\t\t}\n\t\tthis.#worker.terminate();\n\t}\n\n\t#post(cmd: ShredWorkerCommand, transfer: Transferable[] = []): void {\n\t\tthis.#worker.postMessage(cmd, transfer);\n\t}\n\n\t#handleEvent(event: ShredWorkerEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"recovered\":\n\t\t\t\tthis.#onRecovered?.(event.data, event.meta);\n\t\t\t\tbreak;\n\t\t\tcase \"stats\":\n\t\t\t\t// The worker's monotonic consumed-frame total is the drain signal for\n\t\t\t\t// the feedShred in-flight guard. shredsRx counts only valid frames;\n\t\t\t\t// malformed + versionMismatch are consumed off the queue too, so the\n\t\t\t\t// sum is the true \"frames taken off the queue\" and keeps the window\n\t\t\t\t// from inflating (and false-dropping) on a malformed/skewed source.\n\t\t\t\tthis.#processed =\n\t\t\t\t\tevent.stats.shredsRx + event.stats.malformed + event.stats.versionMismatch;\n\t\t\t\tthis.#onStats?.(event.stats);\n\t\t\t\tbreak;\n\t\t\tcase \"error\":\n\t\t\t\tif (event.code) this.#onError?.(event.message, event.code);\n\t\t\t\telse this.#onError?.(event.message);\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction formatWorkerError(event: ErrorEvent): string {\n\tconst parts = [\"Worker error\"];\n\tif (event.message) parts.push(event.message);\n\tif (event.filename) {\n\t\tconst loc = [event.filename];\n\t\tif (event.lineno) loc.push(String(event.lineno));\n\t\tif (event.colno) loc.push(String(event.colno));\n\t\tparts.push(`at ${loc.join(\":\")}`);\n\t}\n\tif (event.error instanceof Error && event.error.stack) parts.push(event.error.stack);\n\treturn parts.join(\": \");\n}\n", "/**\n * @blockcast/fec-worker \u2014 Worker-per-track FEC decode with zero-copy transfers\n *\n * Re-exports shared types and main-thread client.\n */\n\nexport type {\n\tFecWorkerCommand,\n\tFecWorkerEvent,\n\tFecWorkerErrorCode,\n\tFecTrackConfig,\n\tFecTrackStats,\n\tFecBlockSnapshot,\n\tFrameMeta,\n} from \"./fec-worker-types.js\";\n\nexport { FecWorkerClient } from \"./fec-worker-client.js\";\n\n// --- Shred (Reed-Solomon) codec \u2014 sibling to the RaptorQ/MMTP worker above.\n// Different FEC payload identity (slot/fecSet/localIndex vs flat ssId) \u2192 its\n// own command shape and worker; shares the transfer + client/worker pattern.\nexport type {\n\tShredWorkerCommand,\n\tShredWorkerEvent,\n\tShredWorkerConfig,\n\tShredWorkerStats,\n\tShredWorkerErrorCode,\n\tRecoveredMeta,\n\tSlotSample,\n} from \"./shred-worker-types.js\";\n\nexport { ShredWorkerClient } from \"./shred-worker-client.js\";\n", "/**\n * Zero-dependency helper for extracting the detached ALTA authenticator trailer\n * from an MMTP source packet.\n *\n * Lives in its own leaf package so both `@blockcast/mmt-container` (wire-path\n * parsing) and `@blockcast/fec-worker` (FEC-recovered-source parsing) can share\n * one implementation without creating a package cycle between them.\n *\n * Spec: ISO/IEC 23008-1:2023 + draft-ramadan-moq-mmt \u00A712 (detached trailer, Path 2).\n */\n\n// --- Private MMTP/MPU/MFU constants (ISO/IEC 23008-1 \u00A79.2.2\u20139.2.3) ---\n// Duplicated here as immutable spec values so this package stays a leaf with\n// zero `@blockcast/*` dependencies. Re-deriving from `mmt-container` would\n// reintroduce the package cycle this split exists to avoid.\n\nconst MMTP_HEADER_SIZE = 12\nconst MPU_HEADER_SIZE = 8\nconst MPU_LENGTH_FIELD_SIZE = 2\nconst MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE\nconst TIMED_MFU_DU_HEADER_SIZE = 14\nconst NONTIMED_MFU_DU_HEADER_SIZE = 4\nconst PACKET_TYPE_MPU = 0x00\n\n// --- Public ALTA trailer constants ---\n\n/**\n * MMTP header-extension ext_type assigned to the detached ALTA authenticator\n * trailer (Path 2 / 08-07). Carried in an `[ext_type][ext_length][auth]`\n * block appended after the MPU's declared payload bytes, before the 4-byte\n * Source FEC Payload ID. MMTP X-bit stays 0 \u2014 non-ALTA MMTP receivers treat\n * the trailer as bytes past `payload_length` and skip it (per ISO 23008-1\n * \u00A79.2.2 \"may be discarded without impacting correct processing\").\n */\nexport const MMTP_EXT_TYPE_ALTA_AUTH = 0xf0a1\n\n/** Fixed size of the trailer's `[ext_type][ext_length]` prefix (2 + 2 BE). */\nexport const MMTP_EXT_TRAILER_HEADER_SIZE = 4\n\n/** Result of extracting the detached ALTA authenticator from an MMTP packet. */\nexport interface AltaTrailer {\n /** Authenticator bytes (no ext_type/ext_length prefix, no trailing padding). */\n auth: Uint8Array\n /**\n * Wire-packet byte offset where the signed payload ends, i.e. where the\n * trailer begins. Caller uses this to produce\n * `payload = wirePacket.subarray(0, signedEnd)` for verify_detached.\n */\n signedEnd: number\n}\n\n/**\n * Extract the detached ALTA authenticator trailer from an MMTP source packet.\n *\n * Wire layout:\n * `[MMTP hdr][length(2)][remaining MPU hdr(6)][DU?][media][TRAILER][SS_ID?]`\n * `payload_length = 6 + DU bytes + media bytes`\n *\n * Returns `null` when:\n * - the packet is too small / malformed,\n * - `payload_length` cannot contain the remaining six MPU-header bytes,\n * - the declared payload overruns the packet,\n * - no bytes remain between `payload_length` and the SS_ID,\n * - the trailer's `ext_type` isn't {@link MMTP_EXT_TYPE_ALTA_AUTH},\n * - the declared `ext_length` would extend past the SS_ID boundary.\n *\n * Non-MPU packets (repair, signaling) always return `null`.\n *\n * @param options.skipFecId When true, do NOT reserve 4 trailing bytes for the\n * Source FEC Payload ID. Use on FEC-recovered symbols where the PID has\n * already been stripped upstream.\n */\nexport function extractAltaTrailer(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n): AltaTrailer | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n // ISO/IEC 23008-1:2023 Figure 8 places V=0 packet_type in the low six bits.\n const packetType = mmtpPacket[1]! & 0x3f\n if (packetType !== PACKET_TYPE_MPU) return null\n\n const fecType = (mmtpPacket[0]! >> 3) & 0x03\n let headerLength = MMTP_HEADER_SIZE\n if (fecType < 2 && (mmtpPacket[0]! & 0x20) !== 0) headerLength += 4\n if (fecType < 2 && (mmtpPacket[0]! & 0x02) !== 0) {\n if (headerLength + 4 > mmtpPacket.length) return null\n const extensionLength = new DataView(\n mmtpPacket.buffer,\n mmtpPacket.byteOffset + headerLength + 2,\n 2,\n ).getUint16(0, false)\n headerLength += 4 + extensionLength\n }\n if (headerLength + MPU_HEADER_SIZE > mmtpPacket.length) return null\n\n const payload = mmtpPacket.subarray(headerLength)\n\n // Inline a minimal MPU-header read \u2014 only the fields extractAltaTrailer needs.\n // payloadLength: 16 BE at bytes 0-1.\n // Byte 2 packs FT(4) | T(1) | FI(2) | A(1).\n const payloadLength = (payload[0]! << 8) | payload[1]!\n const flagsByte = payload[2]!\n const fragmentType = (flagsByte >> 4) & 0x0f\n const timed = ((flagsByte >> 3) & 0x01) === 1\n const fragmentationIndicator = (flagsByte >> 1) & 0x03\n\n // Only MFU packets (FT=2) carry the detached trailer.\n if (fragmentType !== 2) return null\n\n // FI=0/1 structurally carries a DU header; FI=2/3 does not. Require at\n // least one media byte after that deterministic layout so the bytes at the\n // declared boundary cannot impersonate ALTA for a header-only fragment.\n const dataUnitHeaderSize = fragmentationIndicator <= 1\n ? (timed ? TIMED_MFU_DU_HEADER_SIZE : NONTIMED_MFU_DU_HEADER_SIZE)\n : 0\n if (payloadLength <= MPU_HEADER_REMAINDER_SIZE + dataUnitHeaderSize) return null\n\n if (fecType > 1) return null\n const hasFecId = !options?.skipFecId && fecType === 1\n const payloadEnd = hasFecId ? payload.length - 4 : payload.length\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength\n if (declaredEnd > payloadEnd) return null\n\n if (declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE > payloadEnd) return null\n const extType = (payload[declaredEnd]! << 8) | payload[declaredEnd + 1]!\n if (extType !== MMTP_EXT_TYPE_ALTA_AUTH) return null\n const extLen = (payload[declaredEnd + 2]! << 8) | payload[declaredEnd + 3]!\n\n const authStart = declaredEnd + MMTP_EXT_TRAILER_HEADER_SIZE\n if (authStart + extLen !== payloadEnd) return null\n\n return {\n auth: payload.subarray(authStart, payloadEnd),\n signedEnd: headerLength + declaredEnd,\n }\n}\n", "import {\n MMTP_EXT_TYPE_ALTA_AUTH as _LEAF_MMTP_EXT_TYPE_ALTA_AUTH,\n MMTP_EXT_TRAILER_HEADER_SIZE as _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE,\n extractAltaTrailer as _leafExtractAltaTrailer,\n type AltaTrailer as _LeafAltaTrailer,\n} from '@blockcast/mmt-alta-parse'\n\n/**\n * @mmt/container - MMTP Parser\n *\n * MMTP (MMT Protocol) packet parser based on ISO/IEC 23008-1:2023 Section 9.2\n *\n * MMTP header format (12 bytes):\n * - Byte 0: V(2) | C(1) | FEC_type(2) | r(1) | X(1) | R(1)\n * - Byte 1: reserved(2) | packet_type(6)\n * - Bytes 2-3: packet_id (big-endian)\n * - Bytes 4-7: timestamp (big-endian, 32-bit)\n * - Bytes 8-11: sequence_number (big-endian)\n *\n * FEC_type values:\n * - 0: No FEC / source packet\n * - 1: FEC mode 0 (source with FEC payload ID)\n * - 2: FEC repair packet mode 0\n * - 3: FEC repair packet mode 1\n *\n * @packageDocumentation\n */\n\nexport interface MMTPHeader {\n version: number\n /** Packet type: 0x00=MPU, 0x02=Signaling, 0x03=Repair */\n packetType: number\n packetId: number\n timestamp: bigint\n sequenceNumber: number\n payloadLength: number\n fecType: number\n /** C flag: header carries a 32-bit packet_counter. */\n packetCounterFlag: boolean\n packetCounter?: number\n /** X flag: header carries an extension header and value. */\n extensionFlag: boolean\n extension?: { type: number; data: Uint8Array }\n /** Byte offset at which the MMTP payload begins. */\n headerLength: number\n /** Random Access Point flag - indicates keyframe (ISO 23008-1, byte 0 bit 0) */\n rapFlag: boolean\n}\n\nexport interface MPUHeader {\n mpuSequenceNumber: number\n fragmentationIndicator: number // FI: 0=complete, 1=first, 2=middle, 3=last\n /** 8-bit MPU fragment_counter from byte 3. */\n fragmentCounter: number\n /** T flag: 1=timed media (14-byte MFU DU header), 0=non-timed (4-byte header) */\n timedFlag: number\n /** A flag: aggregated MPU payload. This strict profile requires A=0. */\n aggregationFlag: boolean\n /** MPU payload_length (bytes 0-1): bytes after the length field, including\n * the remaining six MPU-header bytes and all data-unit bytes. */\n payloadLength: number\n}\n\nexport interface MFUDuHeader {\n movieFragmentSequenceNumber: number\n sampleNumber: number\n offset: number\n priority: number\n dependencyCounter: number\n}\n\nexport interface MMTPPacket {\n header: MMTPHeader\n payload: Uint8Array\n}\n\nexport const MMTP_HEADER_SIZE = 12\nexport const MPU_HEADER_SIZE = 8\nexport const MFU_DU_HEADER_SIZE_TIMED = 14 // T=1: timed media (video/audio samples)\nexport const MFU_DU_HEADER_SIZE_NONTIMED = 4 // T=0: non-timed media (item_id only)\n\nconst MPU_LENGTH_FIELD_SIZE = 2\nconst MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE\nconst SOURCE_FEC_PAYLOAD_ID_SIZE = 4\n\n// MMTP Packet Types (ISO/IEC 23008-1:2023 Section 9.2.2)\nexport const PACKET_TYPE_MPU = 0x00\nexport const PACKET_TYPE_SIGNALING = 0x02\nexport const PACKET_TYPE_REPAIR = 0x03\n\n// AL-FEC Signaling Message ID (ISO/IEC 23008-1:2023 Amendment 1:2025)\nexport const ALFEC_MESSAGE_ID = 0x0203\n\n// FEC Code IDs\nexport const FEC_CODE_XOR = 1\nexport const FEC_CODE_RAPTORQ = 2\n\n/**\n * Parse MMTP header from buffer\n */\nexport function parseMMTPHeader(buffer: Uint8Array): MMTPHeader {\n if (buffer.length < MMTP_HEADER_SIZE) {\n throw new Error(`MMTP packet too small: ${buffer.length} bytes`)\n }\n\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n\n const version = (buffer[0]! >> 6) & 0x03\n if (version !== 0) {\n throw new Error(`Unsupported MMTP version: ${version}`)\n }\n\n // Extract FEC type from byte 0, bits 3-4\n const fecType = (buffer[0]! >> 3) & 0x03\n const packetCounterFlag = (buffer[0]! & 0x20) !== 0\n const extensionFlag = (buffer[0]! & 0x02) !== 0\n\n // Extract RAP flag from byte 0, bit 0 (R bit - Random Access Point)\n const rapFlag = (buffer[0]! & 0x01) === 1\n\n // ISO/IEC 23008-1:2023 Figure 8 (V=0) places reserved(2) before\n // packet_type(6), so packet_type occupies the low six bits of byte 1.\n const packetType = buffer[1]! & 0x3F\n\n let offset = 2\n\n // Packet ID (2 bytes, big-endian)\n const packetId = view.getUint16(offset, false)\n offset += 2\n\n // Timestamp (4 bytes, big-endian)\n const timestamp = BigInt(view.getUint32(offset, false))\n offset += 4\n\n // Sequence number (4 bytes, big-endian)\n const sequenceNumber = view.getUint32(offset, false)\n offset += 4\n\n let packetCounter: number | undefined\n if (packetCounterFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in packet_counter')\n packetCounter = view.getUint32(offset, false)\n offset += 4\n }\n\n let extension: { type: number; data: Uint8Array } | undefined\n if (extensionFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in header extension')\n const type = view.getUint16(offset, false)\n const length = view.getUint16(offset + 2, false)\n offset += 4\n if (offset + length > buffer.length) throw new Error('MMTP packet truncated in header extension value')\n extension = { type, data: buffer.subarray(offset, offset + length) }\n offset += length\n }\n\n return {\n version,\n packetType,\n packetId,\n timestamp,\n sequenceNumber,\n payloadLength: buffer.length - offset,\n fecType,\n packetCounterFlag,\n packetCounter,\n extensionFlag,\n extension,\n headerLength: offset,\n rapFlag,\n }\n}\n\n/**\n * Unwrap MMTP packet to extract payload\n */\nexport function unwrapMMTP(buffer: Uint8Array): MMTPPacket {\n const header = parseMMTPHeader(buffer)\n const payload = buffer.subarray(header.headerLength)\n\n return { header, payload }\n}\n\n/**\n * Parse MPU header from payload\n * ISO/IEC 23008-1:2023 Section 9.2.3.3\n *\n * MPU Header format (8 bytes) as written by FFmpeg/libmmt:\n * - Bytes 0-1: payload_length (16 bits, big-endian)\n * - Byte 2: FT(4) | T(1) | FI(2) | A(1)\n * - Byte 3: fragment_counter (8 bits)\n * - Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n */\nexport function parseMPUHeader(payload: Uint8Array): MPUHeader {\n if (payload.length < MPU_HEADER_SIZE) {\n throw new Error(`Payload too small for MPU header: ${payload.length} bytes`)\n }\n\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength)\n\n // Bytes 0-1: payload_length (16 bits, big-endian)\n const payloadLength = view.getUint16(0, false)\n\n // Byte 2: FT(4) | T(1) | FI(2) | A(1)\n const flagsByte = payload[2]!\n const timedFlag = (flagsByte >> 3) & 0x01\n const fragmentationIndicator = (flagsByte >> 1) & 0x03\n const aggregationFlag = (flagsByte & 0x01) !== 0\n\n // Byte 3: fragment_counter (8 bits). The high bit is part of the counter in\n // the current libmmt/FFmpeg writer ABI.\n const fragmentCounter = payload[3]!\n\n // Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n const mpuSequenceNumber = view.getUint32(4, false)\n\n return {\n mpuSequenceNumber,\n fragmentationIndicator,\n fragmentCounter,\n timedFlag,\n aggregationFlag,\n payloadLength,\n }\n}\n\nexport function parseTimedMfuDuHeader(payload: Uint8Array, offset: number): MFUDuHeader | null {\n if (offset < 0 || offset + MFU_DU_HEADER_SIZE_TIMED > payload.length) return null\n const view = new DataView(payload.buffer, payload.byteOffset + offset, MFU_DU_HEADER_SIZE_TIMED)\n return {\n movieFragmentSequenceNumber: view.getUint32(0, false),\n sampleNumber: view.getUint32(4, false),\n offset: view.getUint32(8, false),\n priority: payload[offset + 12]!,\n dependencyCounter: payload[offset + 13]!,\n }\n}\n\n/**\n * Why `extractMFUPayload` refused a packet.\n *\n * These identifiers are part of the public contract: callers surface them in\n * diagnostics, so renaming one is a breaking change.\n */\nexport type MFURejectionGate =\n | 'packet-shorter-than-mmtp-plus-mpu-header'\n | 'mmtp-header-unparseable'\n | 'not-an-mpu-packet'\n | 'payload-shorter-than-mpu-header'\n | 'aggregation-flag-set'\n | 'mpu-payload-length-below-header-remainder'\n | 'mpu-declared-end-exceeds-payload'\n | 'mfu-offset-at-or-past-declared-end'\n | 'source-fec-payload-id-width-mismatch'\n | 'declared-trailers-unresolved'\n\nexport interface MFURejection {\n gate: MFURejectionGate\n /** Human-readable detail, including the byte arithmetic that decided it. */\n detail: string\n}\n\ninterface MfuPayloadLayout {\n hasMfuDuHeader: boolean\n mfuOffset: number\n dataEnd: number\n}\n\nfunction resolveMfuPayloadLayout(\n mpu: MPUHeader,\n fragmentType: number,\n payload: Uint8Array,\n): { layout: MfuPayloadLayout } | { rejection: MFURejection } {\n if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE) {\n return {\n rejection: {\n gate: 'mpu-payload-length-below-header-remainder',\n detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE}`,\n },\n }\n }\n\n // ISO/IEC 23008-1 defines payload_length from immediately after its own\n // two-byte field. It therefore includes the remaining six MPU-header bytes.\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + mpu.payloadLength\n if (declaredEnd > payload.length) {\n return {\n rejection: {\n gate: 'mpu-declared-end-exceeds-payload',\n detail:\n `payload_length=${mpu.payloadLength} implies end=${declaredEnd}, ` +\n `payload is ${payload.length} bytes`,\n },\n }\n }\n\n const hasHeader = fragmentType === 2 && mpu.fragmentationIndicator <= 1\n const mfuHeaderSize = mpu.timedFlag ? MFU_DU_HEADER_SIZE_TIMED : MFU_DU_HEADER_SIZE_NONTIMED\n const mfuOffset = MPU_HEADER_SIZE + (hasHeader ? mfuHeaderSize : 0)\n if (mfuOffset >= declaredEnd) {\n return {\n rejection: {\n gate: 'mfu-offset-at-or-past-declared-end',\n detail: `mfuOffset=${mfuOffset} >= declaredEnd=${declaredEnd}`,\n },\n }\n }\n\n return {\n layout: {\n hasMfuDuHeader: hasHeader,\n mfuOffset,\n dataEnd: declaredEnd,\n },\n }\n}\n\nfunction isEmptyOrExactAltaTrailer(payload: Uint8Array, start: number, end: number): boolean {\n if (start === end) return true\n if (start < 0 || end > payload.length || start + _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE > end) {\n return false\n }\n\n const view = new DataView(\n payload.buffer,\n payload.byteOffset + start,\n end - start,\n )\n if (view.getUint16(0, false) !== _LEAF_MMTP_EXT_TYPE_ALTA_AUTH) return false\n\n const authLength = view.getUint16(2, false)\n return start + _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE + authLength === end\n}\n\ninterface DeclaredTrailerLayout {\n hasFecId: boolean\n}\n\nfunction resolveDeclaredTrailers(\n payload: Uint8Array,\n declaredEnd: number,\n fecType: number,\n options?: { skipFecId?: boolean },\n): DeclaredTrailerLayout | null {\n if (declaredEnd > payload.length) return null\n\n if (options?.skipFecId) {\n return isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length)\n ? { hasFecId: false }\n : null\n }\n\n if (fecType === 1) {\n const fecStart = payload.length - SOURCE_FEC_PAYLOAD_ID_SIZE\n if (fecStart < declaredEnd || !isEmptyOrExactAltaTrailer(payload, declaredEnd, fecStart)) {\n return null\n }\n return { hasFecId: true }\n }\n if (fecType !== 0) return null\n\n if (isEmptyOrExactAltaTrailer(payload, declaredEnd, payload.length)) {\n return { hasFecId: false }\n }\n\n return null\n}\n\n/**\n * Extract NAL unit payload from MMTP packet\n */\nexport function extractNALUnit(mmtpPacket: Uint8Array): Uint8Array {\n const extraction = extractMFUPayload(mmtpPacket)\n if (!extraction) throw new Error('Malformed MMTP MPU payload')\n return extraction.data\n}\n\n/** Result of extracting MFU payload from an MMTP packet */\nexport interface MFUExtraction {\n /** MMTP header fields */\n header: MMTPHeader\n /** MPU header fields */\n mpu: MPUHeader\n /** Fragment type (FT field, 4 bits): 0=metadata, 2=MFU data */\n fragmentType: number\n /** MFU payload data (video/audio bytes, headers stripped, FEC ID excluded) */\n data: Uint8Array\n /** Parsed timed MFU DU header when present, otherwise null */\n mfuDuHeader: MFUDuHeader | null\n /** Non-timed MFU Item_ID when the 4-byte DU header is present. */\n itemId: number | null\n /** Source FEC Payload ID if present (`FEC_type == 1`), or null */\n fecPayloadId: { ssId: number } | null\n}\n\n/**\n * Extract MFU payload from a raw MMTP packet.\n *\n * Handles all the byte arithmetic for:\n * - MMTP header (12 bytes)\n * - MPU sub-header (8 bytes, payloadLength field)\n * - MFU DU header (14 bytes timed / 4 bytes non-timed, only for FI \u2264 1)\n * - Source FEC Payload ID (4 bytes at end, only when `FEC_type == 1`)\n *\n * `payloadLength` is the ISO length-after-field value: six remaining MPU-header\n * bytes, then the MFU DU header when present, then media. ALTA and Source FEC\n * trailers are outside that declared span and must consume the exact suffix.\n *\n * @param mmtpPacket Raw MMTP packet (from multicast or MoQ)\n * @param options.skipFecId When true, do NOT subtract the 4-byte Source FEC Payload ID\n * from the payload boundary, even if fecType >= 1 in the MMTP header. Use this for\n * FEC-recovered symbols where the FEC PID was already stripped before WASM decoding\n * but the fecType bits in byte 0 still reflect the original packet (FEC Type=1 per\n * draft-ramadan-moq-mmt-00 \u00A73.1).\n * @returns Extracted MFU data + metadata, or null if packet is too small\n */\ninterface MfuPlan {\n header: MMTPHeader\n payload: Uint8Array\n mpu: MPUHeader\n fragmentType: number\n layout: MfuPayloadLayout\n trailerLayout: DeclaredTrailerLayout\n}\n\n/**\n * Single source of truth for whether a packet yields an MFU payload.\n *\n * `extractMFUPayload` and `explainMFURejection` both delegate here, so the\n * reason reported to operators can never drift from the decision the parser\n * actually made.\n */\nfunction planMfuExtraction(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n): { plan: MfuPlan } | { rejection: MFURejection } {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'packet-shorter-than-mmtp-plus-mpu-header',\n detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE + MPU_HEADER_SIZE}`,\n },\n }\n }\n\n let header: MMTPHeader\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch (error) {\n return {\n rejection: {\n gate: 'mmtp-header-unparseable',\n detail: error instanceof Error ? error.message : String(error),\n },\n }\n }\n if (header.packetType !== PACKET_TYPE_MPU) {\n return {\n rejection: {\n gate: 'not-an-mpu-packet',\n detail: `packetType=0x${header.packetType.toString(16).padStart(2, '0')}`,\n },\n }\n }\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'payload-shorter-than-mpu-header',\n detail: `${payload.length} bytes < ${MPU_HEADER_SIZE}`,\n },\n }\n }\n\n const mpu = parseMPUHeader(payload)\n if (mpu.aggregationFlag) {\n return {\n rejection: { gate: 'aggregation-flag-set', detail: 'strict profile requires A=0' },\n }\n }\n\n // Fragment type from byte 2 upper nibble (not returned by parseMPUHeader)\n const fragmentType = (payload[2]! >> 4) & 0x0f\n\n const layoutResult = resolveMfuPayloadLayout(mpu, fragmentType, payload)\n if ('rejection' in layoutResult) return layoutResult\n const { layout } = layoutResult\n\n const trailerLayout = resolveDeclaredTrailers(\n payload,\n layout.dataEnd,\n header.fecType,\n options,\n )\n if (!trailerLayout) {\n // The most common cause is a producer whose Source FEC Payload ID width\n // disagrees with ISO/IEC 23008-1 \u00A7C.5.2. Name that explicitly rather than\n // leaving operators to infer it from a generic trailer error.\n if (!options?.skipFecId && header.fecType === 1) {\n const observed = payload.length - layout.dataEnd\n if (observed !== SOURCE_FEC_PAYLOAD_ID_SIZE) {\n return {\n rejection: {\n gate: 'source-fec-payload-id-width-mismatch',\n detail:\n `producer wrote a ${observed}-byte Source FEC Payload ID, ` +\n `ISO/IEC 23008-1 \u00A7C.5.2 requires ${SOURCE_FEC_PAYLOAD_ID_SIZE} ` +\n `(payload=${payload.length}, declaredEnd=${layout.dataEnd})`,\n },\n }\n }\n }\n return {\n rejection: {\n gate: 'declared-trailers-unresolved',\n detail:\n `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty ` +\n `nor an exact ALTA trailer (fecType=${header.fecType})`,\n },\n }\n }\n\n return { plan: { header, payload, mpu, fragmentType, layout, trailerLayout } }\n}\n\n/**\n * Report why `extractMFUPayload` would refuse this packet, or null if it would\n * accept it.\n *\n * `extractMFUPayload` returns bare `null` on ten distinct conditions, which\n * makes a producer/consumer framing disagreement indistinguishable from a\n * publisher that never started \u2014 both render as silence. Use this wherever\n * that distinction matters: diagnostics, contract tests, and operator-facing\n * logs.\n */\nexport function explainMFURejection(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n): MFURejection | null {\n const result = planMfuExtraction(mmtpPacket, options)\n return 'rejection' in result ? result.rejection : null\n}\n\nexport function extractMFUPayload(\n mmtpPacket: Uint8Array,\n options?: { skipFecId?: boolean },\n): MFUExtraction | null {\n const result = planMfuExtraction(mmtpPacket, options)\n if ('rejection' in result) return null\n const { header, payload, mpu, fragmentType, layout, trailerLayout } = result.plan\n\n const mfuDuHeaderPresent = layout.hasMfuDuHeader\n const mfuOffset = layout.mfuOffset\n const dataEnd = layout.dataEnd\n\n const mfuDuHeader =\n mfuDuHeaderPresent && mpu.timedFlag === 1\n ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE)\n : null\n const itemId =\n mfuDuHeaderPresent && mpu.timedFlag === 0\n ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false)\n : null\n\n const data = payload.subarray(mfuOffset, dataEnd)\n\n const fecPayloadId = trailerLayout.hasFecId\n ? {\n ssId: new DataView(\n mmtpPacket.buffer,\n mmtpPacket.byteOffset + mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE,\n SOURCE_FEC_PAYLOAD_ID_SIZE,\n ).getUint32(0, false),\n }\n : null\n\n return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId }\n}\n\n/**\n * Extract timestamp from MMTP packet\n */\nexport function extractTimestamp(mmtpPacket: Uint8Array): bigint {\n const { header } = unwrapMMTP(mmtpPacket)\n return header.timestamp\n}\n\n/**\n * Check if MMTP packet is an FEC repair packet\n * FEC_type 2 and 3 are repair packets\n */\nexport function isFecRepairPacket(mmtpPacket: Uint8Array): boolean {\n if (mmtpPacket.length < 1) return false\n const fecType = (mmtpPacket[0]! >> 3) & 0x03\n return fecType >= 2\n}\n\n/**\n * Extract Source FEC Payload ID (SS_ID) from the last 4 bytes of an MMTP\n * source packet per ISO/IEC 23008-1:2023 Section C.5.2 Figure C.10.\n *\n * SS_ID is a flat 32-bit monotonic counter (incremented by 1 per packet\n * for ssbg_mode0 and ssbg_mode1). Block membership and ESI are derived\n * at recovery time from repair packet SS_Start and SSB_length:\n * SBN = floor(SS_ID / K), ESI = SS_ID % K\n * SS_Start = SBN * K (first SS_ID of the block)\n *\n * This matches FFmpeg moqenc_mmt.c, the drafts (draft-ramadan-moq-fec \u00A76.2),\n * and ATSC A/331 \u00A78.1.2.5 which defers to ISO 23008-1 Annex C.\n */\nexport function extractFecPayloadId(mmtpPacket: Uint8Array): { ssId: number } | null {\n if (!hasSourceFecPayloadId(mmtpPacket)) return null\n // Flat 4-byte SS_ID: encoder writes block_id * K + symbol_id\n const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE\n const view = new DataView(\n mmtpPacket.buffer,\n mmtpPacket.byteOffset + off,\n SOURCE_FEC_PAYLOAD_ID_SIZE,\n )\n return { ssId: view.getUint32(0, false) }\n}\n\n/**\n * Return true when FEC_type=1 and the exact suffix after the ISO-declared MPU\n * payload contains a four-byte Source FEC Payload ID.\n */\nexport function hasSourceFecPayloadId(mmtpPacket: Uint8Array): boolean {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return false\n\n let header: MMTPHeader\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch {\n return false\n }\n if (header.packetType !== PACKET_TYPE_MPU) return false\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) return false\n const mpu = parseMPUHeader(payload)\n if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE) return false\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + mpu.payloadLength\n return resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId ?? false\n}\n\n/**\n * MMTP header-extension ext_type assigned to the detached ALTA authenticator\n * trailer (Path 2 / 08-07). Carried in an `[ext_type][ext_length][auth]`\n * block appended after the MPU's declared payload bytes, before the 4-byte\n * Source FEC Payload ID. MMTP X-bit stays 0 \u2014 non-ALTA MMTP receivers treat\n * the trailer as bytes past `payload_length` and skip it (per ISO 23008-1\n * \u00A79.2.2 \"may be discarded without impacting correct processing\").\n */\n// Re-export the ALTA trailer helper from the zero-dep leaf package. The leaf\n// owns the implementation so container and FEC consumers share one parser\n// without creating a package cycle.\nexport const MMTP_EXT_TYPE_ALTA_AUTH = _LEAF_MMTP_EXT_TYPE_ALTA_AUTH\nexport const MMTP_EXT_TRAILER_HEADER_SIZE = _LEAF_MMTP_EXT_TRAILER_HEADER_SIZE\nexport type AltaTrailer = _LeafAltaTrailer\nexport const extractAltaTrailer = _leafExtractAltaTrailer\n\n/** Size of Repair FEC Payload ID per ISO/IEC 23008-1 Section C.5.3 */\nexport const REPAIR_FEC_PID_SIZE = 13\n\n/** Parse the 13-byte Repair FEC Payload ID after the complete dynamic MMTP header. */\nfunction readRepairFecPayloadIdFields(mmtpPacket: Uint8Array): {\n packetId: number; ssStart: number; rsbLength: number; rsId: number; ssbLength: number; dataOffset: number\n} | null {\n let header: MMTPHeader\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2) return null\n\n const off = header.headerLength\n if (mmtpPacket.length < off + REPAIR_FEC_PID_SIZE) return null\n const view = new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + off, REPAIR_FEC_PID_SIZE)\n const ssStart = view.getUint32(0, false) // 32 bits\n const rsbLength = (mmtpPacket[off + 4]! << 16) | (mmtpPacket[off + 5]! << 8) | mmtpPacket[off + 6]! // 24 bits\n const rsId = (mmtpPacket[off + 7]! << 16) | (mmtpPacket[off + 8]! << 8) | mmtpPacket[off + 9]! // 24 bits\n const ssbLength = (mmtpPacket[off + 10]! << 16) | (mmtpPacket[off + 11]! << 8) | mmtpPacket[off + 12]! // 24 bits\n return {\n packetId: header.packetId,\n ssStart,\n rsbLength,\n rsId,\n ssbLength,\n dataOffset: off + REPAIR_FEC_PID_SIZE,\n }\n}\n\n/**\n * Extract Repair FEC Payload ID from a raw-multicast MMTP repair packet.\n * Per ISO/IEC 23008-1:2023 Section C.5.3 (ssbg_mode0, one-stage, no FFSRP_TS):\n * [MMTP header][SS_Start:4][RSB_length:3][RS_ID:3][SSB_length:3][repair data]\n * OTI is delivered via AL-FEC signaling message (Table C.3), not per-packet.\n *\n * Byte 1 is reserved(2)|packet_type(6) for V=0, so repair type 0x03 is 0x03.\n * Pre-migration high-bits bytes (0x0c) are rejected \u2014 no compatibility decode.\n */\nexport function extractRepairFecPayloadId(mmtpPacket: Uint8Array): {\n ssStart: number; rsbLength: number; rsId: number; ssbLength: number\n} | null {\n const fields = readRepairFecPayloadIdFields(mmtpPacket)\n if (!fields) return null\n return {\n ssStart: fields.ssStart,\n rsbLength: fields.rsbLength,\n rsId: fields.rsId,\n ssbLength: fields.ssbLength,\n }\n}\n\n/** Parsed MoQ repair frame per ISO/IEC 23008-1 Section C.4.3 + C.5.3 */\nexport interface MoqRepairFrame {\n /** MMTP packetId (1=video, 2=audio) \u2014 for routing to correct FEC decoder */\n packetId: number\n /** SS_Start (32 bits): SS_ID of first source symbol in block */\n ssStart: number\n /** RSB_length (24 bits): number of repair symbols (P) */\n rsbLength: number\n /** RS_ID (24 bits): repair symbol index (0-based) */\n rsId: number\n /** SSB_length (24 bits): number of source symbols (K) */\n ssbLength: number\n /** Repair symbol data after the dynamic MMTP header and 13-byte FEC payload ID. */\n symbolData: Uint8Array\n}\n\n/**\n * Parse a MoQ repair frame (ISO/IEC 23008-1 Annex C format).\n *\n * Wire format per ISO 23008-1 \u00A7C.4.3 + \u00A7C.5.3:\n * MMTP Header: fixed 12 bytes plus C/X optional fields\n * then SS_Start (4 bytes) \u2192 block identifier\n * then RSB_length (3 bytes) \u2192 P\n * then RS_ID (3 bytes) \u2192 ESI = K + RS_ID\n * then SSB_length (3 bytes) \u2192 K\n * then Repair Symbol Data (T bytes)\n *\n * @param frame - Raw MoQ repair frame bytes\n * @returns Parsed frame or null if invalid\n */\nexport function parseMoqRepairFrame(frame: Uint8Array): MoqRepairFrame | null {\n const repairId = readRepairFecPayloadIdFields(frame)\n if (!repairId) return null\n\n const symbolData = frame.subarray(repairId.dataOffset)\n if (\n repairId.rsbLength === 0 ||\n repairId.ssbLength === 0 ||\n repairId.rsId >= repairId.rsbLength ||\n symbolData.length === 0\n ) return null\n\n return {\n packetId: repairId.packetId,\n ssStart: repairId.ssStart,\n rsbLength: repairId.rsbLength,\n rsId: repairId.rsId,\n ssbLength: repairId.ssbLength,\n symbolData,\n }\n}\n\n/**\n * AL-FEC signaling configuration parsed from ISO/IEC 23008-1:2023 Table C.3 message.\n */\nexport interface AlFecConfig {\n fecCodeId: number\n fecPayloadIdMode: 0\n repairSymbolSize: number\n maximumK: number\n maximumP: number\n interleaveDepth: number\n oti: Uint8Array\n}\n\nfunction canonicalRaptorQOtiK(oti: Uint8Array, repairSymbolSize: number): number | null {\n if (oti.length !== 12 || oti[5] !== 0) return null\n const transferLength = (oti[0]! * 0x1_0000_0000) +\n (((oti[1]! << 24) | (oti[2]! << 16) | (oti[3]! << 8) | oti[4]!) >>> 0)\n const symbolSize = (oti[6]! << 8) | oti[7]!\n const sourceBlocks = oti[8]!\n const subBlocks = (oti[9]! << 8) | oti[10]!\n const alignment = oti[11]!\n if (!Number.isSafeInteger(transferLength) || transferLength <= 0 ||\n symbolSize !== repairSymbolSize || symbolSize === 0 || symbolSize % 8 !== 0 ||\n sourceBlocks !== 1 || subBlocks !== 1 || alignment !== 8 ||\n transferLength % symbolSize !== 0) {\n return null\n }\n const k = transferLength / symbolSize\n return Number.isInteger(k) && k >= 1 && k <= 56_403 ? k : null\n}\n\n/**\n * Parse AL-FEC signaling message from a full MMTP signaling packet.\n *\n * Wire format (ISO/IEC 23008-1:2023 Table C.3, Amendment 1:2025):\n * [12-byte MMTP header (packet_type=0x02)]\n * message_id(2) = 0x0203\n * version(1)\n * message_length(2) = length of remaining payload\n * flags(1) = fec_flag(1) | private_fec_flag(1) | reserved(6)\n * fec_flow_descriptor:\n * length(2)\n * num_flows(1)\n * per flow:\n * fec_flow_id(1), source_flow_id(1), num_assets(1), packet_ids(2*N),\n * coding_params(1), repair_symbol_size(2),\n * [one-stage]: repair_flow_id(2), fec_code_id(1),\n * [private_fec_flag]: private_header(1){flag(1)|len(7)},\n * current RaptorQ profile OTI(12) + interleave_depth(1)\n * max_k(3), max_p(3), protection_window_time(4), protection_window_size(4)\n *\n * @param mmtpPacket Full MMTP packet including 12-byte header\n * @returns Parsed config or null if not an AL-FEC signaling message\n */\nexport function parseAlFecMessage(mmtpPacket: Uint8Array): AlFecConfig | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + 9) return null\n let header: MMTPHeader\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n if (header.packetType !== PACKET_TYPE_SIGNALING || header.fecType !== 0) return null\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < 9) return null\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength)\n let off = 0\n\n const messageId = view.getUint16(off, false); off += 2\n if (messageId !== ALFEC_MESSAGE_ID) return null\n const version = payload[off]!; off += 1\n const messageLength = view.getUint16(off, false); off += 2\n if (version !== 1 || messageLength === 0 || messageLength !== payload.length - off) return null\n\n const flags = payload[off]!; off += 1\n const fecFlag = (flags >> 7) & 1\n const privateFecFlag = (flags >> 6) & 1\n if (fecFlag !== 1 || privateFecFlag !== 1 || (flags & 0x3f) !== 0x3f) return null\n\n if (off + 2 > payload.length) return null\n const descriptorLength = view.getUint16(off, false); off += 2\n const descriptorEnd = off + descriptorLength\n if (descriptorLength === 0 || descriptorEnd !== payload.length) return null\n\n const numFlows = payload[off]!; off += 1\n if (numFlows !== 1 || off + 3 > descriptorEnd) return null\n\n off += 2 // fec_flow_id, source_flow_id\n const numAssets = payload[off]!; off += 1\n if (numAssets === 0 || off + (numAssets * 2) > descriptorEnd) return null\n off += numAssets * 2\n\n if (off + 3 > descriptorEnd) return null\n const codingParams = payload[off]!; off += 1\n const fecCodingStructure = (codingParams >> 4) & 0x0f\n const ssbgMode = (codingParams >> 2) & 0x03\n const ffsrptsFlag = (codingParams >> 1) & 0x01\n const fecPayloadIdMode = codingParams & 0x01\n if (fecCodingStructure !== 1 || ssbgMode !== 0 || ffsrptsFlag !== 0 || fecPayloadIdMode !== 0) {\n return null\n }\n const repairSymbolSize = view.getUint16(off, false); off += 2\n if (repairSymbolSize === 0 || repairSymbolSize % 8 !== 0) return null\n\n if (off + 4 > descriptorEnd) return null\n off += 2 // Amendment 1:2025 replaces Table C.3 with a 16-bit repair_flow_id\n const fecCodeId = payload[off]!; off += 1\n if (fecCodeId !== FEC_CODE_RAPTORQ) return null\n const privateHeader = payload[off]!; off += 1\n const privateFlag = privateHeader >> 7\n const privateFieldLength = privateHeader & 0x7f\n if (privateFlag !== 1 || privateFieldLength !== 13 || off + 13 > descriptorEnd) return null\n const oti = payload.slice(off, off + 12); off += 12\n const interleaveDepth = payload[off]!; off += 1\n const otiK = canonicalRaptorQOtiK(oti, repairSymbolSize)\n if (otiK === null || interleaveDepth === 0) return null\n\n if (off + 14 !== descriptorEnd) return null\n const maximumK = (payload[off]! << 16) | (payload[off + 1]! << 8) | payload[off + 2]!\n off += 3\n const maximumP = (payload[off]! << 16) | (payload[off + 1]! << 8) | payload[off + 2]!\n off += 3\n if (maximumK !== otiK || maximumP === 0 || maximumK + maximumP - 1 > 0x00ff_ffff) {\n return null\n }\n off += 8 // protection_window_time, protection_window_size\n if (off !== descriptorEnd) return null\n\n return { fecCodeId, fecPayloadIdMode, repairSymbolSize, maximumK, maximumP, interleaveDepth, oti }\n}\n", "/**\n * @mmt/container - MFU Fragment Reassembler\n *\n * Handles authored fragmented MFUs (Media Fragment Units) according to\n * ISO/IEC 23008-1:2023 Section 9.2.3.3. Fragmented runs are admitted in the\n * exact FI/fragment_counter countdown carried on the wire. Transport-run\n * association additionally requires the repository's contiguous-source\n * producer profile; ISO-permitted interleaving remains incomplete and fails\n * closed.\n *\n * Fragmentation Indicator (FI) values:\n * - 0: Complete MFU (single packet, no fragmentation)\n * - 1: First fragment\n * - 2: Middle fragment\n * - 3: Last fragment\n *\n * @packageDocumentation\n */\n\nimport {\n extractMFUPayload,\n isFecRepairPacket,\n} from '@blockcast/mmt-base'\n\n/** Source transport admitted by the MMTP router. */\nexport type MmtpSourcePath = 'multicast' | 'moq'\n\n/**\n * Strict whole-frame delivery provenance, folded over EVERY contributing\n * fragment rather than the head fragment alone.\n *\n * This is deliberately NOT `source`. `source` answers a routing question \u2014\n * \"is this the reliable-MoQ copy that must not confirm multicast takeover?\" \u2014\n * and head-fragment-wins is the documented, deterministic answer to it.\n * Provenance answers an evidence question: \"did every byte of this frame\n * physically arrive over multicast?\" A frame with a multicast head and a\n * QUIC-delivered tail is a legitimate routing win for `source: 'multicast'`\n * and is emphatically NOT multicast-exclusive delivery.\n *\n * - 'multicast' \u2014 every contributing fragment was explicitly multicast.\n * The ONLY value that may be counted as physical-delivery evidence.\n * - 'mixed' \u2014 at least one contributing fragment arrived over MoQ.\n * - 'unknown' \u2014 no fragment arrived over MoQ, but at least one carried no\n * attribution at all. Absence is preserved as its own state rather than\n * defaulted, so a consumer cannot mistake \"never recorded\" for \"proven\".\n */\nexport type MmtpDeliveryProvenance = 'multicast' | 'mixed' | 'unknown'\n\n/**\n * Fold one fragment's `source` into a running provenance verdict.\n *\n * Fail-closed, order-independent and associative: 'mixed' absorbs everything,\n * a missing attribution degrades 'multicast' to 'unknown', and nothing ever\n * upgrades a verdict. Fold over an empty set is 'unknown' \u2014 no fragment ever\n * proved anything.\n */\nexport function foldDeliveryProvenance(\n verdict: MmtpDeliveryProvenance,\n source: MmtpSourcePath | undefined,\n): MmtpDeliveryProvenance {\n if (verdict === 'mixed') return 'mixed'\n if (source === 'moq') return 'mixed'\n if (source === undefined) return 'unknown'\n return verdict\n}\n\n/**\n * One fragment's own provenance.\n *\n * An explicit `deliveryProvenance` wins: FEC-recovered fragments carry no\n * `source` (they were never delivered as themselves) but their block's symbol\n * provenance IS known, and the router supplies it. Falling back to `source`\n * keeps directly-delivered fragments working unchanged.\n */\nfunction fragmentProvenance(fragment: {\n source?: MmtpSourcePath\n deliveryProvenance?: MmtpDeliveryProvenance\n}): MmtpDeliveryProvenance {\n if (fragment.deliveryProvenance !== undefined) return fragment.deliveryProvenance\n if (fragment.source === 'moq') return 'mixed'\n if (fragment.source === 'multicast') return 'multicast'\n return 'unknown'\n}\n\n/** Strict provenance for a complete fragment set. */\nexport function deliveryProvenanceOf(\n fragments: readonly {\n source?: MmtpSourcePath\n deliveryProvenance?: MmtpDeliveryProvenance\n }[],\n): MmtpDeliveryProvenance {\n if (fragments.length === 0) return 'unknown'\n let verdict: MmtpDeliveryProvenance = 'multicast'\n for (const fragment of fragments) {\n const own = fragmentProvenance(fragment)\n verdict =\n verdict === 'mixed' || own === 'mixed'\n ? 'mixed'\n : verdict === 'unknown' || own === 'unknown'\n ? 'unknown'\n : 'multicast'\n }\n return verdict\n}\n\n/**\n * MFU Fragment for reassembly\n */\nexport interface MFUFragment {\n fragmentationIndicator: number\n mpuSequenceNumber: number\n fragmentCounter: number\n data: Uint8Array\n timestamp: bigint\n /** Random Access Point flag from MMTP header - indicates keyframe */\n rapFlag: boolean\n /** MFU DU priority. FFmpeg marks random-access samples with high priority. */\n priority?: number\n sampleNumber?: number\n movieFragmentSequenceNumber?: number\n sampleOffset?: number\n /** Non-timed MFU Item_ID from the 4-byte DU header. */\n itemId?: number\n /** Authored u32 MMTP packet_sequence_number, including on recovered packets. */\n packetSequenceNumber?: number\n /** True when this accepted source packet was reconstructed by FEC. */\n recovered?: boolean\n /** Transport that supplied this accepted fragment. Absent for recovered data. */\n source?: MmtpSourcePath\n /**\n * Explicit provenance for this fragment, overriding what `source` implies.\n * Set by the router for FEC-recovered fragments, which have no `source` of\n * their own but whose FEC block's symbol provenance is known.\n */\n deliveryProvenance?: MmtpDeliveryProvenance\n /** Immutable catalog identity supplied by the owning ingress subscription. */\n catalogTrackName?: string\n /** Physical MMTP stream identity when no catalog identity is available. */\n packetId?: number\n}\n\n/**\n * Reassembled MFU result\n */\nexport interface ReassembledMFU {\n mpuSequenceNumber: number\n /** MMTP packet_sequence_number of the first fragment in delivery order. */\n packetSequenceNumber?: number\n /** Every authoritative MMTP source-packet sequence that formed this MFU. */\n packetSequenceNumbers?: number[]\n /** Subset of packetSequenceNumbers reconstructed by FEC. */\n repairedPacketSequenceNumbers?: number[]\n data: Uint8Array\n timestamp: bigint\n fragmentCount: number\n /** Random Access Point flag - indicates keyframe (from first fragment) */\n rapFlag: boolean\n /** MFU DU priority from the first fragment that carried it. */\n priority?: number\n sampleNumber?: number\n movieFragmentSequenceNumber?: number\n /**\n * This DU's authored base byte offset within its sample (moqcast #37). 0\n * for a whole-sample DU; nonzero identifies one subsample DU among\n * siblings partitioning an oversized sample. Constant across the DU's own\n * fragments \u2014 feed into a sample combiner to reassemble the full sample.\n */\n sampleOffset?: number\n /**\n * Transport that supplied the accepted FI=1 head fragment (or the complete\n * FI=0 frame). This is the deterministic winner for mixed-path assembly.\n *\n * ROUTING ONLY. It describes one fragment, so it is not evidence about the\n * whole frame \u2014 read {@link ReassembledMFU.deliveryProvenance} for that.\n */\n source?: MmtpSourcePath\n /**\n * Strict provenance folded over every contributing fragment. Only\n * 'multicast' proves multicast-exclusive delivery of this frame.\n */\n deliveryProvenance: MmtpDeliveryProvenance\n /** Catalog identity from the accepted FI=1 head fragment, or FI=0 frame. */\n catalogTrackName?: string\n}\n\n/**\n * Reassembler statistics\n */\nexport interface ReassemblerStats {\n totalFragments: number\n reassembledMFUs: number\n /** Fragments rejected by strict authored DU-state validation. */\n rejectedFragments: number\n /** Incomplete MFUs discarded after their configured deadline. */\n timedOutMFUs: number\n /** Incomplete MFUs discarded to enforce the configured memory bound. */\n evictedMemoryLimit: number\n outOfOrderFragments: number\n}\n\nexport const MFU_REASSEMBLY_ERROR_CODES = [\n 'invalid-fragment-state',\n 'missing-data-unit-identity',\n 'missing-packet-sequence',\n 'conflicting-data-unit-identity',\n 'overlapping-data-unit',\n 'fragment-counter-mismatch',\n 'data-unit-identity-mismatch',\n 'offset-overflow',\n 'active-data-unit-limit',\n 'incomplete-terminal-fragment',\n] as const\n\nexport type MfuReassemblyErrorCode = typeof MFU_REASSEMBLY_ERROR_CODES[number]\n\nconst mfuReassemblyErrorCodes = new Set<string>(MFU_REASSEMBLY_ERROR_CODES)\n\nexport function isMfuReassemblyErrorCode(value: unknown): value is MfuReassemblyErrorCode {\n return typeof value === 'string' && mfuReassemblyErrorCodes.has(value)\n}\n\nexport class MfuReassemblyError extends Error {\n readonly name = 'MfuReassemblyError'\n\n constructor(readonly code: MfuReassemblyErrorCode, message: string) {\n super(message)\n }\n}\n\nfunction assembledPacketSequences(fragments: readonly MFUFragment[]): {\n packetSequenceNumbers?: number[]\n repairedPacketSequenceNumbers?: number[]\n} {\n const packetSequenceNumbers: number[] = []\n const repairedPacketSequenceNumbers: number[] = []\n const seen = new Set<number>()\n const repaired = new Set<number>()\n for (const fragment of fragments) {\n const sequence = fragment.packetSequenceNumber\n if (typeof sequence !== 'number' || !Number.isSafeInteger(sequence) ||\n sequence < 0 || sequence > 0xffffffff) {\n return {}\n }\n if (!seen.has(sequence)) {\n seen.add(sequence)\n packetSequenceNumbers.push(sequence)\n }\n if (fragment.recovered === true && !repaired.has(sequence)) {\n repaired.add(sequence)\n repairedPacketSequenceNumbers.push(sequence)\n }\n }\n return {packetSequenceNumbers, repairedPacketSequenceNumbers}\n}\n\nexport interface PendingMfuDebug {\n key: string\n fragments: number\n fi0: number\n fi1: number\n fi2: number\n fi3: number\n firstFi: number\n lastFi: number\n firstOrder: number\n lastOrder: number\n minCounter: number\n maxCounter: number\n minOffset?: number\n maxOffset?: number\n movieFragmentSequenceNumber?: number\n sampleNumber?: number\n mpuSequenceNumber?: number\n sortedHead: Array<{ fi: number; counter: number; order: number; seq?: number; offset?: number }>\n sortedTail: Array<{ fi: number; counter: number; order: number; seq?: number; offset?: number }>\n}\n\nexport interface MFUReassemblerOptions {\n /** Timeout (ms) for incomplete MFUs. Derive from frame duration or FEC interleaveDepthMs.\n * Pass null for live clock-free operation; memory remains bounded by maxBuffered. */\n timeoutMs: number | null\n /** Max concurrent incomplete MFUs, including continuations awaiting FI=1. */\n maxBuffered?: number\n}\n\n/** Reassembly surface consumed by MmtpContainerParser and MmtpRouter.\n * Implemented by both the TypeScript engine and the shared\n * Rust/WASM IFC adapter. */\nexport interface MfuReassemblerBackend {\n timeoutMs: number | null\n readonly stats: ReassemblerStats\n onDeferredAssembly?: (result: ReassembledMFU) => void\n /** Returns null only for a valid incomplete DU; strict rejection throws\n * `MfuReassemblyError`. */\n addFragment(fragment: MFUFragment): ReassembledMFU | null\n flush(): void\n resetStream(catalogTrackName: string): void\n reset(): void\n readonly pendingCount: number\n getStatus(): {\n bufferedMFUs: number\n pendingFragments: number\n pendingSamples: PendingMfuDebug[]\n stats: ReassemblerStats\n }\n}\n\nfunction streamScope(fragment: MFUFragment): readonly [kind: string, identity: string | number | null] {\n if (fragment.catalogTrackName !== undefined) return ['catalog', fragment.catalogTrackName]\n if (fragment.packetId !== undefined) return ['packet', fragment.packetId]\n return ['unspecified', null]\n}\n\nfunction isU32(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 0xffffffff\n}\n\n/**\n * Producer-profile transport coordinate shared by every contiguous source\n * fragment in one data-unit run. This is association state only; objectKey\n * remains the intrinsic DU identity. ISO-permitted interleaving creates a\n * different key and therefore cannot cross-associate samples.\n */\nfunction fragmentRunKey(fragment: MFUFragment): string {\n if (!isU32(fragment.packetSequenceNumber)) {\n throw new Error('[MFUReassembler] fragmented value has no validated packet sequence')\n }\n const terminalPacketSequence =\n (fragment.packetSequenceNumber + fragment.fragmentCounter) % 0x1_0000_0000\n return JSON.stringify([\n ...streamScope(fragment),\n fragment.mpuSequenceNumber,\n terminalPacketSequence,\n ])\n}\n\nfunction hasAnySampleIdentityField(fragment: MFUFragment): boolean {\n return fragment.movieFragmentSequenceNumber !== undefined ||\n fragment.sampleNumber !== undefined || fragment.sampleOffset !== undefined\n}\n\ntype IdentifiedSampleFragment = MFUFragment & Required<Pick<\n MFUFragment,\n 'movieFragmentSequenceNumber' | 'sampleNumber' | 'sampleOffset'\n>>\n\nfunction hasSampleIdentity(fragment: MFUFragment): fragment is IdentifiedSampleFragment {\n return isU32(fragment.movieFragmentSequenceNumber) && fragment.movieFragmentSequenceNumber > 0 &&\n isU32(fragment.sampleNumber) && fragment.sampleNumber > 0 && isU32(fragment.sampleOffset)\n}\n\nfunction hasValidFragmentHeader(fragment: MFUFragment): boolean {\n const { fragmentationIndicator: fi, fragmentCounter: counter } = fragment\n if (!Number.isInteger(fi) || fi < 0 || fi > 3) return false\n if (!Number.isInteger(counter) || counter < 0 || counter > 0xff) return false\n return fi === 0 || fi === 3 ? counter === 0 : counter > 0\n}\n\nfunction fragmentCounterOrder(fragment: MFUFragment): number {\n return 0xff - fragment.fragmentCounter\n}\n\nfunction objectKey(fragment: MFUFragment): string {\n const scope = streamScope(fragment)\n if (fragment.itemId !== undefined) {\n return JSON.stringify([\n ...scope,\n fragment.mpuSequenceNumber,\n 'item',\n fragment.itemId,\n ])\n }\n if (hasSampleIdentity(fragment)) {\n return JSON.stringify([\n ...scope,\n fragment.mpuSequenceNumber,\n 'sample',\n fragment.movieFragmentSequenceNumber,\n fragment.sampleNumber,\n // The DU's authored base offset (moqcast #37): sibling subsample DUs\n // of one sample get distinct keys instead of colliding. Only ever\n // called with the unmutated FI=1 (or FI=0) fragment, whose\n // sampleOffset is that authored base -- never the accumulating\n // per-continuation value written by #associateDataUnit.\n fragment.sampleOffset,\n ])\n }\n return JSON.stringify([\n ...scope,\n fragment.mpuSequenceNumber,\n 'unidentified',\n ])\n}\n\nfunction ordersByOffset(fragments: MFUFragment[]): boolean {\n return fragments.every((fragment) => hasSampleIdentity(fragment))\n}\n\ntype ActiveDataUnit = {\n bufferKey: string\n nextFragmentCounter: number\n catalogTrackName?: string\n} & (\n | {\n kind: 'sample'\n movieFragmentSequenceNumber: number\n sampleNumber: number\n nextOffset: number\n }\n | {\n kind: 'item'\n itemId: number\n }\n)\n\ninterface DataUnitAssociation {\n bufferKey: string\n first: boolean\n terminal: boolean\n}\n\ninterface PendingContinuationRun {\n fragments: Map<number, MFUFragment>\n startedAt: number\n timeoutId?: ReturnType<typeof setTimeout>\n}\n\ninterface BufferTimeoutState {\n startedAt: number\n timeoutId?: ReturnType<typeof setTimeout>\n}\n\nfunction checkedSampleEnd(offset: number, byteLength: number): number | null {\n const end = offset + byteLength\n return Number.isSafeInteger(end) && end <= 0xffffffff ? end : null\n}\n\n/**\n * MFU Reassembler with bounded reordered-fragment association\n * Based on ISO/IEC 23008-1:2023 Section 9.2.3.3\n */\nexport class MFUReassembler {\n // Fragment buffers use only the authored MFU DU identity. Continuations are\n // admitted through bounded producer-profile runs keyed by physical stream,\n // MPU, and the wrapping terminal packet sequence. This requires contiguous\n // source-packet sequences within a fragmented DU; interleaving stays\n // separated and incomplete. Continuations may wait in a counter-indexed queue\n // until FI=1 supplies intrinsic identity; MMTP packet send time is never an\n // association key.\n #buffers: Map<string, MFUFragment[]> = new Map()\n #timeouts: Map<string, BufferTimeoutState> = new Map()\n #activeDataUnits: Map<string, ActiveDataUnit> = new Map()\n #pendingContinuations: Map<string, PendingContinuationRun> = new Map()\n #emitted: Set<string> = new Set() // same sample keys \u2014 reject FEC-recovered duplicates\n #emittedOrder: string[] = []\n #timeoutMs: number | null\n #maxBuffered: number\n\n /** Statistics for debugging */\n readonly stats: ReassemblerStats = {\n totalFragments: 0,\n reassembledMFUs: 0,\n rejectedFragments: 0,\n timedOutMFUs: 0,\n evictedMemoryLimit: 0,\n outOfOrderFragments: 0,\n }\n\n constructor(options: MFUReassemblerOptions) {\n if (options.timeoutMs !== null && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {\n throw new Error('[MFUReassembler] timeoutMs must be > 0 (derive from frame duration or FEC config)')\n }\n const maxBuffered = options.maxBuffered ?? 100\n if (!Number.isInteger(maxBuffered) || maxBuffered <= 0) {\n throw new Error('[MFUReassembler] maxBuffered must be a positive integer')\n }\n this.#timeoutMs = options.timeoutMs\n this.#maxBuffered = maxBuffered\n }\n\n /** Update the expiry policy for all retained and future incomplete MFUs. */\n set timeoutMs(ms: number | null) {\n if (ms !== null && (!Number.isFinite(ms) || ms <= 0)) {\n throw new Error('[MFUReassembler] timeoutMs must be > 0 (derive from frame duration or FEC config)')\n }\n this.#timeoutMs = ms\n const now = Date.now()\n for (const [bufferKey, state] of this.#timeouts) {\n if (state.timeoutId !== undefined) clearTimeout(state.timeoutId)\n delete state.timeoutId\n if (ms !== null) {\n state.timeoutId = setTimeout(\n () => this.#onTimeout(bufferKey),\n Math.max(0, state.startedAt + ms - now),\n )\n }\n }\n for (const [runKey, pending] of this.#pendingContinuations) {\n if (pending.timeoutId !== undefined) clearTimeout(pending.timeoutId)\n delete pending.timeoutId\n if (ms !== null && !this.#activeDataUnits.has(runKey)) {\n pending.timeoutId = setTimeout(\n () => this.#onPendingTimeout(runKey),\n Math.max(0, pending.startedAt + ms - now),\n )\n }\n }\n }\n get timeoutMs(): number | null { return this.#timeoutMs }\n\n /**\n * Add a fragment and return reassembled MFU if complete\n */\n addFragment(fragment: MFUFragment): ReassembledMFU | null {\n this.stats.totalFragments++\n\n if (!hasValidFragmentHeader(fragment)) {\n this.#reject('invalid-fragment-state', 'invalid MFU fragmentation indicator or counter')\n }\n if (fragment.itemId !== undefined && hasAnySampleIdentityField(fragment)) {\n this.#reject(\n 'conflicting-data-unit-identity',\n 'MFU carries timed and non-timed DU identities',\n )\n }\n\n const { mpuSequenceNumber, fragmentationIndicator } = fragment\n\n // A complete FT=2 data unit still requires its intrinsic DU identity.\n // The transport parser strips the deterministic timed/non-timed DU header\n // before calling this layer; direct callers must provide the same identity.\n // Don't check or update #emitted: in MMTP over MoQ, multiple complete MFUs\n // share the same mpuSequenceNumber (all frames in a GOP have the same mpuSeq).\n // Blocking FI=0 would prevent keyframe fragment assembly at the same mpuSeq.\n if (fragmentationIndicator === 0) {\n const sampleIdentity = hasSampleIdentity(fragment)\n const hasSampleFields = hasAnySampleIdentityField(fragment)\n const hasItemIdentity = fragment.itemId !== undefined\n if (!sampleIdentity && !hasItemIdentity) {\n this.#reject('missing-data-unit-identity', 'complete MFU has no intrinsic DU identity')\n }\n if (hasSampleFields && !sampleIdentity) {\n this.#reject('missing-data-unit-identity', 'complete MFU has an incomplete sample identity')\n }\n if (hasItemIdentity && !isU32(fragment.itemId)) {\n this.#reject('missing-data-unit-identity', 'complete MFU has an invalid Item_ID')\n }\n this.stats.reassembledMFUs++\n return {\n mpuSequenceNumber,\n packetSequenceNumber: fragment.packetSequenceNumber,\n ...assembledPacketSequences([fragment]),\n data: fragment.data,\n timestamp: fragment.timestamp,\n fragmentCount: 1,\n rapFlag: fragment.rapFlag,\n priority: fragment.priority,\n sampleNumber: fragment.sampleNumber,\n movieFragmentSequenceNumber: fragment.movieFragmentSequenceNumber,\n sampleOffset: fragment.sampleOffset,\n source: fragment.source,\n deliveryProvenance: deliveryProvenanceOf([fragment]),\n catalogTrackName: fragment.catalogTrackName,\n }\n }\n\n if (!isU32(fragment.packetSequenceNumber)) {\n this.#reject(\n 'missing-packet-sequence',\n 'fragmented MFU has no MMTP packet sequence number',\n )\n }\n const runKey = fragmentRunKey(fragment)\n if (\n fragmentationIndicator === 1 &&\n (hasSampleIdentity(fragment) || isU32(fragment.itemId)) &&\n this.#emitted.has(objectKey(fragment))\n ) {\n this.#deletePendingRun(runKey)\n return null\n }\n\n if (fragmentationIndicator !== 1) {\n const active = this.#activeDataUnits.get(runKey)\n if (active !== undefined && fragment.fragmentCounter > active.nextFragmentCounter) {\n this.#reject(\n 'fragment-counter-mismatch',\n 'MFU continuation is behind the authored fragment-counter countdown',\n )\n }\n if (active === undefined || fragment.fragmentCounter < active.nextFragmentCounter) {\n this.#parkContinuation(runKey, fragment)\n return null\n }\n } else if (this.#activeDataUnits.has(runKey)) {\n this.#reject('overlapping-data-unit', 'fragmented MFU overlaps an active data unit')\n } else {\n const pending = this.#pendingContinuations.get(runKey)\n if (pending !== undefined && [...pending.fragments.keys()].some(\n counter => counter >= fragment.fragmentCounter,\n )) {\n this.#reject(\n 'fragment-counter-mismatch',\n 'queued continuation counter is not below the authored first-fragment counter',\n )\n }\n }\n\n const pendingStartedAt = fragmentationIndicator === 1\n ? this.#pendingContinuations.get(runKey)?.startedAt\n : undefined\n const result = this.#addAssociatedFragment(fragment, runKey, pendingStartedAt)\n if (fragmentationIndicator === 1) this.#clearPendingTimeout(runKey)\n if (result !== null) return result\n return this.#drainPendingContinuations(runKey)\n }\n\n #addAssociatedFragment(\n fragment: MFUFragment,\n runKey: string,\n startedAt?: number,\n ): ReassembledMFU | null {\n const association = this.#associateDataUnit(fragment, runKey)\n const { bufferKey, first, terminal } = association\n\n let fragments = this.#buffers.get(bufferKey)\n if (first) {\n if (fragments !== undefined) {\n this.#reject('overlapping-data-unit', 'intrinsic data-unit buffer already exists')\n }\n fragments = []\n this.#buffers.set(bufferKey, fragments)\n } else if (fragments === undefined) {\n throw new Error('[MFUReassembler] active data unit has no fragment buffer')\n }\n\n // A lossy-path deadline is measured from the first received fragment,\n // including a continuation that arrived before FI=1. Reliable paths are\n // clock-free and never install this timer.\n if (first) this.#setTimeout(bufferKey, startedAt)\n fragments.push(fragment)\n\n if (!terminal) return null\n const result = this.#tryReassemble(bufferKey)\n this.#activeDataUnits.delete(runKey)\n if (result === null) {\n this.#clearTimeout(bufferKey)\n this.#buffers.delete(bufferKey)\n this.#reject(\n 'incomplete-terminal-fragment',\n 'validated terminal fragment did not complete its data unit',\n )\n }\n return result\n }\n\n #parkContinuation(runKey: string, fragment: MFUFragment): void {\n let pending = this.#pendingContinuations.get(runKey)\n if (pending === undefined) {\n const awaitingFirst = !this.#activeDataUnits.has(runKey)\n if (awaitingFirst && this.#incompleteRunCount() >= this.#maxBuffered) {\n this.#reject('active-data-unit-limit', 'active MFU data-unit limit reached')\n }\n const startedAt = Date.now()\n pending = { fragments: new Map(), startedAt }\n if (awaitingFirst && this.#timeoutMs !== null) {\n pending.timeoutId = setTimeout(() => this.#onPendingTimeout(runKey), this.#timeoutMs)\n }\n this.#pendingContinuations.set(runKey, pending)\n }\n if (pending.fragments.has(fragment.fragmentCounter)) {\n this.#reject(\n 'fragment-counter-mismatch',\n 'duplicate queued continuation counter would replace retained media',\n )\n }\n pending.fragments.set(fragment.fragmentCounter, fragment)\n }\n\n #drainPendingContinuations(runKey: string): ReassembledMFU | null {\n while (true) {\n const active = this.#activeDataUnits.get(runKey)\n if (active === undefined) {\n this.#deletePendingRun(runKey)\n return null\n }\n const pending = this.#pendingContinuations.get(runKey)\n const fragment = pending?.fragments.get(active.nextFragmentCounter)\n if (fragment === undefined) return null\n pending!.fragments.delete(active.nextFragmentCounter)\n if (pending!.fragments.size === 0) this.#deletePendingRun(runKey)\n const result = this.#addAssociatedFragment(fragment, runKey)\n if (result !== null) {\n this.#deletePendingRun(runKey)\n return result\n }\n }\n }\n\n #incompleteRunCount(): number {\n let count = this.#activeDataUnits.size\n for (const runKey of this.#pendingContinuations.keys()) {\n if (!this.#activeDataUnits.has(runKey)) count++\n }\n return count\n }\n\n #reject(code: MfuReassemblyErrorCode, message: string): never {\n this.stats.rejectedFragments++\n throw new MfuReassemblyError(code, message)\n }\n\n #associateDataUnit(fragment: MFUFragment, runKey: string): DataUnitAssociation {\n const fi = fragment.fragmentationIndicator\n if (fi === 1) {\n if (this.#activeDataUnits.has(runKey)) {\n this.#reject('overlapping-data-unit', 'fragmented MFU overlaps an active data unit')\n }\n if (!this.#pendingContinuations.has(runKey) &&\n this.#incompleteRunCount() >= this.#maxBuffered) {\n this.#reject('active-data-unit-limit', 'active MFU data-unit limit reached')\n }\n\n const sampleIdentity = hasSampleIdentity(fragment)\n const hasSampleFields = hasAnySampleIdentityField(fragment)\n const hasItemIdentity = fragment.itemId !== undefined\n if (sampleIdentity && hasItemIdentity) {\n this.#reject(\n 'conflicting-data-unit-identity',\n 'fragmented MFU carries timed and non-timed DU identities',\n )\n }\n if (!sampleIdentity && !hasItemIdentity) {\n this.#reject('missing-data-unit-identity', 'first fragment has no intrinsic DU identity')\n }\n if (hasSampleFields && !sampleIdentity) {\n this.#reject('missing-data-unit-identity', 'first fragment has an incomplete sample identity')\n }\n if (hasItemIdentity && !isU32(fragment.itemId)) {\n this.#reject('missing-data-unit-identity', 'first fragment has an invalid Item_ID')\n }\n\n const bufferKey = objectKey(fragment)\n if (this.#buffers.has(bufferKey)) {\n this.#reject('overlapping-data-unit', 'intrinsic data-unit buffer already exists')\n }\n\n let active: ActiveDataUnit\n if (sampleIdentity) {\n const nextOffset = checkedSampleEnd(fragment.sampleOffset, fragment.data.byteLength)\n if (nextOffset === null) this.#reject('offset-overflow', 'sample byte offset overflow')\n active = {\n kind: 'sample',\n bufferKey,\n nextFragmentCounter: fragment.fragmentCounter - 1,\n movieFragmentSequenceNumber: fragment.movieFragmentSequenceNumber,\n sampleNumber: fragment.sampleNumber,\n nextOffset,\n ...(fragment.catalogTrackName === undefined\n ? {}\n : { catalogTrackName: fragment.catalogTrackName }),\n }\n } else {\n active = {\n kind: 'item',\n bufferKey,\n nextFragmentCounter: fragment.fragmentCounter - 1,\n itemId: fragment.itemId!,\n ...(fragment.catalogTrackName === undefined\n ? {}\n : { catalogTrackName: fragment.catalogTrackName }),\n }\n }\n this.#activeDataUnits.set(runKey, active)\n return { bufferKey, first: true, terminal: false }\n }\n\n const active = this.#activeDataUnits.get(runKey)\n if (active === undefined) {\n throw new Error('[MFUReassembler] continuation was not parked before association')\n }\n if (fragment.fragmentCounter !== active.nextFragmentCounter) {\n this.#reject(\n 'fragment-counter-mismatch',\n 'MFU continuation does not follow the authored fragment-counter countdown',\n )\n }\n\n if (active.kind === 'sample') {\n if (fragment.itemId !== undefined) {\n this.#reject('data-unit-identity-mismatch', 'sample continuation carries an Item_ID')\n }\n if (hasAnySampleIdentityField(fragment) && (\n !hasSampleIdentity(fragment) ||\n fragment.movieFragmentSequenceNumber !== active.movieFragmentSequenceNumber ||\n fragment.sampleNumber !== active.sampleNumber || fragment.sampleOffset !== active.nextOffset\n )) {\n this.#reject('data-unit-identity-mismatch', 'sample continuation identity does not match')\n }\n const nextOffset = checkedSampleEnd(active.nextOffset, fragment.data.byteLength)\n if (nextOffset === null) this.#reject('offset-overflow', 'sample byte offset overflow')\n fragment.movieFragmentSequenceNumber = active.movieFragmentSequenceNumber\n fragment.sampleNumber = active.sampleNumber\n fragment.sampleOffset = active.nextOffset\n if (fi === 2) {\n active.nextOffset = nextOffset\n active.nextFragmentCounter = fragment.fragmentCounter - 1\n }\n } else {\n if (hasAnySampleIdentityField(fragment) ||\n (fragment.itemId !== undefined && fragment.itemId !== active.itemId)) {\n this.#reject('data-unit-identity-mismatch', 'Item_ID continuation identity does not match')\n }\n fragment.itemId = active.itemId\n if (fi === 2) active.nextFragmentCounter = fragment.fragmentCounter - 1\n }\n\n return {\n bufferKey: active.bufferKey,\n first: false,\n terminal: fi === 3,\n }\n }\n\n #tryReassemble(bufferKey: string): ReassembledMFU | null {\n const fragments = this.#buffers.get(bufferKey)\n if (!fragments || fragments.length === 0) {\n return null\n }\n\n const sorted = [...fragments].sort((a, b) => this.#orderValue(a) - this.#orderValue(b))\n\n if (\n sorted[0]!.fragmentationIndicator !== 1 ||\n sorted[sorted.length - 1]!.fragmentationIndicator !== 3\n ) {\n return null\n }\n if (sorted.slice(1, -1).some((fragment) => fragment.fragmentationIndicator !== 2)) {\n return null\n }\n\n // ISO fragment_counter is the number of succeeding payloads. After\n // normalization (`255 - counter`), a complete non-wrapping chain is strictly\n // consecutive. This also caps a data unit at 256 payloads.\n for (let i = 1; i < sorted.length; i++) {\n if (this.#orderValue(sorted[i]!) !== this.#orderValue(sorted[i - 1]!) + 1) {\n return null\n }\n }\n\n // When every fragment carries a timed-MFU byte offset, independently prove\n // byte adjacency within this data unit. Counter order remains authoritative;\n // conflicting offsets fail closed instead of changing the fragment order.\n // sorted[0]'s offset is this DU's authored base (moqcast #37: zero for a\n // whole-sample DU, nonzero for one subsample DU of a partitioned sample) --\n // never asserted against a literal zero.\n if (ordersByOffset(sorted)) {\n for (let i = 1; i < sorted.length; i++) {\n const previous = sorted[i - 1]!\n if (\n sorted[i]!.sampleOffset !==\n previous.sampleOffset! + previous.data.byteLength\n ) {\n return null\n }\n }\n }\n\n fragments.splice(0, fragments.length, ...sorted)\n return this.#assemble(bufferKey, fragments)\n }\n\n #assemble(bufferKey: string, fragments: MFUFragment[]): ReassembledMFU {\n // Reassemble: concatenate all fragment data\n const totalSize = fragments.reduce((sum, f) => sum + f.data.length, 0)\n const data = new Uint8Array(totalSize)\n\n let offset = 0\n for (const fragment of fragments) {\n data.set(fragment.data, offset)\n offset += fragment.data.length\n }\n\n // Cleanup + mark the intrinsic identity as emitted to prevent a recovered\n // duplicate from starting another fragmented run.\n this.#clearTimeout(bufferKey)\n this.#buffers.delete(bufferKey)\n this.#markEmitted(bufferKey)\n this.stats.reassembledMFUs++\n\n return {\n mpuSequenceNumber: fragments[0]!.mpuSequenceNumber,\n packetSequenceNumber: fragments[0]!.packetSequenceNumber,\n ...assembledPacketSequences(fragments),\n data,\n timestamp: fragments[0]!.timestamp,\n fragmentCount: fragments.length,\n rapFlag: fragments[0]!.rapFlag,\n priority: fragments.find((f) => f.priority !== undefined)?.priority,\n sampleNumber: fragments[0]!.sampleNumber,\n movieFragmentSequenceNumber: fragments[0]!.movieFragmentSequenceNumber,\n sampleOffset: fragments[0]!.sampleOffset,\n source: fragments[0]!.source,\n deliveryProvenance: deliveryProvenanceOf(fragments),\n catalogTrackName: fragments[0]!.catalogTrackName,\n }\n }\n\n #setTimeout(bufferKey: string, startedAt = Date.now()): void {\n const state: BufferTimeoutState = { startedAt }\n if (this.#timeoutMs !== null) {\n state.timeoutId = setTimeout(\n () => this.#onTimeout(bufferKey),\n Math.max(0, startedAt + this.#timeoutMs - Date.now()),\n )\n }\n this.#timeouts.set(bufferKey, state)\n }\n\n #clearPendingTimeout(runKey: string): void {\n const pending = this.#pendingContinuations.get(runKey)\n if (pending?.timeoutId !== undefined) clearTimeout(pending.timeoutId)\n if (pending !== undefined) {\n delete pending.timeoutId\n }\n }\n\n #deletePendingRun(runKey: string): void {\n this.#clearPendingTimeout(runKey)\n this.#pendingContinuations.delete(runKey)\n }\n\n #onPendingTimeout(runKey: string): void {\n const pending = this.#pendingContinuations.get(runKey)\n if (pending === undefined) return\n this.stats.timedOutMFUs++\n console.warn(\n `[MfuReassembler] timeout: run=${runKey} fragments=${pending.fragments.size} ` +\n '(continuations awaiting first fragment, discarded)',\n )\n this.#pendingContinuations.delete(runKey)\n }\n\n #clearTimeout(bufferKey: string): void {\n const state = this.#timeouts.get(bufferKey)\n if (state !== undefined) {\n if (state.timeoutId !== undefined) clearTimeout(state.timeoutId)\n this.#timeouts.delete(bufferKey)\n }\n }\n\n /** Callback for deferred assembly results (set by caller for async frame delivery) */\n onDeferredAssembly?: (result: ReassembledMFU) => void\n\n #markEmitted(bufferKey: string): void {\n if (this.#emitted.has(bufferKey)) return\n this.#emitted.add(bufferKey)\n this.#emittedOrder.push(bufferKey)\n while (this.#emittedOrder.length > this.#maxBuffered) {\n const oldest = this.#emittedOrder.shift()\n if (oldest !== undefined) this.#emitted.delete(oldest)\n }\n }\n\n #orderValue(fragment: MFUFragment): number {\n return fragmentCounterOrder(fragment)\n }\n\n #removeActiveDataUnitForBuffer(bufferKey: string): void {\n for (const [runKey, active] of this.#activeDataUnits) {\n if (active.bufferKey === bufferKey) {\n this.#activeDataUnits.delete(runKey)\n this.#deletePendingRun(runKey)\n }\n }\n }\n\n #onTimeout(bufferKey: string): void {\n const fragments = this.#buffers.get(bufferKey)\n if (!fragments || fragments.length === 0) {\n this.#buffers.delete(bufferKey)\n this.#timeouts.delete(bufferKey)\n this.#removeActiveDataUnitForBuffer(bufferKey)\n this.stats.timedOutMFUs++\n console.warn(`[MfuReassembler] timeout: key=${bufferKey} fragments=0 (no data buffered)`)\n return\n }\n\n // Discard partial MFUs \u2014 incomplete AVCC data crashes WebCodecs with\n // \"Unable to determine size of bitstream buffer\". FEC recovery handles\n // loss; partial assembly only produces garbage frames.\n this.stats.timedOutMFUs++\n console.warn(\n `[MfuReassembler] timeout: key=${bufferKey} fragments=${fragments.length} (partial, discarded)`,\n )\n\n this.#buffers.delete(bufferKey)\n this.#timeouts.delete(bufferKey)\n this.#removeActiveDataUnitForBuffer(bufferKey)\n }\n\n /**\n * Flush all buffered fragments\n */\n flush(): void {\n for (const key of this.#buffers.keys()) {\n this.#clearTimeout(key)\n }\n this.#buffers.clear()\n this.#timeouts.clear()\n this.#activeDataUnits.clear()\n for (const runKey of this.#pendingContinuations.keys()) {\n this.#clearPendingTimeout(runKey)\n }\n this.#pendingContinuations.clear()\n this.#emitted.clear()\n this.#emittedOrder.length = 0\n }\n\n /** Drop one catalog stream's incomplete and dedup state. */\n resetStream(catalogTrackName: string): void {\n const belongsToStream = (key: string): boolean => {\n try {\n const decoded = JSON.parse(key)\n return Array.isArray(decoded) && decoded[0] === 'catalog' && decoded[1] === catalogTrackName\n } catch {\n return false\n }\n }\n const discarded = new Set<string>()\n for (const [key, fragments] of this.#buffers) {\n if (fragments.some((fragment) => fragment.catalogTrackName === catalogTrackName)) {\n discarded.add(key)\n }\n }\n for (const [runKey, active] of this.#activeDataUnits) {\n if (active.catalogTrackName === catalogTrackName) {\n discarded.add(active.bufferKey)\n this.#activeDataUnits.delete(runKey)\n }\n }\n for (const [runKey, pending] of this.#pendingContinuations) {\n if ([...pending.fragments.values()].some(\n fragment => fragment.catalogTrackName === catalogTrackName,\n )) {\n this.#deletePendingRun(runKey)\n }\n }\n for (const key of this.#emitted) {\n if (belongsToStream(key)) discarded.add(key)\n }\n for (const key of discarded) {\n this.#clearTimeout(key)\n this.#buffers.delete(key)\n this.#emitted.delete(key)\n }\n this.#emittedOrder = this.#emittedOrder.filter((key) => !discarded.has(key))\n }\n\n /** Reset all state \u2014 flush buffers and zero stats */\n reset(): void {\n this.flush()\n this.stats.totalFragments = 0\n this.stats.reassembledMFUs = 0\n this.stats.rejectedFragments = 0\n this.stats.timedOutMFUs = 0\n this.stats.evictedMemoryLimit = 0\n this.stats.outOfOrderFragments = 0\n }\n\n /**\n * Get number of pending MFUs\n */\n get pendingCount(): number {\n let count = this.#buffers.size\n for (const runKey of this.#pendingContinuations.keys()) {\n if (!this.#activeDataUnits.has(runKey)) count++\n }\n return count\n }\n\n /**\n * Get current buffer status\n */\n getStatus(): {\n bufferedMFUs: number\n pendingFragments: number\n pendingSamples: PendingMfuDebug[]\n stats: ReassemblerStats\n } {\n let pendingFragments = 0\n for (const fragments of this.#buffers.values()) {\n pendingFragments += fragments.length\n }\n for (const pending of this.#pendingContinuations.values()) {\n pendingFragments += pending.fragments.size\n }\n\n return {\n bufferedMFUs: this.pendingCount,\n pendingFragments,\n pendingSamples: this.#pendingDebugSamples(),\n stats: { ...this.stats },\n }\n }\n\n #pendingDebugSamples(): PendingMfuDebug[] {\n const out: PendingMfuDebug[] = []\n for (const [key, fragments] of this.#buffers) {\n if (fragments.length === 0) continue\n const sorted = [...fragments].sort((a, b) => this.#orderValue(a) - this.#orderValue(b))\n const fiCounts = [0, 0, 0, 0]\n let minCounter = Infinity\n let maxCounter = -Infinity\n let minOffset = Infinity\n let maxOffset = -Infinity\n for (const fragment of sorted) {\n fiCounts[fragment.fragmentationIndicator] =\n (fiCounts[fragment.fragmentationIndicator] ?? 0) + 1\n minCounter = Math.min(minCounter, fragment.fragmentCounter)\n maxCounter = Math.max(maxCounter, fragment.fragmentCounter)\n if (typeof fragment.sampleOffset === 'number') {\n minOffset = Math.min(minOffset, fragment.sampleOffset)\n maxOffset = Math.max(maxOffset, fragment.sampleOffset)\n }\n }\n const point = (fragment: MFUFragment) => ({\n fi: fragment.fragmentationIndicator,\n counter: fragment.fragmentCounter,\n order: this.#orderValue(fragment),\n ...(typeof fragment.packetSequenceNumber === 'number' ? { seq: fragment.packetSequenceNumber } : {}),\n ...(typeof fragment.sampleOffset === 'number' ? { offset: fragment.sampleOffset } : {}),\n })\n const summary: PendingMfuDebug = {\n key,\n fragments: sorted.length,\n fi0: fiCounts[0] ?? 0,\n fi1: fiCounts[1] ?? 0,\n fi2: fiCounts[2] ?? 0,\n fi3: fiCounts[3] ?? 0,\n firstFi: sorted[0]!.fragmentationIndicator,\n lastFi: sorted[sorted.length - 1]!.fragmentationIndicator,\n firstOrder: this.#orderValue(sorted[0]!),\n lastOrder: this.#orderValue(sorted[sorted.length - 1]!),\n minCounter,\n maxCounter,\n movieFragmentSequenceNumber: sorted.find((f) => f.movieFragmentSequenceNumber !== undefined)\n ?.movieFragmentSequenceNumber,\n sampleNumber: sorted.find((f) => f.sampleNumber !== undefined)?.sampleNumber,\n mpuSequenceNumber: sorted[0]!.mpuSequenceNumber,\n sortedHead: sorted.slice(0, 6).map(point),\n sortedTail: sorted.slice(-6).map(point),\n }\n if (minOffset !== Infinity) summary.minOffset = minOffset\n if (maxOffset !== -Infinity) summary.maxOffset = maxOffset\n out.push(summary)\n if (out.length >= 12) break\n }\n return out\n }\n}\n\n/**\n * Create MFU fragment from MMTP packet for reassembly\n */\nexport function createFragment(mmtpPacket: Uint8Array): MFUFragment {\n const extraction = extractMFUPayload(mmtpPacket)\n if (!extraction) throw new Error('Unable to extract MFU payload from MMTP packet')\n const { header, mpu, data, mfuDuHeader, itemId } = extraction\n\n return {\n fragmentationIndicator: mpu.fragmentationIndicator,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentCounter: mpu.fragmentCounter,\n data,\n timestamp: header.timestamp,\n rapFlag: header.rapFlag,\n sampleNumber: mfuDuHeader?.sampleNumber,\n movieFragmentSequenceNumber: mfuDuHeader?.movieFragmentSequenceNumber,\n sampleOffset: mfuDuHeader?.offset,\n priority: mfuDuHeader?.priority,\n itemId: itemId ?? undefined,\n packetSequenceNumber: header.sequenceNumber,\n packetId: header.packetId,\n }\n}\n\n/**\n * Process MMTP packet with fragment reassembly\n */\nexport function processMMTPWithReassembly(\n mmtpPacket: Uint8Array,\n reassembler: MFUReassembler,\n): ReassembledMFU | null {\n // Skip FEC repair packets\n if (isFecRepairPacket(mmtpPacket)) {\n return null\n }\n\n const fragment = createFragment(mmtpPacket)\n return reassembler.addFragment(fragment)\n}\n\n// =============================================================================\n// Inline Tests (vitest)\n// =============================================================================\n\nif (import.meta.vitest) {\n const { describe, test, expect, vi } = import.meta.vitest\n\n const expectRejection = (call: () => unknown, code: MfuReassemblyErrorCode): void => {\n let rejection: unknown\n try {\n call()\n } catch (error) {\n rejection = error\n }\n expect(rejection).toBeInstanceOf(MfuReassemblyError)\n expect((rejection as MfuReassemblyError).code).toBe(code)\n }\n\n const testPacketSequence = (mpu: number, counter: number, run = 0): number =>\n (mpu * 1000 + run - counter + 0x1_0000_0000) % 0x1_0000_0000\n\n describe('MFUReassembler', () => {\n test('should return complete MFU for FI=0', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const result = reassembler.addFragment({\n fragmentationIndicator: 0,\n mpuSequenceNumber: 1,\n fragmentCounter: 0,\n itemId: 1,\n data: new Uint8Array([1, 2, 3, 4]),\n timestamp: 1000n,\n\n rapFlag: true,\n })\n expect(result).not.toBeNull()\n expect(result?.data).toEqual(new Uint8Array([1, 2, 3, 4]))\n expect(result?.fragmentCount).toBe(1)\n expect(result?.rapFlag).toBe(true)\n })\n\n test('rejects a complete MFU without a valid sample identity', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const complete = (identity: Partial<MFUFragment>): MFUFragment => ({\n fragmentationIndicator: 0,\n mpuSequenceNumber: 1,\n fragmentCounter: 0,\n data: new Uint8Array([1]),\n timestamp: 1000n,\n rapFlag: true,\n ...identity,\n })\n expectRejection(\n () => reassembler.addFragment(complete({})),\n 'missing-data-unit-identity',\n )\n expectRejection(\n () => reassembler.addFragment(complete({\n movieFragmentSequenceNumber: 2,\n sampleNumber: 1,\n })),\n 'missing-data-unit-identity',\n )\n })\n\n test('accepts a complete subsample MFU with a nonzero authored offset', () => {\n // moqcast #37: a subsample DU's offset is its authored base byte\n // position within the sample -- nonzero identifies one subsample DU\n // among siblings partitioning an oversized sample.\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const result = reassembler.addFragment({\n fragmentationIndicator: 0,\n mpuSequenceNumber: 1,\n fragmentCounter: 0,\n movieFragmentSequenceNumber: 2,\n sampleNumber: 1,\n sampleOffset: 4,\n data: new Uint8Array([1]),\n timestamp: 1000n,\n rapFlag: true,\n })\n expect(result).not.toBeNull()\n expect(result?.sampleOffset).toBe(4)\n })\n\n test('accepts a fragmented subsample DU whose first fragment has a nonzero authored offset', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 2,\n fragmentCounter: 1,\n movieFragmentSequenceNumber: 2,\n sampleNumber: 1,\n sampleOffset: 4,\n packetSequenceNumber: testPacketSequence(2, 1),\n data: new Uint8Array([0xa1]),\n timestamp: 1000n,\n rapFlag: true,\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(1)\n expect(reassembler.getStatus().pendingFragments).toBe(1)\n\n const out = reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 2,\n fragmentCounter: 0,\n data: new Uint8Array([0xa2]),\n timestamp: 1001n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(2, 0),\n })\n expect(out?.data).toEqual(new Uint8Array([0xa1, 0xa2]))\n expect(out?.sampleOffset).toBe(4)\n })\n\n test('sibling subsample DUs of one sample reassemble independently by offset', () => {\n // moqcast #37: each subsample DU of a partitioned sample runs its own\n // FI chain and gets its own buffer, keyed in part by its authored base\n // offset -- distinct from a sibling DU sharing (movie, sample).\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n expect(reassembler.addFragment({\n mpuSequenceNumber: 3,\n movieFragmentSequenceNumber: 5,\n sampleNumber: 9,\n fragmentationIndicator: 1,\n fragmentCounter: 1,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp: 2000n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(3, 1, 0),\n })).toBeNull()\n expect(reassembler.addFragment({\n mpuSequenceNumber: 3,\n movieFragmentSequenceNumber: 5,\n sampleNumber: 9,\n fragmentationIndicator: 1,\n fragmentCounter: 1,\n sampleOffset: 3,\n data: new Uint8Array([0xb1]),\n timestamp: 2001n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(3, 1, 1),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(2)\n\n const first = reassembler.addFragment({\n mpuSequenceNumber: 3,\n fragmentationIndicator: 3,\n fragmentCounter: 0,\n data: new Uint8Array([0xa2]),\n timestamp: 2000n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(3, 0, 0),\n })\n expect(first?.data).toEqual(new Uint8Array([0xa1, 0xa2]))\n expect(first?.sampleOffset).toBe(0)\n\n const second = reassembler.addFragment({\n mpuSequenceNumber: 3,\n fragmentationIndicator: 3,\n fragmentCounter: 0,\n data: new Uint8Array([0xb2]),\n timestamp: 2001n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(3, 0, 1),\n })\n expect(second?.data).toEqual(new Uint8Array([0xb1, 0xb2]))\n expect(second?.sampleOffset).toBe(3)\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('isolates colliding fragment coordinates by catalog track identity', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const fragment = (\n catalogTrackName: string,\n fragmentationIndicator: number,\n fragmentCounter: number,\n byte: number,\n ): MFUFragment => ({\n itemId: 1,\n fragmentationIndicator,\n fragmentCounter,\n mpuSequenceNumber: 1,\n data: new Uint8Array([byte]),\n timestamp: 1000n,\n rapFlag: fragmentationIndicator === 1,\n catalogTrackName,\n packetSequenceNumber: testPacketSequence(1, fragmentCounter),\n })\n\n expect(reassembler.addFragment(fragment('video/720p', 1, 1, 0x71))).toBeNull()\n expect(reassembler.addFragment(fragment('video/540p', 1, 1, 0x51))).toBeNull()\n const high = reassembler.addFragment(fragment('video/720p', 3, 0, 0x72))\n const low = reassembler.addFragment(fragment('video/540p', 3, 0, 0x52))\n\n expect(high?.data).toEqual(new Uint8Array([0x71, 0x72]))\n expect(high?.catalogTrackName).toBe('video/720p')\n expect(low?.data).toEqual(new Uint8Array([0x51, 0x52]))\n expect(low?.catalogTrackName).toBe('video/540p')\n })\n\n test('rejects an overlapping non-timed item without replacing the active run', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const fragment = (\n itemId: number,\n fragmentationIndicator: number,\n fragmentCounter: number,\n byte: number,\n ): MFUFragment => ({\n itemId,\n fragmentationIndicator,\n fragmentCounter,\n mpuSequenceNumber: 1,\n data: new Uint8Array([byte]),\n timestamp: 1000n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, fragmentCounter),\n })\n\n expect(reassembler.addFragment(fragment(10, 1, 1, 0x10))).toBeNull()\n expectRejection(\n () => reassembler.addFragment(fragment(20, 1, 1, 0x20)),\n 'overlapping-data-unit',\n )\n expect(reassembler.pendingCount).toBe(1)\n expect(reassembler.addFragment(fragment(10, 3, 0, 0x11))?.data)\n .toEqual(new Uint8Array([0x10, 0x11]))\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('associates continuations by authored state when packet timestamps change', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const fragment = (\n itemId: number | undefined,\n fragmentationIndicator: number,\n fragmentCounter: number,\n byte: number,\n timestamp: bigint,\n ): MFUFragment => ({\n ...(itemId === undefined ? {} : { itemId }),\n fragmentationIndicator,\n fragmentCounter,\n mpuSequenceNumber: 1,\n data: new Uint8Array([byte]),\n timestamp,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, fragmentCounter),\n })\n\n expect(reassembler.addFragment(fragment(10, 1, 2, 0x10, 1000n))).toBeNull()\n expect(reassembler.addFragment(fragment(undefined, 2, 1, 0x11, 1001n))).toBeNull()\n const out = reassembler.addFragment(fragment(undefined, 3, 0, 0x12, 1002n))\n expect(out?.data).toEqual(new Uint8Array([0x10, 0x11, 0x12]))\n expect(out?.timestamp).toBe(1000n)\n })\n\n test('should buffer and reassemble 2 fragments', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // First fragment (FI=1)\n const result1 = reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 1,\n fragmentCounter: 1,\n itemId: 1,\n data: new Uint8Array([1, 2]),\n timestamp: 1000n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n expect(result1).toBeNull()\n\n // Last fragment (FI=3)\n const result2 = reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 1,\n fragmentCounter: 0,\n data: new Uint8Array([3, 4]),\n timestamp: 1000n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })\n expect(result2).not.toBeNull()\n expect(result2?.data).toEqual(new Uint8Array([1, 2, 3, 4]))\n expect(result2?.fragmentCount).toBe(2)\n expect(result2?.rapFlag).toBe(true) // Should use first fragment's rapFlag\n })\n\n test('buffers a terminal continuation until its authored first fragment arrives', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // Last fragment first (FI=3)\n expect(reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 1,\n fragmentCounter: 0,\n data: new Uint8Array([3, 4]),\n timestamp: 1000n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(1)\n\n // Then first fragment (FI=1)\n const completed = reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 1,\n fragmentCounter: 1,\n itemId: 1,\n data: new Uint8Array([1, 2]),\n timestamp: 1000n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n\n expect(completed?.data).toEqual(new Uint8Array([1, 2, 3, 4]))\n expect(completed?.rapFlag).toBe(true)\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('clock-free mode keeps an admitted MFU until completion', async () => {\n const reassembler = new MFUReassembler({ timeoutMs: null })\n\n reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 1,\n fragmentCounter: 1,\n itemId: 1,\n data: new Uint8Array([1, 2]),\n timestamp: 1000n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n\n await new Promise(resolve => setTimeout(resolve, 5))\n\n const result = reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 1,\n fragmentCounter: 0,\n data: new Uint8Array([3, 4]),\n timestamp: 1000n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })\n\n expect(result).not.toBeNull()\n expect(result?.data).toEqual(new Uint8Array([1, 2, 3, 4]))\n expect(reassembler.stats.timedOutMFUs).toBe(0)\n })\n\n test('should track pending count', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n expect(reassembler.pendingCount).toBe(0)\n\n reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 1,\n fragmentCounter: 1,\n itemId: 1,\n data: new Uint8Array([1]),\n timestamp: 1000n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n expect(reassembler.pendingCount).toBe(1)\n\n reassembler.flush()\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('should reassemble 3 fragments (FI=1, FI=2, FI=3)', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // First fragment\n expect(reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 10,\n fragmentCounter: 2, itemId: 1, data: new Uint8Array([0xAA, 0xBB]),\n timestamp: 5000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(10, 2),\n })).toBeNull()\n\n // Middle fragment\n expect(reassembler.addFragment({\n fragmentationIndicator: 2, mpuSequenceNumber: 10,\n fragmentCounter: 1, data: new Uint8Array([0xCC, 0xDD]),\n timestamp: 5000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(10, 1),\n })).toBeNull()\n\n // Last fragment \u2014 triggers reassembly\n const result = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 10,\n fragmentCounter: 0, data: new Uint8Array([0xEE, 0xFF]),\n timestamp: 5000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(10, 0),\n })\n\n expect(result).not.toBeNull()\n expect(result!.data).toEqual(new Uint8Array([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]))\n expect(result!.fragmentCount).toBe(3)\n expect(result!.rapFlag).toBe(true) // from first fragment\n expect(result!.mpuSequenceNumber).toBe(10)\n })\n\n test('should handle interleaved MPU sequences', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // MPU 1: first fragment\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0x01]),\n timestamp: 100n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n // MPU 2: complete (FI=0)\n const r2 = reassembler.addFragment({\n fragmentationIndicator: 0, mpuSequenceNumber: 2,\n fragmentCounter: 0, itemId: 2, data: new Uint8Array([0x02]),\n timestamp: 200n, rapFlag: false,\n })\n expect(r2).not.toBeNull()\n expect(r2!.mpuSequenceNumber).toBe(2)\n\n // MPU 1: last fragment\n const r1 = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([0x03]),\n timestamp: 100n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })\n expect(r1).not.toBeNull()\n expect(r1!.data).toEqual(new Uint8Array([0x01, 0x03]))\n })\n\n test('rejects overlapping fragmented MFUs sharing one stream and MPU', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const mpuSeq = 5\n\n expect(\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: mpuSeq,\n fragmentCounter: 1, itemId: 10,\n data: new Uint8Array([0xa1]), timestamp: 100n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(mpuSeq, 1),\n }),\n ).toBeNull()\n expectRejection(\n () => reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: mpuSeq,\n fragmentCounter: 1, itemId: 20,\n data: new Uint8Array([0xb1]), timestamp: 200n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(mpuSeq, 1),\n }),\n 'overlapping-data-unit',\n )\n expect(reassembler.pendingCount).toBe(1)\n\n const a = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: mpuSeq,\n fragmentCounter: 0, data: new Uint8Array([0xa2]), timestamp: 101n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(mpuSeq, 0),\n })\n expect(a).not.toBeNull()\n expect(a!.data).toEqual(new Uint8Array([0xa1, 0xa2]))\n expect(a!.rapFlag).toBe(true) // from A's first fragment\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: mpuSeq,\n fragmentCounter: 1, itemId: 20,\n data: new Uint8Array([0xb1]), timestamp: 200n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(mpuSeq, 1, 10),\n })).toBeNull()\n const b = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: mpuSeq,\n fragmentCounter: 0, data: new Uint8Array([0xb2]), timestamp: 201n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(mpuSeq, 0, 10),\n })\n expect(b).not.toBeNull()\n expect(b!.data).toEqual(new Uint8Array([0xb1, 0xb2]))\n })\n\n test('reassembles FFmpeg timed MFU fragments by sample number and byte offset', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const common = {\n mpuSequenceNumber: 0,\n movieFragmentSequenceNumber: 9,\n sampleNumber: 17,\n }\n\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 1,\n fragmentCounter: 2,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp: 1000n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(0, 2),\n })).toBeNull()\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 2,\n fragmentCounter: 1,\n sampleOffset: 1,\n data: new Uint8Array([0xb2]),\n timestamp: 1001n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(0, 1),\n })).toBeNull()\n const out = reassembler.addFragment({\n ...common,\n fragmentationIndicator: 3,\n fragmentCounter: 0,\n sampleOffset: 2,\n data: new Uint8Array([0xc3]),\n timestamp: 1002n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(0, 0),\n })\n\n expect(out).not.toBeNull()\n expect(out!.data).toEqual(new Uint8Array([0xa1, 0xb2, 0xc3]))\n expect(out!.timestamp).toBe(1000n)\n expect(out!.sampleNumber).toBe(17)\n expect(out!.movieFragmentSequenceNumber).toBe(9)\n })\n\n test('buffers a counter gap without advancing the active data unit', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const common = {\n mpuSequenceNumber: 0,\n movieFragmentSequenceNumber: 9,\n sampleNumber: 17,\n timestamp: 1000n,\n rapFlag: true,\n }\n\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 1,\n fragmentCounter: 2,\n sampleOffset: 0,\n data: new Uint8Array([0xa1, 0xa2]),\n packetSequenceNumber: testPacketSequence(0, 2),\n })).toBeNull()\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 3,\n fragmentCounter: 0,\n sampleOffset: 4,\n data: new Uint8Array([0xc1, 0xc2]),\n packetSequenceNumber: testPacketSequence(0, 0),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(1)\n\n const out = reassembler.addFragment({\n ...common,\n fragmentationIndicator: 2,\n fragmentCounter: 1,\n sampleOffset: 2,\n data: new Uint8Array([0xb1, 0xb2]),\n packetSequenceNumber: testPacketSequence(0, 1),\n })\n expect(out?.data).toEqual(\n new Uint8Array([0xa1, 0xa2, 0xb1, 0xb2, 0xc1, 0xc2]),\n )\n })\n\n test('reassembles first-only DU-header fragments across changing packet timestamps', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 44,\n fragmentCounter: 2,\n movieFragmentSequenceNumber: 9,\n sampleNumber: 17,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp: 12345n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(44, 2),\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 44,\n fragmentCounter: 1,\n data: new Uint8Array([0xb2]),\n timestamp: 12346n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(44, 1),\n })).toBeNull()\n const out = reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 44,\n fragmentCounter: 0,\n data: new Uint8Array([0xc3]),\n timestamp: 12347n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(44, 0),\n })\n\n expect(out).not.toBeNull()\n expect(out!.data).toEqual(new Uint8Array([0xa1, 0xb2, 0xc3]))\n expect(out!.timestamp).toBe(12345n)\n expect(out!.sampleNumber).toBe(17)\n expect(out!.movieFragmentSequenceNumber).toBe(9)\n })\n\n test('reassembles first-only DU-header fragments with an ISO countdown', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const timestamp = 12345n\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 44,\n fragmentCounter: 3,\n movieFragmentSequenceNumber: 9,\n sampleNumber: 17,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(44, 3),\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 44,\n fragmentCounter: 2,\n data: new Uint8Array([0xb2]),\n timestamp,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(44, 2),\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 44,\n fragmentCounter: 1,\n data: new Uint8Array([0xc3]),\n timestamp,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(44, 1),\n })).toBeNull()\n const out = reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 44,\n fragmentCounter: 0,\n data: new Uint8Array([0xd4]),\n timestamp,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(44, 0),\n })\n\n expect(out).not.toBeNull()\n expect(out!.data).toEqual(new Uint8Array([0xa1, 0xb2, 0xc3, 0xd4]))\n expect(out!.sampleNumber).toBe(17)\n expect(out!.movieFragmentSequenceNumber).toBe(9)\n })\n\n test('rejects fragmented MFUs without an authored DU identity', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const timestamp = 22222n\n\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 55,\n fragmentCounter: 2,\n data: new Uint8Array([0xa1]),\n timestamp,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(55, 2),\n }), 'missing-data-unit-identity')\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('rejects fragmented MFUs without an authored packet sequence', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 55,\n fragmentCounter: 1,\n itemId: 1,\n data: new Uint8Array([0xa1]),\n timestamp: 22222n,\n rapFlag: true,\n }), 'missing-packet-sequence')\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('rejects fragmented MFUs whose authored packet sequence is not u32', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n for (const packetSequenceNumber of [-1, 0x1_0000_0000, 1.5, Number.NaN]) {\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 55,\n fragmentCounter: 1,\n itemId: 1,\n data: new Uint8Array([0xa1]),\n timestamp: 22222n,\n rapFlag: true,\n packetSequenceNumber,\n }), 'missing-packet-sequence')\n }\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('fails closed when one fragmented DU has interleaved packet sequence gaps', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const timestamp = 66666n\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 80,\n fragmentCounter: 17,\n movieFragmentSequenceNumber: 241,\n sampleNumber: 1,\n sampleOffset: 0,\n packetSequenceNumber: 1000,\n data: new Uint8Array([0xa0]),\n timestamp,\n rapFlag: true,\n })).toBeNull()\n\n for (let i = 1; i < 17; i++) {\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 80,\n fragmentCounter: 17 - i,\n packetSequenceNumber: 1000 + i * 2,\n data: new Uint8Array([0xa0 + i]),\n timestamp,\n rapFlag: false,\n })).toBeNull()\n }\n\n const out = reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 80,\n fragmentCounter: 0,\n packetSequenceNumber: 1040,\n data: new Uint8Array([0xb1]),\n timestamp,\n rapFlag: false,\n })\n\n expect(out).toBeNull()\n expect(reassembler.pendingCount).toBe(18)\n expect(reassembler.stats.reassembledMFUs).toBe(0)\n })\n\n test('does not combine adjacent reordered MFUs in one MPU', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const common = { mpuSequenceNumber: 80, packetId: 7, timestamp: 66666n }\n\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 3,\n fragmentCounter: 0,\n packetSequenceNumber: 103,\n data: new Uint8Array([0xb2]),\n rapFlag: false,\n })).toBeNull()\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 1,\n fragmentCounter: 1,\n packetSequenceNumber: 100,\n movieFragmentSequenceNumber: 241,\n sampleNumber: 1,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n rapFlag: true,\n })).toBeNull()\n\n const earlier = reassembler.addFragment({\n ...common,\n fragmentationIndicator: 3,\n fragmentCounter: 0,\n packetSequenceNumber: 101,\n data: new Uint8Array([0xa2]),\n rapFlag: false,\n })\n expect(earlier?.data).toEqual(new Uint8Array([0xa1, 0xa2]))\n\n const later = reassembler.addFragment({\n ...common,\n fragmentationIndicator: 1,\n fragmentCounter: 1,\n packetSequenceNumber: 102,\n movieFragmentSequenceNumber: 241,\n sampleNumber: 2,\n sampleOffset: 0,\n data: new Uint8Array([0xb1]),\n rapFlag: false,\n })\n expect(later?.data).toEqual(new Uint8Array([0xb1, 0xb2]))\n })\n\n test('wraps the fragmented run coordinate at u32 packet sequence', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 81,\n fragmentCounter: 1,\n packetSequenceNumber: 0xffffffff,\n itemId: 1,\n data: new Uint8Array([0xa1]),\n timestamp: 66666n,\n rapFlag: true,\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 81,\n fragmentCounter: 0,\n packetSequenceNumber: 0,\n data: new Uint8Array([0xa2]),\n timestamp: 66667n,\n rapFlag: false,\n })?.data).toEqual(new Uint8Array([0xa1, 0xa2]))\n })\n\n test('dedupes recovered copies by ISO fragment-counter position', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const timestamp = 44444n\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 77,\n fragmentCounter: 2,\n movieFragmentSequenceNumber: 12,\n sampleNumber: 1,\n sampleOffset: 0,\n packetSequenceNumber: 2000,\n data: new Uint8Array([0xa1]),\n timestamp,\n rapFlag: true,\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 77,\n fragmentCounter: 1,\n packetSequenceNumber: 2001,\n data: new Uint8Array([0xb2]),\n timestamp,\n rapFlag: false,\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 77,\n fragmentCounter: 0,\n packetSequenceNumber: 2002,\n data: new Uint8Array([0xc3]),\n timestamp,\n rapFlag: false,\n })).not.toBeNull()\n\n reassembler.flush()\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 78,\n fragmentCounter: 2,\n movieFragmentSequenceNumber: 13,\n sampleNumber: 1,\n sampleOffset: 0,\n packetSequenceNumber: 3000,\n data: new Uint8Array([0xd4]),\n timestamp,\n rapFlag: true,\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 78,\n fragmentCounter: 1,\n packetSequenceNumber: 3001,\n data: new Uint8Array([0xe5]),\n timestamp,\n rapFlag: false,\n })).toBeNull()\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 78,\n fragmentCounter: 1,\n packetSequenceNumber: 3001,\n data: new Uint8Array([0xe5]),\n timestamp,\n rapFlag: false,\n }), 'fragment-counter-mismatch')\n const out = reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 78,\n fragmentCounter: 0,\n packetSequenceNumber: 3002,\n data: new Uint8Array([0xf6]),\n timestamp,\n rapFlag: false,\n })\n\n expect(out).not.toBeNull()\n expect(out!.data).toEqual(new Uint8Array([0xd4, 0xe5, 0xf6]))\n })\n\n test('reassembles middle and terminal continuations that arrive before first', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const timestamp = 54321n\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 45,\n fragmentCounter: 1,\n data: new Uint8Array([0xb2]),\n timestamp,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(45, 1),\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 45,\n fragmentCounter: 0,\n data: new Uint8Array([0xc3]),\n timestamp: timestamp + 1n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(45, 0),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(1)\n const out = reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 45,\n fragmentCounter: 2,\n movieFragmentSequenceNumber: 10,\n sampleNumber: 18,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(45, 2),\n })\n\n expect(out).not.toBeNull()\n expect(out!.data).toEqual(new Uint8Array([0xa1, 0xb2, 0xc3]))\n expect(out!.sampleNumber).toBe(18)\n expect(out!.movieFragmentSequenceNumber).toBe(10)\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('rejects a duplicate continuation before first without replacing retained media', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const timestamp = 54322n\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 46,\n fragmentCounter: 0,\n data: new Uint8Array([0xc3]),\n timestamp,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(46, 0),\n })).toBeNull()\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 46,\n fragmentCounter: 0,\n data: new Uint8Array([0xff]),\n timestamp: timestamp + 1n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(46, 0),\n }), 'fragment-counter-mismatch')\n expect(reassembler.addFragment({\n fragmentationIndicator: 2,\n mpuSequenceNumber: 46,\n fragmentCounter: 1,\n data: new Uint8Array([0xb2]),\n timestamp: timestamp + 2n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(46, 1),\n })).toBeNull()\n\n const out = reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 46,\n fragmentCounter: 2,\n movieFragmentSequenceNumber: 10,\n sampleNumber: 19,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp: timestamp + 3n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(46, 2),\n })\n\n expect(out).not.toBeNull()\n expect(out!.data).toEqual(new Uint8Array([0xa1, 0xb2, 0xc3]))\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('rejects invalid ISO fragmentation-indicator and counter combinations', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const invalidHeaders = [\n { fragmentationIndicator: 0, fragmentCounter: 1 },\n { fragmentationIndicator: 3, fragmentCounter: 1 },\n { fragmentationIndicator: 1, fragmentCounter: 0 },\n { fragmentationIndicator: 2, fragmentCounter: 0 },\n { fragmentationIndicator: -1, fragmentCounter: 1 },\n { fragmentationIndicator: 4, fragmentCounter: 1 },\n { fragmentationIndicator: 1.5, fragmentCounter: 1 },\n { fragmentationIndicator: 1, fragmentCounter: -1 },\n { fragmentationIndicator: 1, fragmentCounter: 256 },\n { fragmentationIndicator: 1, fragmentCounter: 1.5 },\n { fragmentationIndicator: 1, fragmentCounter: Number.NaN },\n ]\n\n for (const header of invalidHeaders) {\n expectRejection(() => reassembler.addFragment({\n ...header,\n mpuSequenceNumber: 48,\n data: new Uint8Array([0xa1]),\n timestamp: 54324n,\n rapFlag: false,\n }), 'invalid-fragment-state')\n }\n\n expect(reassembler.pendingCount).toBe(0)\n expect(reassembler.stats.reassembledMFUs).toBe(0)\n })\n\n test('reassembles the maximum ISO chain of 256 payloads', () => {\n const reassembler = new MFUReassembler({ timeoutMs: null })\n const common = {\n mpuSequenceNumber: 47,\n timestamp: 54323n,\n rapFlag: false,\n }\n\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 1,\n fragmentCounter: 255,\n itemId: 1,\n data: new Uint8Array([0]),\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(47, 255),\n })).toBeNull()\n for (let counter = 254; counter > 0; counter--) {\n expect(reassembler.addFragment({\n ...common,\n fragmentationIndicator: 2,\n fragmentCounter: counter,\n data: new Uint8Array([255 - counter]),\n packetSequenceNumber: testPacketSequence(47, counter),\n })).toBeNull()\n }\n const out = reassembler.addFragment({\n ...common,\n fragmentationIndicator: 3,\n fragmentCounter: 0,\n data: new Uint8Array([255]),\n packetSequenceNumber: testPacketSequence(47, 0),\n })\n\n expect(out?.fragmentCount).toBe(256)\n expect(out?.data).toEqual(Uint8Array.from({ length: 256 }, (_, index) => index))\n })\n\n test('should not reassemble without last fragment (FI=3)', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 2, itemId: 1, data: new Uint8Array([1]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 2),\n })\n const result = reassembler.addFragment({\n fragmentationIndicator: 2, mpuSequenceNumber: 1,\n fragmentCounter: 1, data: new Uint8Array([2]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n expect(result).toBeNull()\n expect(reassembler.pendingCount).toBe(1)\n })\n\n test('bounds continuations that arrive before their first fragments', () => {\n const reassembler = new MFUReassembler({ timeoutMs: null, maxBuffered: 1 })\n expect(reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([1]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })).toBeNull()\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 2,\n fragmentCounter: 0, data: new Uint8Array([2]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(2, 0),\n }), 'active-data-unit-limit')\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 2,\n fragmentCounter: 1, itemId: 2, data: new Uint8Array([2]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(2, 1),\n }), 'active-data-unit-limit')\n expect(reassembler.pendingCount).toBe(1)\n expect(reassembler.getStatus().pendingFragments).toBe(1)\n\n const result = reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([3]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n expect(result?.data).toEqual(new Uint8Array([3, 1]))\n })\n\n test('expires a continuation that never receives its first fragment', () => {\n vi.useFakeTimers()\n try {\n const reassembler = new MFUReassembler({ timeoutMs: 50, maxBuffered: 1 })\n expect(reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([1]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })).toBeNull()\n vi.advanceTimersByTime(50)\n expect(reassembler.pendingCount).toBe(0)\n expect(reassembler.stats.timedOutMFUs).toBe(1)\n } finally {\n vi.useRealTimers()\n }\n })\n\n test('retimes retained continuations when the timeout policy changes', () => {\n vi.useFakeTimers()\n try {\n const enabled = new MFUReassembler({ timeoutMs: null, maxBuffered: 1 })\n expect(enabled.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([1]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })).toBeNull()\n enabled.timeoutMs = 10\n vi.advanceTimersByTime(10)\n expect(enabled.pendingCount).toBe(0)\n\n const disabled = new MFUReassembler({ timeoutMs: 10, maxBuffered: 1 })\n expect(disabled.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 2,\n fragmentCounter: 0, data: new Uint8Array([2]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(2, 0),\n })).toBeNull()\n disabled.timeoutMs = null\n vi.advanceTimersByTime(10)\n expect(disabled.pendingCount).toBe(1)\n disabled.flush()\n } finally {\n vi.useRealTimers()\n }\n })\n\n test('bounds active data units and rejects a new first at maxBuffered', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000, maxBuffered: 2 })\n\n // Two runs remain active; the third first fragment is rejected.\n for (let i = 0; i < 2; i++) {\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: i,\n fragmentCounter: 1, itemId: i, data: new Uint8Array([i]),\n timestamp: BigInt(i * 100), rapFlag: false,\n packetSequenceNumber: testPacketSequence(i, 1),\n })\n }\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 2,\n fragmentCounter: 1, itemId: 2, data: new Uint8Array([2]),\n timestamp: 200n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(2, 1),\n }), 'active-data-unit-limit')\n expect(reassembler.pendingCount).toBe(2)\n expect(reassembler.stats.timedOutMFUs).toBe(0)\n expect(reassembler.stats.evictedMemoryLimit).toBe(0)\n })\n\n test('should report stats correctly', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // One complete MFU\n reassembler.addFragment({\n fragmentationIndicator: 0, mpuSequenceNumber: 1,\n fragmentCounter: 0, itemId: 1, data: new Uint8Array([1]),\n timestamp: 1000n, rapFlag: false,\n })\n\n const status = reassembler.getStatus()\n expect(status.bufferedMFUs).toBe(0)\n expect(status.stats.totalFragments).toBe(1)\n expect(status.stats.reassembledMFUs).toBe(1)\n })\n\n test('rejects an overlapping first without replacing the original fragment', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xAA]),\n timestamp: 1000n, rapFlag: true, source: 'multicast',\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n // Duplicate of fragmentCounter=1\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 1, itemId: 2, data: new Uint8Array([0xBB]),\n timestamp: 1000n, rapFlag: true, source: 'moq',\n packetSequenceNumber: testPacketSequence(1, 1),\n }), 'overlapping-data-unit')\n\n const result = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([0xCC]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })\n expect(result).not.toBeNull()\n expect(result!.data).toEqual(new Uint8Array([0xAA, 0xCC]))\n expect(result!.fragmentCount).toBe(2)\n expect(result!.source).toBe('multicast')\n })\n })\n\n test('should retain an assembly with a gap in fragment counters', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // First fragment declares three succeeding payloads.\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 3, itemId: 1, data: new Uint8Array([0xAA]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(1, 3),\n })\n // Counter 1 arrives while counter 2 is missing.\n expect(reassembler.addFragment({\n fragmentationIndicator: 2, mpuSequenceNumber: 1,\n fragmentCounter: 1, data: new Uint8Array([0xCC]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 1),\n })).toBeNull()\n // Last fragment has the required zero counter.\n expect(reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([0xDD]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(1)\n expect(reassembler.getStatus().pendingFragments).toBe(3)\n })\n\n test('accepts a reordered countdown and rejects a stale duplicate', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // Counters 3, 1, 0 arrive (gap at 2).\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 3, itemId: 1, data: new Uint8Array([0xAA]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(1, 3),\n })\n expect(reassembler.addFragment({\n fragmentationIndicator: 2, mpuSequenceNumber: 1,\n fragmentCounter: 1, data: new Uint8Array([0xCC]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 1),\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 2, mpuSequenceNumber: 1,\n fragmentCounter: 2, data: new Uint8Array([0xBB]),\n timestamp: 1000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 2),\n })).toBeNull()\n expectRejection(() => reassembler.addFragment({\n fragmentationIndicator: 2, mpuSequenceNumber: 1,\n fragmentCounter: 1, data: new Uint8Array([0xCC]),\n timestamp: 1001n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 1),\n }), 'fragment-counter-mismatch')\n const result = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([0xDD]),\n timestamp: 1002n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(1, 0),\n })\n expect(result).not.toBeNull()\n expect(result!.data).toEqual(new Uint8Array([0xAA, 0xBB, 0xCC, 0xDD]))\n expect(result!.fragmentCount).toBe(4)\n })\n\n test('rejects an overlapping first without discarding queued continuations', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const fragment = (\n fragmentationIndicator: number,\n fragmentCounter: number,\n byte: number,\n ): MFUFragment => ({\n fragmentationIndicator,\n mpuSequenceNumber: 2,\n fragmentCounter,\n data: new Uint8Array([byte]),\n timestamp: 1000n,\n rapFlag: fragmentationIndicator === 1,\n packetSequenceNumber: testPacketSequence(2, fragmentCounter),\n ...(fragmentationIndicator === 1 ? { itemId: 9 } : {}),\n })\n\n expect(reassembler.addFragment(fragment(1, 3, 0xAA))).toBeNull()\n expect(reassembler.addFragment(fragment(2, 1, 0xCC))).toBeNull()\n expectRejection(\n () => reassembler.addFragment(fragment(1, 1, 0xEE)),\n 'overlapping-data-unit',\n )\n expect(reassembler.addFragment(fragment(2, 2, 0xBB))).toBeNull()\n expect(reassembler.addFragment(fragment(3, 0, 0xDD))?.data).toEqual(\n new Uint8Array([0xAA, 0xBB, 0xCC, 0xDD]),\n )\n })\n\n test('rejects a duplicate emitted intrinsic identity', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 42,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xAA]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(42, 1),\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 42,\n fragmentCounter: 0, data: new Uint8Array([0xAB]),\n timestamp: 1001n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(42, 0),\n })).not.toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 42,\n fragmentCounter: 0, data: new Uint8Array([0xBC]),\n timestamp: 1002n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(42, 0, 10),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(1)\n expect(reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 42,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xBB]),\n timestamp: 2000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(42, 1, 10),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(0)\n\n const result = reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 43,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xCC]),\n timestamp: 2000n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(43, 1),\n })\n expect(result).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 43,\n fragmentCounter: 0, data: new Uint8Array([0xDD]),\n timestamp: 2001n, rapFlag: false,\n packetSequenceNumber: testPacketSequence(43, 0),\n })?.data).toEqual(new Uint8Array([0xCC, 0xDD]))\n })\n\n test('does not create active state for an emitted identity duplicate', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 50,\n fragmentCounter: 1,\n movieFragmentSequenceNumber: 10,\n sampleNumber: 18,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp: 1000n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(50, 1),\n })).toBeNull()\n expect(reassembler.addFragment({\n fragmentationIndicator: 3,\n mpuSequenceNumber: 50,\n fragmentCounter: 0,\n data: new Uint8Array([0xb2]),\n timestamp: 1000n,\n rapFlag: false,\n packetSequenceNumber: testPacketSequence(50, 0),\n })?.data).toEqual(new Uint8Array([0xa1, 0xb2]))\n expect(reassembler.pendingCount).toBe(0)\n\n expect(reassembler.addFragment({\n fragmentationIndicator: 1,\n mpuSequenceNumber: 50,\n fragmentCounter: 1,\n movieFragmentSequenceNumber: 10,\n sampleNumber: 18,\n sampleOffset: 0,\n data: new Uint8Array([0xa1]),\n timestamp: 2000n,\n rapFlag: true,\n packetSequenceNumber: testPacketSequence(50, 1, 10),\n })).toBeNull()\n expect(reassembler.pendingCount).toBe(0)\n })\n\n test('flush should clear emitted set and allow re-assembly', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n\n // Use fragmented MFU (FI=1 start + FI=3 end) \u2014 only fragmented MFUs\n // go through the #emitted dedup set. FI=0 (complete) bypasses it.\n const first = reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 42,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xAA]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(42, 1),\n })\n expect(first).toBeNull() // first fragment, not yet complete\n const assembled = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 42,\n fragmentCounter: 0, data: new Uint8Array([0xBB]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(42, 0),\n })\n expect(assembled).not.toBeNull() // complete \u2014 mpuSeq 42 now in emitted set\n\n // Rejected before flush \u2014 same mpuSeq already emitted\n expect(reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 42,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xCC]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(42, 1, 10),\n })).toBeNull()\n\n // After flush, same mpuSeq should work again\n reassembler.flush()\n const r2 = reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 42,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xDD]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(42, 1),\n })\n expect(r2).toBeNull() // first fragment of new assembly\n const r3 = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 42,\n fragmentCounter: 0, data: new Uint8Array([0xEE]),\n timestamp: 1000n, rapFlag: true,\n packetSequenceNumber: testPacketSequence(42, 0),\n })\n expect(r3).not.toBeNull()\n expect(r3!.data).toEqual(new Uint8Array([0xDD, 0xEE]))\n })\n\n describe('createFragment', () => {\n test('should create fragment from complete timed MMTP packet', () => {\n // Build: MMTP(12) + MPU(8, T=1 FI=0 payloadLength=25) + MFU_DU(14) + data(5)\n const packet = new Uint8Array(12 + 8 + 14 + 5)\n // MMTP header: byte0 = rap=1, byte1 = MPU type\n packet[0] = 0x01 // RAP flag set\n packet[1] = 0x00 // packet_type = MPU\n // packetId = 1\n const view = new DataView(packet.buffer)\n view.setUint16(2, 1, false)\n view.setUint32(4, 42, false) // timestamp\n view.setUint32(8, 100, false) // sequence number\n\n // MPU header at offset 12\n view.setUint16(12, 25, false) // 6 remaining MPU bytes + 14 DU + 5 media\n packet[14] = 0x28 // FT=2, T=1, FI=0, A=0\n packet[15] = 0 // fragment_counter\n view.setUint32(16, 999, false) // mpuSequenceNumber\n\n // MFU DU header (14 bytes) at offset 20.\n view.setUint32(20, 7, false) // movie_fragment_sequence_number\n view.setUint32(24, 1, false) // sample_number\n // Video data at offset 34\n packet[34] = 0xDE; packet[35] = 0xAD; packet[36] = 0xBE\n packet[37] = 0xEF; packet[38] = 0x01\n\n const frag = createFragment(packet)\n expect(frag.mpuSequenceNumber).toBe(999)\n expect(frag.fragmentationIndicator).toBe(0)\n expect(frag.rapFlag).toBe(true)\n expect(frag.movieFragmentSequenceNumber).toBe(7)\n expect(frag.sampleNumber).toBe(1)\n expect(frag.sampleOffset).toBe(0)\n expect(frag.data.length).toBe(5) // 5 bytes of video data\n })\n\n test('reads Item_ID from a non-timed MFU DU header', () => {\n const packet = new Uint8Array(12 + 8 + 4 + 2)\n const view = new DataView(packet.buffer)\n view.setUint16(2, 1, false)\n view.setUint32(4, 42, false)\n view.setUint32(8, 100, false)\n view.setUint16(12, 12, false) // 6 remaining MPU bytes + 4 DU + 2 media\n packet[14] = 0x20 // FT=2, T=0, FI=0\n view.setUint32(16, 999, false)\n view.setUint32(20, 0x01020304, false)\n packet.set([0xde, 0xad], 24)\n\n const fragment = createFragment(packet)\n expect(fragment.itemId).toBe(0x01020304)\n expect(fragment.data).toEqual(new Uint8Array([0xde, 0xad]))\n })\n })\n\n describe('processMMTPWithReassembly', () => {\n test('should skip FEC repair packets', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const packet = new Uint8Array(12 + 8 + 14 + 4)\n packet[0] = 0x10 // fecType=2 (repair)\n const result = processMMTPWithReassembly(packet, reassembler)\n expect(result).toBeNull()\n })\n\n test('should return reassembled MFU for complete packet', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const packet = new Uint8Array(12 + 8 + 14 + 4)\n packet[0] = 0x01 // RAP\n packet[1] = 0x00 // MPU\n const view = new DataView(packet.buffer)\n view.setUint16(2, 1, false) // packetId\n view.setUint32(4, 100, false) // timestamp\n view.setUint32(8, 1, false) // seq\n // MPU: payloadLength=24 (6 + 14 + 4), FT=2 T=1 FI=0 A=0\n view.setUint16(12, 24, false)\n packet[14] = 0x28\n view.setUint32(16, 42, false) // mpuSeq\n view.setUint32(20, 7, false) // movie_fragment_sequence_number\n view.setUint32(24, 1, false) // sample_number\n\n packet[34] = 0x00; packet[35] = 0x00; packet[36] = 0x00; packet[37] = 0x01\n\n const result = processMMTPWithReassembly(packet, reassembler)\n expect(result).not.toBeNull()\n expect(result!.mpuSequenceNumber).toBe(42)\n expect(result!.rapFlag).toBe(true)\n })\n })\n\n describe('strict delivery provenance', () => {\n test('folds to multicast only when every fragment is explicitly multicast', () => {\n expect(deliveryProvenanceOf([{ source: 'multicast' }, { source: 'multicast' }]))\n .toBe('multicast')\n })\n\n test('a single moq fragment makes the whole frame mixed', () => {\n expect(deliveryProvenanceOf([{ source: 'multicast' }, { source: 'moq' }]))\n .toBe('mixed')\n // ...and it does not matter where in the run it lands.\n expect(deliveryProvenanceOf([{ source: 'moq' }, { source: 'multicast' }]))\n .toBe('mixed')\n })\n\n test('an unattributed fragment degrades multicast to unknown, never to proven', () => {\n expect(deliveryProvenanceOf([{ source: 'multicast' }, {}])).toBe('unknown')\n expect(deliveryProvenanceOf([{}])).toBe('unknown')\n })\n\n test('mixed absorbs unknown \u2014 a moq byte is worse news than a missing label', () => {\n expect(deliveryProvenanceOf([{ source: 'moq' }, {}])).toBe('mixed')\n expect(deliveryProvenanceOf([{}, { source: 'moq' }])).toBe('mixed')\n })\n\n test('an empty fragment set proves nothing', () => {\n expect(deliveryProvenanceOf([])).toBe('unknown')\n })\n\n test('the fold never upgrades a verdict', () => {\n expect(foldDeliveryProvenance('mixed', 'multicast')).toBe('mixed')\n expect(foldDeliveryProvenance('unknown', 'multicast')).toBe('unknown')\n expect(foldDeliveryProvenance('unknown', 'moq')).toBe('mixed')\n })\n\n test('a multicast head with a non-multicast tail is NOT multicast-exclusive', () => {\n // This is the leak this field exists to close. `source` legitimately\n // reports 'multicast' \u2014 head-fragment-wins is the routing contract \u2014\n // but the frame contains bytes that never crossed a multicast socket,\n // so it must never be counted as physical-delivery evidence.\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 1,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xAA]),\n timestamp: 1000n, rapFlag: true, source: 'multicast',\n packetSequenceNumber: testPacketSequence(1, 1),\n })\n const result = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 1,\n fragmentCounter: 0, data: new Uint8Array([0xCC]),\n timestamp: 1000n, rapFlag: false, source: 'moq',\n packetSequenceNumber: testPacketSequence(1, 0),\n })\n expect(result).not.toBeNull()\n expect(result!.fragmentCount).toBe(2)\n expect(result!.source).toBe('multicast')\n expect(result!.deliveryProvenance).toBe('mixed')\n })\n\n test('an all-multicast fragmented frame is multicast-exclusive', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n reassembler.addFragment({\n fragmentationIndicator: 1, mpuSequenceNumber: 2,\n fragmentCounter: 1, itemId: 1, data: new Uint8Array([0xAA]),\n timestamp: 2000n, rapFlag: true, source: 'multicast',\n packetSequenceNumber: testPacketSequence(2, 1),\n })\n const result = reassembler.addFragment({\n fragmentationIndicator: 3, mpuSequenceNumber: 2,\n fragmentCounter: 0, data: new Uint8Array([0xCC]),\n timestamp: 2000n, rapFlag: false, source: 'multicast',\n packetSequenceNumber: testPacketSequence(2, 0),\n })\n expect(result).not.toBeNull()\n expect(result!.deliveryProvenance).toBe('multicast')\n })\n\n test('an unlabelled complete frame proves nothing', () => {\n const reassembler = new MFUReassembler({ timeoutMs: 1000 })\n const result = reassembler.addFragment({\n fragmentationIndicator: 0, mpuSequenceNumber: 3,\n fragmentCounter: 0, itemId: 1, data: new Uint8Array([1]),\n timestamp: 3000n, rapFlag: true,\n })\n expect(result).not.toBeNull()\n expect(result!.deliveryProvenance).toBe('unknown')\n })\n })\n}\n", "/**\n * @mmt/container - Sample-level combining of subsample data units (moqcast #37)\n *\n * {@link MFUReassembler} reassembles exactly one data unit -- a whole-sample\n * DU, or one subsample DU of a partitioned sample. When a timed sample\n * exceeds one data unit's 256-payload capacity, the origin packetizer\n * partitions it into multiple subsample data units (each its own FI chain,\n * its own authored base offset). This module combines those\n * already-reassembled data units back into one contiguous sample.\n *\n * Container-blind, matching {@link MFUReassembler}'s own doctrine: it never\n * parses ISOBMFF itself. The authored sample size a subsample-partitioned\n * sample must total to comes from the host's own FT=1 (movie-fragment-\n * metadata) parse, supplied via {@link SampleCombiner.registerAuthoredSize}.\n * Completion is a protocol event -- every constituent data unit reassembled,\n * byte-adjacent from zero, totaling the registered size -- never a\n * timer-based guess; on lossy paths the existing per-data-unit deadline in\n * {@link MFUReassembler} already bounds each constituent, so this module adds\n * no second timer tier, matching moqcast #37 section 5.2.1.\n *\n * A whole-sample data unit whose length already equals the registered\n * authored size passes through immediately, with no buffering. When\n * `requireAuthoredSize` is false, an offset-zero data unit with no registered\n * size at all also passes through immediately -- the pre-amendment shape, for\n * a host that does not drive FT=1. When true, that same data unit instead\n * buffers and waits for a size to be registered: FT=1 always publishes before\n * any FT=2 sample referencing it (moqcast #37), but on a lossy path FT=1 can\n * still *arrive* after an offset-zero data unit (reordering, or FT=1's own DU\n * FEC-recovered late) -- treating \"no size yet\" as \"whole sample\" in that\n * window would silently truncate a subsample-partitioned sample.\n *\n * Both `#pending` (incomplete samples) and `#authoredSizes` (registered but\n * not yet consumed) are bounded. `#pending` is bounded by `maxBuffered`\n * samples and `maxSegmentsPerSample` data units per sample; a full table\n * evicts its oldest sample by insertion order rather than throwing, so one\n * FEC-lost subsample DU cannot wedge every future sample. `#authoredSizes` is\n * bounded by retiring each key the moment its sample completes, keeping only a\n * bounded recent-retirement set so a late duplicate or FEC-recovered\n * retransmission for that same sample is dropped rather than silently\n * re-emitted or misread as a fresh whole sample.\n *\n * @packageDocumentation\n */\n\nimport type {\n MmtpSourcePath,\n ReassembledMFU,\n MmtpDeliveryProvenance,\n} from './mfu-reassembler.js'\n\n/** Identifies one authored sample: `(catalogTrackName, mpu, movieFragmentSequenceNumber, sampleNumber)`. */\ntype SampleKey = string\n\nfunction sampleKey(\n catalogTrackName: string | undefined,\n mpuSequenceNumber: number,\n movieFragmentSequenceNumber: number,\n sampleNumber: number,\n): SampleKey {\n return JSON.stringify([catalogTrackName ?? null, mpuSequenceNumber, movieFragmentSequenceNumber, sampleNumber])\n}\n\n/** The `catalogTrackName` a key was built from, or undefined when it had none. */\nfunction trackNameOfKey(key: SampleKey): string | undefined {\n const name = (JSON.parse(key) as [string | null, number, number, number])[0]\n return name ?? undefined\n}\n\n/** One authored sample combined from its constituent data unit(s). */\nexport interface CombinedSample {\n mpuSequenceNumber: number\n movieFragmentSequenceNumber: number\n sampleNumber: number\n /** The full sample's media bytes, in ascending-offset order. */\n data: Uint8Array\n timestamp: bigint\n /**\n * The sample's RAP flag is the RAP flag of the FI=0 or FI=1 object of its\n * offset-0 data unit (moqcast #37).\n */\n rapFlag: boolean\n priority?: number\n /** Every authoritative source-packet sequence across all constituent data units, in offset order. */\n packetSequenceNumbers?: number[]\n /** Subset of packetSequenceNumbers reconstructed by FEC. */\n repairedPacketSequenceNumbers?: number[]\n catalogTrackName?: string\n /** Number of subsample data units combined (1 for a whole-sample DU or a non-timed passthrough). */\n subsampleCount: number\n /** MMTP fragments that formed the sample, summed across constituent data units. */\n fragmentCount: number\n /**\n * Transport that supplied the offset-zero data unit's head fragment. Taken\n * from that data unit alone -- like {@link CombinedSample.rapFlag} -- rather\n * than from whichever sibling happened to complete last.\n */\n source?: MmtpSourcePath\n /**\n * Strict provenance folded across EVERY constituent data unit, and within\n * each of them across every MMTP fragment. Unlike {@link CombinedSample.source},\n * which is the offset-zero DU's routing tag, this is whole-sample evidence:\n * only 'multicast' proves that no byte of this sample arrived another way.\n */\n deliveryProvenance: MmtpDeliveryProvenance\n}\n\nexport const SAMPLE_COMBINE_ERROR_CODES = [\n 'duplicate-subsample-offset',\n 'overlapping-subsample',\n 'exceeds-authored-size',\n 'too-many-subsample-segments',\n] as const\n\nexport type SampleCombineErrorCode = typeof SAMPLE_COMBINE_ERROR_CODES[number]\n\n/**\n * Fail-closed rejection reasons for subsample combining. Distinct from\n * {@link MfuReassemblyError}, which governs one data unit's own fragment\n * chain; these govern combining already-reassembled data units into a\n * sample.\n */\nexport class SampleCombineError extends Error {\n readonly name = 'SampleCombineError'\n\n constructor(readonly code: SampleCombineErrorCode, message: string) {\n super(message)\n }\n}\n\nexport interface SampleCombinerStats {\n combinedSamples: number\n passthroughSamples: number\n timedOutSamples: number\n /** Incomplete samples discarded to enforce `maxBuffered`. */\n evictedSamples: number\n}\n\nexport interface SampleCombinerOptions {\n /**\n * Max concurrent incomplete samples. Enforced by evicting the oldest by\n * insertion order, never by throwing, so a host that never calls\n * {@link SampleCombiner.cleanupTimeouts} still recovers from a permanently\n * lost subsample data unit instead of wedging.\n */\n maxBuffered: number\n /**\n * When true, an offset-zero data unit with no registered authored size\n * buffers and waits for one rather than passing through immediately. See\n * the module doc.\n */\n requireAuthoredSize: boolean\n /**\n * Max subsample data units held for one sample at once. Bounds both memory\n * and the per-insert overlap scan; a legitimate subsample-partitioned\n * sample needs at most `ceil(sampleSize / (256 * maxPayload))` data units.\n */\n maxSegmentsPerSample: number\n}\n\ninterface PendingSample {\n /** Held data units keyed by their authored base offset. */\n segments: Map<number, ReassembledMFU>\n startedAt: number\n}\n\n/** Combines subsample data units of one sample into a contiguous whole. */\nexport class SampleCombiner {\n #maxBuffered: number\n #requireAuthoredSize: boolean\n #maxSegmentsPerSample: number\n #authoredSizes: Map<SampleKey, number> = new Map()\n /** Map iteration is insertion-ordered, so the first key is the oldest. */\n #pending: Map<SampleKey, PendingSample> = new Map()\n /**\n * Bounded, insertion-ordered set of recently-completed sample keys, so a\n * late duplicate or FEC-recovered retransmission is dropped rather than\n * re-emitted or misread as a fresh whole sample.\n */\n #retired: Set<SampleKey> = new Set()\n #stats: SampleCombinerStats = {\n combinedSamples: 0,\n passthroughSamples: 0,\n timedOutSamples: 0,\n evictedSamples: 0,\n }\n\n constructor(options: SampleCombinerOptions) {\n const { maxBuffered, requireAuthoredSize, maxSegmentsPerSample } = options\n if (!Number.isInteger(maxBuffered) || maxBuffered <= 0) {\n throw new Error('[SampleCombiner] maxBuffered must be a positive integer')\n }\n if (!Number.isInteger(maxSegmentsPerSample) || maxSegmentsPerSample <= 0) {\n throw new Error('[SampleCombiner] maxSegmentsPerSample must be a positive integer')\n }\n this.#maxBuffered = maxBuffered\n this.#requireAuthoredSize = requireAuthoredSize\n this.#maxSegmentsPerSample = maxSegmentsPerSample\n }\n\n /**\n * Register the authored sample size from the host's own FT=1 parse. Per\n * moqcast #37, FT=1 for a movie fragment publishes before any FT=2 sample\n * referencing it, so a host driving FT=1 as it arrives registers each\n * sample's size before that sample's own data unit(s) arrive.\n *\n * On a lossy path FT=1 can still *arrive* late, after data units this\n * combiner has already buffered. Registering a size therefore re-drives the\n * completion check and returns the combined sample when the buffered data\n * units already satisfy the newly-known size. Callers MUST consume the\n * return value: for a sample carried by a single data unit there is no\n * later {@link SampleCombiner.addMfu} to re-enter, so discarding it\n * silently drops the sample.\n *\n * Registering also clears the key from the retired set. Retirement exists\n * to drop data units that arrive *passively* after a sample completed \u2014 a\n * late duplicate, an FEC-recovered retransmission. A host calling this\n * method is instead asserting that a sample with this identity is authored\n * now, which is what happens when a publisher restarts and its MPU\n * sequence numbers begin again, or when MPU sequence wraps. Without this,\n * such a stream would silently drop every sample whose identity collided\n * with a retired one until the bounded set aged the key out.\n */\n registerAuthoredSize(\n catalogTrackName: string | undefined,\n mpuSequenceNumber: number,\n movieFragmentSequenceNumber: number,\n sampleNumber: number,\n size: number,\n ): CombinedSample | null {\n const key = sampleKey(catalogTrackName, mpuSequenceNumber, movieFragmentSequenceNumber, sampleNumber)\n this.#retired.delete(key)\n this.#authoredSizes.set(key, size)\n return this.#tryComplete(key, size)\n }\n\n /**\n * Add one reassembled data unit. Returns the combined sample once every\n * constituent data unit has arrived and totals the authored size.\n */\n addMfu(mfu: ReassembledMFU): CombinedSample | null {\n if (mfu.movieFragmentSequenceNumber === undefined || mfu.sampleNumber === undefined) {\n // Non-timed (Item) MFU: sample combining does not apply.\n this.#stats.passthroughSamples++\n return fromPassthrough(mfu)\n }\n const offset = mfu.sampleOffset ?? 0\n const key = sampleKey(\n mfu.catalogTrackName,\n mfu.mpuSequenceNumber,\n mfu.movieFragmentSequenceNumber,\n mfu.sampleNumber,\n )\n\n if (this.#retired.has(key)) {\n // Duplicate or late data unit for an already-retired sample (a\n // FEC-recovered retransmission, reordering after completion, or a\n // sample discarded by eviction/timeout). Drop it rather than\n // re-emitting a duplicate downstream or misreading a stray fragment as\n // a fresh whole sample.\n return null\n }\n\n const dataLen = mfu.data.byteLength\n const authoredSize = this.#authoredSizes.get(key)\n\n if (offset === 0 && !this.#pending.has(key)) {\n const passesThrough = authoredSize === undefined\n ? !this.#requireAuthoredSize\n : dataLen === authoredSize\n if (passesThrough) {\n this.#stats.passthroughSamples++\n this.#retire(key)\n return fromPassthrough(mfu)\n }\n // Either shorter than the authored total (a subsample), or awaiting a\n // size under requireAuthoredSize -- buffer below.\n }\n\n // Reject BEFORE mutating any state. An earlier revision allocated the\n // pending entry (evicting the oldest sample to make room) and only then\n // validated, so a data unit the combiner went on to reject could destroy\n // an unrelated in-flight sample and leave an empty entry in its slot.\n const thisEnd = offset + dataLen\n if (authoredSize !== undefined && thisEnd > authoredSize) {\n throw new SampleCombineError(\n 'exceeds-authored-size',\n 'combined subsample data would exceed the registered authored sample size',\n )\n }\n const existing = this.#pending.get(key)\n if (existing !== undefined) {\n if (existing.segments.has(offset)) {\n throw new SampleCombineError(\n 'duplicate-subsample-offset',\n 'subsample data unit duplicates an offset already held for this sample',\n )\n }\n if (existing.segments.size >= this.#maxSegmentsPerSample) {\n throw new SampleCombineError(\n 'too-many-subsample-segments',\n 'a sample already holds the maximum number of subsample data units',\n )\n }\n for (const [segOffset, seg] of existing.segments) {\n const segEnd = segOffset + seg.data.byteLength\n if (offset < segEnd && segOffset < thisEnd) {\n throw new SampleCombineError(\n 'overlapping-subsample',\n \"subsample data unit overlaps a preceding data unit's byte range\",\n )\n }\n }\n }\n\n let pending = existing\n if (pending === undefined) {\n if (this.#pending.size >= this.#maxBuffered) this.#evictOldestPending()\n pending = { segments: new Map(), startedAt: Date.now() }\n this.#pending.set(key, pending)\n }\n pending.segments.set(offset, mfu)\n\n if (authoredSize === undefined) return null\n return this.#tryComplete(key, authoredSize)\n }\n\n /**\n * Emit the sample when its held data units are byte-adjacent from zero and\n * total `size`. Completion is a protocol event, never a timer.\n */\n #tryComplete(key: SampleKey, size: number): CombinedSample | null {\n const pending = this.#pending.get(key)\n if (pending === undefined) return null\n let expected = 0\n const sortedOffsets = [...pending.segments.keys()].sort((a, b) => a - b)\n for (const segOffset of sortedOffsets) {\n if (segOffset !== expected) return null // gap: not yet contiguous from zero.\n expected += pending.segments.get(segOffset)!.data.byteLength\n }\n if (expected !== size) return null // contiguous so far, short of the authored total.\n\n this.#pending.delete(key)\n this.#stats.combinedSamples++\n this.#retire(key)\n return fromSegments(pending.segments, sortedOffsets)\n }\n\n /**\n * Retire a sample's key: free its registered authored size and remember the\n * key in a bounded recency set. Called on every path that removes a sample\n * -- completion, passthrough, eviction, and timeout -- so `#authoredSizes`\n * cannot accumulate entries for samples that never complete, and so a later\n * data unit for a discarded sample is dropped rather than starting a fresh\n * buffer or being misread as a whole sample.\n *\n * The recency set holds `maxBuffered` keys. A duplicate delayed by more\n * than that many retirements escapes it; on a reliable path there is no\n * such duplicate, and on a lossy path the per-data-unit deadline in\n * {@link MFUReassembler} bounds the delay well inside this window.\n */\n #retire(key: SampleKey): void {\n this.#authoredSizes.delete(key)\n this.#retired.add(key)\n while (this.#retired.size > this.#maxBuffered) {\n const oldest = this.#retired.values().next().value\n if (oldest === undefined) break\n this.#retired.delete(oldest)\n }\n }\n\n /**\n * Discard the oldest pending sample by insertion order. This is the only\n * backstop against one FEC-lost subsample data unit wedging\n * {@link SampleCombiner.addMfu} for every future sample once `maxBuffered`\n * is reached, for a host that never drives\n * {@link SampleCombiner.cleanupTimeouts}.\n */\n #evictOldestPending(): void {\n const oldest = this.#pending.keys().next().value\n if (oldest === undefined) return\n this.#pending.delete(oldest)\n this.#stats.evictedSamples++\n this.#retire(oldest)\n }\n\n /**\n * Discard samples whose oldest held segment has waited past `timeoutMs`.\n * Mirrors {@link MFUReassembler}'s clock-driven shape; the per-data-unit\n * deadline inside {@link MFUReassembler} already bounds each constituent,\n * so this is a backstop against a host that registers an authored size but\n * never supplies every subsample DU.\n */\n cleanupTimeouts(timeoutMs: number): void {\n const now = Date.now()\n for (const [key, pending] of [...this.#pending]) {\n if (now - pending.startedAt > timeoutMs) {\n this.#pending.delete(key)\n this.#stats.timedOutSamples++\n this.#retire(key)\n }\n }\n }\n\n flush(): void {\n this.#pending.clear()\n this.#authoredSizes.clear()\n this.#retired.clear()\n }\n\n /**\n * Drop one stream's in-flight, registered-size, and retired state while\n * preserving every other stream (mirrors\n * {@link MFUReassembler.resetStream}).\n */\n resetStream(catalogTrackName: string): void {\n for (const key of this.#pending.keys()) {\n if (trackNameOfKey(key) === catalogTrackName) this.#pending.delete(key)\n }\n for (const key of this.#authoredSizes.keys()) {\n if (trackNameOfKey(key) === catalogTrackName) this.#authoredSizes.delete(key)\n }\n for (const key of this.#retired) {\n if (trackNameOfKey(key) === catalogTrackName) this.#retired.delete(key)\n }\n }\n\n get stats(): SampleCombinerStats {\n return { ...this.#stats }\n }\n\n get pendingCount(): number {\n return this.#pending.size\n }\n\n /**\n * Registered authored sizes still held. Diagnostic only: it exists so tests\n * and health reporting can assert this table stays bounded, since an\n * unbounded one is invisible from `pendingCount` alone.\n */\n get debugAuthoredSizeCount(): number {\n return this.#authoredSizes.size\n }\n\n /** Retired keys still remembered. Diagnostic only, bounded by `maxBuffered`. */\n get debugRetiredCount(): number {\n return this.#retired.size\n }\n}\n\nfunction fromPassthrough(mfu: ReassembledMFU): CombinedSample {\n return {\n mpuSequenceNumber: mfu.mpuSequenceNumber,\n movieFragmentSequenceNumber: mfu.movieFragmentSequenceNumber ?? 0,\n sampleNumber: mfu.sampleNumber ?? 0,\n data: mfu.data,\n timestamp: mfu.timestamp,\n rapFlag: mfu.rapFlag,\n priority: mfu.priority,\n packetSequenceNumbers: mfu.packetSequenceNumbers,\n repairedPacketSequenceNumbers: mfu.repairedPacketSequenceNumbers,\n catalogTrackName: mfu.catalogTrackName,\n subsampleCount: 1,\n fragmentCount: mfu.fragmentCount,\n source: mfu.source,\n // One data unit in, one sample out: its provenance IS the sample's.\n deliveryProvenance: mfu.deliveryProvenance\n }\n}\n\nfunction fromSegments(\n segments: Map<number, ReassembledMFU>,\n sortedOffsets: number[],\n): CombinedSample {\n const totalSize = sortedOffsets.reduce((sum, offset) => sum + segments.get(offset)!.data.byteLength, 0)\n const data = new Uint8Array(totalSize)\n let packetSequenceNumbers: number[] | undefined\n let repairedPacketSequenceNumbers: number[] | undefined\n let writeOffset = 0\n let first: ReassembledMFU | undefined\n let fragmentCount = 0\n // Fold across data units the same way the reassembler folds across the\n // fragments inside one: a sample is multicast-exclusive only if every\n // subsample that formed it was.\n let combinedProvenance: MmtpDeliveryProvenance =\n sortedOffsets.length === 0 ? 'unknown' : 'multicast'\n for (const offset of sortedOffsets) {\n const seg = segments.get(offset)!\n combinedProvenance =\n combinedProvenance === 'mixed' || seg.deliveryProvenance === 'mixed'\n ? 'mixed'\n : combinedProvenance === 'unknown' || seg.deliveryProvenance === 'unknown'\n ? 'unknown'\n : 'multicast'\n data.set(seg.data, writeOffset)\n writeOffset += seg.data.byteLength\n fragmentCount += seg.fragmentCount\n if (seg.packetSequenceNumbers !== undefined) {\n (packetSequenceNumbers ??= []).push(...seg.packetSequenceNumbers)\n }\n if (seg.repairedPacketSequenceNumbers !== undefined) {\n (repairedPacketSequenceNumbers ??= []).push(...seg.repairedPacketSequenceNumbers)\n }\n if (first === undefined) first = seg\n }\n return {\n mpuSequenceNumber: first!.mpuSequenceNumber,\n movieFragmentSequenceNumber: first!.movieFragmentSequenceNumber!,\n sampleNumber: first!.sampleNumber!,\n data,\n timestamp: first!.timestamp,\n rapFlag: first!.rapFlag,\n priority: first!.priority,\n packetSequenceNumbers,\n repairedPacketSequenceNumbers,\n catalogTrackName: first!.catalogTrackName,\n subsampleCount: segments.size,\n fragmentCount,\n source: first!.source,\n deliveryProvenance: combinedProvenance,\n }\n}\n\n// =============================================================================\n// Inline Tests (vitest)\n// =============================================================================\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest\n\n const combiner = (overrides: Partial<SampleCombinerOptions> = {}) =>\n new SampleCombiner({\n maxBuffered: 10,\n requireAuthoredSize: false,\n maxSegmentsPerSample: 8,\n ...overrides,\n })\n\n const mfu = (\n mpu: number,\n movie: number,\n sample: number,\n offset: number,\n data: number[],\n timestamp: bigint,\n rapFlag: boolean,\n deliveryProvenance: MmtpDeliveryProvenance = 'multicast',\n ): ReassembledMFU => ({\n mpuSequenceNumber: mpu,\n data: new Uint8Array(data),\n timestamp,\n fragmentCount: 1,\n rapFlag,\n sampleNumber: sample,\n movieFragmentSequenceNumber: movie,\n sampleOffset: offset,\n packetSequenceNumbers: [offset],\n deliveryProvenance,\n })\n\n describe('SampleCombiner', () => {\n test('whole-sample DU passes through immediately with no registered size', () => {\n const c = combiner()\n const out = c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))\n expect(out).not.toBeNull()\n expect(out!.data).toEqual(new Uint8Array([1, 2, 3, 4, 5]))\n expect(out!.subsampleCount).toBe(1)\n expect(c.pendingCount).toBe(0)\n })\n\n test('whole-sample DU passes through when it matches the registered size', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 5)\n const out = c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))\n expect(out?.data).toEqual(new Uint8Array([1, 2, 3, 4, 5]))\n })\n\n test('subsample data units combine once the authored size is met', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 10)\n\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))).toBeNull()\n expect(c.pendingCount).toBe(1)\n\n const out = c.addMfu(mfu(1, 9, 17, 5, [6, 7, 8, 9, 10], 1001n, false))\n expect(out?.data).toEqual(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))\n expect(out?.subsampleCount).toBe(2)\n expect(out?.rapFlag).toBe(true) // offset-0 DU's RAP flag, per moqcast #37.\n expect(out?.timestamp).toBe(1000n)\n expect(c.pendingCount).toBe(0)\n expect(c.stats.combinedSamples).toBe(1)\n })\n\n test('subsample data units combine out of order', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 10)\n\n expect(c.addMfu(mfu(1, 9, 17, 5, [6, 7, 8, 9, 10], 1001n, false))).toBeNull()\n const out = c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))\n expect(out?.data).toEqual(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))\n })\n\n test('subsample without a registered size never completes', () => {\n // A non-zero-offset DU always buffers (it can never be a whole\n // sample). Without a registered authored size there is nothing to\n // combine against, so it -- and a sibling that arrives after it --\n // stay pending rather than being guessed complete.\n const c = combiner()\n expect(c.addMfu(mfu(1, 9, 17, 3, [1, 2, 3], 1001n, false))).toBeNull()\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3], 1000n, true))).toBeNull()\n expect(c.pendingCount).toBe(1)\n })\n\n test('requireAuthoredSize buffers an offset-zero DU instead of guessing complete', () => {\n // A moqcast-#37-aware host opts into fail-closed buffering: on a lossy\n // path FT=1 can arrive after an offset-zero data unit (reordering, or\n // FT=1's own DU recovered late by FEC). Treating \"no size yet\" as\n // \"whole sample\" would silently truncate a partitioned sample.\n const c = combiner({ requireAuthoredSize: true })\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))).toBeNull()\n expect(c.pendingCount).toBe(1)\n\n c.registerAuthoredSize(undefined, 1, 9, 17, 10)\n const out = c.addMfu(mfu(1, 9, 17, 5, [6, 7, 8, 9, 10], 1001n, false))\n expect(out?.data).toEqual(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))\n })\n\n test('duplicate offset is rejected', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 10)\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))).toBeNull()\n expect(() => c.addMfu(mfu(1, 9, 17, 0, [9, 9, 9, 9, 9], 2000n, true)))\n .toThrow(SampleCombineError)\n })\n\n test('overlapping subsample is rejected', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 10)\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))).toBeNull()\n expect(() => c.addMfu(mfu(1, 9, 17, 3, [9, 9, 9, 9, 9], 2000n, true)))\n .toThrow(SampleCombineError)\n })\n\n test('exceeding the authored size is rejected', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 8)\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))).toBeNull()\n expect(() => c.addMfu(mfu(1, 9, 17, 5, [6, 7, 8, 9, 10], 2000n, false)))\n .toThrow(SampleCombineError)\n })\n\n test('exceeding maxSegmentsPerSample is rejected', () => {\n const c = combiner({ maxSegmentsPerSample: 2 })\n c.registerAuthoredSize(undefined, 1, 9, 17, 100)\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2], 1000n, true))).toBeNull()\n expect(c.addMfu(mfu(1, 9, 17, 2, [3, 4], 1001n, false))).toBeNull()\n expect(() => c.addMfu(mfu(1, 9, 17, 4, [5, 6], 1002n, false)))\n .toThrow(SampleCombineError)\n })\n\n test('a full combiner evicts the oldest pending sample instead of throwing', () => {\n // Eviction is by insertion order, which is also the only backstop\n // against one permanently-lost subsample DU wedging every future\n // sample once maxBuffered incomplete samples accumulate.\n const c = combiner({ maxBuffered: 2 })\n c.registerAuthoredSize(undefined, 1, 9, 17, 10)\n c.registerAuthoredSize(undefined, 1, 9, 18, 10)\n c.registerAuthoredSize(undefined, 1, 9, 19, 10)\n\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 1, 1, 1, 1], 1000n, true))).toBeNull()\n expect(c.addMfu(mfu(1, 9, 18, 0, [2, 2, 2, 2, 2], 1001n, true))).toBeNull()\n expect(c.pendingCount).toBe(2)\n\n // A third distinct incomplete sample evicts sample 17, the oldest.\n expect(c.addMfu(mfu(1, 9, 19, 0, [3, 3, 3, 3, 3], 1002n, true))).toBeNull()\n expect(c.pendingCount).toBe(2)\n expect(c.stats.evictedSamples).toBe(1)\n\n // The surviving samples are undisturbed and still complete normally.\n expect(c.addMfu(mfu(1, 9, 18, 5, [8, 8, 8, 8, 8], 2001n, false))?.data)\n .toEqual(new Uint8Array([2, 2, 2, 2, 2, 8, 8, 8, 8, 8]))\n expect(c.addMfu(mfu(1, 9, 19, 5, [9, 9, 9, 9, 9], 2002n, false))?.data)\n .toEqual(new Uint8Array([3, 3, 3, 3, 3, 9, 9, 9, 9, 9]))\n\n // The combiner still accepts new work rather than being wedged.\n c.registerAuthoredSize(undefined, 1, 9, 20, 10)\n expect(c.addMfu(mfu(1, 9, 20, 0, [4, 4, 4, 4, 4], 3000n, true))).toBeNull()\n expect(c.addMfu(mfu(1, 9, 20, 5, [7, 7, 7, 7, 7], 3001n, false))?.data)\n .toEqual(new Uint8Array([4, 4, 4, 4, 4, 7, 7, 7, 7, 7]))\n })\n\n test('a completed sample frees its authored size and drops a late duplicate', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 10)\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))).toBeNull()\n expect(c.addMfu(mfu(1, 9, 17, 5, [6, 7, 8, 9, 10], 1001n, false))).not.toBeNull()\n\n // A FEC-recovered retransmission of the completed sample's offset-0\n // data unit must not be re-emitted as a second complete sample.\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 2000n, true))).toBeNull()\n })\n\n test('resetStream drops one track and preserves the others', () => {\n const c = combiner()\n c.registerAuthoredSize('video/720p', 1, 9, 17, 10)\n c.registerAuthoredSize('audio', 1, 9, 17, 10)\n const video = { ...mfu(1, 9, 17, 0, [1, 1, 1, 1, 1], 1000n, true), catalogTrackName: 'video/720p' }\n const audio = { ...mfu(1, 9, 17, 0, [2, 2, 2, 2, 2], 1000n, true), catalogTrackName: 'audio' }\n expect(c.addMfu(video)).toBeNull()\n expect(c.addMfu(audio)).toBeNull()\n\n c.resetStream('video/720p')\n expect(c.pendingCount).toBe(1)\n\n const audioTail = { ...mfu(1, 9, 17, 5, [3, 3, 3, 3, 3], 1001n, false), catalogTrackName: 'audio' }\n expect(c.addMfu(audioTail)?.data).toEqual(new Uint8Array([2, 2, 2, 2, 2, 3, 3, 3, 3, 3]))\n })\n\n test('flush clears registered authored sizes', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 9, 17, 5)\n c.flush()\n // A now-unregistered size means an offset-zero DU passes through\n // immediately again (requireAuthoredSize is false here) rather than\n // waiting on a size flush() already discarded.\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))?.data)\n .toEqual(new Uint8Array([1, 2, 3, 4, 5]))\n })\n\n test('a healthy stream accumulates no bookkeeping', () => {\n // Regression: retirement ran only on completion paths, so an\n // abandoned sample's registered size stayed resident forever.\n const c = combiner({ maxBuffered: 4, requireAuthoredSize: true })\n for (let i = 0; i < 1000; i++) {\n expect(c.registerAuthoredSize(undefined, 1, 9, i, 10)).toBeNull()\n expect(c.addMfu(mfu(1, 9, i, 0, [1, 1, 1, 1, 1], 1000n, true))).toBeNull()\n expect(c.addMfu(mfu(1, 9, i, 5, [2, 2, 2, 2, 2], 1001n, false))).not.toBeNull()\n }\n expect(c.stats.combinedSamples).toBe(1000)\n expect(c.pendingCount).toBe(0)\n expect(c.debugAuthoredSizeCount).toBe(0)\n expect(c.debugRetiredCount).toBeLessThanOrEqual(4)\n })\n\n test('samples that never complete release their authored size', () => {\n const c = combiner({ maxBuffered: 4, requireAuthoredSize: true })\n for (let i = 0; i < 1000; i++) {\n expect(c.registerAuthoredSize(undefined, 1, 9, i, 10)).toBeNull()\n expect(c.addMfu(mfu(1, 9, i, 0, [1, 1, 1, 1, 1], 1000n, true))).toBeNull()\n // Deliberately never supply the tail.\n }\n c.cleanupTimeouts(-1) // expire everything still held\n expect(c.pendingCount).toBe(0)\n expect(c.debugAuthoredSizeCount).toBe(0)\n })\n\n test('a late authored size completes an already-buffered sample', () => {\n // Regression: with requireAuthoredSize the completion check ran only\n // inside addMfu, so a sample carried by a SINGLE data unit whose FT=1\n // arrived afterwards had no later data unit to re-enter on and was\n // silently lost.\n const c = combiner({ requireAuthoredSize: true })\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 2, 3, 4, 5], 1000n, true))).toBeNull()\n expect(c.pendingCount).toBe(1)\n\n const out = c.registerAuthoredSize(undefined, 1, 9, 17, 5)\n expect(out?.data).toEqual(new Uint8Array([1, 2, 3, 4, 5]))\n expect(out?.subsampleCount).toBe(1)\n expect(c.pendingCount).toBe(0)\n })\n\n test('a rejected data unit does not evict a live sample', () => {\n // Regression: the pending slot was allocated (evicting the oldest\n // sample to make room) BEFORE validation, so a data unit the combiner\n // went on to reject destroyed an unrelated in-flight sample.\n const c = combiner({ maxBuffered: 2 })\n for (const s of [17, 18, 19]) c.registerAuthoredSize(undefined, 1, 9, s, 10)\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 1, 1, 1, 1], 1000n, true))).toBeNull()\n expect(c.addMfu(mfu(1, 9, 18, 0, [2, 2, 2, 2, 2], 1001n, true))).toBeNull()\n expect(c.pendingCount).toBe(2)\n\n // 8 + 5 = 13 exceeds sample 19's authored size of 10.\n expect(() => c.addMfu(mfu(1, 9, 19, 8, [9, 9, 9, 9, 9], 2000n, false)))\n .toThrow(SampleCombineError)\n expect(c.stats.evictedSamples).toBe(0)\n expect(c.pendingCount).toBe(2)\n\n // Sample 17 is untouched and still completes.\n expect(c.addMfu(mfu(1, 9, 17, 5, [3, 3, 3, 3, 3], 2001n, false))?.data)\n .toEqual(new Uint8Array([1, 1, 1, 1, 1, 3, 3, 3, 3, 3]))\n })\n\n test('an evicted sample is retired rather than reopened', () => {\n const c = combiner({ maxBuffered: 1 })\n for (const s of [17, 18]) c.registerAuthoredSize(undefined, 1, 9, s, 10)\n expect(c.addMfu(mfu(1, 9, 17, 0, [1, 1, 1, 1, 1], 1000n, true))).toBeNull()\n expect(c.addMfu(mfu(1, 9, 18, 0, [2, 2, 2, 2, 2], 1001n, true))).toBeNull()\n expect(c.stats.evictedSamples).toBe(1)\n\n // Sample 17's tail arrives after its eviction; it must not reopen.\n expect(c.addMfu(mfu(1, 9, 17, 5, [3, 3, 3, 3, 3], 2000n, false))).toBeNull()\n expect(c.pendingCount).toBe(1)\n })\n })\n\n describe('sample-level delivery provenance', () => {\n test('one non-multicast subsample makes the whole sample non-exclusive', () => {\n // A 4K/8K sample is split across subsample DUs. If any one of them\n // arrived over QUIC, the assembled sample contains bytes that never\n // crossed a multicast socket, so the sample is not physical evidence\n // even though every other DU was multicast.\n const c = combiner()\n c.registerAuthoredSize(undefined, 1, 1, 1, 4)\n expect(c.addMfu(mfu(1, 1, 1, 0, [1, 2], 100n, true, 'multicast'))).toBeNull()\n const done = c.addMfu(mfu(1, 1, 1, 2, [3, 4], 100n, false, 'mixed'))\n expect(done).not.toBeNull()\n expect(done!.subsampleCount).toBe(2)\n expect(done!.deliveryProvenance).toBe('mixed')\n })\n\n test('an unattributed subsample degrades the sample to unknown', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 2, 1, 1, 4)\n expect(c.addMfu(mfu(2, 1, 1, 0, [1, 2], 100n, true, 'multicast'))).toBeNull()\n const done = c.addMfu(mfu(2, 1, 1, 2, [3, 4], 100n, false, 'unknown'))\n expect(done).not.toBeNull()\n expect(done!.deliveryProvenance).toBe('unknown')\n })\n\n test('an all-multicast sample stays multicast-exclusive', () => {\n const c = combiner()\n c.registerAuthoredSize(undefined, 3, 1, 1, 4)\n expect(c.addMfu(mfu(3, 1, 1, 0, [1, 2], 100n, true, 'multicast'))).toBeNull()\n const done = c.addMfu(mfu(3, 1, 1, 2, [3, 4], 100n, false, 'multicast'))\n expect(done).not.toBeNull()\n expect(done!.deliveryProvenance).toBe('multicast')\n })\n\n test('a passthrough sample inherits its data unit provenance verbatim', () => {\n const c = combiner()\n const done = c.addMfu(mfu(4, 1, 1, 0, [1, 2], 100n, true, 'mixed'))\n expect(done).not.toBeNull()\n expect(done!.subsampleCount).toBe(1)\n expect(done!.deliveryProvenance).toBe('mixed')\n })\n })\n}\n", "/**\n * fMP4 data-unit completion policy (BLO-13291).\n *\n * FFmpeg's `mmt` muxer in fMP4-fragments mode (mfu_mode=0, mmtenc_fmp4.c\n * send_media_fragment) delivers one complete moof+mdat (optionally\n * sidx-prefixed) per MPU data unit, split across FI=1/2/3 MMTP fragments.\n * Neither wire counter can prove the chain is byte-complete:\n *\n * - MPU payload header byte 3 is the eight-bit fragment counter. It proves\n * ordering, but cannot declare the total byte length of a fragmented DU.\n * - Each fragment's payload_length bounds that packet only; it does not declare\n * the total size of the multi-packet fMP4 data unit.\n * - packet_sequence_number has legitimate gaps: repair and signaling packets\n * consume sequence slots in the same flow at every FEC block boundary.\n *\n * The data unit is self-describing instead: ISOBMFF top-level box headers\n * declare their 32-bit sizes. A candidate assembly is complete exactly when\n * it parses as a whole number of known top-level boxes covering its full\n * length. Any interior hole shifts subsequent box headers (type/size garbage)\n * or leaves a declared size overrunning the buffer; missing tail bytes leave\n * the final box short.\n */\n\n/** Top-level box types the MMT fMP4 writers emit around a data unit. */\nconst TOP_LEVEL_BOX_TYPES = new Set(['ftyp', 'styp', 'moov', 'moof', 'mdat', 'sidx', 'free', 'skip', 'prft', 'emsg'])\n\n/**\n * True when `candidate` parses as one or more complete top-level ISOBMFF\n * boxes covering exactly its byte length. size==0 (\"to end of file\") and\n * size==1 (64-bit largesize) are treated as unverifiable and return false \u2014\n * the MMT muxers never emit them for streamed data units.\n */\nexport function isCompleteFmp4DataUnit(candidate: Uint8Array): boolean {\n if (candidate.length < 8) return false\n const dv = new DataView(candidate.buffer, candidate.byteOffset, candidate.byteLength)\n let pos = 0\n while (pos < candidate.length) {\n if (pos + 8 > candidate.length) return false\n const size = dv.getUint32(pos)\n if (size < 8) return false\n const type = String.fromCharCode(candidate[pos + 4]!, candidate[pos + 5]!, candidate[pos + 6]!, candidate[pos + 7]!)\n if (!TOP_LEVEL_BOX_TYPES.has(type)) return false\n if (pos + size > candidate.length) return false\n pos += size\n }\n return pos === candidate.length\n}\n", "/**\n * H.264/AVC codec utilities for MMTP container format conversion.\n *\n * Shared between moq-lib (hang-mmt-fec) and moqtail players.\n * Handles AVCC\u2194Annex-B conversion, SPS/PPS extraction, fallback avcC generation,\n * and MMTP timestamp conversion.\n *\n * @packageDocumentation\n */\n\n// \u2500\u2500\u2500 Public Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Result of extracting a video codec configuration record from an fMP4 init segment. */\nexport interface AvcCInfo {\n codec: string\n description: Uint8Array\n}\n\n/** Result of extracting AAC AudioSpecificConfig from an fMP4 init segment. */\nexport interface AacAscInfo {\n /** Codec string like \"mp4a.40.2\" (AAC-LC). Derived from AOT in ASC[0] >> 3. */\n codec: string\n /** AudioSpecificConfig bytes \u2014 what AudioDecoder.configure({description}) needs for raw AAC. */\n description: Uint8Array\n}\n\n/** SPS and PPS NAL units extracted from an avcC record. */\nexport interface SpsPps {\n sps: Uint8Array\n pps: Uint8Array\n}\n\n/** Options for fallback avcC generation. */\nexport interface FallbackAvcCOptions {\n /** Codec string like \"avc1.640033\". Profile/constraint/level parsed from hex triplet. */\n codec?: string\n /** Coded width in pixels. Used in SPS pic_width_in_mbs. */\n width?: number\n /** Coded height in pixels. Used in SPS pic_height_in_map_units. */\n height?: number\n}\n\n// \u2500\u2500\u2500 Public Functions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Extract the codec configuration record (avcC/hvcC/av1C/vpcC) from\n * an fMP4 init segment (moov box).\n * MMTP FT=0 packets contain a complete fMP4: moov > trak > mdia > minf > stbl > stsd > sample entry > codec config.\n *\n * Walks every trak and returns the first supported video sample entry. This\n * handles both per-track CMSF init segments and multiplexed ftyp+moov blobs\n * where the audio trak may appear before video.\n */\nexport function extractAvcCFromInitSegment(data: Uint8Array): AvcCInfo | undefined {\n if (!hasIsoBmffMagic(data)) return undefined\n\n for (const stsd of findTrackStsdBoxes(data)) {\n const info = extractVideoConfigFromStsd(data, stsd)\n if (info) return info\n }\n\n return undefined\n}\n\nfunction extractVideoConfigFromStsd(data: Uint8Array, stsd: { o: number; sz: number }): AvcCInfo | undefined {\n let s = stsd.o + 16\n const e = stsd.o + stsd.sz\n\n // H.264: avc1 VisualSampleEntry \u2192 avcC.\n const avc1 = findBox(data, s, e, 'avc1')\n if (avc1 && avc1.sz >= 86) {\n const avcC = findBox(data, avc1.o + 86, avc1.o + avc1.sz, 'avcC')\n if (avcC && avcC.sz >= 12) {\n const d = data.subarray(avcC.o + 8, avcC.o + avcC.sz)\n const codec = `avc1.${hex2(d[1])}${hex2(d[2])}${hex2(d[3])}`\n return { codec, description: d }\n }\n }\n // HEVC: hvc1/hev1 VisualSampleEntry \u2192 hvcC. Same +86 child-box offset as\n // avc1 (identical VisualSampleEntry layout). Without this branch, HEVC\n // ftyp+moov inits yield no descriptor, so the WebCodecs path configures the\n // VideoDecoder with no `description` and the first length-prefixed hvc1\n // keyframe decode() throws DataError (\"must fill out the description field\"),\n // which the player's MoQ pump catch swallows \u2192 video track silently 0-fps\n // (BLO-8054).\n const hvc1 = findBox(data, s, e, 'hvc1') ?? findBox(data, s, e, 'hev1')\n if (hvc1 && hvc1.sz >= 86) {\n const hvcC = findBox(data, hvc1.o + 86, hvc1.o + hvc1.sz, 'hvcC')\n // HEVCDecoderConfigurationRecord is \u226523 bytes (+8 box header).\n if (hvcC && hvcC.sz >= 8 + 23) {\n const d = data.subarray(hvcC.o + 8, hvcC.o + hvcC.sz)\n return { codec: hevcCodecStringFromHvcC(d), description: d }\n }\n }\n // AV1: av01 VisualSampleEntry -> av1C. Real FFmpeg MMTP inits can carry an\n // empty av1C box (size=8) while still providing the authoritative sample\n // entry/codec signal. Return an empty description for that shape so callers\n // can open their FT=0 gate without passing a zero-byte WebCodecs description.\n const av01 = findBox(data, s, e, 'av01')\n if (av01 && av01.sz >= 86) {\n const av1C = findBox(data, av01.o + 86, av01.o + av01.sz, 'av1C')\n if (av1C && av1C.sz >= 8) {\n const d = data.subarray(av1C.o + 8, av1C.o + av1C.sz)\n return { codec: codecStringFromAv1C(d), description: d }\n }\n }\n // VP9: vp09 VisualSampleEntry -> vpcC. Some encoders omit the VP codec record\n // payload; use the sample entry as readiness evidence and fall back to a\n // conservative vp09 codec string in that case.\n const vp09 = findBox(data, s, e, 'vp09')\n if (vp09 && vp09.sz >= 86) {\n const vpcC = findBox(data, vp09.o + 86, vp09.o + vp09.sz, 'vpcC')\n if (vpcC && vpcC.sz >= 8) {\n const d = data.subarray(vpcC.o + 8, vpcC.o + vpcC.sz)\n return { codec: codecStringFromVpcC(d), description: d }\n }\n }\n return undefined\n}\n\n/**\n * Generate a minimal avcC (AVCDecoderConfigurationRecord) from codec parameters.\n * Parses \"avc1.XXYYZZ\" \u2192 profile_idc=0xXX, constraint_flags=0xYY, level_idc=0xZZ.\n * Width/height are encoded in the SPS; if not provided, they're inferred from the\n * H.264 level's max frame size (e.g. level 4.2 \u2192 2048x1088).\n * Used when late MoQ subscribers miss the FT=0 init segment (moov/avcC).\n */\nexport function generateFallbackAvcC(options?: FallbackAvcCOptions | string): Uint8Array {\n const opts: FallbackAvcCOptions = typeof options === 'string' ? { codec: options } : (options ?? {})\n\n let profile = 0x64\n let constraint = 0x00\n let level = 0x2a\n\n if (opts.codec) {\n const match = opts.codec.match(/^avc[13]\\.([0-9a-fA-F]{6})$/)\n if (match) {\n profile = Number.parseInt(match[1].substring(0, 2), 16)\n constraint = Number.parseInt(match[1].substring(2, 4), 16)\n level = Number.parseInt(match[1].substring(4, 6), 16)\n }\n }\n\n const { width, height } = resolveResolution(opts.width, opts.height, level)\n const sps = buildMinimalSps(profile, constraint, level, width, height)\n const pps = buildMinimalPps(profile)\n\n const avcC = new Uint8Array(5 + 1 + 2 + sps.length + 1 + 2 + pps.length)\n let w = 0\n avcC[w++] = 0x01\n avcC[w++] = profile\n avcC[w++] = constraint\n avcC[w++] = level\n avcC[w++] = 0xff\n avcC[w++] = 0xe1\n avcC[w++] = (sps.length >> 8) & 0xff\n avcC[w++] = sps.length & 0xff\n avcC.set(sps, w)\n w += sps.length\n avcC[w++] = 0x01\n avcC[w++] = (pps.length >> 8) & 0xff\n avcC[w++] = pps.length & 0xff\n avcC.set(pps, w)\n return avcC\n}\n\n/**\n * Extract raw SPS and PPS NAL unit bytes from an avcC (AVCDecoderConfigurationRecord).\n * Returns the first SPS and first PPS found.\n */\nexport function extractSpsPpsFromAvcC(avcC: Uint8Array): SpsPps | undefined {\n if (avcC.length < 8) return undefined\n const numSPS = avcC[5] & 0x1f\n let offset = 6\n let sps: Uint8Array | undefined\n for (let i = 0; i < numSPS && offset + 2 <= avcC.length; i++) {\n const spsLen = (avcC[offset] << 8) | avcC[offset + 1]\n offset += 2\n if (offset + spsLen <= avcC.length) {\n if (!sps) sps = avcC.subarray(offset, offset + spsLen)\n offset += spsLen\n }\n }\n let pps: Uint8Array | undefined\n if (offset < avcC.length) {\n const numPPS = avcC[offset] & 0x1f\n offset++\n for (let i = 0; i < numPPS && offset + 2 <= avcC.length; i++) {\n const ppsLen = (avcC[offset] << 8) | avcC[offset + 1]\n offset += 2\n if (offset + ppsLen <= avcC.length) {\n if (!pps) pps = avcC.subarray(offset, offset + ppsLen)\n offset += ppsLen\n }\n }\n }\n return sps && pps ? { sps, pps } : undefined\n}\n\n/**\n * Convert AVCC (4-byte NAL length prefixed) to Annex-B (start code prefixed).\n * Prepends SPS/PPS NALs before keyframes.\n */\nexport function avccToAnnexB(avccData: Uint8Array, isKeyframe: boolean, spsPps: SpsPps): Uint8Array {\n let offset = 0\n let totalNalSize = 0\n let nalCount = 0\n while (offset + 4 <= avccData.length) {\n const nalLen =\n (avccData[offset] << 24) |\n (avccData[offset + 1] << 16) |\n (avccData[offset + 2] << 8) |\n avccData[offset + 3]\n offset += 4\n if (nalLen <= 0 || offset + nalLen > avccData.length) break\n totalNalSize += nalLen\n nalCount++\n offset += nalLen\n }\n if (nalCount === 0) return avccData\n\n let outSize = nalCount * 4 + totalNalSize\n if (isKeyframe) {\n outSize += 4 + spsPps.sps.length + 4 + spsPps.pps.length\n }\n const out = new Uint8Array(outSize)\n let w = 0\n\n if (isKeyframe) {\n out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1\n out.set(spsPps.sps, w); w += spsPps.sps.length\n out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1\n out.set(spsPps.pps, w); w += spsPps.pps.length\n }\n\n offset = 0\n while (offset + 4 <= avccData.length) {\n const nalLen =\n (avccData[offset] << 24) |\n (avccData[offset + 1] << 16) |\n (avccData[offset + 2] << 8) |\n avccData[offset + 3]\n offset += 4\n if (nalLen <= 0 || offset + nalLen > avccData.length) break\n out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1\n out.set(avccData.subarray(offset, offset + nalLen), w)\n w += nalLen\n offset += nalLen\n }\n return out\n}\n\n/**\n * Build an avcC record by parsing Annex-B data to extract SPS (NAL type 7) and PPS (NAL type 8).\n * Returns the avcC record, or null if SPS/PPS not found.\n */\nexport function extractCodecDescriptionFromAnnexB(data: Uint8Array): Uint8Array | null {\n const nalUnits: Array<{ type: number; data: Uint8Array }> = []\n let i = 0\n while (i < data.length - 4) {\n if (data[i] === 0 && data[i + 1] === 0) {\n let nalStart = 0\n if (data[i + 2] === 1) {\n nalStart = i + 3\n } else if (data[i + 2] === 0 && data[i + 3] === 1) {\n nalStart = i + 4\n }\n if (nalStart > 0 && nalStart < data.length) {\n let nalEnd = data.length\n for (let j = nalStart; j < data.length - 3; j++) {\n if (data[j] === 0 && data[j + 1] === 0 &&\n (data[j + 2] === 1 || (data[j + 2] === 0 && data[j + 3] === 1))) {\n nalEnd = j\n break\n }\n }\n const nalType = data[nalStart] & 0x1f\n nalUnits.push({ type: nalType, data: data.slice(nalStart, nalEnd) })\n i = nalEnd\n continue\n }\n }\n i++\n }\n\n const sps = nalUnits.find(n => n.type === 7)\n const pps = nalUnits.find(n => n.type === 8)\n if (!sps || !pps) return null\n\n const avcCLength = 6 + 2 + sps.data.length + 1 + 2 + pps.data.length\n const avcC = new Uint8Array(avcCLength)\n let offset = 0\n avcC[offset++] = 1\n avcC[offset++] = sps.data[1]\n avcC[offset++] = sps.data[2]\n avcC[offset++] = sps.data[3]\n avcC[offset++] = 0xff\n avcC[offset++] = 0xe1\n avcC[offset++] = (sps.data.length >> 8) & 0xff\n avcC[offset++] = sps.data.length & 0xff\n avcC.set(sps.data, offset); offset += sps.data.length\n avcC[offset++] = 1\n avcC[offset++] = (pps.data.length >> 8) & 0xff\n avcC[offset++] = pps.data.length & 0xff\n avcC.set(pps.data, offset)\n return avcC\n}\n\n/**\n * Derive codec string from avcC record (e.g. \"avc1.640033\").\n */\nexport function codecStringFromAvcC(avcC: Uint8Array): string {\n if (avcC.length < 4) return 'avc1.640033'\n return `avc1.${hex2(avcC[1])}${hex2(avcC[2])}${hex2(avcC[3])}`\n}\n\n/** VPS/SPS/PPS NAL units extracted from an HEVC keyframe (raw NAL bytes, no start code/length prefix). */\nexport interface HevcParameterSets {\n vps?: Uint8Array\n sps?: Uint8Array\n pps?: Uint8Array\n}\n\n/** True for HEVC/H.265 codec strings (hev1.* / hvc1.*). */\nexport function isHevcCodec(codec: string): boolean {\n return codec.startsWith('hev1') || codec.startsWith('hvc1')\n}\n\n/** HEVC NAL unit type from the first NAL header byte: (byte >> 1) & 0x3f. */\nfunction hevcNalType(nal: Uint8Array): number {\n return (nal[0] >> 1) & 0x3f\n}\n\n/**\n * Remove H.26x emulation-prevention bytes (0x03 after 0x00 0x00) from an RBSP so\n * fixed-position fields (e.g. HEVC profile_tier_level) can be read by byte offset.\n */\nexport function removeEmulationPrevention(rbsp: Uint8Array): Uint8Array {\n const out: number[] = []\n let zeros = 0\n for (let i = 0; i < rbsp.length; i++) {\n const b = rbsp[i]\n if (zeros >= 2 && b === 0x03 && i + 1 < rbsp.length && rbsp[i + 1] <= 0x03) {\n zeros = 0\n continue\n }\n out.push(b)\n zeros = b === 0 ? zeros + 1 : 0\n }\n return Uint8Array.from(out)\n}\n\n/**\n * Split an AVCC/hvcC bitstream (length-prefixed NAL units) into raw NAL byte slices.\n * `lengthSize` is the NAL length prefix width (4 for FFmpeg LOC / fMP4 sample data).\n */\nexport function splitAvccNalUnits(data: Uint8Array, lengthSize = 4): Uint8Array[] {\n const nals: Uint8Array[] = []\n let i = 0\n while (i + lengthSize <= data.length) {\n let len = 0\n for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]\n i += lengthSize\n if (len <= 0 || i + len > data.length) break\n nals.push(data.subarray(i, i + len))\n i += len\n }\n return nals\n}\n\n/**\n * True when `data` is exactly one or more length-prefixed NAL units.\n *\n * This is intentionally stricter than \"starts with a plausible length\": a\n * H.264 AVCC access unit may begin with bytes `00 00 00 01` when the first\n * NAL is a one-byte AUD. That byte pattern is also an Annex-B start code, so\n * callers must prove the whole buffer is complete AVCC before falling back to\n * Annex-B detection/conversion.\n */\nexport function isCompleteAvccSample(data: Uint8Array, lengthSize = 4): boolean {\n let i = 0\n let count = 0\n while (i + lengthSize <= data.length) {\n let len = 0\n for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]\n i += lengthSize\n if (len <= 0 || i + len > data.length) return false\n i += len\n count++\n }\n return count > 0 && i === data.length\n}\n\nexport type H264SampleFormat = 'avcc' | 'annexb' | 'raw-nal' | 'unknown'\nexport type H264NalUnitFormat = H264SampleFormat | 'partial-avcc'\n\nexport interface H264AvccSample {\n data: Uint8Array\n format: H264SampleFormat\n}\n\nexport interface H264NalUnitSample {\n nals: Uint8Array[]\n format: H264NalUnitFormat\n}\n\n/**\n * Normalize a H.264 access unit/sample to length-prefixed AVCC for fMP4 mdat\n * payloads. Already-AVCC samples are returned as-is. Annex-B and single raw\n * NAL samples are converted one-way to AVCC at the MSE/fMP4 boundary.\n */\nexport function normalizeH264SampleToAvcc(data: Uint8Array): H264AvccSample {\n if (isCompleteAvccSample(data)) return { data, format: 'avcc' }\n if (isAnnexB(data)) return { data: annexBToAvcc(data), format: 'annexb' }\n if (isLikelyRawH264NalUnit(data)) return { data: rawH264NalToAvcc(data), format: 'raw-nal' }\n return { data, format: 'unknown' }\n}\n\n/**\n * Split a H.264 access unit/sample into NAL units without changing its framing.\n * Use this for routing, keyframe gates, and diagnostics. Conversion to AVCC is\n * reserved for `normalizeH264SampleToAvcc` at the fMP4/MSE boundary.\n */\nexport function splitH264SampleNalUnits(\n data: Uint8Array,\n options: H264KeyframeDetectionOptions = {},\n): H264NalUnitSample {\n if (isCompleteAvccSample(data)) return { nals: splitAvccNalUnits(data), format: 'avcc' }\n if (isAnnexB(data)) return { nals: splitAnnexBNalUnits(data), format: 'annexb' }\n if (options.allowPartialAvcc === true) {\n const partial = splitPartialAvccNalUnits(data)\n if (partial.length > 0) return { nals: partial, format: 'partial-avcc' }\n }\n if (isLikelyRawH264NalUnit(data)) return { nals: [data], format: 'raw-nal' }\n const nals = splitAvccNalUnits(data)\n return { nals, format: nals.length > 0 ? 'avcc' : 'unknown' }\n}\n\n/**\n * Extract HEVC VPS (type 32) / SPS (33) / PPS (34) NAL units from a keyframe access\n * unit. Accepts either Annex-B (start-code delimited) or AVCC/hvcC (length-prefixed)\n * framing \u2014 FFmpeg LOC delivers the latter.\n */\nexport function extractHevcParameterSets(keyframe: Uint8Array): HevcParameterSets {\n const nals = isAnnexB(keyframe) ? splitAnnexBNalUnits(keyframe) : splitAvccNalUnits(keyframe)\n const out: HevcParameterSets = {}\n for (const nal of nals) {\n if (nal.length === 0) continue\n const t = hevcNalType(nal)\n if (t === 32 && !out.vps) out.vps = nal\n else if (t === 33 && !out.sps) out.sps = nal\n else if (t === 34 && !out.pps) out.pps = nal\n }\n return out\n}\n\n/**\n * Build a minimal hvcC (HEVCDecoderConfigurationRecord, ISO 14496-15 \u00A78.3.3.1) from\n * VPS/SPS/PPS NAL units. The general profile_tier_level (12 bytes) is copied from the\n * SPS (de-emulated); remaining fields use the common 4:2:0 / 8-bit / 4-byte-NAL-length\n * defaults. Suitable as the `description` for a WebCodecs hvc1 VideoDecoderConfig.\n *\n * WebCodecs REQUIRES a description for length-prefixed HEVC \u2014 unlike H.264, Chrome will\n * not self-configure HEVC from the bitstream (BLO-7764).\n */\nexport function generateHvcCFromParameterSets(sets: HevcParameterSets): Uint8Array | null {\n const { vps, sps, pps } = sets\n if (!sps || !pps) return null\n const spsRbsp = removeEmulationPrevention(sps)\n // general profile_tier_level is 12 bytes at RBSP offset 3:\n // 2-byte NAL header + 1 byte (sps_video_parameter_set_id u(4), sps_max_sub_layers_minus1\n // u(3), sps_temporal_id_nesting_flag u(1)), then general_profile_space..general_level_idc.\n const ptl = spsRbsp.subarray(3, 15)\n const arrays = [\n { type: 32, nal: vps },\n { type: 33, nal: sps },\n { type: 34, nal: pps },\n ].filter((a): a is { type: number; nal: Uint8Array } => a.nal !== undefined)\n\n let size = 23\n for (const a of arrays) size += 5 + a.nal.length\n const h = new Uint8Array(size)\n let o = 0\n h[o++] = 1 // configurationVersion\n for (let i = 0; i < 12; i++) h[o++] = ptl[i] ?? 0 // general profile_tier_level\n h[o++] = 0xf0; h[o++] = 0x00 // reserved(1111) + min_spatial_segmentation_idc(0)\n h[o++] = 0xfc // reserved(111111) + parallelismType(0)\n h[o++] = 0xfd // reserved(111111) + chromaFormat(1 = 4:2:0)\n h[o++] = 0xf8 // reserved(11111) + bitDepthLumaMinus8(0)\n h[o++] = 0xf8 // reserved(11111) + bitDepthChromaMinus8(0)\n h[o++] = 0x00; h[o++] = 0x00 // avgFrameRate(0 = unspecified)\n h[o++] = 0x0b // CFR(0) numTempLayers(1) nested(0) lengthSizeMinusOne(3)\n h[o++] = arrays.length // numOfArrays\n for (const a of arrays) {\n h[o++] = a.type & 0x3f // array_completeness(0) + NAL_unit_type\n h[o++] = 0; h[o++] = 1 // numNalus = 1\n h[o++] = (a.nal.length >> 8) & 0xff\n h[o++] = a.nal.length & 0xff\n h.set(a.nal, o); o += a.nal.length\n }\n return h.subarray(0, o)\n}\n\n/**\n * Build an hvcC `description` for a WebCodecs HEVC VideoDecoderConfig directly from a\n * keyframe access unit (Annex-B or length-prefixed). Returns null if VPS/SPS/PPS aren't\n * present in-band. HEVC analogue of {@link extractCodecDescriptionFromAnnexB}.\n */\nexport function extractHevcCodecDescription(keyframe: Uint8Array): Uint8Array | null {\n return generateHvcCFromParameterSets(extractHevcParameterSets(keyframe))\n}\n\n/**\n * Strip non-VCL NAL units (VPS/SPS/PPS/SEI/AUD, type >= 32) from an HEVC access unit,\n * returning only the VCL NAL units (type <= 31) as a 4-byte length-prefixed buffer.\n *\n * Required when decoding `hvc1` with an explicit hvcC `description`: WebCodecs rejects a\n * sample whose data still carries in-band parameter sets (\"Unable to determine size of\n * bitstream buffer\"). Accepts Annex-B or length-prefixed input. Returns the input\n * unchanged if it already contains only VCL NALs (cheap no-op for delta frames).\n */\nexport function hevcVclOnly(data: Uint8Array): Uint8Array {\n const nals = isAnnexB(data) ? splitAnnexBNalUnits(data) : splitAvccNalUnits(data)\n const vcl = nals.filter((n) => n.length > 0 && hevcNalType(n) <= 31)\n if (vcl.length === nals.length && !isAnnexB(data)) return data\n let size = 0\n for (const n of vcl) size += 4 + n.length\n const out = new Uint8Array(size)\n let o = 0\n for (const n of vcl) {\n out[o++] = (n.length >> 24) & 0xff\n out[o++] = (n.length >> 16) & 0xff\n out[o++] = (n.length >> 8) & 0xff\n out[o++] = n.length & 0xff\n out.set(n, o); o += n.length\n }\n return out\n}\n\n/**\n * Check if data starts with an Annex-B start code (00 00 01 or 00 00 00 01).\n */\nexport function isAnnexB(data: Uint8Array): boolean {\n if (data.length >= 4 && data[0] === 0 && data[1] === 0 &&\n (data[2] === 1 || (data[2] === 0 && data[3] === 1))) {\n return true\n }\n return false\n}\n\n/**\n * Find the byte offset of the first Annex-B start code in data.\n * Returns -1 if no start code found.\n */\nexport function findAnnexBStartCode(data: Uint8Array): number {\n for (let i = 0; i < data.length - 3; i++) {\n if (data[i] === 0 && data[i + 1] === 0) {\n if (data[i + 2] === 1) return i\n if (data[i + 2] === 0 && data[i + 3] === 1) return i\n }\n }\n return -1\n}\n\n/**\n * Split Annex-B bitstream into NAL units.\n * Returns array of NAL unit byte slices (without start codes).\n */\nexport function splitAnnexBNalUnits(data: Uint8Array): Uint8Array[] {\n const nalUnits: Uint8Array[] = []\n let i = 0\n while (i < data.length - 3) {\n if (data[i] === 0 && data[i + 1] === 0) {\n let scLen = 0\n if (data[i + 2] === 1) scLen = 3\n else if (data[i + 2] === 0 && i + 3 < data.length && data[i + 3] === 1) scLen = 4\n if (scLen > 0) {\n const nalStart = i + scLen\n let nalEnd = data.length\n for (let j = nalStart + 1; j < data.length - 2; j++) {\n if (data[j] === 0 && data[j + 1] === 0 &&\n (data[j + 2] === 1 || (data[j + 2] === 0 && j + 3 < data.length && data[j + 3] === 1))) {\n nalEnd = j\n break\n }\n }\n if (nalEnd > nalStart) nalUnits.push(data.subarray(nalStart, nalEnd))\n i = nalEnd\n continue\n }\n }\n i++\n }\n return nalUnits\n}\n\n/**\n * Convert Annex-B (start-code delimited) to AVCC (4-byte length prefixed).\n * Optionally prepends SPS/PPS NALs for keyframes.\n */\nexport function annexBToAvcc(data: Uint8Array, spsPps?: SpsPps): Uint8Array {\n const nalUnits = splitAnnexBNalUnits(data)\n if (nalUnits.length === 0) return data\n\n let extraSize = 0\n if (spsPps) {\n extraSize = 4 + spsPps.sps.length + 4 + spsPps.pps.length\n }\n const frameSize = nalUnits.reduce((sum, nal) => sum + 4 + nal.length, 0)\n const output = new Uint8Array(extraSize + frameSize)\n let w = 0\n\n if (spsPps) {\n w = writeNalLength(output, w, spsPps.sps.length)\n output.set(spsPps.sps, w); w += spsPps.sps.length\n w = writeNalLength(output, w, spsPps.pps.length)\n output.set(spsPps.pps, w); w += spsPps.pps.length\n }\n\n for (const nal of nalUnits) {\n w = writeNalLength(output, w, nal.length)\n output.set(nal, w); w += nal.length\n }\n return output\n}\n\nexport interface H264KeyframeDetectionOptions {\n /**\n * Allow classifying a length-prefixed NAL when the declared NAL length runs\n * beyond this buffer. MMTP routing can see only the first packet fragment of\n * a sample, where the NAL header is present but the full NAL is not.\n */\n allowPartialAvcc?: boolean\n}\n\n/**\n * Classify H.264 keyframe status by scanning NAL units. Returns undefined when\n * the payload is not H.264-classifiable. Handles Annex-B and AVCC formats.\n */\nexport function classifyH264Keyframe(\n data: Uint8Array,\n options: H264KeyframeDetectionOptions = {},\n): boolean | undefined {\n const nals = h264NalUnitsForKeyframeDetection(data, options)\n if (nals.length === 0) return undefined\n let sawIntraSlice = false\n let sawNonIntraVcl = false\n let sawNal = false\n for (const nal of nals) {\n sawNal = true\n const nalType = nal[0] !== undefined ? nal[0] & 0x1f : -1\n if (nalType === 5 || nalType === 7) return true\n if (nalType === 1) {\n if (isH264IntraSlice(nal)) sawIntraSlice = true\n else sawNonIntraVcl = true\n }\n }\n if (sawIntraSlice && !sawNonIntraVcl) return true\n return sawNal ? false : undefined\n}\n\n/**\n * Detect H.264 keyframe by scanning for IDR/SPS or intra non-IDR slices.\n * Handles both Annex-B and AVCC formats.\n */\nexport function detectH264Keyframe(\n data: Uint8Array,\n options: H264KeyframeDetectionOptions = {},\n): boolean {\n return classifyH264Keyframe(data, options) === true\n}\n\n/**\n * Convert MMTP timestamp (NTP short format) to monotonic microseconds.\n * Per ISO 23008-1 \u00A79.2.2 / RFC 5905: upper 16 bits = seconds (lower 16\n * of NTP seconds), lower 16 bits = fractional seconds.\n * Returns a monotonically increasing \u00B5s value within an ~18-hour window.\n */\nexport function mmtpTimestampToMicros(rawTs: bigint | number): number {\n const ts = Number(rawTs) >>> 0 // ensure unsigned 32-bit\n if (ts === 0) return 0\n const seconds = ts >>> 16\n const frac = ts & 0xFFFF\n return seconds * 1_000_000 + Math.floor(frac * 1_000_000 / 65536)\n}\n\n/** Validate the CMSF/MMTP catalog initData contract: complete top-level ftyp+moov. */\nexport function isFmp4InitSegment(data: Uint8Array): boolean {\n let offset = 0\n let sawFtyp = false\n let sawMoov = false\n\n while (offset + 8 <= data.byteLength) {\n const view = new DataView(data.buffer, data.byteOffset + offset, data.byteLength - offset)\n let size = view.getUint32(0, false)\n const type = String.fromCharCode(data[offset + 4]!, data[offset + 5]!, data[offset + 6]!, data[offset + 7]!)\n let headerSize = 8\n\n if (size === 1) {\n if (offset + 16 > data.byteLength) return false\n const largeSize = view.getBigUint64(8, false)\n if (largeSize > BigInt(Number.MAX_SAFE_INTEGER)) return false\n size = Number(largeSize)\n headerSize = 16\n } else if (size === 0) {\n size = data.byteLength - offset\n }\n\n if (size < headerSize || offset + size > data.byteLength) return false\n if (type === 'ftyp') sawFtyp = true\n if (type === 'moov') {\n if (!sawFtyp) return false\n sawMoov = true\n }\n offset += size\n }\n\n return offset === data.byteLength && sawFtyp && sawMoov\n}\n\n// \u2500\u2500\u2500 Internal Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction hasIsoBmffMagic(data: Uint8Array): boolean {\n for (let i = 0; i + 8 <= data.length && i < 32; i += 4) {\n const tag = String.fromCharCode(data[i + 4], data[i + 5], data[i + 6], data[i + 7])\n if (tag === 'ftyp' || tag === 'moov') return true\n }\n return false\n}\n\nfunction findTrackStsdBoxes(data: Uint8Array): Array<{ o: number; sz: number }> {\n const moov = findBox(data, 0, data.length, 'moov')\n if (!moov) return []\n\n const out: Array<{ o: number; sz: number }> = []\n for (const trak of findBoxes(data, moov.o + 8, moov.o + moov.sz, 'trak')) {\n const stsd = findNestedBox(data, trak.o + 8, trak.o + trak.sz, ['mdia', 'minf', 'stbl', 'stsd'])\n if (stsd) out.push(stsd)\n }\n return out\n}\n\nfunction findNestedBox(\n buf: Uint8Array,\n start: number,\n end: number,\n path: string[],\n): { o: number; sz: number } | undefined {\n let s = start\n let e = end\n let current: { o: number; sz: number } | undefined\n for (const type of path) {\n current = findBox(buf, s, e, type)\n if (!current) return undefined\n s = current.o + 8\n e = current.o + current.sz\n }\n return current\n}\n\nfunction findBox(buf: Uint8Array, start: number, end: number, type: string): { o: number; sz: number } | undefined {\n return findBoxes(buf, start, end, type)[0]\n}\n\nfunction findBoxes(buf: Uint8Array, start: number, end: number, type: string): Array<{ o: number; sz: number }> {\n const boxes: Array<{ o: number; sz: number }> = []\n let o = start\n while (o + 8 <= end) {\n let sz = (buf[o] << 24) | (buf[o + 1] << 16) | (buf[o + 2] << 8) | buf[o + 3]\n const t = String.fromCharCode(buf[o + 4], buf[o + 5], buf[o + 6], buf[o + 7])\n if (sz === 0) sz = end - o\n if (sz < 8 || o + sz > end) break\n if (t === type) boxes.push({ o, sz })\n o += sz\n }\n return boxes\n}\n\nfunction writeNalLength(buf: Uint8Array, offset: number, len: number): number {\n buf[offset] = (len >> 24) & 0xff\n buf[offset + 1] = (len >> 16) & 0xff\n buf[offset + 2] = (len >> 8) & 0xff\n buf[offset + 3] = len & 0xff\n return offset + 4\n}\n\nfunction h264NalUnitsForKeyframeDetection(\n data: Uint8Array,\n options: H264KeyframeDetectionOptions,\n): Uint8Array[] {\n return splitH264SampleNalUnits(data, options).nals\n}\n\nfunction splitPartialAvccNalUnits(data: Uint8Array, lengthSize = 4): Uint8Array[] {\n const nals: Uint8Array[] = []\n let i = 0\n while (i + lengthSize < data.length) {\n let len = 0\n for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]\n i += lengthSize\n if (len <= 0 || i >= data.length) break\n const end = Math.min(i + len, data.length)\n nals.push(data.subarray(i, end))\n if (end < i + len) break\n i = end\n }\n return nals\n}\n\nfunction isLikelyRawH264NalUnit(data: Uint8Array): boolean {\n if (data.length === 0) return false\n const header = data[0]!\n if ((header & 0x80) !== 0) return false\n const nalType = header & 0x1f\n return nalType > 0 && nalType < 24\n}\n\nfunction rawH264NalToAvcc(data: Uint8Array): Uint8Array {\n const out = new Uint8Array(4 + data.length)\n new DataView(out.buffer, out.byteOffset, out.byteLength).setUint32(0, data.length)\n out.set(data, 4)\n return out\n}\n\nfunction isH264IntraSlice(nal: Uint8Array): boolean {\n const sliceType = readH264SliceType(nal)\n return sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9\n}\n\nfunction readH264SliceType(nal: Uint8Array): number | null {\n if (nal.length < 2 || (nal[0]! & 0x1f) !== 1) return null\n const rbsp = removeEmulationPrevention(nal.subarray(1))\n const state = { bitOffset: 0 }\n if (readUnsignedExpGolomb(rbsp, state) === null) return null\n return readUnsignedExpGolomb(rbsp, state)\n}\n\nfunction readUnsignedExpGolomb(data: Uint8Array, state: { bitOffset: number }): number | null {\n let leadingZeroBits = 0\n while (true) {\n const bit = readBit(data, state)\n if (bit === null) return null\n if (bit === 1) break\n leadingZeroBits++\n if (leadingZeroBits > 30) return null\n }\n\n let suffix = 0\n for (let i = 0; i < leadingZeroBits; i++) {\n const bit = readBit(data, state)\n if (bit === null) return null\n suffix = (suffix << 1) | bit\n }\n return (1 << leadingZeroBits) - 1 + suffix\n}\n\nfunction readBit(data: Uint8Array, state: { bitOffset: number }): 0 | 1 | null {\n if (state.bitOffset >= data.length * 8) return null\n const byte = data[state.bitOffset >> 3]!\n const bit = (byte >> (7 - (state.bitOffset & 7))) & 1\n state.bitOffset++\n return bit as 0 | 1\n}\n\nfunction hex2(n: number): string {\n return n.toString(16).padStart(2, '0')\n}\n\nfunction pad2(n: number): string {\n return String(n).padStart(2, '0')\n}\n\nfunction codecStringFromAv1C(av1C: Uint8Array): string {\n if (av1C.length < 4) return 'av01.0.01M.08'\n const profile = (av1C[1]! >>> 5) & 0x07\n const level = av1C[1]! & 0x1f\n const tier = (av1C[2]! & 0x80) !== 0 ? 'H' : 'M'\n let bitDepth = 8\n if ((av1C[2]! & 0x40) !== 0) {\n bitDepth = (av1C[2]! & 0x20) !== 0 ? 12 : 10\n }\n return `av01.${profile}.${pad2(level)}${tier}.${pad2(bitDepth)}`\n}\n\nfunction codecStringFromVpcC(vpcC: Uint8Array): string {\n if (vpcC.length < 3) return 'vp09.00.10.08'\n // ISO/IEC 14496-15 defines vpcC as a FullBox. Some tooling hands callers\n // the VPCodecConfigurationRecord after the 4-byte FullBox header; tolerate\n // both shapes because WebCodecs callers only need a stable RFC 6381 fallback.\n const offset = vpcC.length >= 7 && vpcC[0] <= 1 ? 4 : 0\n if (offset + 2 >= vpcC.length) return 'vp09.00.10.08'\n const profile = vpcC[offset]!\n const level = vpcC[offset + 1]!\n const bitDepth = (vpcC[offset + 2]! >>> 4) || 8\n return `vp09.${pad2(profile)}.${pad2(level)}.${pad2(bitDepth)}`\n}\n\n/**\n * Derive an RFC 6381 HEVC codec string (e.g. \"hvc1.1.6.L93.90\") from the\n * payload of an hvcC box (the HEVCDecoderConfigurationRecord, ISO 14496-15 \u00A78.3.3.1).\n * Only the general_* fields in the fixed 23-byte header are needed:\n * byte 1: general_profile_space(2) | general_tier_flag(1) | general_profile_idc(5)\n * bytes 2-5: general_profile_compatibility_flags (32 bits)\n * bytes 6-11: general_constraint_indicator_flags (48 bits)\n * byte 12: general_level_idc\n */\nfunction hevcCodecStringFromHvcC(c: Uint8Array): string {\n const profileSpace = (c[1] >> 6) & 0x3\n const tierFlag = (c[1] >> 5) & 0x1\n const profileIdc = c[1] & 0x1f\n // Compatibility flags are emitted reversed bit-order as hex, leading zeros trimmed.\n const compat = ((c[2] << 24) | (c[3] << 16) | (c[4] << 8) | c[5]) >>> 0\n let rev = 0\n let v = compat\n for (let i = 0; i < 32; i++) {\n rev = ((rev << 1) | (v & 1)) >>> 0\n v >>>= 1\n }\n const compatStr = rev.toString(16)\n // Constraint flags: dot-separated hex bytes, trailing zero bytes trimmed.\n const constraint = c.subarray(6, 12)\n let lastNonZero = -1\n for (let i = 0; i < constraint.length; i++) if (constraint[i] !== 0) lastNonZero = i\n const constraintParts: string[] = []\n for (let i = 0; i <= lastNonZero; i++) constraintParts.push(constraint[i].toString(16))\n const space = profileSpace === 0 ? '' : String.fromCharCode(64 + profileSpace) // 1\u2192'A', 2\u2192'B', 3\u2192'C'\n const tier = tierFlag === 0 ? 'L' : 'H'\n let s = `hvc1.${space}${profileIdc}.${compatStr}.${tier}${c[12]}`\n if (constraintParts.length > 0) s += '.' + constraintParts.join('.')\n return s\n}\n\n/** Exp-Golomb bitstream writer for SPS/PPS generation. */\nclass BitWriter {\n buf: number[] = []\n byte = 0\n bit = 7\n\n u(val: number, bits: number) {\n for (let i = bits - 1; i >= 0; i--) {\n this.byte |= ((val >> i) & 1) << this.bit\n if (--this.bit < 0) {\n this.buf.push(this.byte)\n this.byte = 0\n this.bit = 7\n }\n }\n }\n\n ue(val: number) {\n const v = val + 1\n const bits = Math.floor(Math.log2(v)) + 1\n this.u(0, bits - 1)\n this.u(v, bits)\n }\n\n se(val: number) {\n this.ue(val <= 0 ? -val * 2 : val * 2 - 1)\n }\n\n finish(): Uint8Array {\n if (this.bit < 7) {\n this.byte |= 1 << this.bit\n this.buf.push(this.byte)\n }\n return new Uint8Array(this.buf)\n }\n}\n\nfunction resolveResolution(\n explicitW: number | undefined,\n explicitH: number | undefined,\n level: number,\n): { width: number; height: number } {\n if (explicitW && explicitH) return { width: explicitW, height: explicitH }\n\n const levelResolutions: Record<number, [number, number]> = {\n 10: [176, 144], 11: [352, 288], 12: [352, 288], 13: [352, 288],\n 20: [352, 288], 21: [720, 576], 22: [720, 576],\n 30: [1280, 720], 31: [1280, 720], 32: [1280, 1024],\n 40: [1920, 1088], 41: [1920, 1088], 42: [2048, 1088],\n 50: [4096, 2160], 51: [4096, 2160], 52: [4096, 2160],\n }\n\n const [w, h] = levelResolutions[level] ?? [1920, 1088]\n return { width: explicitW ?? w, height: explicitH ?? h }\n}\n\nfunction buildMinimalSps(\n profile: number, constraint: number, level: number,\n width: number, height: number,\n): Uint8Array {\n const widthInMbs = Math.ceil(width / 16)\n const heightInMapUnits = Math.ceil(height / 16)\n\n const w = new BitWriter()\n w.u(0, 1); w.u(3, 2); w.u(7, 5) // NAL header: SPS\n w.u(profile, 8); w.u(constraint, 8); w.u(level, 8)\n w.ue(0) // seq_parameter_set_id\n\n if (profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44) {\n w.ue(1); w.ue(0); w.ue(0) // chroma + bit depth\n w.u(0, 1); w.u(0, 1) // transform bypass + scaling matrix\n }\n\n w.ue(0) // log2_max_frame_num_minus4\n w.ue(2) // pic_order_cnt_type=2\n w.ue(1) // max_num_ref_frames\n w.u(0, 1) // gaps_in_frame_num\n w.ue(widthInMbs - 1)\n w.ue(heightInMapUnits - 1)\n w.u(1, 1) // frame_mbs_only\n w.u(1, 1) // direct_8x8_inference\n\n const cropRight = widthInMbs * 16 - width\n const cropBottom = heightInMapUnits * 16 - height\n if (cropRight > 0 || cropBottom > 0) {\n w.u(1, 1)\n w.ue(0); w.ue(cropRight / 2); w.ue(0); w.ue(cropBottom / 2)\n } else {\n w.u(0, 1)\n }\n\n w.u(0, 1) // vui_parameters_present\n return w.finish()\n}\n\nfunction buildMinimalPps(profile: number): Uint8Array {\n const isHighProfile = profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44\n const w = new BitWriter()\n w.u(0, 1); w.u(3, 2); w.u(8, 5) // NAL header: PPS\n\n w.ue(0); w.ue(0) // pps_id, sps_id\n w.u(isHighProfile ? 1 : 0, 1) // entropy_coding_mode\n w.u(0, 1) // bottom_field_pic_order\n w.ue(0); w.ue(0); w.ue(0) // slice groups, ref_idx defaults\n w.u(0, 1); w.u(0, 2) // weighted_pred\n w.se(0); w.se(0); w.se(0) // qp offsets\n w.u(1, 1); w.u(0, 1); w.u(0, 1) // deblock, constrained_intra, redundant_pic_cnt\n\n if (isHighProfile) {\n w.u(1, 1); w.u(0, 1); w.se(0) // transform_8x8, scaling_matrix, chroma_qp_offset\n }\n\n return w.finish()\n}\n\n// \u2500\u2500\u2500 AAC / ADTS Utilities \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** ISO 14496-3 sampling frequency index lookup. */\nconst ADTS_SAMPLE_RATES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350] as const\n\n/**\n * Prepend a 7-byte ADTS header to a raw AAC access unit.\n *\n * WebCodecs AudioDecoder configured WITHOUT an AudioSpecificConfig `description`\n * expects ADTS-framed AAC. MMTP carries raw AAC (no ADTS sync), so this wrapper\n * must be applied before calling `AudioDecoder.decode()`.\n */\nexport function wrapAdtsFrame(raw: Uint8Array, sampleRate: number, channels: number): Uint8Array {\n const sri = ADTS_SAMPLE_RATES.indexOf(sampleRate as typeof ADTS_SAMPLE_RATES[number])\n const idx = sri >= 0 ? sri : 3 // default 48 kHz\n const len = 7 + raw.length\n const out = new Uint8Array(len)\n out[0] = 0xFF\n out[1] = 0xF1 // MPEG-4, Layer 0, no CRC\n out[2] = (1 << 6) | (idx << 2) | (channels >> 2) // profile=AAC-LC(1), freq, ch high\n out[3] = ((channels & 3) << 6) | ((len >> 11) & 3)\n out[4] = (len >> 3) & 0xFF\n out[5] = ((len & 7) << 5) | 0x1F\n out[6] = 0xFC\n out.set(raw, 7)\n return out\n}\n\n/**\n * Extract AAC AudioSpecificConfig (ASC) from an fMP4 init segment.\n *\n * Walks moov > trak > mdia > minf > stbl > stsd > mp4a > esds, then descends\n * the MPEG-4 descriptor chain (ES_Descriptor 0x03 > DecoderConfigDescriptor\n * 0x04 > DecoderSpecificInfo 0x05) to return the ASC payload.\n *\n * Sibling of extractAvcCFromInitSegment, used by the same BLO-4830 / CMSF \u00A73.1\n * path: when the catalog initData is the full ftyp+moov, WebCodecs' AudioDecoder\n * needs the raw ASC (~2 bytes for AAC-LC) as `description`, NOT the whole\n * segment. Without this extraction the configure() succeeds but every decode()\n * silently produces zero output (no error fires), exactly the symptom that\n * shows up as \"Audio continuity FAILED: energy unstable CV=1.00\" in the\n * 7-point E2E test (CV=1.00 is the meanEnergy=0 sentinel from audio-fft.mjs).\n *\n * Returns undefined when:\n * - data has no ftyp/moov magic in the first 32 bytes\n * - no mp4a sample entry exists in any trak's stbl\n * - the esds descriptor chain doesn't yield a DecoderSpecificInfo\n * - the ASC payload is shorter than 2 bytes (AOT + sampling-freq nibble)\n *\n * Walks every trak and returns the first AAC sample entry. This mirrors\n * extractAvcCFromInitSegment so multiplexed ftyp+moov init blobs work\n * regardless of track order.\n */\nexport function extractAacAscFromInitSegment(data: Uint8Array): AacAscInfo | undefined {\n if (!hasIsoBmffMagic(data)) return undefined\n\n for (const stsd of findTrackStsdBoxes(data)) {\n const info = extractAacAscFromStsd(data, stsd)\n if (info) return info\n }\n\n return undefined\n}\n\nfunction extractAacAscFromStsd(data: Uint8Array, stsd: { o: number; sz: number }): AacAscInfo | undefined {\n // stsd: 8 box header + 4 version+flags + 4 entry_count = 16 bytes before entries.\n const s = stsd.o + 16\n const e = stsd.o + stsd.sz\n // mp4a AudioSampleEntry minimum length: 8 box header + 28 fixed fields = 36 bytes\n // before any child boxes (esds lives here).\n const mp4a = findBox(data, s, e, 'mp4a')\n if (!mp4a || mp4a.sz < 36) return undefined\n const esds = findBox(data, mp4a.o + 36, mp4a.o + mp4a.sz, 'esds')\n // esds = 8 box header + 4 full-box version+flags = 12 bytes minimum before descriptors.\n if (!esds || esds.sz < 12) return undefined\n const esdsPayload = data.subarray(esds.o + 8, esds.o + esds.sz)\n const asc = extractAudioSpecificConfig(esdsPayload)\n if (!asc || asc.length < 2) return undefined\n // AudioObjectType = top 5 bits of ASC[0]. AAC-LC = 2, HE-AAC = 5, etc.\n const aot = asc[0] >> 3\n return { codec: `mp4a.40.${aot}`, description: asc }\n}\n\n/**\n * Walk an esds descriptor chain to find DecoderSpecificInfo (tag 0x05).\n * Step over fixed-size headers of container descriptors (0x03, 0x04) so the\n * scan continues into their children rather than skipping their full payload.\n *\n * Assumes the ES_Descriptor flags byte is zero (no streamDependence, URL, OCR\n * ES_ID fields). FFmpeg's mov/mp4 muxer emits exactly that for all standard\n * AAC tracks; the assumption is the same one extractAvcCFromInitSegment makes\n * about the avc1 sample entry's reserved fields.\n *\n * Input is the esds box payload AFTER the 8-byte box header; the first 4 bytes\n * (full-box version+flags) are skipped here.\n */\nfunction extractAudioSpecificConfig(esds: Uint8Array): Uint8Array | undefined {\n let offset = 4 // skip esds version+flags\n while (offset < esds.length) {\n const tag = esds[offset++]\n let size = 0\n for (let i = 0; i < 4 && offset < esds.length; i++) {\n const b = esds[offset++]\n size = (size << 7) | (b & 0x7f)\n if ((b & 0x80) === 0) break\n }\n if (tag === 0x05) {\n if (offset + size > esds.length) return undefined\n return esds.subarray(offset, offset + size)\n }\n if (tag === 0x03) {\n offset += 3 // ES_ID (2) + flags (1)\n } else if (tag === 0x04) {\n offset += 13 // OTI (1) + streamType (1) + bufferSizeDB (3) + maxBitrate (4) + avgBitrate (4)\n } else {\n offset += size\n }\n }\n return undefined\n}\n\n// =============================================================================\n// Inline Tests (vitest)\n// =============================================================================\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest\n\n describe('codec-utils', () => {\n describe('isAnnexB', () => {\n test('4-byte start code', () => {\n expect(isAnnexB(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(true)\n })\n test('3-byte start code', () => {\n expect(isAnnexB(new Uint8Array([0, 0, 1, 0x65]))).toBe(true)\n })\n test('rejects AVCC', () => {\n expect(isAnnexB(new Uint8Array([0, 0, 0, 10, 0x65]))).toBe(false)\n })\n })\n\n describe('findAnnexBStartCode', () => {\n test('at offset 0', () => {\n expect(findAnnexBStartCode(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(0)\n })\n test('at offset 5', () => {\n expect(findAnnexBStartCode(new Uint8Array([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0, 0, 1, 0x65]))).toBe(5)\n })\n test('returns -1 when none', () => {\n expect(findAnnexBStartCode(new Uint8Array([0xAA, 0xBB, 0xCC]))).toBe(-1)\n })\n })\n\n describe('splitAnnexBNalUnits', () => {\n test('splits two NALs', () => {\n const data = new Uint8Array([0, 0, 0, 1, 0x65, 0xAA, 0xBB, 0, 0, 0, 1, 0x41, 0xCC])\n const nals = splitAnnexBNalUnits(data)\n expect(nals.length).toBe(2)\n expect(nals[0]).toEqual(new Uint8Array([0x65, 0xAA, 0xBB]))\n expect(nals[1]).toEqual(new Uint8Array([0x41, 0xCC]))\n })\n test('handles 3-byte start codes', () => {\n const nals = splitAnnexBNalUnits(new Uint8Array([0, 0, 1, 0x67, 0x42, 0, 0, 1, 0x68, 0x01]))\n expect(nals.length).toBe(2)\n })\n })\n\n describe('annexBToAvcc', () => {\n test('converts single NAL', () => {\n const annexB = new Uint8Array([0, 0, 0, 1, 0x41, 0xAA, 0xBB])\n const avcc = annexBToAvcc(annexB)\n expect(avcc[3]).toBe(3) // NAL length\n expect(avcc[4]).toBe(0x41)\n })\n test('prepends SPS/PPS', () => {\n const annexB = new Uint8Array([0, 0, 0, 1, 0x65, 0x01])\n const avcc = annexBToAvcc(annexB, { sps: new Uint8Array([0x67, 0x42]), pps: new Uint8Array([0x68, 0x01]) })\n expect(avcc[3]).toBe(2) // SPS length\n expect(avcc[4]).toBe(0x67)\n })\n })\n\n describe('detectH264Keyframe', () => {\n test('IDR in Annex-B', () => {\n expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(true)\n })\n test('SPS in Annex-B', () => {\n expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x67]))).toBe(true)\n })\n test('IDR in AVCC', () => {\n expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 5, 0x65, 1, 2, 3, 4]))).toBe(true)\n })\n test('IDR after AVCC AUD', () => {\n expect(detectH264Keyframe(new Uint8Array([\n 0, 0, 0, 1, 0x09,\n 0, 0, 0, 2, 0x65, 0xaa,\n ]))).toBe(true)\n })\n test('non-IDR AVC I-slice in AVCC', () => {\n expect(detectH264Keyframe(new Uint8Array([\n 0, 0, 0, 2, 0x41, 0x88,\n ]))).toBe(true)\n })\n test('raw H.264 IDR NAL', () => {\n expect(detectH264Keyframe(new Uint8Array([0x65, 0x88, 0x84]))).toBe(true)\n })\n test('raw H.264 non-IDR I-slice NAL', () => {\n expect(detectH264Keyframe(new Uint8Array([0x41, 0x88]))).toBe(true)\n })\n test('classifies partial length-prefixed IDR only when requested', () => {\n const partial = new Uint8Array([0, 0, 0x20, 0, 0x65])\n expect(classifyH264Keyframe(partial)).toBeUndefined()\n expect(classifyH264Keyframe(partial, { allowPartialAvcc: true })).toBe(true)\n })\n test('rejects P-frame', () => {\n expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x41]))).toBe(false)\n })\n test('rejects AVCC sample with only non-IDR slices', () => {\n expect(detectH264Keyframe(new Uint8Array([\n 0, 0, 0, 2, 0x41, 0xc0,\n 0, 0, 0, 2, 0x41, 0xc0,\n ]))).toBe(false)\n })\n })\n\n describe('avccToAnnexB', () => {\n test('converts with SPS/PPS on keyframe', () => {\n const avcc = new Uint8Array([0, 0, 0, 3, 0x65, 0xAA, 0xBB])\n const spsPps: SpsPps = { sps: new Uint8Array([0x67, 0x42]), pps: new Uint8Array([0x68, 0x01]) }\n const annexB = avccToAnnexB(avcc, true, spsPps)\n expect(annexB[3]).toBe(1) // start code\n expect(annexB[4]).toBe(0x67) // SPS\n })\n })\n\n describe('codecStringFromAvcC', () => {\n test('derives avc1.64002a', () => {\n expect(codecStringFromAvcC(new Uint8Array([1, 100, 0, 42]))).toBe('avc1.64002a')\n })\n })\n\n describe('extractAacAscFromInitSegment', () => {\n // Build a minimal valid ftyp+moov containing one mp4a sample entry whose\n // esds carries the supplied AudioSpecificConfig. The walker only needs\n // the box hierarchy moov > trak > mdia > minf > stbl > stsd > mp4a > esds;\n // mvhd/tkhd/mdhd/hdlr/smhd/dinf/stts/stsc/stsz/stco are spec-required for\n // a truly valid init but the extractor never reads them, so we omit them\n // to keep the fixture tight (~150 bytes vs ~500). FFmpeg's real output\n // includes them; the production path passes through `findBox` exactly\n // the same way.\n function buildMinimalAacInit(asc: Uint8Array): Uint8Array {\n const box = (type: string, payload: Uint8Array): Uint8Array => {\n const sz = 8 + payload.length\n const out = new Uint8Array(sz)\n out[0] = (sz >>> 24) & 0xff\n out[1] = (sz >>> 16) & 0xff\n out[2] = (sz >>> 8) & 0xff\n out[3] = sz & 0xff\n out[4] = type.charCodeAt(0); out[5] = type.charCodeAt(1)\n out[6] = type.charCodeAt(2); out[7] = type.charCodeAt(3)\n out.set(payload, 8)\n return out\n }\n const concat = (...arrs: Uint8Array[]): Uint8Array => {\n const total = arrs.reduce((n, a) => n + a.length, 0)\n const out = new Uint8Array(total)\n let off = 0\n for (const a of arrs) { out.set(a, off); off += a.length }\n return out\n }\n // ftyp: major_brand=iso5, minor=0x200, compat=iso5,iso6\n const ftyp = box('ftyp', new Uint8Array([\n 0x69, 0x73, 0x6f, 0x35, // 'iso5'\n 0x00, 0x00, 0x02, 0x00, // minor 0x200\n 0x69, 0x73, 0x6f, 0x35, // 'iso5'\n 0x69, 0x73, 0x6f, 0x36, // 'iso6'\n ]))\n\n // ESDescriptor (0x03) > DecoderConfigDescriptor (0x04) > DecoderSpecificInfo (0x05) > ASC.\n // Use 1-byte BER sizes (all payloads < 128).\n const dsi = new Uint8Array(2 + asc.length)\n dsi[0] = 0x05; dsi[1] = asc.length\n dsi.set(asc, 2)\n // DCD = 13 fixed bytes + DSI: OTI=0x40 (AAC), streamType=0x05<<2|0x01 (audio, upStream=0)\n const dcd = new Uint8Array(2 + 13 + dsi.length)\n dcd[0] = 0x04; dcd[1] = 13 + dsi.length\n dcd[2] = 0x40 // objectTypeIndication: Audio ISO/IEC 14496-3\n dcd[3] = (0x05 << 2) | 1 // streamType=AudioStream(5), upStream=0, reserved=1\n // bufferSizeDB(3) + maxBitrate(4) + avgBitrate(4) all zero is fine for the walker.\n dcd.set(dsi, 15)\n // ESDescriptor: ES_ID=0, flags=0, then DCD\n const esd = new Uint8Array(2 + 3 + dcd.length)\n esd[0] = 0x03; esd[1] = 3 + dcd.length\n // ES_ID(2) + flags(1) all zero\n esd.set(dcd, 5)\n // esds box = full-box (4-byte version+flags) + ES_Descriptor chain\n const esdsPayload = new Uint8Array(4 + esd.length)\n esdsPayload.set(esd, 4)\n const esds = box('esds', esdsPayload)\n\n // mp4a sample entry: 6 reserved + 2 dref_idx + 8 reserved + 2 channels + 2 samplesize +\n // 2 pre_defined + 2 reserved + 4 samplerate(48000<<16) = 28 bytes,\n // then esds child.\n const mp4aFixed = new Uint8Array(28)\n mp4aFixed[7] = 0x01 // data_reference_index = 1\n mp4aFixed[16] = 0x00; mp4aFixed[17] = 0x01 // channelcount = 1\n mp4aFixed[18] = 0x00; mp4aFixed[19] = 0x10 // samplesize = 16\n // samplerate at offset 24-27 (top 16 bits = sampleRate). 48000 = 0xBB80.\n mp4aFixed[24] = 0xBB; mp4aFixed[25] = 0x80\n const mp4a = box('mp4a', concat(mp4aFixed, esds))\n\n // stsd: 4 ver+flags + 4 entry_count(=1) + entries\n const stsdPayload = new Uint8Array(8 + mp4a.length)\n stsdPayload[7] = 0x01 // entry_count = 1\n stsdPayload.set(mp4a, 8)\n const stsd = box('stsd', stsdPayload)\n\n const stbl = box('stbl', stsd)\n const minf = box('minf', stbl)\n const mdia = box('mdia', minf)\n const trak = box('trak', mdia)\n const moov = box('moov', trak)\n\n return concat(ftyp, moov)\n }\n\n test('extracts AAC-LC mono 48k ASC and derives codec', () => {\n // AOT=2 (00010), samplingFrequencyIndex=3 (0011, 48000Hz), channelConfig=1 (0001),\n // GA: frameLengthFlag=0, dependsOnCoreCoder=0, extensionFlag=0 \u2192 0x11 0x88.\n const asc = new Uint8Array([0x11, 0x88])\n const init = buildMinimalAacInit(asc)\n const result = extractAacAscFromInitSegment(init)\n expect(result).toBeDefined()\n expect(result!.codec).toBe('mp4a.40.2')\n expect(Array.from(result!.description)).toEqual([0x11, 0x88])\n })\n\n test('derives AOT 5 (HE-AAC) from ASC[0] >> 3', () => {\n // AOT=5 (00101), rest don't matter for codec derivation.\n const asc = new Uint8Array([0x29, 0x88]) // 00101 001 1000 1000\n const init = buildMinimalAacInit(asc)\n expect(extractAacAscFromInitSegment(init)?.codec).toBe('mp4a.40.5')\n })\n\n test('rejects data without ftyp/moov magic', () => {\n expect(extractAacAscFromInitSegment(new Uint8Array(64))).toBeUndefined()\n })\n\n test('rejects ftyp+moov with no audio (no mp4a sample entry)', () => {\n // ftyp + moov with empty trak chain \u2014 no stsd, let alone mp4a.\n const ftyp = new Uint8Array([\n 0, 0, 0, 24, 0x66, 0x74, 0x79, 0x70,\n 0x69, 0x73, 0x6f, 0x35, 0, 0, 2, 0,\n 0x69, 0x73, 0x6f, 0x35, 0x69, 0x73, 0x6f, 0x36,\n ])\n const moov = new Uint8Array([0, 0, 0, 8, 0x6d, 0x6f, 0x6f, 0x76]) // empty moov\n const cat = new Uint8Array(ftyp.length + moov.length)\n cat.set(ftyp); cat.set(moov, ftyp.length)\n expect(extractAacAscFromInitSegment(cat)).toBeUndefined()\n })\n })\n\n describe('hevc', () => {\n // Minimal HEVC NAL units: header byte0 = (type << 1). VPS=32(0x40), SPS=33(0x42), PPS=34(0x44).\n const vps = new Uint8Array([0x40, 0x01, 0x0c, 0x01, 0xff, 0xff])\n const sps = new Uint8Array([0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xa0, 0x05, 0x02])\n const pps = new Uint8Array([0x44, 0x01, 0xc1, 0x72, 0xb4, 0x62])\n const idr = new Uint8Array([0x26, 0x01, 0xaf, 0x06]) // type 19 (IDR_W_RADL)\n const lp = (...nals: Uint8Array[]) => {\n const parts: number[] = []\n for (const n of nals) { parts.push((n.length >> 24) & 0xff, (n.length >> 16) & 0xff, (n.length >> 8) & 0xff, n.length & 0xff, ...n) }\n return Uint8Array.from(parts)\n }\n\n test('isHevcCodec', () => {\n expect(isHevcCodec('hev1.1.6.L93.B0')).toBe(true)\n expect(isHevcCodec('hvc1.1.6.L93.B0')).toBe(true)\n expect(isHevcCodec('avc1.640033')).toBe(false)\n })\n\n test('removeEmulationPrevention strips 0x03', () => {\n expect(Array.from(removeEmulationPrevention(new Uint8Array([0, 0, 0x03, 1])))).toEqual([0, 0, 1])\n expect(Array.from(removeEmulationPrevention(new Uint8Array([0xaa, 0xbb])))).toEqual([0xaa, 0xbb])\n })\n\n test('splitAvccNalUnits splits length-prefixed NALs', () => {\n const nals = splitAvccNalUnits(lp(vps, sps))\n expect(nals.length).toBe(2)\n expect(nals[0]).toEqual(vps)\n expect(nals[1]).toEqual(sps)\n })\n\n test('isCompleteAvccSample accepts complete length-prefixed NAL samples', () => {\n expect(isCompleteAvccSample(lp(new Uint8Array([0x09]), idr))).toBe(true)\n })\n\n test('isCompleteAvccSample rejects Annex-B start-code samples', () => {\n expect(isCompleteAvccSample(new Uint8Array([0, 0, 0, 1, 0x65, 0xaa]))).toBe(false)\n })\n\n test('isCompleteAvccSample rejects truncated length-prefixed samples', () => {\n expect(isCompleteAvccSample(new Uint8Array([0, 0, 0, 4, 0x65, 0xaa]))).toBe(false)\n })\n\n test('normalizeH264SampleToAvcc leaves complete AVCC samples unchanged', () => {\n const avcc = lp(new Uint8Array([0x09]), idr)\n const normalized = normalizeH264SampleToAvcc(avcc)\n expect(normalized.format).toBe('avcc')\n expect(normalized.data).toBe(avcc)\n })\n\n test('normalizeH264SampleToAvcc converts a single raw H.264 NAL to AVCC', () => {\n const raw = new Uint8Array([0x65, 0x88, 0x84])\n const normalized = normalizeH264SampleToAvcc(raw)\n expect(normalized.format).toBe('raw-nal')\n expect(normalized.data).toEqual(new Uint8Array([0, 0, 0, 3, 0x65, 0x88, 0x84]))\n })\n\n test('splitH264SampleNalUnits exposes Annex-B NALs without AVCC conversion', () => {\n const annexB = new Uint8Array([0, 0, 0, 1, 0x65, 0x88, 0x84])\n const sample = splitH264SampleNalUnits(annexB)\n expect(sample.format).toBe('annexb')\n expect(sample.nals).toHaveLength(1)\n expect(sample.nals[0]).toEqual(new Uint8Array([0x65, 0x88, 0x84]))\n })\n\n test('splitH264SampleNalUnits exposes single raw H.264 NALs without wrapping', () => {\n const raw = new Uint8Array([0x65, 0x88, 0x84])\n const sample = splitH264SampleNalUnits(raw)\n expect(sample.format).toBe('raw-nal')\n expect(sample.nals).toEqual([raw])\n })\n\n test('splitH264SampleNalUnits can expose partial AVCC NAL headers', () => {\n const partial = new Uint8Array([0, 0, 0, 5, 0x65, 0x88])\n const sample = splitH264SampleNalUnits(partial, { allowPartialAvcc: true })\n expect(sample.format).toBe('partial-avcc')\n expect(sample.nals).toHaveLength(1)\n expect(sample.nals[0]).toEqual(new Uint8Array([0x65, 0x88]))\n })\n\n test('extractHevcParameterSets finds VPS/SPS/PPS (length-prefixed)', () => {\n const sets = extractHevcParameterSets(lp(vps, sps, pps, idr))\n expect(sets.vps).toEqual(vps)\n expect(sets.sps).toEqual(sps)\n expect(sets.pps).toEqual(pps)\n })\n\n test('extractHevcParameterSets finds VPS/SPS/PPS (Annex-B)', () => {\n const sc = new Uint8Array([0, 0, 0, 1])\n const annexb = Uint8Array.from([...sc, ...vps, ...sc, ...sps, ...sc, ...pps, ...sc, ...idr])\n const sets = extractHevcParameterSets(annexb)\n expect(sets.vps).toEqual(vps)\n expect(sets.sps).toEqual(sps)\n expect(sets.pps).toEqual(pps)\n })\n\n test('generateHvcCFromParameterSets builds a valid hvcC', () => {\n const hvcc = generateHvcCFromParameterSets({ vps, sps, pps })!\n expect(hvcc).not.toBeNull()\n expect(hvcc[0]).toBe(1) // configurationVersion\n expect(hvcc[1]).toBe(sps[3]) // general profile_tier_level byte 0 copied from SPS[3]\n expect(hvcc[22]).toBe(3) // numOfArrays (VPS+SPS+PPS)\n // first array entry: NAL_unit_type 32 (VPS), numNalus 1\n expect(hvcc[23] & 0x3f).toBe(32)\n expect((hvcc[24] << 8) | hvcc[25]).toBe(1)\n })\n\n test('generateHvcCFromParameterSets returns null without SPS/PPS', () => {\n expect(generateHvcCFromParameterSets({ vps })).toBeNull()\n })\n\n test('extractHevcCodecDescription end-to-end from keyframe', () => {\n const desc = extractHevcCodecDescription(lp(vps, sps, pps, idr))\n expect(desc).not.toBeNull()\n expect(desc![0]).toBe(1)\n })\n\n test('hevcVclOnly strips parameter-set NALs, keeps VCL', () => {\n const stripped = hevcVclOnly(lp(vps, sps, pps, idr))\n const nals = splitAvccNalUnits(stripped)\n expect(nals.length).toBe(1) // only the IDR VCL NAL remains\n expect(nals[0]).toEqual(idr)\n })\n\n test('hevcVclOnly is a no-op for VCL-only length-prefixed input', () => {\n const vclOnly = lp(idr)\n expect(hevcVclOnly(vclOnly)).toBe(vclOnly)\n })\n })\n })\n}\n", "/**\n * @mmt/container - LOC FEC Extensions\n *\n * FEC Source Info per draft-ramadan-moq-fec \u00A77.1:\n * SBN (32 bits) + ESI (32 bits) = 8 bytes minimum.\n * Optional Original Object Length (QUIC varint) when Length > 8.\n *\n * @packageDocumentation\n */\n\n/**\n * LOC Header Extension IDs\n */\nexport enum LOCHeaderExtensionId {\n CaptureTimestamp = 2,\n VideoFrameMarking = 4,\n AudioLevel = 6,\n /** FEC Source Info: SBN(32) + ESI(32) [+ Original Object Length (varint)] */\n FecSourceInfo = 0x10,\n VideoConfig = 13,\n /** Content authentication tag */\n AuthTag = 14,\n}\n\n/** Maximum value for SBN and ESI (32 bits each) */\nexport const FEC_SBN_MAX = 0xffffffff\nexport const FEC_ESI_MAX = 0xffffffff\n\n/**\n * Encode FEC Source Info value bytes: SBN(4B BE) + ESI(4B BE).\n * Returns 8-byte Uint8Array.\n */\nexport function encodeFecSourceInfo(sbn: number, esi: number): Uint8Array {\n const buf = new Uint8Array(8)\n const view = new DataView(buf.buffer)\n view.setUint32(0, sbn >>> 0, false)\n view.setUint32(4, esi >>> 0, false)\n return buf\n}\n\n/**\n * Decode FEC Source Info value bytes: reads SBN(4B BE) + ESI(4B BE).\n * Returns [sbn, esi].\n */\nexport function decodeFecSourceInfo(buf: Uint8Array, offset = 0): [sbn: number, esi: number] {\n const view = new DataView(buf.buffer, buf.byteOffset + offset, 8)\n return [view.getUint32(0, false), view.getUint32(4, false)]\n}\n\n/**\n * FEC Source Info for LOC/CMAF extension headers.\n * Per draft-ramadan-moq-fec \u00A77.1: SBN(32) + ESI(32), fixed 8-byte minimum.\n */\nexport class FecSourceInfo {\n static readonly TYPE = LOCHeaderExtensionId.FecSourceInfo\n\n constructor(\n /** Source Block Number (32 bits) */\n public readonly sbn: number,\n /** Encoding Symbol ID (32 bits) */\n public readonly esi: number,\n /** Original object length (optional, for zero-pad stripping) */\n public readonly objectLength?: number,\n ) {}\n\n /** Encode as extension value bytes */\n encode(): Uint8Array {\n const base = encodeFecSourceInfo(this.sbn, this.esi)\n if (this.objectLength == null) return base\n // Append QUIC varint for object length\n const lenBytes = encodeQuicVarint(this.objectLength)\n const result = new Uint8Array(8 + lenBytes.length)\n result.set(base, 0)\n result.set(lenBytes, 8)\n return result\n }\n\n /** Decode from extension value bytes */\n static decode(buf: Uint8Array): FecSourceInfo {\n const [sbn, esi] = decodeFecSourceInfo(buf)\n let objectLength: number | undefined\n if (buf.length > 8) {\n const [val] = decodeQuicVarint(buf, 8)\n objectLength = val\n }\n return new FecSourceInfo(sbn, esi, objectLength)\n }\n}\n\n/**\n * AuthTag for end-to-end content authentication.\n */\nexport class AuthTag {\n static readonly TYPE = LOCHeaderExtensionId.AuthTag\n\n constructor(public readonly tag: Uint8Array) {\n if (tag.length === 0) throw new Error('AuthTag must not be empty')\n }\n}\n\n// QUIC varint helpers\nfunction encodeQuicVarint(value: number): Uint8Array {\n if (value < 64) return new Uint8Array([value])\n if (value < 16384) {\n const buf = new Uint8Array(2)\n new DataView(buf.buffer).setUint16(0, value | 0x4000)\n return buf\n }\n if (value < 1073741824) {\n const buf = new Uint8Array(4)\n new DataView(buf.buffer).setUint32(0, value | 0x80000000)\n return buf\n }\n const buf = new Uint8Array(8)\n new DataView(buf.buffer).setBigUint64(0, BigInt(value) | 0xc000000000000000n)\n return buf\n}\n\nfunction decodeQuicVarint(buf: Uint8Array, offset: number): [number, number] {\n if (offset >= buf.length) return [0, 0]\n const first = buf[offset]!\n const prefix = first >> 6\n const len = 1 << prefix\n if (offset + len > buf.length) return [0, 0]\n const view = new DataView(buf.buffer, buf.byteOffset + offset, len)\n switch (len) {\n case 1: return [first & 0x3f, 1]\n case 2: return [view.getUint16(0) & 0x3fff, 2]\n case 4: return [view.getUint32(0) & 0x3fffffff, 4]\n case 8: return [Number(view.getBigUint64(0) & 0x3fffffffffffffffn), 8]\n default: return [0, 0]\n }\n}\n\n// =============================================================================\n// Inline Tests (vitest)\n// =============================================================================\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest\n\n describe('FEC Source Info (32+32 bit)', () => {\n test('encode/decode roundtrip', () => {\n const [sbn, esi] = [1234, 5678]\n const encoded = encodeFecSourceInfo(sbn, esi)\n expect(encoded.length).toBe(8)\n const [s, e] = decodeFecSourceInfo(encoded)\n expect(s).toBe(1234)\n expect(e).toBe(5678)\n })\n\n test('max 32-bit values', () => {\n const [sbn, esi] = [0xffffffff, 0xffffffff]\n const encoded = encodeFecSourceInfo(sbn, esi)\n const [s, e] = decodeFecSourceInfo(encoded)\n expect(s).toBe(0xffffffff)\n expect(e).toBe(0xffffffff)\n })\n\n test('FecSourceInfo with object length', () => {\n const info = new FecSourceInfo(100, 5, 9999)\n const encoded = info.encode()\n expect(encoded.length).toBeGreaterThan(8) // 8 + varint\n const decoded = FecSourceInfo.decode(encoded)\n expect(decoded.sbn).toBe(100)\n expect(decoded.esi).toBe(5)\n expect(decoded.objectLength).toBe(9999)\n })\n\n test('FecSourceInfo without object length', () => {\n const info = new FecSourceInfo(7, 3)\n const encoded = info.encode()\n expect(encoded.length).toBe(8)\n const decoded = FecSourceInfo.decode(encoded)\n expect(decoded.sbn).toBe(7)\n expect(decoded.esi).toBe(3)\n expect(decoded.objectLength).toBeUndefined()\n })\n\n })\n}\n", "/**\n * `multicast.auth` v2 \u2014 catalog representation for per-track source-auth\n * scopes and manifest tracks (BLO-16700 decision).\n *\n * Reconciles two previously-accepted, mutually incompatible contracts:\n * - BLO-16481 (S0): every publisher session/track/source-symbol-format\n * needs its own fresh, unpredictable 32-byte `auth_scope`, bound into\n * the signed per-group Merkle root (see `sourceAuthSignaturePreimage`\n * in ./source-auth.ts).\n * - BLO-16485 (S3 draft): a singular `multicast.auth` block with one\n * `manifestTrack` string and no `authScope` field at all \u2014 unable to\n * describe two protected tracks (e.g. audio + video) in one session.\n *\n * `multicast.auth` v2 keeps one shared signing identity (scheme, hash,\n * sig, publicKey, keyId, previousKey, compaction, enforcement) but moves\n * `manifestTrack` into a `tracks[]` array, one entry per protected media\n * track, each carrying its own `manifestTrack` name, `authScope`, and\n * `sourceSymbolFormat`. This is additive over the S3 draft shape (every\n * top-level field the draft specified still exists at the top level) \u2014\n * only `manifestTrack` moves from top-level into each track entry.\n *\n * `manifestTrack` is carried as an opaque string field, deliberately NOT\n * run through `parseTrackRole`/`VALID_ROLE_SUFFIXES` in ./carp-catalog.ts.\n * `carp-catalog.ts` only recognises `base`/`delta`/`repair` role suffixes\n * and throws on anything else (including a hypothetical `manifest` role);\n * resolving BLO-16485's \"manifest-track name collision\" MANDATORY item by\n * adding a role would force every catalog consumer to learn a fourth role\n * and would break `validateSwitchingSet`'s base/delta/repair completeness\n * check. Carrying it as a typed field here avoids both.\n */\n\nexport type MulticastAuthScheme = \"bc-provenance\";\nexport type MulticastAuthHash = \"blake3\";\nexport type MulticastAuthSig = \"ed25519\";\nexport type MulticastAuthCompaction = \"merkle\" | \"none\";\nexport type MulticastAuthEnforcement = \"shadow\" | \"enforce\";\nexport type MulticastAuthMode = \"off\" | MulticastAuthEnforcement;\n\nconst RAW_ED25519_KEY_LENGTH = 32;\nconst SOURCE_AUTH_SCOPE_LENGTH = 32;\n\nconst BASE64URL_ALPHABET =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n/** Encode raw bytes as unpadded base64url \u2014 the one accepted encoding for\n * `authScope`/`publicKey`/`previousKey` (BLO-16483's operator quickstart). */\nexport function encodeBase64Url(bytes: Uint8Array): string {\n\tlet output = \"\";\n\tfor (let i = 0; i < bytes.length; i += 3) {\n\t\tconst b0 = bytes[i]!;\n\t\tconst b1 = bytes[i + 1];\n\t\tconst b2 = bytes[i + 2];\n\t\toutput += BASE64URL_ALPHABET[b0 >> 2];\n\t\toutput += BASE64URL_ALPHABET[((b0 & 0x03) << 4) | (b1 === undefined ? 0 : b1 >> 4)];\n\t\tif (b1 === undefined) break;\n\t\toutput += BASE64URL_ALPHABET[((b1 & 0x0f) << 2) | (b2 === undefined ? 0 : b2 >> 6)];\n\t\tif (b2 === undefined) break;\n\t\toutput += BASE64URL_ALPHABET[b2 & 0x3f];\n\t}\n\treturn output;\n}\n\nconst BASE64URL_DECODE_TABLE = (() => {\n\tconst table = new Int8Array(128).fill(-1);\n\tfor (let i = 0; i < BASE64URL_ALPHABET.length; i++) {\n\t\ttable[BASE64URL_ALPHABET.charCodeAt(i)] = i;\n\t}\n\treturn table;\n})();\n\n/** Decode unpadded base64url text to raw bytes. Throws on any padding\n * (`=`), standard-alphabet (`+`/`/`), or otherwise invalid character. */\nexport function decodeBase64Url(text: string): Uint8Array {\n\tif (text.length % 4 === 1) {\n\t\tthrow new Error(`[multicast-auth-catalog] invalid unpadded base64url length for \"${text}\"`);\n\t}\n\tconst bytes: number[] = [];\n\tlet buffer = 0;\n\tlet bits = 0;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst code = text.charCodeAt(i);\n\t\tconst value = code < 128 ? BASE64URL_DECODE_TABLE[code] : -1;\n\t\tif (value === undefined || value < 0) {\n\t\t\tthrow new Error(`[multicast-auth-catalog] invalid base64url character in \"${text}\"`);\n\t\t}\n\t\tbuffer = (buffer << 6) | value;\n\t\tbits += 6;\n\t\tif (bits >= 8) {\n\t\t\tbits -= 8;\n\t\t\tbytes.push((buffer >> bits) & 0xff);\n\t\t}\n\t}\n\tconst decoded = Uint8Array.from(bytes);\n\tif (encodeBase64Url(decoded) !== text) {\n\t\tthrow new Error(`[multicast-auth-catalog] non-canonical base64url encoding \"${text}\"`);\n\t}\n\treturn decoded;\n}\n\n/** Decode + length-validate an `authScope` field. Throws unless it decodes\n * to exactly `SOURCE_AUTH_SCOPE_LENGTH` (32) raw bytes. */\nexport function decodeAuthScope(authScope: string): Uint8Array {\n\tconst bytes = decodeBase64Url(authScope);\n\tif (bytes.byteLength !== SOURCE_AUTH_SCOPE_LENGTH) {\n\t\tthrow new Error(\n\t\t\t`[multicast-auth-catalog] authScope must decode to ${SOURCE_AUTH_SCOPE_LENGTH} raw bytes, got ${bytes.byteLength}`,\n\t\t);\n\t}\n\treturn bytes;\n}\n\n/** Decode a catalog key in the only accepted representation: exactly 32 raw\n * ed25519 bytes encoded as canonical, unpadded base64url. */\nexport function decodeEd25519PublicKey(publicKey: string): Uint8Array {\n\tconst bytes = decodeBase64Url(publicKey);\n\tif (bytes.byteLength !== RAW_ED25519_KEY_LENGTH) {\n\t\tthrow new Error(\n\t\t\t`[multicast-auth-catalog] public key must decode to ${RAW_ED25519_KEY_LENGTH} raw bytes, got ${bytes.byteLength}`,\n\t\t);\n\t}\n\treturn bytes;\n}\n\n/**\n * One protected media track's manifest + scope binding.\n *\n * - `mediaTrack`: the actual media track name (e.g. `\"video/base\"`).\n * - `manifestTrack`: the sibling track carrying this track's signed\n * manifest objects (see ./source-auth.ts `SourceAuthObjectEntry`/\n * `SourceAuthGroupClose`). Opaque string \u2014 not role-parsed.\n * - `authScope`: this track's unique 32-byte scope, raw bytes as unpadded\n * base64url. BLO-16481 requires a scope fresh per (publisher session,\n * track, source-symbol format) \u2014 a track publishing under two source-\n * symbol formats in the same session MUST appear as two entries here,\n * each with its own scope.\n * - `sourceSymbolFormat`: names the deployed source-symbol profile this\n * track's authenticated bytes are derived from (e.g.\n * `\"full-mmtp-packet\"` or `\"fec-padded-recovery\"`, the two formats\n * BLO-16481's S0 fixture covers).\n */\nexport interface MulticastAuthTrackConfig {\n\tmediaTrack: string;\n\tmanifestTrack: string;\n\tauthScope: string;\n\tsourceSymbolFormat: string;\n}\n\n/**\n * `multicast.auth` v2 \u2014 supersedes the BLO-16485 draft's singular\n * `manifestTrack`/no-`authScope` shape (BLO-16700 decision).\n */\nexport interface MulticastAuthConfig {\n\tscheme: MulticastAuthScheme;\n\thash: MulticastAuthHash;\n\tsig: MulticastAuthSig;\n\t/** Raw 32-byte ed25519 public key, unpadded base64url. */\n\tpublicKey: string;\n\tkeyId: string;\n\t/** Raw 32-byte ed25519 public key, unpadded base64url. Accepted\n\t * alongside `publicKey` during a key-rotation overlap window. */\n\tpreviousKey?: string;\n\tcompaction: MulticastAuthCompaction;\n\t/** Player-config-wins default; catalog value only selects the default\n\t * when the player hasn't pinned one (S3 enforcement precedence). */\n\tenforcement?: MulticastAuthEnforcement;\n\ttracks: MulticastAuthTrackConfig[];\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.length > 0;\n}\n\nfunction isValidAuthScopeField(value: unknown): value is string {\n\tif (!isNonEmptyString(value)) return false;\n\ttry {\n\t\tdecodeAuthScope(value);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isValidPublicKeyField(value: unknown): value is string {\n\tif (!isNonEmptyString(value)) return false;\n\ttry {\n\t\tdecodeEd25519PublicKey(value);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction hasOnlyKeys(object: Record<string, unknown>, allowed: readonly string[]): boolean {\n\tconst allowedKeys = new Set(allowed);\n\treturn Object.keys(object).every((key) => allowedKeys.has(key));\n}\n\nexport function isMulticastAuthTrackConfig(obj: unknown): obj is MulticastAuthTrackConfig {\n\tif (typeof obj !== \"object\" || obj === null) return false;\n\tconst o = obj as Record<string, unknown>;\n\treturn (\n\t\thasOnlyKeys(o, [\"mediaTrack\", \"manifestTrack\", \"authScope\", \"sourceSymbolFormat\"]) &&\n\t\tisNonEmptyString(o[\"mediaTrack\"]) &&\n\t\tisNonEmptyString(o[\"manifestTrack\"]) &&\n\t\to[\"mediaTrack\"] !== o[\"manifestTrack\"] &&\n\t\tisValidAuthScopeField(o[\"authScope\"]) &&\n\t\tisNonEmptyString(o[\"sourceSymbolFormat\"])\n\t);\n}\n\nconst VALID_COMPACTION = new Set<string>([\"merkle\", \"none\"]);\nconst VALID_ENFORCEMENT = new Set<string>([\"shadow\", \"enforce\"]);\n\n/** Semantic uniqueness rule shared by runtime parsing and the published\n * catalog schema's `uniqueMulticastAuthTrackBindings` keyword. */\nexport function hasUniqueMulticastAuthTrackBindings(tracks: readonly unknown[]): boolean {\n\tconst bindings = tracks.flatMap((track) => {\n\t\tif (typeof track !== \"object\" || track === null) return [];\n\t\tconst value = track as Record<string, unknown>;\n\t\tif (typeof value[\"mediaTrack\"] !== \"string\" || typeof value[\"sourceSymbolFormat\"] !== \"string\") {\n\t\t\treturn [];\n\t\t}\n\t\treturn [JSON.stringify([value[\"mediaTrack\"], value[\"sourceSymbolFormat\"]])];\n\t});\n\treturn new Set(bindings).size === bindings.length;\n}\n\nexport function isMulticastAuthConfig(obj: unknown): obj is MulticastAuthConfig {\n\tif (typeof obj !== \"object\" || obj === null) return false;\n\tconst o = obj as Record<string, unknown>;\n\n\tif (o[\"scheme\"] !== \"bc-provenance\") return false;\n\tif (o[\"hash\"] !== \"blake3\") return false;\n\tif (o[\"sig\"] !== \"ed25519\") return false;\n\tif (!hasOnlyKeys(o, [\"scheme\", \"hash\", \"sig\", \"publicKey\", \"keyId\", \"previousKey\", \"compaction\", \"enforcement\", \"tracks\"])) return false;\n\tif (!isValidPublicKeyField(o[\"publicKey\"])) return false;\n\tif (!isNonEmptyString(o[\"keyId\"])) return false;\n\tif (o[\"previousKey\"] !== undefined && !isValidPublicKeyField(o[\"previousKey\"])) return false;\n\tif (o[\"previousKey\"] === o[\"publicKey\"]) return false;\n\tif (typeof o[\"compaction\"] !== \"string\" || !VALID_COMPACTION.has(o[\"compaction\"])) return false;\n\tif (o[\"enforcement\"] !== undefined) {\n\t\tif (typeof o[\"enforcement\"] !== \"string\" || !VALID_ENFORCEMENT.has(o[\"enforcement\"])) return false;\n\t}\n\n\tif (!Array.isArray(o[\"tracks\"]) || o[\"tracks\"].length === 0) return false;\n\tconst tracks = o[\"tracks\"] as unknown[];\n\tif (!tracks.every((t) => isMulticastAuthTrackConfig(t))) return false;\n\tconst typedTracks = tracks as MulticastAuthTrackConfig[];\n\n\t// BLO-16481: a fresh scope per track \u2014 catalog-declared scopes must be\n\t// pairwise distinct within one auth block.\n\tconst scopes = typedTracks.map((t) => t.authScope);\n\tif (new Set(scopes).size !== scopes.length) return false;\n\tif (!hasUniqueMulticastAuthTrackBindings(typedTracks)) return false;\n\n\t// Each entry must point at its own manifest track.\n\tconst manifestTracks = typedTracks.map((t) => t.manifestTrack);\n\tif (new Set(manifestTracks).size !== manifestTracks.length) return false;\n\n\treturn true;\n}\n\nexport interface ResolvedMulticastAuthConfig {\n\tkeyId: string;\n\t/** Current key first, followed by the overlap key when advertised. */\n\tacceptedPublicKeys: Uint8Array[];\n\tmode: MulticastAuthMode;\n\tcompaction: MulticastAuthCompaction;\n\ttracks: Array<Omit<MulticastAuthTrackConfig, \"authScope\"> & { authScope: Uint8Array }>;\n}\n\n/** Player configuration is authoritative, including an explicit `off`.\n * Catalog enforcement is only a default; an auth block without it defaults\n * to shadow, while a catalog without auth defaults to off. */\nexport function resolveMulticastAuthMode(\n\tplayerMode: MulticastAuthMode | undefined,\n\tauth: MulticastAuthConfig | undefined,\n): MulticastAuthMode {\n\tif (playerMode !== undefined) return playerMode;\n\tif (auth === undefined) return \"off\";\n\treturn auth.enforcement ?? \"shadow\";\n}\n\n/** Parse and resolve the catalog auth block into verifier-ready raw keys and\n * scopes. Invalid blocks and dangling media/manifest track references fail\n * closed instead of silently falling back to unauthenticated playback. */\nexport function resolveMulticastAuthCatalog(\n\tcatalog: unknown,\n\tplayerMode?: MulticastAuthMode,\n): ResolvedMulticastAuthConfig | undefined {\n\tif (typeof catalog !== \"object\" || catalog === null || Array.isArray(catalog)) {\n\t\tthrow new Error(\"[multicast-auth-catalog] catalog must be an object\");\n\t}\n\tconst root = catalog as Record<string, unknown>;\n\tconst rawAuth = root[\"multicast.auth\"];\n\tif (rawAuth === undefined) {\n\t\tif (playerMode === \"enforce\") {\n\t\t\tthrow new Error(\"[multicast-auth-catalog] player enforce mode requires multicast.auth\");\n\t\t}\n\t\treturn undefined;\n\t}\n\tif (!isMulticastAuthConfig(rawAuth)) {\n\t\tthrow new Error(\"[multicast-auth-catalog] invalid multicast.auth v2 block\");\n\t}\n\n\tconst rawTracks = root[\"tracks\"];\n\tif (!Array.isArray(rawTracks)) {\n\t\tthrow new Error(\"[multicast-auth-catalog] catalog tracks must be an array\");\n\t}\n\tconst catalogTrackNames = new Set(\n\t\trawTracks.flatMap((track) =>\n\t\t\ttypeof track === \"object\" && track !== null &&\n\t\t\ttypeof (track as Record<string, unknown>)[\"name\"] === \"string\"\n\t\t\t\t? [(track as Record<string, unknown>)[\"name\"] as string]\n\t\t\t\t: []\n\t\t),\n\t);\n\tfor (const track of rawAuth.tracks) {\n\t\tif (!catalogTrackNames.has(track.mediaTrack)) {\n\t\t\tthrow new Error(`[multicast-auth-catalog] unknown mediaTrack \"${track.mediaTrack}\"`);\n\t\t}\n\t\tif (!catalogTrackNames.has(track.manifestTrack)) {\n\t\t\tthrow new Error(`[multicast-auth-catalog] unknown manifestTrack \"${track.manifestTrack}\"`);\n\t\t}\n\t}\n\n\treturn {\n\t\tkeyId: rawAuth.keyId,\n\t\tacceptedPublicKeys: [\n\t\t\tdecodeEd25519PublicKey(rawAuth.publicKey),\n\t\t\t...(rawAuth.previousKey === undefined ? [] : [decodeEd25519PublicKey(rawAuth.previousKey)]),\n\t\t],\n\t\tmode: resolveMulticastAuthMode(playerMode, rawAuth),\n\t\tcompaction: rawAuth.compaction,\n\t\ttracks: rawAuth.tracks.map((track) => ({\n\t\t\t...track,\n\t\t\tauthScope: decodeAuthScope(track.authScope),\n\t\t})),\n\t};\n}\n", "/**\n * CARP Catalog Types \u2014 FEC and Multicast catalog extensions.\n *\n * Per draft-law-ramadan-moq-carp-reliability \u00A73 (FEC) and \u00A74 (Multicast).\n * Plain TypeScript interfaces + type guards \u2014 no Zod dependency.\n *\n * These are the canonical type definitions. Upstream Zod schemas\n * should derive from these via z.infer<> to avoid duplication.\n */\n\nimport { resolveMulticastAuthCatalog, type MulticastAuthConfig } from \"./multicast-auth-catalog.js\";\n\n// \u2500\u2500 Track Role (Phase 14, D-02..D-05) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Track role derived from the last slash-segment of the track name.\n *\n * Convention: `{content}/base`, `{content}/delta`, `{content}/repair`.\n * Single-track (no slash) legacy streams use `\"single\"`.\n *\n * D-01: No `trackRole` / `role` / `kind` field in the catalog.\n * D-02: Role is expressed via track name suffix only.\n * D-03: Unknown suffixes throw at parse time (error on bad init).\n * D-04: Legacy single-track streams (no slash suffix) return `\"single\"`.\n * D-05: Single role per track \u2014 no multi-role arrays.\n */\nexport type TrackRole = \"base\" | \"delta\" | \"repair\" | \"single\";\n\nconst VALID_ROLE_SUFFIXES = new Set<string>([\"base\", \"delta\", \"repair\"]);\n\n/**\n * Derive the role of a track from its name.\n *\n * - Names with no `/` \u2192 `\"single\"` (legacy single-track streams, D-04).\n * - Names ending with `/base`, `/delta`, or `/repair` \u2192 that role (D-02).\n * - Empty name \u2192 throws (error on bad init).\n * - Trailing slash (empty last segment) \u2192 throws.\n * - Non-ASCII or control characters anywhere in the name \u2192 throws (T-14-01 fuzz hardening).\n * - Unknown last segment \u2192 throws.\n *\n * @throws {Error} On empty name, non-ASCII/control chars, empty suffix, or unknown suffix.\n */\nexport function parseTrackRole(trackName: string): TrackRole {\n\tif (trackName === \"\") {\n\t\tthrow new Error(\"[carp-catalog] empty track name\");\n\t}\n\n\t// T-14-01 fuzz hardening: reject non-ASCII and control characters.\n\t// This prevents catalog injection attacks where a publisher sends a crafted\n\t// track name that bypasses role checks by embedding unusual code points.\n\tfor (let i = 0; i < trackName.length; i++) {\n\t\tconst cp = trackName.charCodeAt(i);\n\t\t// Reject control characters (0x00-0x1F, 0x7F) and non-ASCII (>0x7E)\n\t\tif (cp <= 0x1f || cp === 0x7f || cp > 0x7e) {\n\t\t\tthrow new Error(\n\t\t\t\t`[carp-catalog] track name contains non-ASCII or control character (code point 0x${cp.toString(16)}) in \"${trackName}\"`,\n\t\t\t);\n\t\t}\n\t}\n\n\tconst lastSlash = trackName.lastIndexOf(\"/\");\n\tif (lastSlash === -1) {\n\t\t// No slash \u2014 legacy single-track stream (D-04)\n\t\treturn \"single\";\n\t}\n\n\tconst suffix = trackName.slice(lastSlash + 1);\n\tif (suffix === \"\") {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] empty role suffix in track name \"${trackName}\"; track names must not end with \"/\"`,\n\t\t);\n\t}\n\n\tif (!VALID_ROLE_SUFFIXES.has(suffix)) {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] unknown role suffix \"${suffix}\" in track name \"${trackName}\"; expected one of: base, delta, repair`,\n\t\t);\n\t}\n\n\treturn suffix as TrackRole;\n}\n\n/**\n * Non-throwing switching-set detector (BLO-8158).\n *\n * Returns `true` iff at least one track name carries a media role suffix\n * (`base` or `delta`) \u2014 i.e. the set looks like a base/delta/repair switching\n * set. A `/repair` track alone is ordinary FEC topology and is not sufficient\n * evidence of a switching set. Unlike `parseTrackRole`, this NEVER throws:\n * names without a `/`, and names with an unrecognised suffix (e.g. an ABR\n * resolution ladder like `video/1080p`, `video/720p`, `video/8k`), simply do\n * not count as a role.\n *\n * Use this for DETECTION (\"is this a switching set?\"). Once a switching set is\n * confirmed, `parseTrackRole` / `validateSwitchingSet` still strictly gate the\n * actual wiring. ABR ladders yield `false` here and fall through to the\n * ABR/single path instead of throwing.\n */\nexport function hasSwitchingSetRole(names: string[]): boolean {\n\treturn names.some((name) => {\n\t\tconst lastSlash = name.lastIndexOf(\"/\");\n\t\tif (lastSlash === -1) return false;\n\t\tconst suffix = name.slice(lastSlash + 1);\n\t\treturn suffix === \"base\" || suffix === \"delta\";\n\t});\n}\n\n/**\n * Validate that a set of track names forms a complete switching set.\n *\n * - Empty or single-track sets (all parsing to `\"single\"`) \u2192 `{ ok: true }`.\n * - Multi-track sets (any name has a role suffix) MUST include all three\n * roles (`base`, `delta`, `repair`). Missing roles are listed.\n *\n * Propagates throws from `parseTrackRole` (non-ASCII, unknown suffix, etc.).\n *\n * D-04: Legacy single-track streams pass without enforcement.\n * D-04: Once any suffix appears, the set is multi-track and must be complete.\n */\nexport function validateSwitchingSet(\n\ttrackNames: string[],\n): { ok: true } | { ok: false; missing: TrackRole[] } {\n\tif (trackNames.length === 0) return { ok: true };\n\n\tconst roles = trackNames.map(parseTrackRole);\n\n\t// If every role is \"single\", this is a legacy single-track stream \u2014 pass.\n\tif (roles.every((r) => r === \"single\")) {\n\t\treturn { ok: true };\n\t}\n\n\t// Multi-track: at least one name has a role suffix.\n\t// The set MUST contain all three required roles.\n\t// Explicit Set<TrackRole> so the TrackRole-typed `required.filter`\n\t// callback can call .has(r) without a narrow-mismatch complaint.\n\tconst presentRoles: Set<TrackRole> = new Set(roles.filter((r) => r !== \"single\"));\n\tconst required: TrackRole[] = [\"base\", \"delta\", \"repair\"];\n\tconst missing = required.filter((r) => !presentRoles.has(r));\n\n\tif (missing.length > 0) {\n\t\treturn { ok: false, missing };\n\t}\n\treturn { ok: true };\n}\n\n// \u2500\u2500 FEC Catalog Extension (CARP \u00A73.2) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type FecAlgorithm = \"none\" | \"raptorq\" | \"reed-solomon\";\n\nexport interface FecCatalogConfig {\n\t/** FEC algorithm identifier (REQUIRED per \u00A74.1, values per \u00A73.1). */\n\talgorithm: FecAlgorithm;\n\t/** K \u2014 source symbols per block (REQUIRED). */\n\tsourceSymbols: number;\n\t/** P \u2014 repair symbols per block (REQUIRED). */\n\trepairSymbols: number;\n\t/** T \u2014 bytes per symbol, 8-byte aligned (REQUIRED). */\n\tsymbolSize: number;\n\t/** FEC block span in MILLISECONDS (OPTIONAL, default 0 = single-group blocks).\n\t * D is the positive round-half-up value of interleaveDepthMs / GOP_duration_ms.\n\t * BLO-14526: renamed from the legacy `interleaveDepth` count-era key \u2014 no fallback read. */\n\tinterleaveDepthMs?: number;\n\t/** Declared jitter/reorder allowance in MILLISECONDS.\n\t * Added to interleaveDepthMs to form a block's deadline; never multiplied\n\t * by it. Jitter is a property of the network path and the block span is a\n\t * property of the encoder, so a multiplicative \"safety factor\" couples two\n\t * independent quantities and grants different slack per track for the same\n\t * network. Consumed identically by FECManager, FEC workers and player\n\t * policies so one signaled value governs end to end. */\n\treorderToleranceMs?: number;\n\t/** Track name for repair symbols (REQUIRED per \u00A76.1). */\n\trepairTrack: string;\n\t/** FEC block mode: \"interleaved\" (default) or \"object\" (per-frame). */\n\t// extensions feature \u2014 see moqcast-draft docs/fec-extensions-material.md, not in public -00\n\tmode?: \"interleaved\" | \"object\";\n\t/** UEP (Unequal Error Protection) \u2014 extensions feature, see moqcast-draft docs/fec-extensions-material.md, not in public -00. When present, repair count\n\t * varies per frame type. repairSymbols becomes the base (P-frame) count.\n\t * MUST be omitted on multi-track base/delta streams (D-08). */\n\tuep?: {\n\t\t/** Keyframe repair ratio: P = ceil(K * keyRatio). Default 0.5. */\n\t\tkeyRatio: number;\n\t\t/** Delta frame repair ratio: P = ceil(K * deltaRatio). Default 0.25. */\n\t\tdeltaRatio: number;\n\t};\n\t/**\n\t * Base-only fallback window in FEC blocks (D-11, Phase 14).\n\t *\n\t * When the delta track's FEC block failure rate exceeds the derived threshold\n\t * over this many consecutive blocks, MediaRouter falls back to base-only mode.\n\t * Derivation: framerate * fallback_window_seconds. If absent, construction\n\t * of the multi-track subscriber MUST throw (error on bad init).\n\t *\n\t * Present only on the base track's fec config (not the delta or repair track).\n\t */\n\tdeltaFailWindowBlocks?: number;\n\t/**\n\t * Delta re-subscribe probe interval in milliseconds (D-13, Phase 14).\n\t *\n\t * After a base-only fallback, wait this long before re-subscribing to the\n\t * delta track to check if conditions have improved.\n\t * Derivation: computeFecDeliveryWindowMs(...) \u2014 the single FEC recovery-window\n\t * source of truth (draft-ramadan-moq-fec \u00A79). Never a fixed interleave multiple.\n\t *\n\t * Present only on the base track's fec config (not the delta or repair track).\n\t */\n\tdeltaProbeIntervalMs?: number;\n\t/**\n\t * Jitter + one-way-delay + reorder budget in MILLISECONDS (draft \u00A79).\n\t *\n\t * The single catalog source for the ADDITIVE margin that the FEC\n\t * recovery/loss window adds on top of max(interleave span, source+repair horizon,\n\t * block serialization) \u2014 see computeFecDeliveryWindowMs. It is a margin, not\n\t * a multiplier. OPTIONAL: when absent, the derivation falls back to the\n\t * un-rounded interleave window as the reorder-budget bound (margin-policy).\n\t */\n\tjitterBufferMs?: number;\n}\n\nexport function isFecCatalogConfig(obj: unknown): obj is FecCatalogConfig {\n\tif (typeof obj !== \"object\" || obj === null) return false;\n\tconst o = obj as Record<string, unknown>;\n\tconst algorithm = o[\"algorithm\"];\n\tconst symbolSize = o[\"symbolSize\"];\n\treturn (\n\t\ttypeof algorithm === \"string\" &&\n\t\t(algorithm === \"none\" || algorithm === \"raptorq\" || algorithm === \"reed-solomon\") &&\n\t\ttypeof o[\"sourceSymbols\"] === \"number\" && o[\"sourceSymbols\"] >= 1 &&\n\t\ttypeof o[\"repairSymbols\"] === \"number\" && o[\"repairSymbols\"] >= 1 &&\n\t\ttypeof symbolSize === \"number\" && symbolSize >= 1 &&\n\t\t(algorithm !== \"raptorq\" || symbolSize % 8 === 0) &&\n\t\ttypeof o[\"repairTrack\"] === \"string\" && (o[\"repairTrack\"] as string).length > 0\n\t);\n}\n\n// \u2500\u2500 Multicast Catalog Extension (CARP \u00A74.1-4.4) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type MulticastProtocol = \"ssm\" | \"asm\" | \"amt\" | \"atsc3\";\n\n/**\n * Per-track endpoint membership, per draft-ramadan-moq-multicast-00 \u00A74.1.\n *\n * `packetId` is the MMTP packet_id used for packet-level demux on the\n * multicast wire. New emitters MUST use this object form; the legacy\n * `string` form is accepted by parsers for one release for backward\n * compatibility (BLO-3323 migration).\n */\nexport interface MulticastTrackObject {\n\tname: string;\n\tpacketId: number;\n}\n\n/** Track entry in a multicast endpoint \u2014 accepts both legacy and v2 shape. */\nexport type MulticastTrack = string | MulticastTrackObject;\n\nexport interface MulticastEndpoint {\n\t/** Transport protocol. Defaults to SSM when sourceAddress is present. */\n\tprotocol?: MulticastProtocol;\n\t/** SSM source IP address (REQUIRED for SSM). */\n\tsourceAddress?: string;\n\t/** Multicast group address (REQUIRED). */\n\tgroupAddress: string;\n\t/** UDP port number (REQUIRED). */\n\tport: number;\n\t/**\n\t * Tracks available on this endpoint (REQUIRED).\n\t *\n\t * Accepts both legacy `string` (track name only) and v2 object form\n\t * (`{ name, packetId }`) for backward compatibility. New emitters\n\t * MUST use the object form (BLO-3323 / draft \u00A74.1). Consumers can\n\t * type-narrow with `typeof t === \"string\"` to handle both.\n\t */\n\ttracks: MulticastTrack[];\n\t/** AMT relay hostname (REQUIRED for AMT). */\n\trelayAddress?: string;\n\t/**\n\t * Aggregate bandwidth of this endpoint in bits per second.\n\t * RECOMMENDED per draft \u00A74.1 \u2014 enables capacity-aware joins.\n\t */\n\tbandwidth?: number;\n}\n\nexport type AmtDiscovery = \"driad\" | \"manual\";\n\nexport interface MulticastCatalogConfig {\n\t/** Enable/disable multicast (OPTIONAL, default true). */\n\tenabled?: boolean;\n\t/** Multicast endpoints (REQUIRED). */\n\tendpoints: MulticastEndpoint[];\n\t/** AMT fallback configuration (OPTIONAL). */\n\tamtFallback?: {\n\t\tdiscovery: AmtDiscovery;\n\t\trelayDns?: string;\n\t};\n\t/**\n\t * Monotonic catalog version. Publishers MUST bump on any\n\t * `endpoints[]` mutation (BLO-3323 catalog freshness model).\n\t */\n\tversion?: number;\n\t/**\n\t * Unix-ms timestamp after which subscribers SHOULD refetch the\n\t * catalog. Refetch-on-multicast-receive-timeout short-circuits this.\n\t */\n\tvalidUntil?: number;\n}\n\nexport function isMulticastTrackObject(obj: unknown): obj is MulticastTrackObject {\n\tif (typeof obj !== \"object\" || obj === null) return false;\n\tconst o = obj as Record<string, unknown>;\n\treturn typeof o[\"name\"] === \"string\" && typeof o[\"packetId\"] === \"number\";\n}\n\n/**\n * Wire-shape predicate: returns `true` when `obj` matches the\n * MulticastEndpoint JSON shape strictly enough to be safely cast.\n *\n * Intentionally less strict than `multicastFromConfig`'s\n * `validateEndpoint` (in `multicast-input.ts`):\n *\n * - `tracks: []` is accepted here (Array.isArray check only) but\n * rejected by `validateEndpoint` as `empty_tracks`.\n * - SSM-specific group-range and source-required checks are not\n * enforced here.\n * - Bandwidth/AmtFallback/version are not type-checked.\n *\n * That asymmetry is deliberate: this predicate guards JSON parsed off\n * the wire (already-published catalogs that downstream may have\n * trimmed), while `validateEndpoint` guards publisher input *before*\n * a catalog is composed. Subscribers are tolerant; publishers are\n * strict \u2014 the catalog format never re-validates upstream invariants.\n */\nexport function isMulticastEndpoint(obj: unknown): obj is MulticastEndpoint {\n\tif (typeof obj !== \"object\" || obj === null) return false;\n\tconst o = obj as Record<string, unknown>;\n\tconst protocol = o[\"protocol\"] ??\n\t\t(typeof o[\"sourceAddress\"] === \"string\" && o[\"sourceAddress\"].length > 0 ? \"ssm\" : undefined);\n\treturn (\n\t\ttypeof protocol === \"string\" &&\n\t\t(protocol === \"ssm\" || protocol === \"asm\" || protocol === \"amt\" || protocol === \"atsc3\") &&\n\t\ttypeof o[\"groupAddress\"] === \"string\" && (o[\"groupAddress\"] as string).length > 0 &&\n\t\ttypeof o[\"port\"] === \"number\" && (o[\"port\"] as number) > 0 && (o[\"port\"] as number) <= 65535 &&\n\t\tArray.isArray(o[\"tracks\"]) &&\n\t\t(o[\"tracks\"] as unknown[]).every((t: unknown) => typeof t === \"string\" || isMulticastTrackObject(t))\n\t);\n}\n\nexport function isMulticastCatalogConfig(obj: unknown): obj is MulticastCatalogConfig {\n\tif (typeof obj !== \"object\" || obj === null) return false;\n\tconst o = obj as Record<string, unknown>;\n\treturn (\n\t\tArray.isArray(o[\"endpoints\"]) &&\n\t\t(o[\"endpoints\"] as unknown[]).every((ep: unknown) => isMulticastEndpoint(ep))\n\t);\n}\n\n// \u2500\u2500 Track Timing (mmt-00 \u00A74.4.4) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Per-track group-duration signaling.\n *\n * The catalog publishes either `groupDurationMs` (integer ms) or the exact\n * `groupDurationTicks` value used by `groupIdForTicks` in align.ts. The tick\n * form is required for cases where milliseconds cannot represent the cadence\n * exactly:\n *\n * - Per-frame group durations at non-integer-ms cadences\n * (1/30 s = 33.333\u2026 ms; 1/60 s = 16.666\u2026 ms).\n * - Audio sample-rate timescales not divisible by 1000 Hz\n * (e.g. 44 100 Hz \u00D7 333 ms = 14 685.3 ticks).\n *\n * If both fields are present, `groupDurationTicks` wins (matches the\n * spec, mmt-00 \u00A74.4.4).\n *\n * @see draft-ramadan-moq-mmt-00 \u00A74.4.4\n * @see ./align.ts groupIdForTicks\n */\nexport interface TrackTimingConfig {\n\t/** Group duration in milliseconds (positive integer). Required unless an\n\t * exact groupDurationTicks value is supplied. */\n\tgroupDurationMs?: number;\n\t/** Integer-tick override (OPTIONAL, positive integer). Wins over the\n\t * ms field when both are present. Required when\n\t * `(groupDurationMs * timescale) % 1000 != 0`. */\n\tgroupDurationTicks?: number;\n}\n\nexport function isTrackTimingConfig(obj: unknown): obj is TrackTimingConfig {\n\tif (typeof obj !== \"object\" || obj === null) return false;\n\tconst o = obj as Record<string, unknown>;\n\tconst ms = o[\"groupDurationMs\"];\n\tconst ticks = o[\"groupDurationTicks\"];\n\tif (ms === undefined && ticks === undefined) return false;\n\tif (ms !== undefined && (typeof ms !== \"number\" || !Number.isInteger(ms) || ms <= 0)) return false;\n\tif (\"groupDurationTicks\" in o) {\n\t\tif (ticks === undefined) return ms !== undefined;\n\t\tif (typeof ticks !== \"number\" || !Number.isInteger(ticks) || ticks <= 0) return false;\n\t}\n\treturn true;\n}\n\n/** Resolve catalog group timing to an operational wall-clock duration.\n *\n * `groupDurationTicks` wins when both wire fields are present, matching\n * mmt-00 \u00A74.4.4. The result may be fractional because exact frame cadences\n * such as 33_333 ticks at 1 MHz cannot be represented by the wire catalog's\n * integer `groupDurationMs` field.\n */\nexport function resolveGroupDurationMs(\n\ttiming: TrackTimingConfig,\n\ttimescale: number,\n): number {\n\tif (!Number.isSafeInteger(timescale) || timescale <= 0) {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] resolveGroupDurationMs: timescale must be a positive safe integer Hz, got ${timescale}`,\n\t\t);\n\t}\n\tif (!isTrackTimingConfig(timing)) {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] resolveGroupDurationMs: invalid TrackTimingConfig: ${JSON.stringify(timing)}`,\n\t\t);\n\t}\n\tif (timing.groupDurationTicks === undefined) {\n\t\treturn timing.groupDurationMs!;\n\t}\n\n\tconst durationMs = (timing.groupDurationTicks / timescale) * 1000;\n\tif (!Number.isFinite(durationMs) || durationMs <= 0 || durationMs > Number.MAX_SAFE_INTEGER) {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] resolveGroupDurationMs: groupDurationTicks=${timing.groupDurationTicks} ` +\n\t\t\t\t`at timescale=${timescale} cannot be represented as a positive safe millisecond duration`,\n\t\t);\n\t}\n\treturn durationMs;\n}\n\nexport type CmsfCatalog = {\n\tversion: number;\n\ttracks: Record<string, unknown>[];\n\tcommonTrackFields?: Record<string, unknown>;\n\tmulticast?: Record<string, unknown>;\n\t\"multicast.auth\"?: MulticastAuthConfig;\n\t[key: string]: unknown;\n};\n\nexport type CmsfCatalogErrorCode =\n\t| \"catalog_object\"\n\t| \"catalog_version\"\n\t| \"catalog_tracks\"\n\t| \"track_object\"\n\t| \"track_name\"\n\t| \"track_packaging\"\n\t| \"track_is_live\"\n\t| \"track_codec\"\n\t| \"track_mmtp_mode\"\n\t| \"track_timescale\"\n\t| \"track_group_duration_ms\"\n\t| \"track_group_duration_ticks\"\n\t| \"track_group_duration_exact\"\n\t| \"track_keyframe_interval\"\n\t| \"track_fec\"\n\t| \"track_repair_track\"\n\t| \"track_switching_set\"\n\t| \"multicast_auth\"\n\t| \"multicast_track\"\n\t| \"multicast_packet_id\";\n\nexport class CmsfCatalogValidationError extends Error {\n\tconstructor(\n\t\tpublic readonly code: CmsfCatalogErrorCode,\n\t\tpublic readonly path: string,\n\t\tmessage: string,\n\t) {\n\t\tsuper(`[cmsf:${code}] ${path}: ${message}`);\n\t\tthis.name = \"CmsfCatalogValidationError\";\n\t}\n}\n\nfunction catalogError(code: CmsfCatalogErrorCode, path: string, message: string): never {\n\tthrow new CmsfCatalogValidationError(code, path, message);\n}\n\n/** Validate the common CMSF shape and the MMTP required-field profile. */\nexport function validateCmsfMmtpCatalog(value: unknown): CmsfCatalog {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tcatalogError(\"catalog_object\", \"$\", \"catalog must be an object\");\n\t}\n\tconst catalog = value as Record<string, unknown>;\n\t// IETF-draft catalogs (cast `ladder_catalog.rs`, moq-server\n\t// `catalog-synthesizer.ts`, consumed by the hang player) carry a string\n\t// version tag such as \"draft-ietf-moq-catalogformat-01\" instead of the CMSF\n\t// `version: 1` integer. Nothing keys behavior off the catalog version, so\n\t// normalize the tag to a number rather than rejecting the whole catalog \u2014\n\t// mirrors the relay (dual-stack-relay `bridge/catalog.rs`\n\t// `deserialize_optional_u32_lenient`, #825) and moqtail-ts\n\t// `normalizeCatalogVersion`.\n\tconst rawVersion = catalog[\"version\"];\n\tconst normalizedVersion =\n\t\ttypeof rawVersion === \"string\"\n\t\t\t? Number.isFinite(Number(rawVersion))\n\t\t\t\t? Number(rawVersion)\n\t\t\t\t: rawVersion.startsWith(\"draft-ietf-moq-catalogformat\")\n\t\t\t\t\t? 1\n\t\t\t\t\t: NaN\n\t\t\t: rawVersion;\n\tif (!Number.isInteger(normalizedVersion) || (normalizedVersion as number) <= 0) {\n\t\tcatalogError(\"catalog_version\", \"$.version\", \"version must be a positive integer or a recognized format tag\");\n\t}\n\tcatalog[\"version\"] = normalizedVersion;\n\tif (!Array.isArray(catalog[\"tracks\"])) {\n\t\tcatalogError(\"catalog_tracks\", \"$.tracks\", \"tracks must be an array\");\n\t}\n\n\tconst common = typeof catalog[\"commonTrackFields\"] === \"object\" &&\n\t\tcatalog[\"commonTrackFields\"] !== null && !Array.isArray(catalog[\"commonTrackFields\"])\n\t\t? catalog[\"commonTrackFields\"] as Record<string, unknown>\n\t\t: {};\n\tconst tracks = catalog[\"tracks\"] as unknown[];\n\tconst trackRecords: Record<string, unknown>[] = [];\n\tconst effective = (track: Record<string, unknown>, field: string): unknown => track[field] ?? common[field];\n\tconst positiveInteger = (input: unknown): input is number => Number.isSafeInteger(input) && (input as number) > 0;\n\tconst positiveNumber = (input: unknown): input is number =>\n\t\ttypeof input === \"number\" && Number.isFinite(input) && input > 0;\n\n\tfor (let index = 0; index < tracks.length; index++) {\n\t\tconst raw = tracks[index];\n\t\tconst path = `$.tracks[${index}]`;\n\t\tif (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n\t\t\tcatalogError(\"track_object\", path, \"track must be an object\");\n\t\t}\n\t\tconst track = raw as Record<string, unknown>;\n\t\tconst name = track[\"name\"];\n\t\tif (typeof name !== \"string\" || name.length === 0) {\n\t\t\tcatalogError(\"track_name\", `${path}.name`, \"name must be a non-empty string\");\n\t\t}\n\t\ttrackRecords.push(track);\n\n\t\tconst packaging = effective(track, \"packaging\");\n\t\tif (typeof packaging !== \"string\" || packaging.length === 0) {\n\t\t\tcatalogError(\"track_packaging\", `${path}.packaging`, \"packaging must be a non-empty string\");\n\t\t}\n\t\tconst trackHasIsLive = Object.prototype.hasOwnProperty.call(track, \"isLive\");\n\t\tconst hasIsLive = trackHasIsLive || Object.prototype.hasOwnProperty.call(common, \"isLive\");\n\t\tconst isLive = trackHasIsLive ? track[\"isLive\"] : common[\"isLive\"];\n\t\tif (hasIsLive && typeof isLive !== \"boolean\") {\n\t\t\tcatalogError(\"track_is_live\", `${path}.isLive`, \"isLive must be a boolean\");\n\t\t}\n\t\ttrack[\"isLive\"] = isLive ?? true;\n\t\tconst selectionValue = effective(track, \"selectionParams\");\n\t\tconst selection = typeof selectionValue === \"object\" && selectionValue !== null && !Array.isArray(selectionValue)\n\t\t\t? selectionValue as Record<string, unknown>\n\t\t\t: undefined;\n\t\tconst codec = selection?.[\"codec\"] ?? effective(track, \"codec\");\n\t\tif (packaging !== \"timeline\" && packaging !== \"mediatimeline\" && packaging !== \"eventtimeline\" && packaging !== \"fec-repair\" &&\n\t\t\t(typeof codec !== \"string\" || codec.length === 0)) {\n\t\t\tcatalogError(\"track_codec\", `${path}.codec`, `codec is required for packaging=${packaging}`);\n\t\t}\n\n\t\tconst timescale = effective(track, \"timescale\");\n\t\tconst groupDurationMs = effective(track, \"groupDurationMs\");\n\t\tconst groupDurationTicks = effective(track, \"groupDurationTicks\");\n\t\tconst keyframeIntervalMs = effective(track, \"keyframeIntervalMs\");\n\t\tconst keyframeIntervalTicks = effective(track, \"keyframeIntervalTicks\");\n\t\tif (groupDurationMs !== undefined && !positiveInteger(groupDurationMs)) {\n\t\t\tcatalogError(\"track_group_duration_ms\", `${path}.groupDurationMs`, \"must be a positive integer\");\n\t\t}\n\t\tif (groupDurationTicks !== undefined && !positiveInteger(groupDurationTicks)) {\n\t\t\tcatalogError(\"track_group_duration_ticks\", `${path}.groupDurationTicks`, \"must be a positive integer\");\n\t\t}\n\t\tif ((keyframeIntervalMs !== undefined && !positiveNumber(keyframeIntervalMs)) ||\n\t\t\t(keyframeIntervalTicks !== undefined && !positiveInteger(keyframeIntervalTicks))) {\n\t\t\tcatalogError(\"track_keyframe_interval\", path, \"keyframe interval fields must be positive numbers\");\n\t\t}\n\t\tif (packaging === \"mmtp\") {\n\t\t\tconst mmtpMode = effective(track, \"mmtpMode\");\n\t\t\tif (mmtpMode !== \"mpu\" && mmtpMode !== \"mfu\") {\n\t\t\t\tcatalogError(\"track_mmtp_mode\", `${path}.mmtpMode`, \"must be mpu or mfu\");\n\t\t\t}\n\t\t\tif (!positiveInteger(timescale)) {\n\t\t\t\tcatalogError(\"track_timescale\", `${path}.timescale`, \"must be a positive integer\");\n\t\t\t}\n\t\t\tif (groupDurationMs === undefined && groupDurationTicks === undefined) {\n\t\t\t\tcatalogError(\n\t\t\t\t\t\"track_group_duration_ms\",\n\t\t\t\t\tpath,\n\t\t\t\t\t\"groupDurationMs or groupDurationTicks must be a positive integer\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (groupDurationTicks === undefined &&\n\t\t\t\t(BigInt(groupDurationMs as number) * BigInt(timescale)) % 1000n !== 0n) {\n\t\t\t\tcatalogError(\"track_group_duration_exact\", `${path}.groupDurationMs`, \"does not resolve to an exact integer tick count\");\n\t\t\t}\n\t\t}\n\n\t\tif (track[\"fec\"] !== undefined) {\n\t\t\tconst fec = track[\"fec\"];\n\t\t\tif (typeof fec !== \"object\" || fec === null || Array.isArray(fec)) {\n\t\t\t\tcatalogError(\"track_fec\", `${path}.fec`, \"FEC must be an object\");\n\t\t\t}\n\t\t\tconst config = fec as Record<string, unknown>;\n\t\t\tconst algorithm = config[\"algorithm\"];\n\t\t\tif (algorithm !== \"none\" && algorithm !== \"raptorq\" && algorithm !== \"reed-solomon\") {\n\t\t\t\tcatalogError(\"track_fec\", `${path}.fec.algorithm`, \"algorithm is not supported\");\n\t\t\t}\n\t\t\tif (\"interleaveDepth\" in config || \"interleaveMs\" in config) {\n\t\t\t\tcatalogError(\"track_fec\", `${path}.fec`, \"interleaveDepthMs is the only supported interleave field\");\n\t\t\t}\n\t\t\tif (config[\"interleaveDepthMs\"] !== undefined && !positiveInteger(config[\"interleaveDepthMs\"])) {\n\t\t\t\tcatalogError(\"track_fec\", `${path}.fec.interleaveDepthMs`, \"must be a positive integer\");\n\t\t\t}\n\t\t\tif (algorithm !== \"none\") {\n\t\t\t\tfor (const field of [\"sourceSymbols\", \"repairSymbols\", \"symbolSize\"] as const) {\n\t\t\t\t\tif (!positiveInteger(config[field])) {\n\t\t\t\t\t\tcatalogError(\"track_fec\", `${path}.fec.${field}`, \"must be a positive integer\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (typeof config[\"repairTrack\"] !== \"string\" || config[\"repairTrack\"].length === 0) {\n\t\t\t\t\tcatalogError(\"track_fec\", `${path}.fec.repairTrack`, \"must be a non-empty string\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (let index = 0; index < trackRecords.length; index++) {\n\t\tconst track = trackRecords[index]!;\n\t\tconst fec = track[\"fec\"] as Record<string, unknown> | undefined;\n\t\tif (!fec || fec[\"algorithm\"] === \"none\") continue;\n\t\tconst repairTrack = fec[\"repairTrack\"] as string;\n\t\tconst target = trackRecords.find((candidate) => candidate[\"name\"] === repairTrack);\n\t\tif (!target || effective(target, \"packaging\") !== \"fec-repair\") {\n\t\t\tcatalogError(\"track_repair_track\", `$.tracks[${index}].fec.repairTrack`, `unknown FEC repair track ${JSON.stringify(repairTrack)}`);\n\t\t}\n\t}\n\n\tconst switchingSets = new Map<number, { name: string; ticks: bigint; timescale: bigint }>();\n\tfor (let index = 0; index < trackRecords.length; index++) {\n\t\tconst track = trackRecords[index]!;\n\t\tif (effective(track, \"packaging\") !== \"mmtp\") continue;\n\t\tconst altGroup = effective(track, \"altGroup\");\n\t\tif (!Number.isSafeInteger(altGroup)) continue;\n\t\tconst timescale = BigInt(effective(track, \"timescale\") as number);\n\t\tconst durationTicks = effective(track, \"groupDurationTicks\");\n\t\tconst ticks = durationTicks !== undefined\n\t\t\t? BigInt(durationTicks as number)\n\t\t\t: BigInt(effective(track, \"groupDurationMs\") as number) * timescale / 1000n;\n\t\tconst anchor = switchingSets.get(altGroup as number);\n\t\tif (anchor && ticks * anchor.timescale !== anchor.ticks * timescale) {\n\t\t\tcatalogError(\"track_switching_set\", `$.tracks[${index}]`, `group duration does not match ${JSON.stringify(anchor.name)} in altGroup ${altGroup}`);\n\t\t}\n\t\tif (!anchor) switchingSets.set(altGroup as number, { name: track[\"name\"] as string, ticks, timescale });\n\t}\n\n\tconst multicast = catalog[\"multicast\"];\n\tif (typeof multicast === \"object\" && multicast !== null && !Array.isArray(multicast)) {\n\t\tconst endpoints = (multicast as Record<string, unknown>)[\"endpoints\"];\n\t\tif (Array.isArray(endpoints)) {\n\t\t\tfor (let endpointIndex = 0; endpointIndex < endpoints.length; endpointIndex++) {\n\t\t\t\tconst endpoint = endpoints[endpointIndex];\n\t\t\t\tif (typeof endpoint !== \"object\" || endpoint === null || Array.isArray(endpoint)) continue;\n\t\t\t\tconst refs = (endpoint as Record<string, unknown>)[\"tracks\"];\n\t\t\t\tif (!Array.isArray(refs)) continue;\n\t\t\t\tconst packetIds = new Map<number, string>();\n\t\t\t\tfor (let refIndex = 0; refIndex < refs.length; refIndex++) {\n\t\t\t\t\tconst ref = refs[refIndex];\n\t\t\t\t\tif (typeof ref !== \"object\" || ref === null || Array.isArray(ref)) continue;\n\t\t\t\t\tconst trackRef = ref as Record<string, unknown>;\n\t\t\t\t\tconst refPath = `$.multicast.endpoints[${endpointIndex}].tracks[${refIndex}]`;\n\t\t\t\t\tconst name = trackRef[\"name\"];\n\t\t\t\t\tif (typeof name !== \"string\" || !trackRecords.some((track) => track[\"name\"] === name)) {\n\t\t\t\t\t\tcatalogError(\"multicast_track\", `${refPath}.name`, `unknown catalog track ${JSON.stringify(name)}`);\n\t\t\t\t\t}\n\t\t\t\t\tconst packetId = trackRef[\"packetId\"];\n\t\t\t\t\tif (!Number.isSafeInteger(packetId) || (packetId as number) < 0 || (packetId as number) > 65535) {\n\t\t\t\t\t\tcatalogError(\"multicast_packet_id\", `${refPath}.packetId`, \"packetId must be an integer from 0 to 65535\");\n\t\t\t\t\t}\n\t\t\t\t\tconst previous = packetIds.get(packetId as number);\n\t\t\t\t\tif (previous !== undefined) {\n\t\t\t\t\t\tcatalogError(\"multicast_packet_id\", `${refPath}.packetId`, `packetId is already assigned to ${JSON.stringify(previous)}`);\n\t\t\t\t\t}\n\t\t\t\t\tpacketIds.set(packetId as number, name as string);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\tresolveMulticastAuthCatalog(catalog);\n\t} catch (error) {\n\t\tcatalogError(\n\t\t\t\"multicast_auth\",\n\t\t\t\"$.multicast.auth\",\n\t\t\terror instanceof Error ? error.message : \"invalid multicast.auth v2 block\",\n\t\t);\n\t}\n\n\treturn catalog as CmsfCatalog;\n}\n\n/**\n * Resolve a track's group duration to integer ticks using the supplied\n * timescale (Hz). Implements mmt-00 \u00A74.4.4:\n *\n * - If `groupDurationTicks` is present, return it (override wins).\n * - Else compute `groupDurationMs * timescale / 1000`. The result MUST\n * be an exact integer; throws if `(ms * timescale) % 1000 != 0`.\n *\n * Throws on invalid timing config or non-positive timescale (CLAUDE.md:\n * error on bad init).\n */\nexport function resolveGroupDurationTicks(\n\ttiming: TrackTimingConfig,\n\ttimescale: number,\n): number {\n\tif (!Number.isFinite(timescale) || !Number.isInteger(timescale) || timescale <= 0) {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] resolveGroupDurationTicks: timescale must be a positive integer Hz, got ${timescale}`,\n\t\t);\n\t}\n\tif (!isTrackTimingConfig(timing)) {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] resolveGroupDurationTicks: invalid TrackTimingConfig: ${JSON.stringify(timing)}`,\n\t\t);\n\t}\n\tif (timing.groupDurationTicks !== undefined) {\n\t\treturn timing.groupDurationTicks;\n\t}\n\tif (timing.groupDurationMs === undefined) {\n\t\tthrow new Error(\n\t\t\t\"[carp-catalog] resolveGroupDurationTicks: groupDurationMs is required when groupDurationTicks is absent\",\n\t\t);\n\t}\n\tconst product = timing.groupDurationMs * timescale;\n\tif (product % 1000 !== 0) {\n\t\tthrow new Error(\n\t\t\t`[carp-catalog] resolveGroupDurationTicks: groupDurationMs=${timing.groupDurationMs} at timescale=${timescale} ` +\n\t\t\t\t`produces non-integer ticks (${product / 1000}); publisher must use groupDurationTicks override (mmt-00 \u00A74.4.4)`,\n\t\t);\n\t}\n\treturn product / 1000;\n}\n\n/**\n * Validate that every track in a switching set agrees on the same\n * wall-clock group duration (mmt-00 \u00A74.4.4). Per-track timescales may\n * differ, so raw tick counts cannot be compared directly.\n *\n * Returns `{ ok: true }` when all tracks agree (or the set is empty);\n * `{ ok: false, mismatches }` listing each track whose effective ticks\n * differ from the first track's value. Subscribers per spec SHOULD\n * reject the catalog on mismatch.\n *\n * Throws if any track's timing config is invalid (per\n * `resolveGroupDurationTicks`).\n */\nexport function validateSwitchingSetTiming(\n\ttracks: Array<{ name: string; timing: TrackTimingConfig; timescale: number }>,\n):\n\t| { ok: true }\n\t| { ok: false; expectedTicks: number; mismatches: Array<{ name: string; ticks: number }> } {\n\tif (tracks.length === 0) return { ok: true };\n\tconst ticksByTrack = tracks.map((t) => ({\n\t\tname: t.name,\n\t\tticks: resolveGroupDurationTicks(t.timing, t.timescale),\n\t\ttimescale: t.timescale,\n\t}));\n\tconst expectedTicks = ticksByTrack[0].ticks;\n\tconst expectedTimescale = ticksByTrack[0].timescale;\n\tconst mismatches = ticksByTrack\n\t\t.slice(1)\n\t\t.filter(\n\t\t\t(t) =>\n\t\t\t\tBigInt(t.ticks) * BigInt(expectedTimescale) !==\n\t\t\t\tBigInt(expectedTicks) * BigInt(t.timescale),\n\t\t)\n\t\t.map(({ name, ticks }) => ({ name, ticks }));\n\tif (mismatches.length === 0) return { ok: true };\n\treturn { ok: false, expectedTicks, mismatches };\n}\n", "// Module-local ambient: `multicastFromEnv` reads `process.env` when present\n// (Node, FFI host) and falls back to `{}` in browser contexts. The package\n// targets both, so we don't pull in `@types/node` org-wide \u2014 declaring the\n// minimal surface we touch keeps the dependency graph tight and the\n// published .d.ts free of node-isms.\ndeclare const process: { env: Record<string, string | undefined> } | undefined;\n\n/**\n * Multicast catalog input contract \u2014 validator + builder.\n *\n * Per draft-ramadan-moq-multicast-00 \u00A73 + \u00A74.1, the publisher (FFmpeg,\n * cast bridge, GStreamer, OBS-bridge) populates `multicast.endpoints[]`\n * in the catalog when its output includes a UDP multicast destination.\n *\n * This module is the TypeScript-side validator over a versioned input\n * shape. Sister-language validators (FFmpeg C, cast Rust) target the\n * same invariants \u2014 eventually via a shared JSON Schema + fixtures\n * (BLO-3341 follow-up); today parity is enforced by code review against\n * 1:1 ports. Each publisher's integration MUST also assert that the\n * declared `(sourceAddress, groupAddress, port)` matches what it\n * actually emits \u2014 that's a separate runtime check the helper cannot\n * perform.\n *\n * Validation invariants (per codex review of BLO-3323 v2):\n * - protocol === 'ssm' \u2192 sourceAddress REQUIRED\n * - protocol === 'amt' \u2192 relayAddress REQUIRED\n * - groupAddress is a valid IPv4 multicast group (224.0.0.0/4)\n * or IPv6 multicast group (ff00::/8)\n * - SSM groupAddress MUST be in 232.0.0.0/8 (IPv4) or ff3x::/32 (IPv6),\n * where the IPv6 form requires P=1 + T=1 (the `3` nibble) AND the\n * 16-bit reserved-zero second hextet per RFC 3306 \u00A74 / RFC 4607.\n * - port in 1..65535\n * - tracks non-empty; per-track packetId in 0..65535\n * - packetIds unique within an endpoint\n * - (sourceAddress, groupAddress, port) tuple unique across endpoints\n * - bandwidth (if present) > 0\n * - validUntil (if present) is a positive unix-ms timestamp\n *\n * @see draft-ramadan-moq-multicast-00 \u00A74.1\n * @see .planning/drafts/blo-3323-multicast-catalog-source-of-truth.md\n */\n\nimport {\n\ttype AmtDiscovery,\n\ttype MulticastCatalogConfig,\n\ttype MulticastEndpoint,\n\ttype MulticastProtocol,\n\ttype MulticastTrackObject,\n} from \"./carp-catalog.js\";\n\n/**\n * Strict typed input for one multicast endpoint. The publisher provides\n * this shape (env vars / CLI / config record), the helper validates and\n * lifts it into a `MulticastEndpoint` ready to embed in the catalog.\n */\nexport interface MulticastEndpointInput {\n\treadonly protocol: MulticastProtocol;\n\treadonly sourceAddress?: string;\n\treadonly groupAddress: string;\n\treadonly port: number;\n\t/** New emitters MUST use the object form (name + packetId). */\n\treadonly tracks: ReadonlyArray<MulticastTrackObject>;\n\treadonly relayAddress?: string;\n\t/** Aggregate bandwidth in bits per second. RECOMMENDED per draft \u00A74.1. */\n\treadonly bandwidth?: number;\n}\n\n/**\n * Strict typed input for the catalog-level multicast extension.\n *\n * `version` (monotonic) + `validUntil` (unix ms) support the catalog\n * freshness model: subscribers refetch before expiry, and publishers\n * MUST republish (with bumped `version`) on any endpoint mutation.\n */\nexport interface MulticastInput {\n\treadonly enabled?: boolean;\n\treadonly endpoints: ReadonlyArray<MulticastEndpointInput>;\n\treadonly amtFallback?: { readonly discovery: AmtDiscovery; readonly relayDns?: string };\n\treadonly version?: number;\n\treadonly validUntil?: number;\n}\n\nexport type MulticastInputErrorKind =\n\t| \"empty_endpoints\"\n\t| \"ssm_requires_source\"\n\t| \"amt_requires_relay\"\n\t| \"invalid_group_address\"\n\t| \"ssm_group_out_of_range\"\n\t| \"invalid_port\"\n\t| \"empty_tracks\"\n\t| \"invalid_track_name\"\n\t| \"invalid_packet_id\"\n\t| \"duplicate_packet_id\"\n\t| \"duplicate_endpoint_tuple\"\n\t| \"invalid_bandwidth\"\n\t| \"invalid_valid_until\"\n\t| \"invalid_version\"\n\t| \"invalid_env_shape\";\n\nexport class MulticastInputError extends Error {\n\tconstructor(\n\t\tpublic readonly kind: MulticastInputErrorKind,\n\t\tpublic readonly endpointIndex: number | null,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"MulticastInputError\";\n\t}\n}\n\n/**\n * Validate a typed publisher input and lift it into a\n * `MulticastCatalogConfig` ready for `catalog.multicast = \u2026`.\n *\n * Throws `MulticastInputError` on any invariant violation. Fail-fast\n * at config-load time, never at runtime \u2014 silent fallbacks hide bugs\n * upstream (per the project's \"no anti-patterns\" directive).\n */\nexport function multicastFromConfig(input: MulticastInput): MulticastCatalogConfig {\n\tif (input.endpoints.length === 0) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"empty_endpoints\",\n\t\t\tnull,\n\t\t\t\"endpoints[] must be non-empty \u2014 a multicast field with zero endpoints declares nothing\",\n\t\t);\n\t}\n\tif (input.version !== undefined && (!Number.isInteger(input.version) || input.version < 0)) {\n\t\tthrow new MulticastInputError(\"invalid_version\", null, `version must be a non-negative integer; got ${input.version}`);\n\t}\n\tif (input.validUntil !== undefined) {\n\t\tif (!Number.isFinite(input.validUntil) || input.validUntil <= 0) {\n\t\t\tthrow new MulticastInputError(\n\t\t\t\t\"invalid_valid_until\",\n\t\t\t\tnull,\n\t\t\t\t`validUntil must be a positive unix-ms timestamp; got ${input.validUntil}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tconst seenTuples = new Set<string>();\n\tconst endpoints: MulticastEndpoint[] = input.endpoints.map((ep, idx) => validateEndpoint(ep, idx, seenTuples));\n\n\tconst out: MulticastCatalogConfig = { endpoints };\n\tif (input.enabled !== undefined) out.enabled = input.enabled;\n\tif (input.amtFallback !== undefined) out.amtFallback = { ...input.amtFallback };\n\tif (input.version !== undefined) out.version = input.version;\n\tif (input.validUntil !== undefined) out.validUntil = input.validUntil;\n\treturn out;\n}\n\nfunction validateEndpoint(\n\tep: MulticastEndpointInput,\n\tidx: number,\n\tseenTuples: Set<string>,\n): MulticastEndpoint {\n\tif (ep.protocol === \"ssm\" && (!ep.sourceAddress || ep.sourceAddress.length === 0)) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"ssm_requires_source\",\n\t\t\tidx,\n\t\t\t`endpoint[${idx}] protocol=ssm requires sourceAddress`,\n\t\t);\n\t}\n\tif (ep.protocol === \"amt\" && (!ep.relayAddress || ep.relayAddress.length === 0)) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"amt_requires_relay\",\n\t\t\tidx,\n\t\t\t`endpoint[${idx}] protocol=amt requires relayAddress`,\n\t\t);\n\t}\n\n\tif (!isMulticastGroupAddress(ep.groupAddress)) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"invalid_group_address\",\n\t\t\tidx,\n\t\t\t`endpoint[${idx}] groupAddress \"${ep.groupAddress}\" is not a valid multicast address`,\n\t\t);\n\t}\n\tif (ep.protocol === \"ssm\" && !isSsmGroupAddress(ep.groupAddress)) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"ssm_group_out_of_range\",\n\t\t\tidx,\n\t\t\t`endpoint[${idx}] protocol=ssm requires groupAddress in 232.0.0.0/8 (IPv4) or ff3x::/32 (IPv6); got \"${ep.groupAddress}\"`,\n\t\t);\n\t}\n\n\tif (!Number.isInteger(ep.port) || ep.port < 1 || ep.port > 65535) {\n\t\tthrow new MulticastInputError(\"invalid_port\", idx, `endpoint[${idx}] port must be 1..65535; got ${ep.port}`);\n\t}\n\n\tif (ep.tracks.length === 0) {\n\t\tthrow new MulticastInputError(\"empty_tracks\", idx, `endpoint[${idx}] tracks must be non-empty`);\n\t}\n\n\tconst seenPids = new Set<number>();\n\tfor (const t of ep.tracks) {\n\t\tif (typeof t.name !== \"string\" || t.name.length === 0) {\n\t\t\tthrow new MulticastInputError(\n\t\t\t\t\"invalid_track_name\",\n\t\t\t\tidx,\n\t\t\t\t`endpoint[${idx}] track name must be a non-empty string`,\n\t\t\t);\n\t\t}\n\t\tif (!Number.isInteger(t.packetId) || t.packetId < 0 || t.packetId > 65535) {\n\t\t\tthrow new MulticastInputError(\n\t\t\t\t\"invalid_packet_id\",\n\t\t\t\tidx,\n\t\t\t\t`endpoint[${idx}] track \"${t.name}\" packetId must be 0..65535; got ${t.packetId}`,\n\t\t\t);\n\t\t}\n\t\tif (seenPids.has(t.packetId)) {\n\t\t\tthrow new MulticastInputError(\n\t\t\t\t\"duplicate_packet_id\",\n\t\t\t\tidx,\n\t\t\t\t`endpoint[${idx}] duplicate packetId ${t.packetId}`,\n\t\t\t);\n\t\t}\n\t\tseenPids.add(t.packetId);\n\t}\n\n\tif (ep.bandwidth !== undefined && (!Number.isFinite(ep.bandwidth) || ep.bandwidth <= 0)) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"invalid_bandwidth\",\n\t\t\tidx,\n\t\t\t`endpoint[${idx}] bandwidth must be positive; got ${ep.bandwidth}`,\n\t\t);\n\t}\n\n\tconst tupleKey = `${ep.sourceAddress ?? \"\"}|${ep.groupAddress}|${ep.port}`;\n\tif (seenTuples.has(tupleKey)) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"duplicate_endpoint_tuple\",\n\t\t\tidx,\n\t\t\t`endpoint[${idx}] duplicates (sourceAddress, groupAddress, port) tuple \"${tupleKey}\"`,\n\t\t);\n\t}\n\tseenTuples.add(tupleKey);\n\n\tconst out: MulticastEndpoint = {\n\t\tprotocol: ep.protocol,\n\t\tgroupAddress: ep.groupAddress,\n\t\tport: ep.port,\n\t\ttracks: ep.tracks.map((t) => ({ name: t.name, packetId: t.packetId })),\n\t};\n\tif (ep.sourceAddress !== undefined) out.sourceAddress = ep.sourceAddress;\n\tif (ep.relayAddress !== undefined) out.relayAddress = ep.relayAddress;\n\tif (ep.bandwidth !== undefined) out.bandwidth = ep.bandwidth;\n\treturn out;\n}\n\nfunction isMulticastGroupAddress(addr: string): boolean {\n\tif (addr.includes(\":\")) {\n\t\t// IPv6: ff00::/8. Prefix-only check; full IPv6 syntax validation\n\t\t// is delegated to the OS bind() call downstream.\n\t\treturn /^ff[0-9a-f]{2}:/i.test(addr);\n\t}\n\tconst m = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(addr);\n\tif (!m) return false;\n\tfor (let i = 1; i <= 4; i++) {\n\t\tconst v = Number(m[i]);\n\t\tif (!Number.isInteger(v) || v < 0 || v > 255) return false;\n\t}\n\tconst first = Number(m[1]);\n\treturn first >= 224 && first <= 239;\n}\n\nfunction isSsmGroupAddress(addr: string): boolean {\n\tif (addr.includes(\":\")) {\n\t\t// SSM IPv6: ff3x::/32 per RFC 3306 \u00A74. Requires P=1 + T=1 (the `3`\n\t\t// nibble) AND the second 16-bit hextet to be all-zero (RFC 3306's\n\t\t// \"prefix length = 0\" \u2192 next 32 bits are 0). The regex matches\n\t\t// `ff3?:0000:` collapsed forms (`ff3e::`, `ff3e:0::`, `ff3e:0:`,\n\t\t// `ff3e:0000:`) and rejects `ff3e:beef::1` etc.\n\t\treturn /^ff3[0-9a-f]:0{0,4}:/i.test(addr);\n\t}\n\tconst m = /^(\\d{1,3})\\./.exec(addr);\n\treturn m !== null && Number(m[1]) === 232;\n}\n\n// Env-var helper (BLO-3337): read MOQCAST_MULTICAST_* keys and route\n// through multicastFromConfig so all per-endpoint invariants apply.\n\n/**\n * Read `MOQCAST_MULTICAST_*` env vars and return a\n * `MulticastCatalogConfig`. Returns `undefined` when no per-endpoint\n * key is set.\n *\n * Env contract \u2014 per-endpoint keys take an optional non-negative index:\n *\n * MOQCAST_MULTICAST[_<idx>]_PROTOCOL=ssm\n * MOQCAST_MULTICAST[_<idx>]_GROUP=232.1.1.1\n * MOQCAST_MULTICAST[_<idx>]_PORT=5004\n * MOQCAST_MULTICAST[_<idx>]_SOURCE=10.0.0.20 (required for ssm)\n * MOQCAST_MULTICAST[_<idx>]_RELAY=amt.example.com (required for amt)\n * MOQCAST_MULTICAST[_<idx>]_TRACKS=video:1,audio:3\n * MOQCAST_MULTICAST[_<idx>]_BANDWIDTH=8000000 (optional, bps)\n *\n * MOQCAST_MULTICAST_AMT_DISCOVERY=driad (catalog-level)\n * MOQCAST_MULTICAST_AMT_RELAY_DNS=relay.example.com (catalog-level)\n *\n * The unindexed form (`MOQCAST_MULTICAST_GROUP`) is exactly index 0;\n * mixing it with explicit `_0_` keys is rejected as ambiguous. Indices\n * need not be contiguous; observed indices are sorted ascending.\n */\nexport function multicastFromEnv(\n\tenv: Record<string, string | undefined> = ((globalThis as { process?: { env?: Record<string, string | undefined> } })\n\t\t.process?.env ?? {}) as Record<string, string | undefined>,\n): MulticastCatalogConfig | undefined {\n\tconst indices = collectMulticastIndices(env);\n\tif (indices.length === 0) {\n\t\t// Catalog-level AMT keys without per-endpoint keys is a\n\t\t// misconfiguration, not a no-op: `MOQCAST_MULTICAST_AMT_DISCOVERY`\n\t\t// only makes sense alongside endpoints to fall back FOR. Throw\n\t\t// loudly instead of silently dropping the request \u2014 a publisher\n\t\t// that meant to declare AMT fallback shouldn't have its config\n\t\t// disappear without any signal.\n\t\tconst strayCatalog = env[\"MOQCAST_MULTICAST_AMT_DISCOVERY\"] !== undefined\n\t\t\t|| env[\"MOQCAST_MULTICAST_AMT_RELAY_DNS\"] !== undefined;\n\t\tif (strayCatalog) {\n\t\t\tthrow new MulticastInputError(\n\t\t\t\t\"invalid_env_shape\",\n\t\t\t\tnull,\n\t\t\t\t\"MOQCAST_MULTICAST_AMT_* env keys are set but no per-endpoint keys \" +\n\t\t\t\t\t\"(MOQCAST_MULTICAST_GROUP / _PROTOCOL / _PORT / _TRACKS) were found. \" +\n\t\t\t\t\t\"AMT discovery is a catalog-level fallback for endpoints; declaring it \" +\n\t\t\t\t\t\"with zero endpoints is a misconfiguration.\",\n\t\t\t);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tconst endpoints: MulticastEndpointInput[] = indices.map((idx) => readEndpointAt(env, idx));\n\n\tconst input: MulticastInput = {\n\t\tendpoints,\n\t\t...(env[\"MOQCAST_MULTICAST_AMT_DISCOVERY\"]\n\t\t\t? {\n\t\t\t\t\tamtFallback: {\n\t\t\t\t\t\tdiscovery: env[\"MOQCAST_MULTICAST_AMT_DISCOVERY\"] as AmtDiscovery,\n\t\t\t\t\t\t...(env[\"MOQCAST_MULTICAST_AMT_RELAY_DNS\"]\n\t\t\t\t\t\t\t? { relayDns: env[\"MOQCAST_MULTICAST_AMT_RELAY_DNS\"] }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t};\n\treturn multicastFromConfig(input);\n}\n\nconst ENV_PER_FIELD = [\"PROTOCOL\", \"GROUP\", \"PORT\", \"SOURCE\", \"RELAY\", \"TRACKS\", \"BANDWIDTH\"];\nconst ENV_CATALOG_LEVEL = new Set([\"AMT_DISCOVERY\", \"AMT_RELAY_DNS\"]);\n\nfunction collectMulticastIndices(env: Record<string, string | undefined>): number[] {\n\tconst indices = new Set<number>();\n\tlet hasUnindexed = false;\n\tlet hasZeroIndexed = false;\n\n\tfor (const [key, val] of Object.entries(env)) {\n\t\tif (val === undefined) continue;\n\t\tif (!key.startsWith(\"MOQCAST_MULTICAST_\")) continue;\n\t\tconst suffix = key.slice(\"MOQCAST_MULTICAST_\".length);\n\t\tif (ENV_CATALOG_LEVEL.has(suffix)) continue;\n\n\t\tconst m = /^(\\d+)_(.+)$/.exec(suffix);\n\t\tif (m) {\n\t\t\tconst idx = Number(m[1]);\n\t\t\tconst field = m[2];\n\t\t\tif (!ENV_PER_FIELD.includes(field)) continue;\n\t\t\tif (idx === 0) hasZeroIndexed = true;\n\t\t\tindices.add(idx);\n\t\t} else if (ENV_PER_FIELD.includes(suffix)) {\n\t\t\thasUnindexed = true;\n\t\t\tindices.add(0);\n\t\t}\n\t}\n\n\tif (hasUnindexed && hasZeroIndexed) {\n\t\tthrow new MulticastInputError(\n\t\t\t\"invalid_env_shape\",\n\t\t\tnull,\n\t\t\t\"MOQCAST_MULTICAST_* env contract: cannot mix unindexed keys (MOQCAST_MULTICAST_GROUP) \" +\n\t\t\t\t\"with explicitly-indexed keys at index 0 (MOQCAST_MULTICAST_0_GROUP) \u2014 they refer to \" +\n\t\t\t\t\"the same endpoint. Use one form consistently.\",\n\t\t);\n\t}\n\n\treturn [...indices].sort((a, b) => a - b);\n}\n\nfunction readEndpointAt(env: Record<string, string | undefined>, idx: number): MulticastEndpointInput {\n\tconst lookup = (field: string): string | undefined => {\n\t\tif (idx === 0) {\n\t\t\treturn env[`MOQCAST_MULTICAST_${field}`] ?? env[`MOQCAST_MULTICAST_0_${field}`];\n\t\t}\n\t\treturn env[`MOQCAST_MULTICAST_${idx}_${field}`];\n\t};\n\n\tconst portStr = lookup(\"PORT\");\n\tconst port = portStr !== undefined ? Number(portStr) : NaN;\n\tconst tracks = parseTracksEnv(lookup(\"TRACKS\"), idx);\n\tconst bandwidthStr = lookup(\"BANDWIDTH\");\n\tconst bandwidth = bandwidthStr !== undefined ? Number(bandwidthStr) : undefined;\n\tconst source = lookup(\"SOURCE\");\n\tconst relay = lookup(\"RELAY\");\n\n\treturn {\n\t\tprotocol: lookup(\"PROTOCOL\") as MulticastProtocol,\n\t\tgroupAddress: lookup(\"GROUP\") ?? \"\",\n\t\tport,\n\t\ttracks,\n\t\t...(source ? { sourceAddress: source } : {}),\n\t\t...(relay ? { relayAddress: relay } : {}),\n\t\t...(bandwidth !== undefined ? { bandwidth } : {}),\n\t};\n}\n\nfunction parseTracksEnv(raw: string | undefined, endpointIdx: number): MulticastTrackObject[] {\n\tif (!raw || raw.length === 0) return [];\n\treturn raw.split(\",\").map((entry, i) => {\n\t\tconst colon = entry.lastIndexOf(\":\");\n\t\tif (colon < 0) {\n\t\t\tthrow new MulticastInputError(\n\t\t\t\t\"invalid_track_name\",\n\t\t\t\tendpointIdx,\n\t\t\t\t`endpoint[${endpointIdx}] TRACKS[${i}] missing \":\" \u2014 expected \"name:packetId\", got ${JSON.stringify(entry)}`,\n\t\t\t);\n\t\t}\n\t\tconst name = entry.slice(0, colon).trim();\n\t\tconst idStr = entry.slice(colon + 1).trim();\n\t\tconst packetId = Number(idStr);\n\t\tif (!name || idStr.length === 0 || !Number.isFinite(packetId) || !Number.isInteger(packetId)) {\n\t\t\tthrow new MulticastInputError(\n\t\t\t\t\"invalid_packet_id\",\n\t\t\t\tendpointIdx,\n\t\t\t\t`endpoint[${endpointIdx}] TRACKS[${i}] entry malformed (need \"name:integer\"): ${JSON.stringify(entry)}`,\n\t\t\t);\n\t\t}\n\t\treturn { name, packetId };\n\t});\n}\n", "/**\n * Phase 14 catalog-override hook (test harness only).\n *\n * When `globalThis.__PHASE14_CATALOG_OVERRIDE` is set (injected by the\n * 7point test via `page.addInitScript` \u2014 see\n * packages/moq-server/tests/7point-validation.test.mjs `applyPhase14CatalogOverride`)\n * the override's `tracks[]` are merged into whatever catalog the MoQ-delivered\n * JSON produced:\n *\n * - Dedupe by `track.name`.\n * - When both sides carry a track with the same name, the override's\n * `fec` config wins \u2014 this is how the 7point harness steers multi-track\n * tests onto the Phase 14 `video/base` + `video/delta` + `video/repair`\n * switching set without asking the publisher to change its catalog.\n * - Tracks present only in the override are appended as-is.\n * - Tracks present only in the MoQ catalog are preserved unchanged.\n *\n * This module is deliberately player-agnostic. It exposes:\n *\n * getPhase14CatalogOverride() \u2014 read + shape-check the global.\n * mergeOverrideIntoHangRenditions() \u2014 merge into a `@moq/hang`\n * Catalog.Root `video.renditions` /\n * `audio.renditions` dict. Used by\n * hang-mmt-fec/js/watch/src/broadcast.ts.\n * mergeOverrideIntoCmsfTracks() \u2014 merge into moqtail's CMSF-flat\n * `tracks[]` array. Used by\n * moqtail-private CMSFCatalog.from().\n *\n * Production builds never set the global, so all three helpers are no-ops at\n * runtime unless the test harness opts in.\n */\n\nimport type { FecAlgorithm, FecCatalogConfig } from \"./carp-catalog.js\";\n\n// ---------------------------------------------------------------------------\n// Override fixture shape (IETF draft-ietf-moq-catalogformat-01)\n// ---------------------------------------------------------------------------\n\n/**\n * A single override track entry \u2014 mirrors the fixture at\n * `packages/moq-server/tests/fixtures/multi-track-catalog.json`.\n *\n * Kept intentionally permissive: the canonical fixture carries `name`,\n * `altGroup`, `framerate`, and `fec`, but future 7point scenarios may extend\n * with additional hints without wanting this module to gate-keep them.\n */\nexport interface Phase14OverrideTrack {\n\tname: string;\n\t/** CMSF packaging kind. Optional per track \u2014 when absent, the merge\n\t * derives it from the override's top-level `streamingFormat`. */\n\tpackaging?: \"mmtp\" | \"cmaf\" | \"loc\" | \"timeline\";\n\t/** Required when packaging is `mmtp`; the current publisher emits MFU mode. */\n\tmmtpMode?: \"mpu\" | \"mfu\";\n\t/** Required when packaging is `mmtp`; copied into synthesized CMSF tracks. */\n\ttimescale?: number;\n\t/** Required when packaging is `mmtp`; copied into synthesized CMSF tracks. */\n\tgroupDurationMs?: number;\n\t/** Codec string. Optional per track \u2014 when absent, the merge inherits\n\t * from the existing video/audio rendition in the MoQ catalog (the\n\t * publisher's declaration). Throws only when neither the override nor\n\t * any existing rendition supplies one. */\n\tcodec?: string;\n\trole?: \"video\" | \"audio\" | \"timeline\";\n\taltGroup?: number;\n\tframerate?: number;\n\tfec?: Partial<FecCatalogConfig> & { algorithm: FecAlgorithm };\n}\n\n/**\n * The shape the 7point harness writes to `globalThis.__PHASE14_CATALOG_OVERRIDE`.\n * Extra top-level fields (`version`, `streamingFormat`, ...) are ignored \u2014\n * the merge only consumes `tracks[]`.\n */\nexport interface Phase14CatalogOverride {\n\tversion?: string;\n\tstreamingFormat?: string;\n\ttracks: Phase14OverrideTrack[];\n}\n\n// ---------------------------------------------------------------------------\n// Global reader\n// ---------------------------------------------------------------------------\n\n/**\n * Read `globalThis.__PHASE14_CATALOG_OVERRIDE` and return it only when it has\n * a well-formed `tracks[]` array. Malformed values log + return `null` rather\n * than throw so a broken fixture never kills the MoQ path for a real stream.\n */\nexport function getPhase14CatalogOverride(): Phase14CatalogOverride | null {\n\t// `globalThis` is defined in workers, service workers, and the main window;\n\t// it's the portable way to reach the test-injected hook from any context.\n\tconst g = globalThis as Record<string, unknown>;\n\tconst raw = g[\"__PHASE14_CATALOG_OVERRIDE\"];\n\tif (raw === undefined || raw === null) return null;\n\tif (typeof raw !== \"object\") {\n\t\tconsole.warn(\"[phase14-catalog-override] hook is not an object; ignoring\");\n\t\treturn null;\n\t}\n\tconst obj = raw as Record<string, unknown>;\n\tconst tracks = obj[\"tracks\"];\n\tif (!Array.isArray(tracks)) {\n\t\tconsole.warn(\"[phase14-catalog-override] hook.tracks is not an array; ignoring\");\n\t\treturn null;\n\t}\n\t// Shallow filter: keep only entries that at least have a string name.\n\t// Each consumer (hang or CMSF merge) validates shape further.\n\tconst filtered: Phase14OverrideTrack[] = [];\n\tfor (const t of tracks) {\n\t\tif (typeof t === \"object\" && t !== null) {\n\t\t\tconst entry = t as Record<string, unknown>;\n\t\t\tif (typeof entry[\"name\"] === \"string\" && (entry[\"name\"] as string).length > 0) {\n\t\t\t\tfiltered.push(entry as unknown as Phase14OverrideTrack);\n\t\t\t}\n\t\t}\n\t}\n\tif (filtered.length === 0) return null;\n\treturn {\n\t\tversion: typeof obj[\"version\"] === \"string\" ? (obj[\"version\"] as string) : undefined,\n\t\tstreamingFormat:\n\t\t\ttypeof obj[\"streamingFormat\"] === \"string\" ? (obj[\"streamingFormat\"] as string) : undefined,\n\t\ttracks: filtered,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Hang renditions merge (moq-lib / hang-mmt-fec)\n// ---------------------------------------------------------------------------\n\n/**\n * Merge override tracks into a `@moq/hang` Catalog.Root-like object. The\n * caller passes the already-parsed Catalog.Root (mutates in place and also\n * returns it for convenience). No-op when `override` is null.\n *\n * Dedupe rule: override wins for `fec` config and `framerate`. Everything\n * else (codec, container kind) is preserved from the MoQ-delivered rendition\n * when it exists, or synthesised from the override when it does not.\n *\n * Typed `any` here rather than narrow-matching `Catalog.Root` because the\n * upstream `@moq/hang` schema is branded (u53) and the container `kind` union\n * varies by version (`loc` kind appears in hang-mmt-fec's fork). Matching\n * that surface exactly would over-couple this module to a single upstream\n * revision; the runtime shape we rely on (`.video.renditions` dict mutation)\n * is stable.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function mergeOverrideIntoHangRenditions<T extends Record<string, any>>(\n\tcatalog: T,\n\toverride: Phase14CatalogOverride | null,\n): T {\n\tif (!override) return catalog;\n\n\tfor (const track of override.tracks) {\n\t\t// Default role inference: names without an explicit role field are\n\t\t// treated as video unless the name suffix says audio.\n\t\tconst role: \"video\" | \"audio\" =\n\t\t\ttrack.role === \"audio\" || track.name.startsWith(\"audio\") ? \"audio\" : \"video\";\n\n\t\tconst bagKey = role === \"audio\" ? \"audio\" : \"video\";\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst bag: { renditions: Record<string, any>; priority?: number } =\n\t\t\tcatalog[bagKey] ?? { renditions: {}, priority: 1 };\n\t\tconst existing = bag.renditions[track.name];\n\n\t\t// Merge: override fills gaps, does NOT overwrite existing fec/framerate.\n\t\t// The publisher's real catalog is ground truth for wire parameters:\n\t\t// if the existing rendition already has a matching fec config, that's\n\t\t// what the publisher's packets actually carry. Overwriting it with a\n\t\t// fixture value (e.g. Phase 14 target K=768 when publisher emits K=4)\n\t\t// causes the FEC manager to wait for symbols that never arrive.\n\t\t// Only when the rendition is brand-new (no existing fec) do we seed\n\t\t// from the fixture.\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst merged: Record<string, any> = {\n\t\t\t...(existing ?? {}),\n\t\t\tcontainer: existing?.container ?? { kind: \"mmtp-fec\" },\n\t\t\tcodec: existing?.codec ?? \"\",\n\t\t};\n\t\tif (existing?.framerate === undefined && track.framerate !== undefined) {\n\t\t\tmerged[\"framerate\"] = track.framerate;\n\t\t}\n\t\tif (existing?.fec === undefined && track.fec !== undefined) {\n\t\t\tmerged[\"fec\"] = track.fec;\n\t\t}\n\n\t\tbag.renditions[track.name] = merged;\n\t\t// Reassign so a freshly-created bag is actually reachable on the\n\t\t// catalog when the MoQ side was missing video/audio entirely.\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t(catalog as any)[bagKey] = bag;\n\t}\n\treturn catalog;\n}\n\n// ---------------------------------------------------------------------------\n// CMSF tracks merge (moqtail-private)\n// ---------------------------------------------------------------------------\n\n/**\n * Shape-minimal description of a CMSF track input (pre-validation JSON, not\n * the parsed `CMSFTrack`). Keeps this module free of a hard dependency on\n * moqtail-ts.\n */\ntype CmsfTrackJson = {\n\tname: string;\n\tpackaging?: string;\n\tmmtpMode?: string;\n\ttimescale?: number;\n\tgroupDurationMs?: number;\n\trole?: string;\n\tcodec?: string;\n\tframerate?: number;\n\tfec?: unknown;\n\t[k: string]: unknown;\n};\n\nfunction isPositiveInteger(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value > 0;\n}\n\n/**\n * Merge override tracks into a raw CMSF `tracks[]` JSON array (as produced\n * by `JSON.parse(catalog.json)` inside `CMSFCatalog.from`).\n *\n * Dedupe by `name`. For name collisions, publisher-emitted `fec` + `framerate`\n * are preserved (the publisher is ground truth for wire parameters). For new\n * track names, missing `packaging` is derived from the override's top-level\n * `streamingFormat`, and missing `codec` is inherited from any existing video\n * track in the catalog. Throws on bad init when neither source supplies them.\n */\nexport function mergeOverrideIntoCmsfTracks(\n\ttracks: CmsfTrackJson[],\n\toverride: Phase14CatalogOverride | null,\n): CmsfTrackJson[] {\n\tif (!override) return tracks;\n\n\tconst byName = new Map<string, CmsfTrackJson>();\n\tfor (const t of tracks) {\n\t\tif (typeof t?.name === \"string\") byName.set(t.name, t);\n\t}\n\n\t// Catalog-derived fallbacks (NOT magic numbers) \u2014 inherited from existing\n\t// publisher tracks. Used only when the override track doesn't carry them.\n\tconst derivedPackaging = override.streamingFormat;\n\tconst existingVideoTrack = tracks.find(\n\t\t(t) => t.role === \"video\" && typeof t.codec === \"string\" && (t.codec as string).length > 0,\n\t);\n\tconst existingAudioTrack = tracks.find(\n\t\t(t) => t.role === \"audio\" && typeof t.codec === \"string\" && (t.codec as string).length > 0,\n\t);\n\n\tfor (const ot of override.tracks) {\n\t\tconst existing = byName.get(ot.name);\n\t\tif (existing !== undefined) {\n\t\t\t// Preserve publisher-emitted fec + framerate \u2014 those describe what\n\t\t\t// is actually on the wire. Overwriting with fixture values causes\n\t\t\t// the player's FEC manager to wait for symbols that never arrive\n\t\t\t// when the fixture's K/P don't match the publisher's.\n\t\t\tif (existing.fec === undefined && ot.fec !== undefined) existing.fec = ot.fec;\n\t\t\tif (existing.framerate === undefined && ot.framerate !== undefined) {\n\t\t\t\texisting.framerate = ot.framerate;\n\t\t\t}\n\t\t\tif (ot.role !== undefined && existing.role === undefined) existing.role = ot.role;\n\t\t\tif (existing.packaging === \"mmtp\") {\n\t\t\t\tconst mode = existing.mmtpMode ?? ot.mmtpMode;\n\t\t\t\tif (mode !== \"mpu\" && mode !== \"mfu\") {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has packaging='mmtp' but no valid mmtpMode`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\texisting.mmtpMode = mode;\n\t\t\t\tif (existing.timescale === undefined && ot.timescale !== undefined) existing.timescale = ot.timescale;\n\t\t\t\tif (existing.groupDurationMs === undefined && ot.groupDurationMs !== undefined) {\n\t\t\t\t\texisting.groupDurationMs = ot.groupDurationMs;\n\t\t\t\t}\n\t\t\t\tif (!isPositiveInteger(existing.timescale)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has packaging='mmtp' but no valid timescale`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!isPositiveInteger(existing.groupDurationMs)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has packaging='mmtp' but no valid groupDurationMs`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst packaging = ot.packaging ?? derivedPackaging;\n\t\t\tif (typeof packaging !== \"string\" || packaging.length === 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has no packaging \u2014 supply per-track or set top-level streamingFormat`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Role inference: repair tracks (ending in /repair) stay role-less\n\t\t\t// per CMSF convention; others default to video.\n\t\t\tconst role = ot.role ?? (ot.name.endsWith(\"/repair\") ? undefined : \"video\");\n\t\t\tconst inheritedCodec =\n\t\t\t\trole === \"audio\" ? existingAudioTrack?.codec : existingVideoTrack?.codec;\n\t\t\tconst codec = ot.codec ?? inheritedCodec;\n\t\t\tif (packaging !== \"timeline\" && (typeof codec !== \"string\" || codec.length === 0)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has no codec (packaging='${packaging}') \u2014 supply per-track or ensure the publisher catalog has a ${role ?? \"video\"} track to inherit from`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst mmtpMode = ot.mmtpMode;\n\t\t\tif (packaging === \"mmtp\" && mmtpMode !== \"mpu\" && mmtpMode !== \"mfu\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has packaging='mmtp' but no valid mmtpMode`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (packaging === \"mmtp\" && !isPositiveInteger(ot.timescale)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has packaging='mmtp' but no valid timescale`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (packaging === \"mmtp\" && !isPositiveInteger(ot.groupDurationMs)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[phase14-catalog-override] track \"${ot.name}\" has packaging='mmtp' but no valid groupDurationMs`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst entry: CmsfTrackJson = {\n\t\t\t\tname: ot.name,\n\t\t\t\tpackaging,\n\t\t\t\tcodec: codec as string,\n\t\t\t};\n\t\t\tif (mmtpMode !== undefined) entry.mmtpMode = mmtpMode;\n\t\t\tif (ot.timescale !== undefined) entry.timescale = ot.timescale;\n\t\t\tif (ot.groupDurationMs !== undefined) entry.groupDurationMs = ot.groupDurationMs;\n\t\t\tif (role !== undefined) entry.role = role;\n\t\t\tif (ot.framerate !== undefined) entry.framerate = ot.framerate;\n\t\t\tif (ot.fec !== undefined) entry.fec = ot.fec;\n\t\t\ttracks.push(entry);\n\t\t\tbyName.set(ot.name, entry);\n\t\t}\n\t}\n\treturn tracks;\n}\n", "/**\n * @mmt/container - Container-aware multicast MMTP unwrapper\n *\n * Dispatches raw multicast MMTP packets into typed media or Annex C repair\n * frames. Handles FEC source payload ID extraction and repair packet parsing.\n *\n * @packageDocumentation\n */\n\nimport {\n type MMTPHeader,\n type MFUExtraction,\n parseMMTPHeader,\n parseMoqRepairFrame,\n extractMFUPayload,\n hasSourceFecPayloadId,\n MMTP_HEADER_SIZE,\n PACKET_TYPE_REPAIR,\n} from '@blockcast/mmt-base'\n\n/** The immutable reader contract selected by a multicast consumer. */\nexport interface MulticastContainerContract {\n version: 1\n kind: 'mmtp'\n}\n\nexport type MulticastContainerErrorCode =\n | 'unsupported_version'\n | 'unsupported_kind'\n | 'ambiguous_kind'\n | 'malformed_selector'\n\n/** Versioned, fail-closed contract selection error. */\nexport class MulticastContainerContractError extends TypeError {\n readonly version = 1\n readonly code: MulticastContainerErrorCode\n\n constructor(code: MulticastContainerErrorCode, detail: string) {\n super(`multicast-container/v1/${code}: ${detail}`)\n this.name = 'MulticastContainerContractError'\n this.code = code\n }\n}\n\n/** Validate the only reader currently frozen by the container contract. */\nexport function validateMulticastContainerContract(\n contract: unknown,\n): MulticastContainerContract {\n if (contract === null || typeof contract !== 'object') {\n throw new MulticastContainerContractError('malformed_selector', 'selector is required')\n }\n\n const value = contract as Record<string, unknown>\n if (value['version'] !== 1) {\n throw new MulticastContainerContractError('unsupported_version', 'only version 1 is supported')\n }\n if (value['kind'] === undefined || value['kind'] === null || value['kind'] === '') {\n throw new MulticastContainerContractError('ambiguous_kind', 'container kind is required')\n }\n if (value['kind'] !== 'mmtp') {\n throw new MulticastContainerContractError('unsupported_kind', `reader is not implemented for ${String(value['kind'])}`)\n }\n return { version: 1, kind: 'mmtp' }\n}\n\n/** Source FEC Payload ID \u2014 flat 32-bit SS_ID per ISO 23008-1 \u00A7C.5.2 */\nexport interface FecPayloadId {\n ssId: number\n}\n\n/** Discriminated union of unwrapped multicast frames */\nexport type MulticastFrame =\n | { kind: 'mmtp'; header: MMTPHeader; mfu: MFUExtraction; fecId: FecPayloadId | null }\n | {\n kind: 'repair'\n header: MMTPHeader\n ssStart: number\n rsbLength: number\n rsId: number\n ssbLength: number\n data: Uint8Array\n }\n\n/**\n * Unwrap a raw MMTP multicast packet into a typed MulticastFrame.\n *\n * This function handles MMTP-framed multicast only (container 0x01). The\n * versioned contract is validated before any packet bytes are parsed, so an\n * unsupported version or container kind fails closed rather than being\n * reinterpreted under a reader that does not match the wire format.\n *\n * @param packet - Raw MMTP packet bytes from multicast UDP\n * @param contract - Required `multicast-container/v1` selector\n * @throws {MulticastContainerContractError} if the selector is absent or unsupported\n * @returns Parsed frame, or null if the packet is too small / malformed\n */\nexport function unwrapMulticastPacket(\n packet: Uint8Array,\n contract: unknown,\n): MulticastFrame | null {\n validateMulticastContainerContract(contract)\n if (packet.length < MMTP_HEADER_SIZE) return null\n\n let header: MMTPHeader\n try {\n header = parseMMTPHeader(packet)\n } catch {\n return null\n }\n\n if (header.packetType === PACKET_TYPE_REPAIR || header.fecType >= 2) {\n const repair = parseMoqRepairFrame(packet)\n if (!repair) return null\n return {\n kind: 'repair',\n header,\n ssStart: repair.ssStart,\n rsbLength: repair.rsbLength,\n rsId: repair.rsId,\n ssbLength: repair.ssbLength,\n data: repair.symbolData,\n }\n }\n\n const hasFec = hasSourceFecPayloadId(packet)\n\n // MMTP source packet \u2014 extract MFU payload\n const mfu = extractMFUPayload(packet)\n if (!mfu) return null\n const fecId = hasFec ? mfu.fecPayloadId : null\n return { kind: 'mmtp', header, mfu, fecId }\n}\n", "/**\n * Container Timestamp Codec\n *\n * Encodes and decodes timestamps across container formats:\n * - \"legacy\": QUIC VarInt encoded (variable length 1-8 bytes)\n * - \"raw\": Fixed 8-byte BigUint64\n * - \"mmtp\" / \"mmtp-fec\": 12-byte MMTP header with embedded timestamp\n * - \"loc\": LOC extension header encoding\n * - \"fmp4\": Not yet implemented\n */\nimport { extractNALUnit, extractTimestamp } from \"@blockcast/mmt-base\";\n\nexport type ContainerFormat = \"legacy\" | \"raw\" | \"mmtp\" | \"mmtp-fec\" | \"loc\" | \"fmp4\";\n\nconst DEFAULT_CONTAINER: ContainerFormat = \"legacy\";\n\n/** Encodes a timestamp in microseconds according to the specified container format. */\nexport function encodeTimestamp(timestamp: number, container: ContainerFormat = DEFAULT_CONTAINER): Uint8Array {\n\tswitch (container) {\n\t\tcase \"legacy\":\n\t\t\treturn encodeVarInt(timestamp);\n\t\tcase \"raw\":\n\t\t\treturn encodeU64(timestamp);\n\t\tcase \"mmtp\":\n\t\tcase \"mmtp-fec\":\n\t\t\treturn encodeU64(timestamp);\n\t\tcase \"loc\":\n\t\t\treturn encodeLocTimestamp(timestamp);\n\t\tcase \"fmp4\":\n\t\t\tthrow new Error(\"fmp4 container not yet implemented\");\n\t}\n}\n\n/**\n * Decodes a timestamp from a buffer according to the specified container format.\n * @returns [timestamp in microseconds, remaining buffer after timestamp]\n */\nexport function decodeTimestamp(buffer: Uint8Array, container: ContainerFormat = DEFAULT_CONTAINER): [number, Uint8Array] {\n\tswitch (container) {\n\t\tcase \"legacy\": {\n\t\t\tconst [value, remaining] = decodeVarInt(buffer);\n\t\t\treturn [value, remaining];\n\t\t}\n\t\tcase \"raw\": {\n\t\t\tconst [value, remaining] = decodeU64(buffer);\n\t\t\treturn [value, remaining];\n\t\t}\n\t\tcase \"mmtp\":\n\t\tcase \"mmtp-fec\": {\n\t\t\tconst timestamp = extractTimestamp(buffer);\n\t\t\tconst payload = extractNALUnit(buffer);\n\t\t\treturn [Number(timestamp), payload];\n\t\t}\n\t\tcase \"loc\": {\n\t\t\tconst [ts, payload] = decodeLocTimestamp(buffer);\n\t\t\treturn [ts, payload];\n\t\t}\n\t\tcase \"fmp4\":\n\t\t\tthrow new Error(\"fmp4 container not yet implemented\");\n\t}\n}\n\n/** Gets the size in bytes of an encoded timestamp for the given container format. */\nexport function getTimestampSize(container: ContainerFormat = DEFAULT_CONTAINER): number {\n\tswitch (container) {\n\t\tcase \"legacy\":\n\t\t\treturn 8;\n\t\tcase \"raw\":\n\t\t\treturn 8;\n\t\tcase \"mmtp\":\n\t\tcase \"mmtp-fec\":\n\t\t\treturn 12;\n\t\tcase \"loc\":\n\t\t\t// [ExtID=2][ts][ExtID=4][kf]: two 1-byte IDs + a varint timestamp\n\t\t\t// (<=8 bytes) + a 1-byte keyframe flag.\n\t\t\treturn 11;\n\t\tcase \"fmp4\":\n\t\t\tthrow new Error(\"fmp4 container not yet implemented\");\n\t}\n}\n\n// LEGACY VARINT IMPLEMENTATION\nconst MAX_U6 = 2 ** 6 - 1;\nconst MAX_U14 = 2 ** 14 - 1;\nconst MAX_U30 = 2 ** 30 - 1;\nconst MAX_U53 = Number.MAX_SAFE_INTEGER;\n\nfunction decodeVarInt(buf: Uint8Array): [number, Uint8Array] {\n\tconst size = 1 << ((buf[0] & 0xc0) >> 6);\n\tconst view = new DataView(buf.buffer, buf.byteOffset, size);\n\tconst remain = new Uint8Array(buf.buffer, buf.byteOffset + size, buf.byteLength - size);\n\tlet v: number;\n\tif (size === 1) {\n\t\tv = buf[0] & 0x3f;\n\t} else if (size === 2) {\n\t\tv = view.getUint16(0) & 0x3fff;\n\t} else if (size === 4) {\n\t\tv = view.getUint32(0) & 0x3fffffff;\n\t} else if (size === 8) {\n\t\tv = Number(view.getBigUint64(0) & 0x3fffffffffffffffn);\n\t} else {\n\t\tthrow new Error(\"impossible\");\n\t}\n\treturn [v, remain];\n}\n\nfunction encodeVarInt(v: number): Uint8Array {\n\tif (v > MAX_U53) throw new RangeError(\"value too large for varint\");\n\tlet size: 1 | 2 | 4 | 8;\n\tif (v <= MAX_U6) {\n\t\tsize = 1;\n\t} else if (v <= MAX_U14) {\n\t\tsize = 2;\n\t} else if (v <= MAX_U30) {\n\t\tsize = 4;\n\t} else {\n\t\tsize = 8;\n\t}\n\tconst arr = new Uint8Array(size);\n\tconst view = new DataView(arr.buffer, arr.byteOffset, size);\n\tconst prefixMap: Record<1 | 2 | 4 | 8, number> = { 1: 0b00, 2: 0b01, 4: 0b10, 8: 0b11 };\n\tconst prefix = prefixMap[size];\n\tif (size === 1) {\n\t\tview.setUint8(0, (prefix << 6) | v);\n\t} else if (size === 2) {\n\t\tview.setUint16(0, (prefix << 14) | v);\n\t} else if (size === 4) {\n\t\tview.setUint32(0, (prefix << 30) | v);\n\t} else if (size === 8) {\n\t\tview.setBigUint64(0, (BigInt(prefix) << 62n) | BigInt(v));\n\t}\n\treturn arr;\n}\n\n// U64 IMPLEMENTATION\nfunction decodeU64(buf: Uint8Array): [number, Uint8Array] {\n\tconst view = new DataView(buf.buffer, buf.byteOffset, 8);\n\tconst remain = new Uint8Array(buf.buffer, buf.byteOffset + 8, buf.byteLength - 8);\n\tconst v = Number(view.getBigUint64(0));\n\treturn [v, remain];\n}\n\nfunction encodeU64(v: number): Uint8Array {\n\tconst arr = new Uint8Array(8);\n\tconst view = new DataView(arr.buffer, arr.byteOffset, 8);\n\tview.setBigUint64(0, BigInt(v));\n\treturn arr;\n}\n\n// LOC TIMESTAMP IMPLEMENTATION\n//\n// The LOC wire format is defined by hang's `ContainerMode::Loc` encoder\n// (hang-mmt-fec/rs/hang/src/container/producer.rs, the `ContainerMode::Loc`\n// arm), which writes a FIXED two-pair extension header and then the payload:\n//\n// [ExtID=2][CaptureTimestamp_us][ExtID=4][keyframe][payload]\n//\n// all four fields QUIC varints. It is exactly two pairs \u2014 no count, no length,\n// no terminator \u2014 so the reader is deterministic: consume four varints, and the\n// payload starts at the fifth. Do NOT reintroduce a payload sniff (the removed\n// `firstByte === 0x00` heuristic guessed at the boundary, which held for AVCC\n// video and destroyed AAC audio, whose first byte is ID_SCE = 0x00-0x1f).\n//\n// A LOC track advertising `packaging: \"loc\"` MUST be emitted through\n// `ContainerMode::Loc`. A producer that stamps the catalog but leaves the track\n// in Legacy `[VarInt ts][payload]` mode is broken on the producer side \u2014 fix it\n// there rather than making this reader guess which framing it was handed.\n\n/** Number of varints in the fixed LOC extension header (2 ID/value pairs). */\nconst LOC_HEADER_VARINTS = 4;\n\n/** LOC extension IDs (draft-ietf-moq-loc). */\nconst LOC_EXT_CAPTURE_TIMESTAMP = 2;\nconst LOC_EXT_VIDEO_FRAME_MARKING = 4;\n\n/** Decode a LOC frame header.\n * Returns [timestamp in microseconds, remaining payload]. */\nexport function decodeLocTimestamp(buffer: Uint8Array): [number, Uint8Array] {\n\tconst { timestamp, payload } = decodeLocHeader(buffer);\n\treturn [timestamp, payload];\n}\n\n// \u2500\u2500 LOC Header Parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Parsed LOC extension header (CaptureTimestamp + VideoFrameMarking). */\nexport interface LocHeader {\n\t/** Capture timestamp in microseconds (extension ID 2). */\n\ttimestamp: number;\n\t/** Keyframe flag (extension ID 4, bit 0). */\n\tkeyframe?: boolean;\n\t/** Payload after the extension header (raw codec bitstream). */\n\tpayload: Uint8Array;\n}\n\n/**\n * Decode the fixed LOC extension header:\n * `[ExtID=2][timestamp_us][ExtID=4][keyframe][payload]`.\n *\n * Throws if the header is not the two pairs the producer emits \u2014 a truncated or\n * mis-framed buffer is a contract break, and returning a plausible-looking\n * payload from it just relocates the failure into the decoder.\n */\nexport function decodeLocHeader(buffer: Uint8Array): LocHeader {\n\t/** Read one varint, converting an over-run into a contract-break message. */\n\tconst readVarInt = (buf: Uint8Array, field: string): [number, Uint8Array] => {\n\t\tif (buf.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`LOC header truncated at ${field}: buffer is ${buffer.byteLength} bytes, ` +\n\t\t\t\t\t`expected ${LOC_HEADER_VARINTS} varints`,\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\treturn decodeVarInt(buf);\n\t\t} catch {\n\t\t\t// decodeVarInt builds a DataView sized by the 2-bit prefix; a short\n\t\t\t// tail throws \"Invalid DataView length\" and hides what actually broke.\n\t\t\tthrow new Error(\n\t\t\t\t`LOC header truncated at ${field}: ${buf.length} byte(s) remain, ` +\n\t\t\t\t\t`too few for the declared varint width`,\n\t\t\t);\n\t\t}\n\t};\n\n\tconst [tsId, afterTsId] = readVarInt(buffer, \"CaptureTimestamp ID\");\n\t// Validate each ID as soon as it is read, so a Legacy-framed buffer reports\n\t// the contract break rather than an over-run further along the walk.\n\tif (tsId !== LOC_EXT_CAPTURE_TIMESTAMP) {\n\t\tthrow new Error(\n\t\t\t`LOC header extension IDs: expected ${LOC_EXT_CAPTURE_TIMESTAMP} ` +\n\t\t\t\t`(CaptureTimestamp) first, got ${tsId}. The producer is advertising ` +\n\t\t\t\t`packaging=\"loc\" but not emitting LOC framing \u2014 see ` +\n\t\t\t\t`hang Avc1::set_explicit_container / Aac::set_container.`,\n\t\t);\n\t}\n\n\tconst [timestamp, afterTs] = readVarInt(afterTsId, \"CaptureTimestamp value\");\n\tconst [kfId, afterKfId] = readVarInt(afterTs, \"VideoFrameMarking ID\");\n\tif (kfId !== LOC_EXT_VIDEO_FRAME_MARKING) {\n\t\tthrow new Error(\n\t\t\t`LOC header extension IDs: expected ${LOC_EXT_VIDEO_FRAME_MARKING} ` +\n\t\t\t\t`(VideoFrameMarking) second, got ${kfId}.`,\n\t\t);\n\t}\n\tconst [kfValue, payload] = readVarInt(afterKfId, \"VideoFrameMarking value\");\n\n\treturn { timestamp, keyframe: (kfValue & 1) === 1, payload };\n}\n\n/** Encode a LOC frame header: `[ExtID=2][timestamp_us][ExtID=4][keyframe]`. */\nexport function encodeLocTimestamp(timestamp: number, keyframe = false): Uint8Array {\n\tconst parts = [\n\t\tencodeVarInt(LOC_EXT_CAPTURE_TIMESTAMP),\n\t\tencodeVarInt(timestamp),\n\t\tencodeVarInt(LOC_EXT_VIDEO_FRAME_MARKING),\n\t\tencodeVarInt(keyframe ? 1 : 0),\n\t];\n\tconst total = parts.reduce((n, p) => n + p.length, 0);\n\tconst out = new Uint8Array(total);\n\tlet off = 0;\n\tfor (const p of parts) {\n\t\tout.set(p, off);\n\t\toff += p.length;\n\t}\n\treturn out;\n}\n", "/**\n * MMTP \u2192 CMAF converter per draft-ramadan-moq-mmt \u00A75.3.\n *\n * Converts reassembled MFU frames (raw codec samples from MMTP) into\n * fMP4 segments (moof+mdat) suitable for MSE SourceBuffer.appendBuffer().\n *\n * MPU metadata (fragment_type=0) packets from FFmpeg are complete\n * ftyp+moov init segments and can be used directly as MSE init data.\n *\n * MFU data (fragment_type=2) contains raw AVCC samples that must be\n * wrapped in moof+mdat boxes for MSE consumption.\n *\n * ISO 14496-12 (ISOBMFF) box construction is inline \u2014 no dependencies.\n */\n\n// \u2500\u2500 ISOBMFF box helpers \u2500\u2500\n\nfunction box(type: string, ...children: Uint8Array[]): Uint8Array {\n let size = 8\n for (const c of children) size += c.length\n const buf = new Uint8Array(size)\n new DataView(buf.buffer).setUint32(0, size)\n buf[4] = type.charCodeAt(0)\n buf[5] = type.charCodeAt(1)\n buf[6] = type.charCodeAt(2)\n buf[7] = type.charCodeAt(3)\n let pos = 8\n for (const c of children) {\n buf.set(c, pos)\n pos += c.length\n }\n return buf\n}\n\nfunction fullBox(type: string, version: number, flags: number, ...children: Uint8Array[]): Uint8Array {\n let size = 12\n for (const c of children) size += c.length\n const buf = new Uint8Array(size)\n const dv = new DataView(buf.buffer)\n dv.setUint32(0, size)\n buf[4] = type.charCodeAt(0)\n buf[5] = type.charCodeAt(1)\n buf[6] = type.charCodeAt(2)\n buf[7] = type.charCodeAt(3)\n dv.setUint32(8, (version << 24) | (flags & 0xffffff))\n let pos = 12\n for (const c of children) {\n buf.set(c, pos)\n pos += c.length\n }\n return buf\n}\n\nfunction u32(v: number): Uint8Array {\n const b = new Uint8Array(4)\n new DataView(b.buffer).setUint32(0, v)\n return b\n}\n\n// \u2500\u2500 Sample flags per ISO 14496-12 \u00A78.8.3.1 \u2500\u2500\nconst KEYFRAME_FLAGS = 0x02000000\nconst DELTA_FLAGS = 0x01010000\n\n// \u2500\u2500 Track geometry field ranges per ISO 14496-12 \u2500\u2500\n// tkhd/trex/tfhd track_ID and the mdhd/mvhd timescales are all unsigned 32-bit\n// fields, written below with DataView.setUint32. That call takes the value\n// modulo 2^32 without complaint, so an out-of-range configuration does not\n// fail \u2014 it silently emits different geometry than the caller asked for\n// (trackId 2^32 becomes track 0, which no fragment can ever reference).\nconst UINT32_MAX = 0xffffffff\n// mvhd next_track_ID is emitted as track_ID + 1 by both buildVideoInitSegment\n// and buildAudioInitSegment (ISO 14496-12 \u00A78.2.2.3, \"a non-zero integer\"). At\n// track_ID = UINT32_MAX that sum wraps to 0, which is both invalid and\n// indistinguishable from an unset field, so the largest usable track_ID is one\n// below the field maximum. (Cited by symbol, not line: this repo's docs gate\n// exists because line anchors rot silently \u2014 see rustfmt.yml's citation check.)\nconst MAX_TRACK_ID = UINT32_MAX - 1\nconst INT32_MIN = -0x80000000\nconst INT32_MAX = 0x7fffffff\n\n/**\n * Fail closed on a track_ID the fMP4 writers below cannot serialize faithfully.\n *\n * Called from both low-level encoders (encodeDataSegment,\n * encodeMultiSampleSegment) so every fragment path is covered by construction\n * rather than by enumerating callers. The class wrappers and init builders call\n * it earlier as well \u2014 partly for a clearer label, partly because the encoders\n * receive an already-incremented sequence number, so their throw would gap the\n * sequence on a call that emitted nothing.\n */\nfunction assertTrackId(trackId: number, label: string): void {\n if (!Number.isInteger(trackId) || trackId <= 0 || trackId > MAX_TRACK_ID) {\n throw new RangeError(`${label} must be an integer in 1..${MAX_TRACK_ID}, got ${trackId}`)\n }\n}\n\n/** Fail closed on a timescale the mdhd/mvhd uint32 fields cannot hold. */\nfunction assertTimescale(timescale: number, label: string): void {\n if (!Number.isInteger(timescale) || timescale <= 0 || timescale > UINT32_MAX) {\n throw new RangeError(`${label} must be an integer in 1..${UINT32_MAX}, got ${timescale}`)\n }\n}\n\n/** Fail closed on a signed trun sample_composition_time_offset. */\nfunction assertCompositionTimeOffsets(\n samples: Array<{ compositionTimeOffset?: number }>,\n): void {\n for (let i = 0; i < samples.length; i++) {\n const offset = samples[i]!.compositionTimeOffset\n if (offset !== undefined &&\n (!Number.isInteger(offset) || offset < INT32_MIN || offset > INT32_MAX)) {\n throw new RangeError(\n `sample ${i} compositionTimeOffset must be an integer in ${INT32_MIN}..${INT32_MAX}, got ${offset}`,\n )\n }\n }\n}\n\n/**\n * Wrap multiple samples in a single moof+mdat fMP4 segment.\n *\n * Builds a trun with N sample entries (duration + size + flags each).\n * Used by CmafBlockAssembler to emit one segment per FEC source block.\n *\n * @param samples - Array of samples with data, duration (timescale units), keyframe flag,\n * and an optional signed composition-time offset (same timescale units)\n * @param baseDecodeTime - Decode time of the first sample in timescale units\n * @param sequence - moof sequence number\n * @param trackId - ISO track ID (default 1)\n * @returns moof+mdat bytes ready for SourceBuffer.appendBuffer()\n */\nexport function encodeMultiSampleSegment(\n samples: Array<{\n data: Uint8Array\n duration: number\n keyframe: boolean\n compositionTimeOffset?: number\n }>,\n baseDecodeTime: number,\n sequence: number,\n trackId = 1,\n): Uint8Array {\n if (samples.length === 0) return new Uint8Array(0)\n assertTrackId(trackId, 'trackId')\n assertCompositionTimeOffsets(samples)\n\n const mfhd = fullBox('mfhd', 0, 0, u32(sequence))\n const tfhd = fullBox('tfhd', 0, 0x020000, u32(trackId))\n\n // tfdt version=1 for 64-bit baseMediaDecodeTime\n const tfdtBody = new Uint8Array(8)\n const tfdtView = new DataView(tfdtBody.buffer)\n tfdtView.setUint32(0, Math.floor(baseDecodeTime / 0x100000000))\n tfdtView.setUint32(4, baseDecodeTime >>> 0)\n const tfdt = fullBox('tfdt', 1, 0, tfdtBody)\n\n // A composition-time offset is signed only in trun version 1. When one is\n // present, every sample gets the field; omitted offsets are encoded as zero.\n const hasCompositionTimeOffset = samples.some(sample => sample.compositionTimeOffset !== undefined)\n const trunVersion = hasCompositionTimeOffset ? 1 : 0\n const trunFlags = 0x000701 | (hasCompositionTimeOffset ? 0x000800 : 0)\n // 4 bytes sample_count + 4 bytes data_offset + N * (4+4+4 [+4]) per sample\n const sampleCount = samples.length\n const sampleEntrySize = hasCompositionTimeOffset ? 16 : 12\n const trunBody = new Uint8Array(8 + sampleCount * sampleEntrySize)\n const trunView = new DataView(trunBody.buffer)\n trunView.setUint32(0, sampleCount)\n trunView.setUint32(4, 0) // dataOffset placeholder\n for (let i = 0; i < sampleCount; i++) {\n const s = samples[i]!\n const off = 8 + i * sampleEntrySize\n trunView.setUint32(off, s.duration >>> 0)\n trunView.setUint32(off + 4, s.data.length)\n trunView.setUint32(off + 8, s.keyframe ? KEYFRAME_FLAGS : DELTA_FLAGS)\n if (hasCompositionTimeOffset) {\n trunView.setInt32(off + 12, s.compositionTimeOffset ?? 0)\n }\n }\n const trun = fullBox('trun', trunVersion, trunFlags, trunBody)\n\n const traf = box('traf', tfhd, tfdt, trun)\n const moof = box('moof', mfhd, traf)\n\n // Concatenate all sample data into mdat\n let mdatSize = 0\n for (const s of samples) mdatSize += s.data.length\n const mdatPayload = new Uint8Array(mdatSize)\n let pos = 0\n for (const s of samples) {\n mdatPayload.set(s.data, pos)\n pos += s.data.length\n }\n const mdat = box('mdat', mdatPayload)\n\n // Fix trun.dataOffset: moof size + 8 (mdat box header)\n const dataOffsetPos = 8 + mfhd.length + 8 + tfhd.length + tfdt.length + 12 + 4\n new DataView(moof.buffer).setUint32(dataOffsetPos, moof.length + 8)\n\n const result = new Uint8Array(moof.length + mdat.length)\n result.set(moof, 0)\n result.set(mdat, moof.length)\n return result\n}\n\n// \u2500\u2500 Green fill / silence generators \u2500\u2500\n\n/**\n * Generate a minimal H.264 IDR slice NAL for green fill (YUV 0,128,128).\n *\n * Reuses SPS/PPS from the stream's avcC so the fill frame is compatible\n * with the existing decoder configuration. Returns AVCC-framed data\n * (4-byte length prefix per NAL) ready for moof+mdat wrapping.\n *\n * @param avcC - Raw avcC payload from catalog description\n * @param width - Video width in pixels\n * @param height - Video height in pixels\n */\nexport function generateGreenFillNAL(avcC: Uint8Array, width: number, height: number): Uint8Array {\n // Parse SPS and PPS from avcC box payload\n // avcC layout: version(1) + profile(1) + compat(1) + level(1) + lengthSizeMinusOne(1)\n // + numSPS(1) + [spsLen(2) + spsData]... + numPPS(1) + [ppsLen(2) + ppsData]...\n if (avcC.length < 8) return new Uint8Array(0)\n\n const lengthSize = (avcC[4]! & 0x03) + 1 // NAL length field size (usually 4)\n let offset = 5\n const numSPS = avcC[offset++]! & 0x1f\n const nals: Uint8Array[] = []\n\n // Extract SPS NAL(s)\n for (let i = 0; i < numSPS; i++) {\n if (offset + 2 > avcC.length) break\n const spsLen = (avcC[offset]! << 8) | avcC[offset + 1]!\n offset += 2\n if (offset + spsLen > avcC.length) break\n const sps = avcC.subarray(offset, offset + spsLen)\n // AVCC frame: 4-byte length + NAL\n const framed = new Uint8Array(4 + sps.length)\n new DataView(framed.buffer).setUint32(0, sps.length)\n framed.set(sps, 4)\n nals.push(framed)\n offset += spsLen\n }\n\n // Extract PPS NAL(s)\n if (offset < avcC.length) {\n const numPPS = avcC[offset++]!\n for (let i = 0; i < numPPS; i++) {\n if (offset + 2 > avcC.length) break\n const ppsLen = (avcC[offset]! << 8) | avcC[offset + 1]!\n offset += 2\n if (offset + ppsLen > avcC.length) break\n const pps = avcC.subarray(offset, offset + ppsLen)\n const framed = new Uint8Array(4 + pps.length)\n new DataView(framed.buffer).setUint32(0, pps.length)\n framed.set(pps, 4)\n nals.push(framed)\n offset += ppsLen\n }\n }\n\n // Build minimal IDR slice NAL (NAL type 5)\n // Slice header: first_mb_in_slice=0, slice_type=7 (I), pic_parameter_set_id=0\n // Exp-Golomb coded: 0(1 bit) + 0001000(slice_type=7=I-all, ue(v)) + 1(pps_id=0, ue(v))\n // Then fill with green macroblocks.\n //\n // For a minimal valid IDR that decodes to green, we use:\n // nal_type=5 (IDR), slice_type=I, then CABAC/CAVLC all-intra-16x16\n // with luma DC=0, chroma DC=128. Exact encoding depends on SPS entropy mode.\n //\n // Simplified approach: generate a very small IDR slice that MSE will accept.\n // The slice header + minimal macroblock data. The decoder fills with the\n // predicted values which default to mid-gray/green.\n const mbWidth = Math.ceil(width / 16)\n const mbHeight = Math.ceil(height / 16)\n const totalMBs = mbWidth * mbHeight\n\n // Minimal IDR slice: nal_ref_idc=3, nal_type=5\n // Slice header bits: first_mb=0 (1 bit ue), slice_type=7 (I-all, 0001000 ue),\n // pps_id=0 (1 bit ue), frame_num=0 (depends on SPS log2_max)\n // We construct a minimal bitstream that encodes an all-skip I-slice.\n // For simplicity, build a fixed small IDR payload \u2014 the decoder will\n // pad/error-conceal remaining MBs producing a solid fill frame.\n const idrSlice = new Uint8Array([\n 0x65, // nal_ref_idc=3, nal_type=5 (IDR)\n 0x88, // slice header: first_mb_in_slice=0, slice_type=7 (I)\n 0x80, // pps_id=0, frame_num=0, idr_pic_id=0\n 0x40, // slice data: mb_skip_run covering remaining MBs\n // Truncated \u2014 decoder will error-conceal remaining MBs as green/gray\n ])\n const idrFramed = new Uint8Array(4 + idrSlice.length)\n new DataView(idrFramed.buffer).setUint32(0, idrSlice.length)\n idrFramed.set(idrSlice, 4)\n nals.push(idrFramed)\n\n // Concatenate all NALs: SPS + PPS + IDR\n let totalLen = 0\n for (const n of nals) totalLen += n.length\n const result = new Uint8Array(totalLen)\n let p = 0\n for (const n of nals) {\n result.set(n, p)\n p += n.length\n }\n return result\n}\n\n/**\n * Generate a minimal silent audio frame for FEC fill.\n *\n * Supports AAC (mp4a) and Opus codecs. Returns raw frame data (no ADTS/OBU\n * header) suitable for fMP4/CMAF wrapping.\n *\n * @param codec - Audio codec string (e.g. 'mp4a.40.2', 'opus')\n * @param sampleRate - Audio sample rate (e.g. 48000)\n * @param channels - Channel count (e.g. 1 or 2)\n */\nexport function generateSilenceFrame(codec: string, sampleRate: number, channels: number): Uint8Array {\n if (codec.startsWith('opus') || codec.startsWith('Opus')) {\n return generateSilenceOpus(channels)\n }\n return generateSilenceAAC(sampleRate, channels)\n}\n\n/** Minimal silent AAC-LC access unit (raw, no ADTS). */\nfunction generateSilenceAAC(sampleRate: number, channels: number): Uint8Array {\n if (channels >= 2) {\n // CPE (channel pair element) + END\n return new Uint8Array([\n 0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80,\n ])\n }\n // SCE (single channel element) + END\n return new Uint8Array([\n 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,\n ])\n}\n\n/** Minimal silent Opus frame.\n * RFC 6716 \u00A73.1: a 1-byte frame with code 0 encodes silence for SILK/CELT. */\nfunction generateSilenceOpus(channels: number): Uint8Array {\n // Opus TOC byte: config=0 (SILK NB 10ms), s=0 (mono/stereo from channel mapping), c=0 (1 frame)\n // Frame data: single zero byte \u2192 silence\n // For stereo, channel mapping in fMP4 init handles routing; the Opus frame itself is the same.\n return new Uint8Array([0xF8, 0xFF, 0xFE])\n}\n\nexport function normalizeAacSampleForCmaf(data: Uint8Array): Uint8Array {\n if (data.length < 7 || data[0] !== 0xff || (data[1]! & 0xf6) !== 0xf0) {\n return data\n }\n const headerLength = (data[1]! & 0x01) ? 7 : 9\n if (data.length <= headerLength) return data\n const frameLength = ((data[3]! & 0x03) << 11) | (data[4]! << 3) | ((data[5]! & 0xe0) >>> 5)\n if (frameLength !== data.length || frameLength <= headerLength) return data\n return data.subarray(headerLength)\n}\n\n/**\n * Wrap a single raw codec sample in a moof+mdat fMP4 segment.\n *\n * @param data - Raw sample bytes (AVCC H.264 or raw AAC)\n * @param timestamp - Decode time in timescale units\n * @param duration - Sample duration in timescale units\n * @param keyframe - true for IDR/sync sample\n * @param sequence - moof sequence number (monotonically incrementing)\n * @param trackId - ISO track ID (default 1)\n * @returns moof+mdat bytes ready for SourceBuffer.appendBuffer()\n */\nexport function encodeDataSegment(\n data: Uint8Array,\n timestamp: number,\n duration: number,\n keyframe: boolean,\n sequence: number,\n trackId = 1,\n compositionTimeOffset?: number,\n): Uint8Array {\n assertTrackId(trackId, 'trackId')\n assertCompositionTimeOffsets([{ compositionTimeOffset }])\n if (compositionTimeOffset !== undefined) {\n return encodeMultiSampleSegment(\n [{ data, duration, keyframe, compositionTimeOffset }],\n timestamp,\n sequence,\n trackId,\n )\n }\n const sampleFlags = keyframe ? KEYFRAME_FLAGS : DELTA_FLAGS\n\n const mfhd = fullBox('mfhd', 0, 0, u32(sequence))\n const tfhd = fullBox('tfhd', 0, 0x020000, u32(trackId))\n\n // tfdt version=1 for 64-bit baseMediaDecodeTime\n const tfdtBody = new Uint8Array(8)\n const tfdtView = new DataView(tfdtBody.buffer)\n tfdtView.setUint32(0, Math.floor(timestamp / 0x100000000))\n tfdtView.setUint32(4, timestamp >>> 0)\n const tfdt = fullBox('tfdt', 1, 0, tfdtBody)\n\n // trun: flags=0x000701 (data-offset + duration + size + flags)\n const trunBody = new Uint8Array(20)\n const trunView = new DataView(trunBody.buffer)\n trunView.setUint32(0, 1) // sample_count\n trunView.setUint32(4, 0) // dataOffset placeholder\n trunView.setUint32(8, duration >>> 0)\n trunView.setUint32(12, data.length)\n trunView.setUint32(16, sampleFlags)\n const trun = fullBox('trun', 0, 0x000701, trunBody)\n\n const traf = box('traf', tfhd, tfdt, trun)\n const moof = box('moof', mfhd, traf)\n const mdat = box('mdat', data)\n\n // Fix trun.dataOffset: moof size + 8 (mdat box header)\n const dataOffsetPos = 8 + mfhd.length + 8 + tfhd.length + tfdt.length + 12 + 4\n new DataView(moof.buffer).setUint32(dataOffsetPos, moof.length + 8)\n\n const result = new Uint8Array(moof.length + mdat.length)\n result.set(moof, 0)\n result.set(mdat, moof.length)\n return result\n}\n\n/**\n * Build an ftyp+moov init segment for H.264 video MSE from an avcC box.\n *\n * @param avcC - Raw avcC box payload (SPS/PPS configuration)\n * @param width - Video width in pixels\n * @param height - Video height in pixels\n * @param timescale - Timescale (default 1_000_000 = microseconds)\n * @param trackId - Track ID (default 1)\n */\nexport function buildVideoInitSegment(\n avcC: Uint8Array,\n width: number,\n height: number,\n timescale = 1_000_000,\n trackId = 1,\n): Uint8Array {\n // Validated here as well as in setVideoTrackParams: this builder is exported\n // from the package index, so a direct caller reaches the same uint32 tkhd /\n // trex / mdhd / mvhd fields without passing through the class.\n assertTrackId(trackId, 'video trackId')\n assertTimescale(timescale, 'video timescale')\n const ftyp = box('ftyp', new Uint8Array([\n 0x69, 0x73, 0x6f, 0x6d, 0, 0, 2, 0,\n 0x69, 0x73, 0x6f, 0x6d,\n 0x69, 0x73, 0x6f, 0x36,\n 0x6d, 0x70, 0x34, 0x31,\n ]))\n\n const avcCBox = box('avcC', avcC)\n const avc1Body = new Uint8Array(78 + avcCBox.length)\n const avc1View = new DataView(avc1Body.buffer)\n avc1View.setUint16(6, 1)\n avc1View.setUint16(24, width)\n avc1View.setUint16(26, height)\n avc1View.setUint32(28, 0x00480000)\n avc1View.setUint32(32, 0x00480000)\n avc1View.setUint16(40, 1)\n avc1View.setUint16(74, 0x0018)\n avc1View.setInt16(76, -1)\n avc1Body.set(avcCBox, 78)\n const avc1 = box('avc1', avc1Body)\n\n const stsd = fullBox('stsd', 0, 0, u32(1), avc1)\n const stbl = box('stbl', stsd,\n fullBox('stts', 0, 0, u32(0)),\n fullBox('stsc', 0, 0, u32(0)),\n fullBox('stsz', 0, 0, u32(0), u32(0)),\n fullBox('stco', 0, 0, u32(0)),\n )\n const dinf = box('dinf', fullBox('dref', 0, 0, u32(1), fullBox('url ', 0, 1)))\n const minf = box('minf', fullBox('vmhd', 0, 1), dinf, stbl)\n const hdlr = fullBox('hdlr', 0, 0, new Uint8Array([\n 0, 0, 0, 0, 0x76, 0x69, 0x64, 0x65,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0x56, 0x69, 0x64, 0x65, 0x6f, 0,\n ]))\n const mdhdBody = new Uint8Array(20)\n new DataView(mdhdBody.buffer).setUint32(8, timescale)\n const mdhd = fullBox('mdhd', 0, 0, mdhdBody)\n const mdia = box('mdia', mdhd, hdlr, minf)\n\n const tkhdBody = new Uint8Array(80)\n const tkhdView = new DataView(tkhdBody.buffer)\n tkhdView.setUint32(8, trackId)\n tkhdView.setUint32(36, 0x00010000)\n tkhdView.setUint32(52, 0x00010000)\n tkhdView.setUint32(64, 0x40000000)\n tkhdView.setUint32(72, width << 16)\n tkhdView.setUint32(76, height << 16)\n const tkhd = fullBox('tkhd', 0, 3, tkhdBody)\n const trak = box('trak', tkhd, mdia)\n\n const mvhdBody = new Uint8Array(96)\n const mvhdView = new DataView(mvhdBody.buffer)\n mvhdView.setUint32(8, timescale)\n mvhdView.setUint32(16, 0x00010000)\n mvhdView.setUint16(20, 0x0100)\n mvhdView.setUint32(32, 0x00010000)\n mvhdView.setUint32(48, 0x00010000)\n mvhdView.setUint32(60, 0x40000000)\n mvhdView.setUint32(92, trackId + 1)\n const mvhd = fullBox('mvhd', 0, 0, mvhdBody)\n\n const trexBody = new Uint8Array(20)\n new DataView(trexBody.buffer).setUint32(0, trackId)\n new DataView(trexBody.buffer).setUint32(4, 1)\n const mvex = box('mvex', fullBox('trex', 0, 0, trexBody))\n\n const moov = box('moov', mvhd, trak, mvex)\n const result = new Uint8Array(ftyp.length + moov.length)\n result.set(ftyp, 0)\n result.set(moov, ftyp.length)\n return result\n}\n\n/**\n * Build ftyp+moov init segment for AAC audio MSE.\n */\nexport function buildAudioInitSegment(\n sampleRate: number,\n channels: number,\n asc?: Uint8Array,\n timescale?: number,\n trackId = 1,\n): Uint8Array {\n // trackId only: this reaches the same uint32 tkhd/trex/mvhd fields as the\n // video builder. The audio *timescale* is deliberately left unchecked here \u2014\n // wrapAudioSample branches on its exact value, so constraining it belongs\n // with that path rather than in this range fix.\n assertTrackId(trackId, 'audio trackId')\n const ts = timescale ?? sampleRate\n\n if (!asc) {\n const freqTable = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]\n const fi = freqTable.indexOf(sampleRate)\n const freqIdx = fi >= 0 ? fi : 0x0f\n if (freqIdx <= 0x0e) {\n const v = (2 << 11) | (freqIdx << 7) | (channels << 3)\n asc = new Uint8Array([(v >> 8) & 0xff, v & 0xff])\n } else {\n asc = new Uint8Array([\n (2 << 3) | 0x07,\n (0x08 | ((sampleRate >> 20) & 0x07)) & 0xff,\n (sampleRate >> 12) & 0xff,\n (sampleRate >> 4) & 0xff,\n (((sampleRate & 0x0f) << 4) | (channels << 0)) & 0xff,\n ])\n }\n }\n\n // esds: ES_Descriptor \u2192 DecoderConfigDescriptor \u2192 AudioSpecificConfig\n const dsi = new Uint8Array(2 + asc.length)\n dsi[0] = 0x05; dsi[1] = asc.length; dsi.set(asc, 2)\n const dcdBody = new Uint8Array(13 + dsi.length)\n dcdBody[0] = 0x40; dcdBody[1] = 0x15; dcdBody.set(dsi, 13)\n const dcd = new Uint8Array(2 + dcdBody.length)\n dcd[0] = 0x04; dcd[1] = dcdBody.length; dcd.set(dcdBody, 2)\n const esBody = new Uint8Array(3 + dcd.length)\n esBody[0] = 0; esBody[1] = 0x01; esBody[2] = 0; esBody.set(dcd, 3)\n const es = new Uint8Array(2 + esBody.length)\n es[0] = 0x03; es[1] = esBody.length; es.set(esBody, 2)\n const esdsBox = fullBox('esds', 0, 0, es)\n\n const mp4aBody = new Uint8Array(28 + esdsBox.length)\n const mp4aView = new DataView(mp4aBody.buffer)\n mp4aView.setUint16(6, 1)\n mp4aView.setUint16(16, channels)\n mp4aView.setUint16(18, 16)\n mp4aView.setUint32(24, sampleRate << 16)\n mp4aBody.set(esdsBox, 28)\n const mp4a = box('mp4a', mp4aBody)\n\n const stsd = fullBox('stsd', 0, 0, u32(1), mp4a)\n const stbl = box('stbl', stsd,\n fullBox('stts', 0, 0, u32(0)),\n fullBox('stsc', 0, 0, u32(0)),\n fullBox('stsz', 0, 0, u32(0), u32(0)),\n fullBox('stco', 0, 0, u32(0)),\n )\n const dinf = box('dinf', fullBox('dref', 0, 0, u32(1), fullBox('url ', 0, 1)))\n const minf = box('minf', fullBox('smhd', 0, 0, new Uint8Array(4)), dinf, stbl)\n const hdlr = fullBox('hdlr', 0, 0, new Uint8Array([\n 0, 0, 0, 0, 0x73, 0x6f, 0x75, 0x6e,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0x53, 0x6f, 0x75, 0x6e, 0x64, 0,\n ]))\n const mdhdBody = new Uint8Array(20)\n new DataView(mdhdBody.buffer).setUint32(8, ts)\n const mdhd = fullBox('mdhd', 0, 0, mdhdBody)\n const mdia = box('mdia', mdhd, hdlr, minf)\n\n const tkhdBody = new Uint8Array(80)\n const tkhdView = new DataView(tkhdBody.buffer)\n tkhdView.setUint32(8, trackId)\n tkhdView.setUint32(36, 0x00010000)\n tkhdView.setUint32(52, 0x00010000)\n tkhdView.setUint32(64, 0x40000000)\n const tkhd = fullBox('tkhd', 0, 3, tkhdBody)\n const trak = box('trak', tkhd, mdia)\n\n const mvhdBody = new Uint8Array(96)\n const mvhdView = new DataView(mvhdBody.buffer)\n mvhdView.setUint32(8, ts)\n mvhdView.setUint32(16, 0x00010000)\n mvhdView.setUint16(20, 0x0100)\n mvhdView.setUint32(32, 0x00010000)\n mvhdView.setUint32(48, 0x00010000)\n mvhdView.setUint32(60, 0x40000000)\n mvhdView.setUint32(92, trackId + 1)\n const mvhd = fullBox('mvhd', 0, 0, mvhdBody)\n\n const trexBody = new Uint8Array(20)\n new DataView(trexBody.buffer).setUint32(0, trackId)\n new DataView(trexBody.buffer).setUint32(4, 1)\n const mvex = box('mvex', fullBox('trex', 0, 0, trexBody))\n\n const moov = box('moov', mvhd, trak, mvex)\n const ftyp = box('ftyp', new Uint8Array([\n 0x69, 0x73, 0x6f, 0x6d, 0, 0, 2, 0,\n 0x69, 0x73, 0x6f, 0x6d,\n 0x69, 0x73, 0x6f, 0x36,\n 0x6d, 0x70, 0x34, 0x31,\n ]))\n const result = new Uint8Array(ftyp.length + moov.length)\n result.set(ftyp, 0)\n result.set(moov, ftyp.length)\n return result\n}\n\n/**\n * MMTP \u2192 CMAF converter.\n *\n * Accepts reassembled MFU frames (raw codec samples) from MmtpDecoder\n * and produces fMP4 segments for MSE SourceBuffer.\n *\n * Per draft-ramadan-moq-mmt \u00A75.3:\n * MPU metadata (ftyp+moov) \u2192 init segment\n * MFU sample bytes \u2192 moof+mdat data segment\n *\n * Timescale is microseconds (1_000_000) matching MMTP NTP short timestamps\n * converted via mmtpTimestampToMicros().\n */\nexport class MmtpToCmaf {\n #videoSeq = 1\n #audioSeq = 1\n #videoInitSegment: Uint8Array | null = null\n // Known only for locally-built init segments. Wire-supplied MPU metadata is\n // intentionally opaque here, so setVideoTrackParams remains the API that\n // lets a caller declare its track geometry before wrapping samples.\n #videoInitTrackId: number | null = null\n #audioInitSegment: Uint8Array | null = null\n #lastVideoTimestamp = 0\n // Sticky sample duration in video timescale units, updated from caller-\n // supplied durations or adjacent-timestamp deltas. null means \"nothing\n // observed yet\", in which case #videoFrameDuration derives ~30fps from the\n // configured timescale (33333 at the 1_000_000 default, i.e. unchanged).\n #frameDuration: number | null = null\n // Per-track rebase: each track anchors on its OWN first sample so\n // baseMediaDecodeTime is never negative. A shared base was wrong \u2014 if the\n // first sample of one track arrives later than the other's first sample,\n // the earlier track computes `timestamp - sharedBase` as a negative number,\n // which serializes into tfdt as a huge unsigned uint64 (~2^64 - 46) and\n // Chromium rejects the segment with CHUNK_DEMUXER_ERROR_APPEND_FAILED,\n // closing the MediaSource and detaching all SourceBuffers.\n // For A/V alignment, choose a COMMON anchor: set when the first sample of\n // EITHER track arrives, but only if that sample's timestamp is <= any\n // subsequent track's first sample. Implementation: each track rebases to\n // the minimum timestamp seen across tracks on their respective first sample.\n #videoBaseTs = -1\n #audioBaseTs = -1\n\n // Video track geometry. These pair with the video init segment's tkhd\n // track_ID and mdhd/mvhd timescale: every moof this converter emits must\n // reference the same track at the same rate or the browser either drops the\n // fragment (unknown track) or plays it at the wrong rate. They are\n // configuration rather than stream state, so \u2014 unlike the sequence numbers\n // and time bases above \u2014 reset() deliberately preserves them, matching the\n // fact that reset() also preserves #videoInitSegment.\n #videoTrackId = 1\n #videoTimescale = 1_000_000\n\n /**\n * Point the single-sample video wrappers at a specific track and rate.\n *\n * Call this when the init segment came off the wire (setInitSegmentFromMpu)\n * rather than from buildVideoInit, so the wrappers match the publisher's\n * tkhd/mdhd instead of assuming trackId=1 at microsecond rate. Consumers\n * that build their init through buildVideoInit can pass the same values\n * there instead and skip this call.\n *\n * The rate is settable only until the first single-sample wrap \u2014 see the\n * throw below for why.\n */\n setVideoTrackParams(trackId: number, timescale: number) {\n assertTrackId(trackId, 'video trackId')\n assertTimescale(timescale, 'video timescale')\n // #frameDuration, #lastVideoTimestamp and #videoBaseTs are all denominated\n // in #videoTimescale, so moving the rate out from under them reinterprets\n // every one at the new rate. Both consequences are silent:\n // - a duration latched as 33333 (1 MHz ~30fps) re-emits as 33333 ticks of\n // 90 kHz, a 370 ms frame;\n // - a base captured on the old scale rebases new-scale timestamps to a\n // negative relTs, which tfdt serializes as a ~2^64 uint64 and Chromium\n // rejects with CHUNK_DEMUXER_ERROR_APPEND_FAILED, closing the\n // MediaSource (same failure the #videoBaseTs comment above describes).\n // Rescaling instead of refusing would be a guess: the class cannot tell\n // whether the caller's next timestamps continue this timeline in new units\n // or open a fresh epoch. reset() is the exact mechanism for that\n // discontinuity \u2014 it clears all three fields while preserving geometry \u2014\n // so require it rather than infer. Re-asserting the same rate is a no-op.\n // A cached init also fixes the track ID in tkhd/trex; changing only the\n // wrapper setting would make later tfhd IDs disagree with that init.\n if (trackId !== this.#videoTrackId && this.#videoInitTrackId !== null) {\n throw new Error(\n `video track ID cannot change from ${this.#videoTrackId} to ${trackId} ` +\n 'after a video init has been cached; replace the init atomically',\n )\n }\n if (timescale !== this.#videoTimescale && this.#videoBaseTs >= 0) {\n throw new Error(\n `video timescale cannot change from ${this.#videoTimescale} to ${timescale} ` +\n `after video samples have been wrapped; call reset() first`,\n )\n }\n this.#videoTrackId = trackId\n this.#videoTimescale = timescale\n }\n\n /** Set init segment from MPU metadata packet (fragment_type=0).\n * These are complete ftyp+moov bytes from FFmpeg \u2014 use directly. */\n setInitSegmentFromMpu(data: Uint8Array, isAudio: boolean) {\n if (isAudio) {\n this.#audioInitSegment = data\n } else {\n this.#videoInitSegment = data\n this.#videoInitTrackId = null\n }\n }\n\n /** Build init segment from codec config (catalog path \u2014 no MPU metadata available).\n * Passing timescale/trackId also configures the wrappers via\n * setVideoTrackParams, so init and media segments cannot disagree. */\n buildVideoInit(avcC: Uint8Array, width: number, height: number, timescale?: number, trackId?: number) {\n const nextTimescale = timescale ?? this.#videoTimescale\n const nextTrackId = trackId ?? this.#videoTrackId\n assertTrackId(nextTrackId, 'video trackId')\n assertTimescale(nextTimescale, 'video timescale')\n if (nextTimescale !== this.#videoTimescale && this.#videoBaseTs >= 0) {\n throw new Error(\n `video timescale cannot change from ${this.#videoTimescale} to ${nextTimescale} ` +\n 'after video samples have been wrapped; call reset() first',\n )\n }\n // Build first, then publish both geometry and init together. This is the\n // sanctioned path for replacing a cached init with a new track ID.\n const init = buildVideoInitSegment(\n avcC,\n width,\n height,\n nextTimescale,\n nextTrackId,\n )\n this.#videoTimescale = nextTimescale\n this.#videoTrackId = nextTrackId\n this.#videoInitSegment = init\n this.#videoInitTrackId = nextTrackId\n }\n\n buildAudioInit(sampleRate: number, channels: number, asc?: Uint8Array, timescale?: number) {\n const ts = timescale ?? sampleRate\n this.#audioTimescale = ts\n this.#audioInitSegment = buildAudioInitSegment(sampleRate, channels, asc, ts)\n }\n\n get videoInitSegment(): Uint8Array | null { return this.#videoInitSegment }\n get audioInitSegment(): Uint8Array | null { return this.#audioInitSegment }\n\n /** Convert a raw video sample to an fMP4 moof+mdat segment. */\n wrapVideoSample(data: Uint8Array, timestamp: number, keyframe: boolean, duration?: number): Uint8Array {\n return this.wrapVideoSampleWithTiming(data, timestamp, keyframe, duration).segment\n }\n\n /**\n * Convert a raw video sample and return the exact browser media interval\n * encoded into tfdt/trun. Consumers that correlate SourceBuffer appends with\n * requestVideoFrameCallback must use this rebased timestamp, not the wire or\n * caller-owned timestamp passed above.\n */\n wrapVideoSampleWithTiming(\n data: Uint8Array,\n timestamp: number,\n keyframe: boolean,\n duration?: number,\n ): Readonly<{ segment: Uint8Array; mediaTimestamp: number; duration: number }> {\n // Prefer a caller-provided media duration when the browser append timeline\n // is intentionally decoupled from sparse MMTP wire timestamps. Fall back to\n // estimating from adjacent timestamps for legacy/direct callers. A delta of\n // a full second or more is not a frame interval, so it is rejected as a\n // discontinuity rather than latched.\n if (duration !== undefined && Number.isFinite(duration) && duration > 0) {\n this.#frameDuration = Math.round(duration)\n } else if (this.#lastVideoTimestamp > 0 && timestamp > this.#lastVideoTimestamp) {\n const delta = timestamp - this.#lastVideoTimestamp\n if (delta > 0 && delta < this.#videoTimescale) this.#frameDuration = delta\n }\n this.#lastVideoTimestamp = timestamp\n if (this.#videoBaseTs < 0) this.#videoBaseTs = timestamp\n const relTs = timestamp - this.#videoBaseTs\n const frameDuration = this.#videoFrameDuration\n return Object.freeze({\n segment: encodeDataSegment(\n data,\n relTs,\n frameDuration,\n keyframe,\n this.#videoSeq++,\n this.#videoTrackId,\n ),\n mediaTimestamp: relTs,\n duration: frameDuration,\n })\n }\n\n /** Sticky duration if one has been observed, else ~30fps at the current rate. */\n get #videoFrameDuration(): number {\n return this.#frameDuration ?? Math.max(1, Math.round(this.#videoTimescale / 30))\n }\n\n /** Convert an append-safe video batch using the caller-owned timeline.\n * Durations, baseDecodeTime, and compositionTimeOffset are in the\n * configured video timescale. compositionTimeOffset is the signed PTS-DTS\n * delta; its presence selects a version-1 trun for the whole batch.\n * An explicit trackId is range-checked before #videoSeq++ for the same\n * reason as wrapAudioSamples: the encoder's own check fires too late to\n * keep the sequence ungapped. */\n wrapVideoSamples(\n samples: Array<{\n data: Uint8Array\n duration: number\n keyframe: boolean\n compositionTimeOffset?: number\n }>,\n baseDecodeTime: number,\n trackId = this.#videoTrackId,\n ): Uint8Array {\n assertTrackId(trackId, 'video trackId')\n assertCompositionTimeOffsets(samples)\n return encodeMultiSampleSegment(samples, baseDecodeTime, this.#videoSeq++, trackId)\n }\n\n // Audio timescale from init segment (default 1M \u00B5s; real FFmpeg init uses sampleRate).\n // Set by buildAudioInit or setAudioTimescale.\n #audioTimescale = 1_000_000\n\n /** Set audio timescale to match the init segment's mdhd timescale.\n * Call after appending the real encoder init (FT=0 ftyp+moov). */\n setAudioTimescale(timescale: number) { this.#audioTimescale = timescale }\n\n // Audio sample counter for drift-free baseMediaDecodeTime.\n // AAC frames are exactly 1024 samples at sampleRate. Using sequential\n // counter \u00D7 1024 avoids floating-point rounding drift from timestamp conversion.\n #audioSampleCount = 0\n\n /** AAC access-unit length in samples. ISO/IEC 14496-3: an AAC-LC frame is\n * exactly 1024 samples at every sample rate \u2014 it is not rate-dependent. */\n readonly #aacFrameSamples = 1024\n\n /** Convert a raw audio sample to an fMP4 moof+mdat segment. */\n wrapAudioSample(\n data: Uint8Array,\n timestamp: number,\n sampleDuration = 21333,\n compositionTimeOffset?: number,\n ): Uint8Array {\n assertCompositionTimeOffsets([{ compositionTimeOffset }])\n const sampleData = normalizeAacSampleForCmaf(data)\n if (this.#audioBaseTs < 0) this.#audioBaseTs = timestamp\n\n // When timescale matches sampleRate (e.g. 48000), use integer sample counting\n // for exact baseMediaDecodeTime alignment. This eliminates cumulative rounding\n // drift from Math.round(\u00B5s * sampleRate / 1_000_000) that causes frequency\n // wandering in FFT analysis (42% consistency \u2192 99%+ with exact counting).\n //\n // samplesPerFrame is the AAC access-unit length, NOT a rescaled duration.\n // Deriving it as Math.round(sampleDuration * timescale / 1e6) only lands on\n // 1024 when the rate happens to be 48 kHz (21333 * 48000 / 1e6 = 1023.98),\n // which made the default silently correct at 48 kHz and wrong everywhere\n // else: at 44.1 kHz it yields 941, so every moof declared ~8% short frames\n // and Chrome rejected the first packet with PIPELINE_ERROR_DECODE\n // (\"Failed to send audio packet for decoding\", duration=21337\u00B5s = 941/44100).\n const samplesPerFrame = this.#audioTimescale !== 1_000_000\n ? this.#aacFrameSamples\n : sampleDuration\n const scaledTs = this.#audioTimescale !== 1_000_000\n ? this.#audioSampleCount * samplesPerFrame\n : (timestamp - this.#audioBaseTs)\n this.#audioSampleCount++\n\n return encodeDataSegment(\n sampleData,\n scaledTs,\n samplesPerFrame,\n true,\n this.#audioSeq++,\n 1,\n compositionTimeOffset,\n )\n }\n\n /** Convert an ordered audio batch using the caller-owned timeline. */\n wrapAudioSamples(\n samples: Array<{\n data: Uint8Array\n duration: number\n compositionTimeOffset?: number\n }>,\n baseDecodeTime: number,\n trackId = 1,\n ): Uint8Array {\n // Checked before #audioSeq++ below: the encoder validates too, but its\n // throw would land after the counter had already advanced, leaving the\n // sequence gapped by a call that produced no segment.\n assertTrackId(trackId, 'audio trackId')\n assertCompositionTimeOffsets(samples)\n return encodeMultiSampleSegment(\n samples.map(sample => ({\n data: normalizeAacSampleForCmaf(sample.data),\n duration: sample.duration,\n keyframe: true,\n compositionTimeOffset: sample.compositionTimeOffset,\n })),\n baseDecodeTime,\n this.#audioSeq++,\n trackId,\n )\n }\n\n /** Clear per-stream sequencing and time bases.\n * Cached init segments and the video track geometry survive, so a converter\n * reset mid-subscription keeps emitting moofs the existing init can resolve.\n * Clearing the timescale-denominated timing state also re-opens the video\n * rate for change \u2014 this is the sanctioned way to retarget mid-subscription. */\n reset() {\n this.#videoSeq = 1\n this.#audioSeq = 1\n this.#audioSampleCount = 0\n this.#lastVideoTimestamp = 0\n this.#frameDuration = null\n this.#videoBaseTs = -1\n this.#audioBaseTs = -1\n this.#audioTimescale = 1_000_000\n }\n}\n", "/**\n * MMTP Signaling Parser for Multicast\n *\n * Provides packet type detection and AL-FEC signaling message parsing.\n * Used by the multicast pipeline to classify and route MMTP packets.\n *\n * Packet types (ISO/IEC 23008-1:2023 Section 9.2.2):\n * - 0x00: MPU (Media Processing Unit) - video/audio data\n * - 0x02: Signaling - control messages including AL-FEC config\n * - 0x03: Repair - FEC repair symbols\n */\nimport {\n\tALFEC_MESSAGE_ID,\n\tFEC_CODE_RAPTORQ,\n\tFEC_CODE_XOR,\n\tPACKET_TYPE_MPU,\n\tPACKET_TYPE_REPAIR,\n\tPACKET_TYPE_SIGNALING,\n\tparseAlFecMessage,\n} from \"@blockcast/mmt-base\";\n\nexport {\n\tALFEC_MESSAGE_ID,\n\tFEC_CODE_RAPTORQ,\n\tFEC_CODE_XOR,\n\tPACKET_TYPE_MPU,\n\tPACKET_TYPE_REPAIR,\n\tPACKET_TYPE_SIGNALING,\n\tparseAlFecMessage,\n};\n\nexport type PacketClass = \"source\" | \"signaling\" | \"repair\" | \"unknown\";\n\n/** Get the MMTP packet type from raw multicast packet bytes.\n *\n * ISO/IEC 23008-1:2023 Figure 8 (V=0): byte 1 is\n * reserved(2)|packet_type(6), so signaling is 0x02 and repair is 0x03.\n * Pre-migration FFmpeg high-bits bytes (0x08/0x0c) are NOT accepted:\n * writers and readers move together in the pinned smoke train.\n */\nexport function getPacketType(mmtpPacket: Uint8Array): number {\n\tif (mmtpPacket.length < 2) return -1;\n\treturn mmtpPacket[1] & 0x3f;\n}\n\n/** Check if packet is a signaling packet (type 0x02). */\nexport function isSignalingPacket(mmtpPacket: Uint8Array): boolean {\n\treturn getPacketType(mmtpPacket) === PACKET_TYPE_SIGNALING;\n}\n\n/** Determine if packet is a source (video/audio) packet. */\nexport function isSourcePacket(mmtpPacket: Uint8Array): boolean {\n\tif (mmtpPacket.length < 2) return false;\n\tconst packetType = mmtpPacket[1] & 0x3f;\n\tconst fecType = (mmtpPacket[0] >> 3) & 0x03;\n\treturn packetType === 0x00 && fecType <= 1;\n}\n\n/** Classify MMTP packet for multicast pipeline routing. */\nexport function classifyPacket(mmtpPacket: Uint8Array): PacketClass {\n\tif (mmtpPacket.length < 2) return \"unknown\";\n\tconst packetType = mmtpPacket[1] & 0x3f;\n\tconst fecType = (mmtpPacket[0] >> 3) & 0x03;\n\tif ((mmtpPacket[0] >> 6) !== 0) return \"unknown\";\n\tswitch (packetType) {\n\t\tcase 0x00:\n\t\t\tif (mmtpPacket.length < 20) return \"unknown\";\n\t\t\tif (fecType >= 2) return \"repair\";\n\t\t\treturn \"source\";\n\t\tcase 0x02:\n\t\t\treturn \"signaling\";\n\t\tcase 0x03:\n\t\t\tif (mmtpPacket.length < 20) return \"unknown\";\n\t\t\treturn \"repair\";\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n}\n", "/**\n * Exact timing authored by an ISO-BMFF media fragment.\n *\n * `timestamp`/`duration` fields on the browser-facing APIs are converted to\n * microseconds for legacy callers. This value remains in the media track's\n * native timescale so consumers that own a CMAF/WebCodecs timeline do not\n * have to reconstruct integer ticks from rounded wall-clock values.\n */\nexport interface AuthoredTiming {\n /** Exact decode time in the media track's timescale. */\n decodeTime: bigint\n /** Exact presentation time in the media track's timescale, including CTS. */\n presentationTime: bigint\n /**\n * Exact sample duration in the media track's timescale. This remains a\n * number because ISO-BMFF stores sample_duration as an unsigned 32-bit\n * field, whose full range is exactly representable by JavaScript numbers.\n */\n durationTicks: number\n /** `mdhd` timescale for all values above. */\n mediaTimescale: number\n}\n\n/** Convert authored media ticks to an integer target timescale without first\n * narrowing the authored tick value to a wall-clock float. */\nexport function scaleAuthoredTicks(\n ticks: bigint,\n mediaTimescale: number,\n targetTimescale: number,\n): number | null {\n if (!Number.isSafeInteger(mediaTimescale) || mediaTimescale <= 0 ||\n !Number.isSafeInteger(targetTimescale) || targetTimescale <= 0) return null\n\n const negative = ticks < 0n\n const numerator = (negative ? -ticks : ticks) * BigInt(targetTimescale)\n const denominator = BigInt(mediaTimescale)\n const whole = numerator / denominator\n const remainder = numerator % denominator\n if (whole > BigInt(Number.MAX_SAFE_INTEGER)) return null\n const value = Number(whole) + Number(remainder) / mediaTimescale\n return Number.isFinite(value) ? (negative ? -value : value) : null\n}\n\n/** Convert exact authored presentation time and duration to an assembler's\n * public timestamp scale. */\nexport function scaleAuthoredTiming(\n timing: AuthoredTiming,\n targetTimescale: number,\n): { timestamp: number; duration: number } | null {\n if (!Number.isSafeInteger(timing.durationTicks) || timing.durationTicks <= 0) return null\n const timestamp = scaleAuthoredTicks(\n timing.presentationTime,\n timing.mediaTimescale,\n targetTimescale,\n )\n const duration = scaleAuthoredTicks(\n BigInt(timing.durationTicks),\n timing.mediaTimescale,\n targetTimescale,\n )\n if (timestamp === null || duration === null || duration <= 0) return null\n return { timestamp, duration }\n}\n", "/**\n * FEC-block-aware CMAF segment assembler for MSE multicast.\n *\n * Buffers reassembled frames per FEC source block (SBN). On block completion\n * (K source symbols received clean, or FEC recovery), sorts frames by ESI\n * and builds a single multi-sample moof+mdat CMAF segment. On block timeout,\n * generates fill frames (green video / silence audio) for missing ESI slots.\n *\n * One assembler instance per track (video or audio). MulticastManager owns\n * these instances and calls them directly with SBN/ESI from the FEC pipeline.\n */\n\nimport {\n encodeDataSegment,\n buildVideoInitSegment,\n buildAudioInitSegment,\n generateGreenFillNAL,\n normalizeAacSampleForCmaf,\n} from './mmtp-to-cmaf.js'\nimport {\n classifyH264Keyframe,\n detectH264Keyframe,\n splitH264SampleNalUnits,\n} from './codec-utils.js'\nimport {\n scaleAuthoredTicks,\n scaleAuthoredTiming,\n type AuthoredTiming,\n} from './authored-timing.js'\nimport type { MmtpDeliveryProvenance } from './mfu-reassembler.js'\n\nconst INT32_MIN = -0x80000000\nconst INT32_MAX = 0x7fffffff\n\ninterface BlockFrame {\n data: Uint8Array\n timestamp: number\n keyframe: boolean\n /** Exact ISO-BMFF timing retained from the parser for FEC/source ordering. */\n authoredTiming?: AuthoredTiming\n /** MMTP packet_sequence_number identifying the delivered access unit. */\n packetSequenceNumber?: number\n /** Every authoritative MMTP source-packet sequence that formed this sample. */\n packetSequenceNumbers?: number[]\n /** Subset of packetSequenceNumbers reconstructed by FEC. */\n repairedPacketSequenceNumbers?: number[]\n /** Native MMTP MPU group coordinate retained across source/FEC ordering. */\n mpuSequenceNumber?: number\n catalogTrackName?: string\n /**\n * Whole-frame delivery verdict folded across every contributing fragment.\n * Absent means no caller declared one; it is never an implied 'multicast'.\n */\n deliveryProvenance?: MmtpDeliveryProvenance\n}\n\nexport interface CmafBlockSample {\n data: Uint8Array\n timestamp: number\n duration: number\n keyframe: boolean\n /** Signed PTS-DTS offset in the configured CMAF timescale. */\n compositionTimeOffset?: number\n /** Exact ISO-BMFF timing in the media track's native timescale. */\n authoredTiming?: AuthoredTiming\n /** MMTP packet_sequence_number identifying the delivered access unit. */\n packetSequenceNumber?: number\n /** Every authoritative MMTP source-packet sequence that formed this sample. */\n packetSequenceNumbers?: number[]\n /** Subset of packetSequenceNumbers reconstructed by FEC. */\n repairedPacketSequenceNumbers?: number[]\n /** Native MMTP MPU group coordinate retained for exact replay selection. */\n mpuSequenceNumber?: number\n /** Immutable catalog identity supplied by the owning ingress subscription. */\n catalogTrackName?: string\n /**\n * Whole-frame delivery verdict, carried so MSE `onSample` consumers can see\n * the same evidence the non-MSE frame path gets. Without it the MSE branch\n * silently loses a computed 'mixed' or 'multicast' finding.\n */\n deliveryProvenance?: MmtpDeliveryProvenance\n}\n\nexport type CmafSampleOrderPolicy =\n | { mode: 'arrival' }\n | {\n /** Release samples according to media progression, without a wall clock. */\n mode: 'media-window'\n /** Delivery window in the same units as CmafBlockSample.timestamp. */\n deliveryWindow: number\n /** Optional modulo period for wrapped input timestamps, in timestamp units. */\n timestampWrapPeriod?: number\n }\n\nexport interface CmafSampleOrderStats {\n staleSamples: number\n duplicateSamples: number\n preRapDeadlineDrops: number\n noRapFlushDrops: number\n forcedReleases: number\n}\n\nexport interface BlockAssemblerConfig {\n /** Number of source symbols per FEC block */\n k: number\n /** Timescale for CMAF segments (default 1_000_000 = microseconds) */\n timescale?: number\n /** ISO track ID (default 1) */\n trackId?: number\n /** Track type \u2014 determines fill frame generation and init segment format */\n trackType: 'video' | 'audio'\n /** Video/audio codec string (e.g. 'avc1.64002a', 'mp4a.40.2'). */\n codec?: string\n // Video config (required when trackType='video')\n /** Raw avcC payload from catalog description \u2014 for init + green fill */\n avcC?: Uint8Array\n /** Video width in pixels */\n width?: number\n /** Video height in pixels */\n height?: number\n // Audio config (required when trackType='audio')\n /** Audio codec string (e.g. 'mp4a.40.2', 'opus') \u2014 for silence fill generation */\n audioCodec?: string\n /** Audio sample rate (e.g. 48000) */\n sampleRate?: number\n /** Audio channel count (e.g. 1 or 2) */\n channels?: number\n /** Raw AudioSpecificConfig from catalog */\n asc?: Uint8Array\n // Shared\n /** Frames per second \u2014 fallback for duration estimation */\n fps?: number\n /** Explicit sample ordering policy. Datagram/FEC callers use media-window. */\n sampleOrder?: CmafSampleOrderPolicy\n /**\n * Bounded sample reorder depth for callback mode.\n *\n * Default 0 preserves immediate emission. When set on video tracks with\n * onSample enabled, samples are sorted by timestamp and the oldest sample is\n * emitted only after this many newer samples are available.\n * @deprecated Use sampleOrder with an explicit policy.\n */\n sampleReorderWindow?: number\n}\n\ninterface OrderedSample extends CmafBlockSample {\n advancesHorizon: boolean\n}\n\nexport class CmafBlockAssembler {\n #config: Required<Pick<BlockAssemblerConfig, 'k' | 'timescale' | 'trackId' | 'trackType'>>\n #blocks = new Map<number, Map<number, BlockFrame>>()\n #pendingFlush = new Set<number>()\n #pendingRecovered = new Map<number, BlockFrame>()\n #sequence = 1\n #baseTs = -1\n #fillFrame: Uint8Array\n #lastDecodableAudioFrame: Uint8Array | null = null\n #frameDuration: number\n #lastEmittedTs = -1\n #lastSourceTs = -1\n #sampleReorderWindow = 0\n #sampleOrder: CmafSampleOrderPolicy = { mode: 'arrival' }\n #sampleOrderCapacity = 0\n #sampleReorderQueue: OrderedSample[] = []\n #recoveredHorizonTs = -1\n #sampleOrderStats: CmafSampleOrderStats = {\n staleSamples: 0,\n duplicateSamples: 0,\n preRapDeadlineDrops: 0,\n noRapFlushDrops: 0,\n forcedReleases: 0,\n }\n #validateH264Keyframes = false\n #keyframeRejectDumpCount = 0\n\n /** Callback: emits a complete moof+mdat CMAF segment */\n onSegment: ((segment: Uint8Array) => void) | null = null\n /** Callback: emits a gated/deduped raw sample for callers that already own the MSE timeline. */\n onSample: ((sample: CmafBlockSample) => void) | null = null\n /** Callback: emits an init segment (ftyp+moov) */\n onInitSegment: ((init: Uint8Array) => void) | null = null\n\n // Video keyframe gate (BLO-3035 layer 3): Chrome's CHUNK_DEMUXER rejects a\n // moof+mdat whose first sample is non-sync with no prior reference frames\n // (\"CHUNK_DEMUXER_ERROR_APPEND_FAILED: Failed to prepare video sample for\n // decode\") and detaches the SourceBuffer for the rest of the session.\n // Mid-stream join almost guarantees the first FEC blocks are mid-GOP \u2014 all\n // delta frames. Gate emission until the first block containing an IDR;\n // within that block, splice samples to start at the IDR index. Audio passes\n // unconditionally \u2014 valid for the codecs supported here (AAC, Opus) where\n // every frame is independently decodable. SBR-only AAC, USAC, or HE-AAC v2\n // with PCE re-entry would need a similar gate.\n #armedVideo = false\n\n // Legacy SBN emission watermark for the old block-buffered path. Direct\n // source emission is now timestamp-ordered instead; recovered frames are\n // queued by media timestamp so they can fill gaps without racing ahead of\n // source frames.\n #lastEmittedSbn = -1\n\n /** Attempt to claim emission for `sbn`. Returns true if `sbn` is past\n * the watermark (and advances it); false if this sbn has already been\n * claimed or is older (caller should drop). */\n #claimSbnEmission(sbn: number): boolean {\n if (sbn <= this.#lastEmittedSbn) return false\n this.#lastEmittedSbn = sbn\n return true\n }\n\n /** Truncate `frames` to start at the first keyframe for video tracks before\n * the first emission. Returns the (possibly spliced) frames, or null to\n * drop the block entirely. Audio + already-armed video: identity. */\n #applyVideoKeyframeGate<T extends { data: Uint8Array; keyframe: boolean }>(frames: T[]): T[] | null {\n const keyedFrames = this.#normalizeVideoKeyframes(frames)\n if (this.#config.trackType !== 'video' || this.#armedVideo) return keyedFrames\n const firstKfIdx = keyedFrames.findIndex(f => f.keyframe)\n if (firstKfIdx < 0) return null\n return firstKfIdx === 0 ? keyedFrames : keyedFrames.slice(firstKfIdx)\n }\n\n #normalizeVideoKeyframes<T extends { data: Uint8Array; keyframe: boolean }>(frames: T[]): T[] {\n if (this.#config.trackType !== 'video' || !this.#validateH264Keyframes) return frames\n return frames.map((frame) => this.#withEffectiveVideoKeyframe(frame))\n }\n\n #withEffectiveVideoKeyframe<T extends { data: Uint8Array; keyframe: boolean }>(frame: T): T {\n if (this.#config.trackType !== 'video' || !this.#validateH264Keyframes) return frame\n const keyframe = detectH264Keyframe(frame.data)\n if (!keyframe) this.#dumpRejectedVideoKeyframe(frame.data, frame.keyframe)\n return keyframe === frame.keyframe ? frame : { ...frame, keyframe }\n }\n\n #dumpRejectedVideoKeyframe(data: Uint8Array, metadataKeyframe: boolean): void {\n if (this.#keyframeRejectDumpCount >= 12) return\n this.#keyframeRejectDumpCount++\n const sample = splitH264SampleNalUnits(data)\n const nalTypes = describeH264NalTypes(sample.nals)\n const classify = classifyH264Keyframe(data)\n const hex = Array.from(data.subarray(0, Math.min(48, data.length)))\n .map(b => (b as number).toString(16).padStart(2, '0'))\n .join(' ')\n console.log(\n `[L4-KF-GATE] reject#${this.#keyframeRejectDumpCount} len=${data.length} ` +\n `metadataKf=${metadataKeyframe} classify=${classify} format=${sample.format} ` +\n `nalTypes=[${nalTypes.join(',')}] first48=[${hex}]`,\n )\n }\n\n /** Mark video as already synchronized by an external decoder path.\n * Used by MoQ\u2192multicast MSE continuity when MoQ has already appended a\n * keyframe to the same SourceBuffer timeline before multicast takeover. */\n markVideoSynced(): void {\n if (this.#config.trackType === 'video') this.#armedVideo = true\n }\n\n /** Emit each frame as its own single-sample moof+mdat segment. Matches the\n * case-20 path (`MmtpToCmaf.wrapVideoSample` \u2192 `encodeDataSegment`) byte-\n * for-byte per frame. Working hypothesis (pending Chromium-source\n * validation against `media/filters/chunk_demuxer.cc`):\n * `encodeMultiSampleSegment` blobs are accepted by ChunkDemuxer in this\n * pipeline but only the first sample renders for live, possibly because\n * the pipeline expects per-frame moof boundaries. Track in BLO-3035.\n *\n * Stale-tfdt defense-in-depth: drops any frame whose timestamp is older\n * than `#lastEmittedTs`. The SBN watermark already prevents out-of-order\n * FEC-block emissions, but `addBypassFrame` (oversized MFU / no-FEC path)\n * doesn't touch the watermark \u2014 a bypass frame at t=T followed by a FEC\n * block whose frames precede T would otherwise inject earlier tfdt and\n * detach the SourceBuffer. */\n #ordersSamples(): boolean {\n return this.#sampleOrder.mode === 'media-window' || this.#sampleReorderWindow > 0\n }\n\n #unwrapTimestamp(timestamp: number): number {\n const wrapPeriod = this.#sampleOrder.mode === 'media-window'\n ? this.#sampleOrder.timestampWrapPeriod\n : undefined\n if (!wrapPeriod) return timestamp\n const reference = this.#lastSourceTs >= 0\n ? this.#lastSourceTs\n : this.#recoveredHorizonTs >= 0\n ? this.#recoveredHorizonTs\n : this.#lastEmittedTs\n if (reference < 0) return timestamp\n return timestamp + Math.round((reference - timestamp) / wrapPeriod) * wrapPeriod\n }\n\n #emitSample(\n data: Uint8Array,\n timestamp: number,\n duration: number,\n keyframe: boolean,\n advancesHorizon: boolean,\n catalogTrackName: string | undefined,\n mpuSequenceNumber: number | undefined,\n packetSequenceNumber: number | undefined,\n packetSequenceNumbers: number[] | undefined,\n repairedPacketSequenceNumbers: number[] | undefined,\n authoredTiming: AuthoredTiming | undefined,\n // Required, not optional: an omitted optional argument is silently\n // undefined, which is exactly how this field went missing from the\n // recovered path in the first place. Callers with nothing to declare pass\n // `undefined` on purpose.\n deliveryProvenance: MmtpDeliveryProvenance | undefined,\n ): void {\n const scaledTiming = authoredTiming === undefined\n ? null\n : scaleAuthoredTiming(authoredTiming, this.#config.timescale)\n if (authoredTiming !== undefined && scaledTiming === null) return\n const authoredTimestamp = scaledTiming?.timestamp ?? timestamp\n const authoredDuration = scaledTiming?.duration ?? duration\n let compositionTimeOffset: number | undefined\n if (authoredTiming !== undefined) {\n const scaledOffset = scaleAuthoredTicks(\n authoredTiming.presentationTime - authoredTiming.decodeTime,\n authoredTiming.mediaTimescale,\n this.#config.timescale,\n )\n if (scaledOffset === null) return\n // trun stores composition offsets as integer target-timescale ticks.\n // Preserve the nearest representable tick, then enforce the signed\n // int32 serialization range on the rounded value.\n const roundedOffset = Math.round(scaledOffset)\n if (\n !Number.isSafeInteger(roundedOffset) ||\n roundedOffset < INT32_MIN ||\n roundedOffset > INT32_MAX\n ) return\n compositionTimeOffset = roundedOffset\n }\n const unwrappedTimestamp = this.#unwrapTimestamp(authoredTimestamp)\n if (unwrappedTimestamp <= this.#lastEmittedTs) {\n this.#sampleOrderStats.staleSamples++\n return\n }\n\n const sample = this.#withEffectiveVideoKeyframe({\n data,\n timestamp: unwrappedTimestamp,\n duration: authoredDuration,\n keyframe,\n ...(compositionTimeOffset === undefined ? {} : { compositionTimeOffset }),\n advancesHorizon,\n ...(authoredTiming === undefined ? {} : { authoredTiming }),\n ...(packetSequenceNumber === undefined ? {} : { packetSequenceNumber }),\n ...(packetSequenceNumbers === undefined ? {} : { packetSequenceNumbers }),\n ...(repairedPacketSequenceNumbers === undefined\n ? {}\n : { repairedPacketSequenceNumbers }),\n ...(mpuSequenceNumber === undefined ? {} : { mpuSequenceNumber }),\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n ...(deliveryProvenance === undefined ? {} : { deliveryProvenance }),\n })\n if (advancesHorizon && unwrappedTimestamp > this.#lastSourceTs) {\n this.#lastSourceTs = unwrappedTimestamp\n } else if (!advancesHorizon && unwrappedTimestamp > this.#recoveredHorizonTs) {\n this.#recoveredHorizonTs = unwrappedTimestamp\n }\n\n if (this.#ordersSamples()) {\n this.#queueSample(sample)\n return\n }\n this.#emitArrivalOrdered(sample)\n }\n\n #emitArrivalOrdered(sample: OrderedSample): void {\n if (this.#config.trackType === 'video' && !this.#armedVideo && !sample.keyframe) {\n this.#sampleOrderStats.preRapDeadlineDrops++\n return\n }\n this.#emitSampleNow(sample)\n }\n\n #queueSample(sample: OrderedSample): void {\n if (this.#sampleReorderQueue.some(s => s.timestamp === sample.timestamp)) {\n this.#sampleOrderStats.duplicateSamples++\n return\n }\n this.#sampleReorderQueue.push(sample)\n this.#sampleReorderQueue.sort((a, b) => a.timestamp - b.timestamp)\n this.#drainSampleReorder(false)\n this.#enforceSampleOrderCapacity()\n }\n\n #releaseHorizon(): number {\n return this.#lastSourceTs >= 0 ? this.#lastSourceTs : this.#recoveredHorizonTs\n }\n\n #sampleIsReleasable(sample: OrderedSample, flushAll: boolean): boolean {\n if (flushAll) return true\n if (this.#sampleOrder.mode === 'media-window') {\n const horizon = this.#releaseHorizon()\n return horizon >= 0 && sample.timestamp < horizon - this.#sampleOrder.deliveryWindow\n }\n return this.#sampleReorderQueue.length > this.#sampleReorderWindow\n }\n\n #dropPreRapSamples(flushAll: boolean): boolean {\n if (this.#config.trackType !== 'video' || this.#armedVideo) return false\n\n const firstRap = this.#sampleReorderQueue.findIndex(\n sample => sample.keyframe && this.#sampleIsReleasable(sample, flushAll),\n )\n if (firstRap >= 0) {\n if (firstRap > 0) {\n this.#sampleOrderStats.preRapDeadlineDrops += firstRap\n this.#sampleReorderQueue.splice(0, firstRap)\n }\n return false\n }\n\n let dropped = 0\n while (\n this.#sampleReorderQueue[0] &&\n this.#sampleIsReleasable(this.#sampleReorderQueue[0]!, flushAll)\n ) {\n this.#sampleReorderQueue.shift()\n dropped++\n }\n if (flushAll) this.#sampleOrderStats.noRapFlushDrops += dropped\n else this.#sampleOrderStats.preRapDeadlineDrops += dropped\n return dropped > 0\n }\n\n #drainSampleReorder(flushAll: boolean): void {\n while (this.#sampleReorderQueue.length > 0) {\n if (this.#dropPreRapSamples(flushAll) && this.#sampleReorderQueue.length === 0) return\n const sample = this.#sampleReorderQueue[0]\n if (!sample || !this.#sampleIsReleasable(sample, flushAll)) return\n this.#sampleReorderQueue.shift()\n this.#emitQueuedSample(sample)\n }\n }\n\n #emitQueuedSample(sample: OrderedSample): void {\n const next = this.#sampleReorderQueue.find(s => s.timestamp > sample.timestamp)\n const duration = sample.authoredTiming\n ? sample.duration\n : next ? next.timestamp - sample.timestamp : sample.duration\n this.#emitSampleNow({\n ...sample,\n duration: duration > 0\n ? duration\n : sample.authoredTiming ? sample.duration : this.#frameDuration,\n })\n }\n\n #enforceSampleOrderCapacity(): void {\n if (this.#sampleOrder.mode !== 'media-window') return\n while (this.#sampleReorderQueue.length > this.#sampleOrderCapacity) {\n const sample = this.#sampleReorderQueue.shift()\n if (!sample) return\n this.#sampleOrderStats.forcedReleases++\n console.warn(\n `[CmafBlockAssembler] ${this.#config.trackType} media-window queue exceeded ` +\n `${this.#sampleOrderCapacity} samples; force-releasing timestamp=${sample.timestamp}`,\n )\n if (this.#config.trackType === 'video' && !this.#armedVideo && !sample.keyframe) {\n this.#sampleOrderStats.preRapDeadlineDrops++\n continue\n }\n this.#emitQueuedSample(sample)\n }\n }\n\n #emitSampleNow(sample: CmafBlockSample): void {\n if (sample.timestamp <= this.#lastEmittedTs) {\n this.#sampleOrderStats.staleSamples++\n return\n }\n this.#emitAudioGapFillNowBefore(sample.timestamp)\n this.#writeSampleNow(sample, true)\n }\n\n #writeSampleNow(sample: CmafBlockSample, rememberAudio: boolean): void {\n const { timestamp, duration, keyframe } = sample\n this.#lastEmittedTs = timestamp\n if (this.#config.trackType === 'video' && keyframe) this.#armedVideo = true\n const data = this.#normalizeSampleData(sample.data)\n if (this.#config.trackType === 'audio' && rememberAudio && data.length > 0) {\n // Keep decoder-proven codec data for concealment only after the FEC\n // delivery deadline has passed without the missing audio sample.\n this.#lastDecodableAudioFrame = data.slice()\n }\n if (this.onSample) {\n this.onSample(data === sample.data ? sample : { ...sample, data })\n } else {\n const baseTime = this.#rebase(timestamp)\n const segment = encodeDataSegment(\n data,\n baseTime,\n duration,\n keyframe,\n this.#sequence++,\n this.#config.trackId,\n sample.compositionTimeOffset,\n )\n this.onSegment?.(segment)\n }\n }\n\n #emitAudioGapFillNowBefore(timestamp: number): void {\n if (\n this.#config.trackType !== 'audio' ||\n this.#lastEmittedTs < 0 ||\n this.#lastDecodableAudioFrame === null\n ) return\n\n const gap = timestamp - this.#lastEmittedTs\n if (gap <= this.#frameDuration * 1.5) return\n\n const missingFrames = Math.max(0, Math.round(gap / this.#frameDuration) - 1)\n for (let i = 0; i < missingFrames; i++) {\n const fillTs = this.#lastEmittedTs + this.#frameDuration\n this.#writeSampleNow({\n data: this.#lastDecodableAudioFrame.slice(),\n timestamp: fillTs,\n duration: this.#frameDuration,\n keyframe: true,\n }, false)\n }\n }\n\n #emitPerSample(\n frames: BlockFrame[],\n audioDurationOverride?: number,\n advancesHorizon = false,\n ): void {\n for (let i = 0; i < frames.length; i++) {\n const f = frames[i]!\n // Per-frame duration from delta to next frame, or fallback.\n let duration: number\n if (this.#config.trackType === 'audio') {\n duration = audioDurationOverride ?? this.#frameDuration\n } else if (i < frames.length - 1) {\n duration = frames[i + 1]!.timestamp - f.timestamp\n } else if (i > 0) {\n duration = f.timestamp - frames[i - 1]!.timestamp\n } else {\n duration = audioDurationOverride ?? this.#frameDuration\n }\n if (duration <= 0) duration = audioDurationOverride ?? this.#frameDuration\n this.#emitSample(\n f.data,\n f.timestamp,\n duration,\n f.keyframe,\n advancesHorizon,\n f.catalogTrackName,\n f.mpuSequenceNumber,\n f.packetSequenceNumber,\n f.packetSequenceNumbers,\n f.repairedPacketSequenceNumbers,\n f.authoredTiming,\n f.deliveryProvenance,\n )\n }\n }\n\n // [BLO-3035-LAYER4-DIAG] Byte-dump for the first 6 video emits + 1Hz\n // aggregate counters that survive the whole run. The counters tell us\n // whether \"emits=low\" (assembler emits too few segments) or\n // \"addFrame=low\" (parser delivers too few frames) \u2014 which routes the\n // root-cause hunt to assembler vs router vs publisher.\n #dumpCount = 0\n #emitCount = 0\n #sampleCount = 0\n #keyframeCount = 0\n #addFrameCount = 0\n #recoveredBlockCount = 0\n #completeBlockCount = 0\n #failBlockCount = 0\n #aggregateTimer: ReturnType<typeof setInterval> | null = null\n #startAggregateTimer(): void {\n if (this.#aggregateTimer) return\n this.#aggregateTimer = setInterval(() => {\n console.log(\n `[L4-AGG-${this.#config.trackType}] emits=${this.#emitCount} samples=${this.#sampleCount} ` +\n `kf=${this.#keyframeCount} addFrame=${this.#addFrameCount} ` +\n `recovered=${this.#recoveredBlockCount} complete=${this.#completeBlockCount} ` +\n `fail=${this.#failBlockCount} pendingBlocks=${this.#blocks.size} ` +\n `pendingFlushes=${this.#pendingFlush.size} armedVideo=${this.#armedVideo}`,\n )\n this.#emitCount = 0\n this.#sampleCount = 0\n this.#keyframeCount = 0\n this.#addFrameCount = 0\n this.#recoveredBlockCount = 0\n this.#completeBlockCount = 0\n this.#failBlockCount = 0\n }, 1000)\n }\n #dumpEmit(path: string, samples: Array<{ data: Uint8Array; keyframe: boolean; timestamp?: number }>, sbn?: number): void {\n this.#emitCount++\n this.#sampleCount += samples.length\n this.#keyframeCount += samples.filter(s => s.keyframe).length\n // Always log video keyframes (no cap) \u2014 needed to see bytes of every IDR\n // including the t=5s one that Chrome's decoder rejected (BLO-3035 L4).\n const anyKf = this.#config.trackType === 'video' && samples.some(s => s.keyframe)\n if (this.#config.trackType !== 'video' || (this.#dumpCount >= 6 && !anyKf)) return\n this.#dumpCount++\n const s0 = samples[0]\n if (!s0) return\n const d = s0.data\n const hex = Array.from(d.subarray(0, Math.min(48, d.length)))\n .map(b => (b as number).toString(16).padStart(2, '0'))\n .join(' ')\n // djb2 hash of full s0 buffer \u2014 used to compare clean vs FEC-recovered emit bytes.\n let h = 5381\n for (let i = 0; i < d.length; i++) h = (h * 33 + (d[i] as number)) | 0\n const hash = (h >>> 0).toString(16).padStart(8, '0')\n const ts0 = s0.timestamp ?? -1\n const tsN = samples[samples.length - 1]?.timestamp ?? -1\n console.log(\n `[L4-DIAG] EMIT#${this.#dumpCount} path=${path}${sbn !== undefined ? ` sbn=${sbn}` : ''} ` +\n `samples=${samples.length} s0.kf=${s0.keyframe} s0.len=${d.length} ` +\n `ts0=${ts0} tsN=${tsN} dur=${tsN - ts0} hash=${hash} first48=[${hex}]`,\n )\n }\n\n constructor(config: BlockAssemblerConfig) {\n this.#config = {\n k: config.k,\n timescale: config.timescale ?? 1_000_000,\n trackId: config.trackId ?? 1,\n trackType: config.trackType,\n }\n\n // Compute default frame duration from fps (updated from timestamp deltas at runtime)\n if (config.fps && config.fps > 0) {\n this.#frameDuration = Math.round(this.#config.timescale / config.fps)\n } else if (config.trackType === 'audio' && config.sampleRate) {\n this.#frameDuration = Math.round(this.#config.timescale * 1024 / config.sampleRate)\n } else {\n // No fps info \u2014 will be refined from actual timestamp deltas\n this.#frameDuration = 33333\n }\n\n // Build fill frame based on track type (requires codec config from catalog)\n if (config.trackType === 'video' && config.avcC && config.width && config.height) {\n this.#fillFrame = generateGreenFillNAL(config.avcC, config.width, config.height)\n } else {\n this.#fillFrame = new Uint8Array(0)\n }\n\n const sampleReorderWindow = config.sampleReorderWindow ?? 0\n if (!Number.isFinite(sampleReorderWindow) || sampleReorderWindow < 0) {\n throw new Error('[CmafBlockAssembler] sampleReorderWindow must be finite and non-negative')\n }\n if (config.sampleOrder && sampleReorderWindow > 0) {\n throw new Error('[CmafBlockAssembler] configure sampleOrder or sampleReorderWindow, not both')\n }\n this.#sampleReorderWindow = Math.floor(sampleReorderWindow)\n this.#sampleOrder = config.sampleOrder ?? { mode: 'arrival' }\n if (this.#sampleOrder.mode === 'media-window') {\n const { deliveryWindow, timestampWrapPeriod } = this.#sampleOrder\n if (!Number.isSafeInteger(deliveryWindow) || deliveryWindow <= 0) {\n throw new Error(\n '[CmafBlockAssembler] media-window deliveryWindow must be a positive safe integer',\n )\n }\n if (\n timestampWrapPeriod !== undefined &&\n (!Number.isSafeInteger(timestampWrapPeriod) || timestampWrapPeriod <= deliveryWindow * 2)\n ) {\n throw new Error(\n '[CmafBlockAssembler] timestampWrapPeriod must be a safe integer larger than two delivery windows',\n )\n }\n const samplesPerSecond = config.trackType === 'audio' && config.sampleRate\n ? config.sampleRate / 1024\n : config.fps\n if (!samplesPerSecond || !Number.isFinite(samplesPerSecond) || samplesPerSecond <= 0) {\n throw new Error(\n '[CmafBlockAssembler] media-window ordering requires catalog-derived media cadence',\n )\n }\n this.#sampleOrderCapacity =\n Math.ceil(deliveryWindow * samplesPerSecond / this.#config.timescale) + 1\n }\n this.#validateH264Keyframes =\n config.trackType === 'video' &&\n (!!config.avcC || !config.codec || isH264Codec(config.codec))\n if (config.trackType === 'video') {\n console.log(\n `[L4-KF-CONFIG] validate=${this.#validateH264Keyframes} ` +\n `codec=${config.codec ?? ''} avcC=${config.avcC?.length ?? 0}`,\n )\n }\n\n // Store config for init segment generation\n this._videoConfig = config.trackType === 'video' && config.avcC\n ? { avcC: config.avcC, width: config.width ?? 0, height: config.height ?? 0 }\n : null\n this._audioConfig = config.trackType === 'audio' && config.sampleRate && config.channels\n ? { sampleRate: config.sampleRate, channels: config.channels, asc: config.asc }\n : null\n }\n\n private _videoConfig: { avcC?: Uint8Array; width: number; height: number } | null\n private _audioConfig: { sampleRate: number; channels: number; asc?: Uint8Array } | null\n\n get sampleOrderStats(): Readonly<CmafSampleOrderStats> {\n return { ...this.#sampleOrderStats }\n }\n\n /** Emit a single frame immediately as a one-sample moof+mdat segment.\n *\n * BLO-3035 layer 4 (4b): the prior SBN-grouped buffering scheme stranded\n * frames whenever a multi-packet frame spanned FEC block boundaries \u2014\n * intermediate SBNs had packets-but-no-frames and got `completeBlock`\n * events that landed on empty `#blocks` (\u2192 `pendingFlush` leak), while\n * the SBN where the multi-packet frame finally completed received the\n * `addFrame` but its `trackBlock` count never reached K (\u2192 `pendingBlocks`\n * leak). Result: ~50% of received frames never reached MSE.\n *\n * This path now mirrors `addBypassFrame` \u2014 emit on arrival, no buffering.\n * FEC recovery (`addRecoveredBlock`) keeps its SBN-watermark dedup against\n * the same SBN being re-emitted from two paths. Out-of-order regression\n * is guarded by the per-frame `lastEmittedTs` check (also enforced in\n * `#emitPerSample`). The `sbn`/`esi` parameters are kept for the call-site\n * signature but no longer affect routing. */\n addFrame(\n sbn: number,\n esi: number,\n data: Uint8Array,\n timestamp: number,\n keyframe: boolean,\n catalogTrackName?: string,\n mpuSequenceNumber?: number,\n packetSequenceNumber?: number,\n packetSequenceNumbers?: number[],\n repairedPacketSequenceNumbers?: number[],\n authoredTiming?: AuthoredTiming,\n deliveryProvenance?: MmtpDeliveryProvenance,\n ): void {\n void sbn; void esi\n this.#addFrameCount++\n this.#startAggregateTimer()\n\n if (!this.#ordersSamples()) this.#drainRecoveredUpTo(timestamp)\n\n // Per-frame duration: default to catalog-derived `#frameDuration`. For\n // variable-rate content Chrome refines duration from the next sample's\n // DTS at append time, so a fallback here is safe.\n this.#emitSample(\n data,\n timestamp,\n this.#frameDuration,\n keyframe,\n true,\n catalogTrackName,\n mpuSequenceNumber,\n packetSequenceNumber,\n packetSequenceNumbers,\n repairedPacketSequenceNumbers,\n authoredTiming,\n deliveryProvenance,\n )\n\n // Stats \u2014 #dumpEmit increments emit/sample/keyframe counters internally.\n this.#dumpEmit('addFrame-direct', [{ data, timestamp, keyframe }])\n }\n\n /** Mark a block as complete (all K source symbols received or FEC recovery succeeded).\n * If all frames are already buffered, flushes immediately. Otherwise sets pending-flush. */\n completeBlock(sbn: number): void {\n void sbn\n this.#completeBlockCount++\n this.#startAggregateTimer()\n // Source samples are emitted directly from addFrame(). A clean FEC block\n // completion is still useful for stats, but it must not enqueue stale\n // pending flushes from the retired block-buffered path.\n }\n\n /** Handle block timeout \u2014 emit whatever frames we have + fill for missing slots. */\n failBlock(sbn: number): void {\n this.#failBlockCount++\n this.#startAggregateTimer()\n this.#pendingFlush.delete(sbn)\n const block = this.#blocks.get(sbn)\n if (!block || block.size === 0) {\n this.#blocks.delete(sbn)\n return\n }\n this.#flushBlockWithFill(sbn)\n }\n\n /** Add all frames from a FEC-recovered block at once and emit immediately. */\n addRecoveredBlock(sbn: number, frames: BlockFrame[]): void {\n void sbn\n this.#recoveredBlockCount++\n this.#startAggregateTimer()\n // Build samples directly \u2014 recovered frames are already sorted\n this.#pendingFlush.delete(sbn)\n this.#blocks.delete(sbn)\n\n if (frames.length === 0) return\n const normalized = this.#normalizeVideoKeyframes(frames)\n this.#dumpEmit('addRecoveredBlock', normalized, sbn)\n if (this.#ordersSamples()) {\n this.#emitPerSample(normalized, undefined, false)\n return\n }\n\n // Arrival mode preserves the legacy protection against future recovered\n // samples advancing past source media. Explicit media-window mode routes\n // both paths through one queue instead.\n if (this.#lastSourceTs >= 0) {\n this.#enqueueRecovered(normalized)\n this.#drainRecoveredUpTo(this.#lastSourceTs)\n } else {\n const gated = this.#applyVideoKeyframeGate(normalized)\n if (gated) this.#emitPerSample(gated, undefined, false)\n }\n }\n\n /** Emit a single frame immediately as a one-sample segment (for oversized packets bypassing FEC). */\n addBypassFrame(\n data: Uint8Array,\n timestamp: number,\n keyframe: boolean,\n catalogTrackName?: string,\n mpuSequenceNumber?: number,\n packetSequenceNumber?: number,\n packetSequenceNumbers?: number[],\n repairedPacketSequenceNumbers?: number[],\n authoredTiming?: AuthoredTiming,\n deliveryProvenance?: MmtpDeliveryProvenance,\n ): void {\n if (!this.#ordersSamples()) this.#drainRecoveredUpTo(timestamp)\n this.#emitSample(\n data,\n timestamp,\n this.#frameDuration,\n keyframe,\n true,\n catalogTrackName,\n mpuSequenceNumber,\n packetSequenceNumber,\n packetSequenceNumbers,\n repairedPacketSequenceNumbers,\n authoredTiming,\n deliveryProvenance,\n )\n }\n\n /** Build and emit init segment from catalog config. */\n emitInit(): void {\n let init: Uint8Array | null = null\n if (this._videoConfig?.avcC) {\n init = buildVideoInitSegment(\n this._videoConfig.avcC,\n this._videoConfig.width,\n this._videoConfig.height,\n this.#config.timescale,\n this.#config.trackId,\n )\n } else if (this._audioConfig) {\n init = buildAudioInitSegment(\n this._audioConfig.sampleRate,\n this._audioConfig.channels,\n this._audioConfig.asc,\n this.#config.timescale,\n this.#config.trackId,\n )\n }\n if (init) this.onInitSegment?.(init)\n }\n\n /** Drain all ordered samples at end-of-stream or before reconfiguration. */\n flush(): void {\n if (this.#ordersSamples()) {\n this.#drainSampleReorder(true)\n return\n }\n if (this.#pendingRecovered.size === 0) return\n const frames = [...this.#pendingRecovered.values()].sort((a, b) => a.timestamp - b.timestamp)\n this.#pendingRecovered.clear()\n const gated = this.#applyVideoKeyframeGate(frames)\n if (gated) this.#emitPerSample(gated, undefined, false)\n else if (this.#config.trackType === 'video') {\n this.#sampleOrderStats.noRapFlushDrops += frames.length\n }\n }\n\n /** Drop all queued state and begin a fresh timestamp epoch. */\n reset(): void {\n this.#blocks.clear()\n this.#pendingFlush.clear()\n this.#pendingRecovered.clear()\n this.#sequence = 1\n this.#baseTs = -1\n this.#lastEmittedTs = -1\n this.#lastSourceTs = -1\n this.#recoveredHorizonTs = -1\n this.#lastDecodableAudioFrame = null\n this.#armedVideo = false\n this.#lastEmittedSbn = -1\n this.#sampleReorderQueue = []\n }\n\n dispose(): void {\n this.reset()\n this.onSegment = null\n this.onSample = null\n this.onInitSegment = null\n if (this.#aggregateTimer) {\n clearInterval(this.#aggregateTimer)\n this.#aggregateTimer = null\n }\n }\n\n // \u2500\u2500 Private \u2500\u2500\n\n /** Rebase an absolute timestamp to relative media time (starting near 0). */\n #rebase(timestamp: number): number {\n if (this.#baseTs < 0) this.#baseTs = timestamp\n return timestamp - this.#baseTs\n }\n\n #normalizeSampleData(data: Uint8Array): Uint8Array {\n return this.#config.trackType === 'audio' ? normalizeAacSampleForCmaf(data) : data\n }\n\n #enqueueRecovered(frames: BlockFrame[]): void {\n for (const frame of frames) {\n if (this.#lastEmittedTs >= 0 && frame.timestamp <= this.#lastEmittedTs) {\n this.#sampleOrderStats.staleSamples++\n continue\n }\n if (this.#pendingRecovered.has(frame.timestamp)) {\n this.#sampleOrderStats.duplicateSamples++\n continue\n }\n this.#pendingRecovered.set(frame.timestamp, frame)\n }\n }\n\n #drainRecoveredUpTo(horizonTs: number): void {\n if (this.#pendingRecovered.size === 0) return\n const ready = [...this.#pendingRecovered.values()]\n .filter(frame => frame.timestamp <= horizonTs)\n .sort((a, b) => a.timestamp - b.timestamp)\n\n for (const frame of ready) {\n this.#pendingRecovered.delete(frame.timestamp)\n this.#emitSample(\n frame.data,\n frame.timestamp,\n this.#frameDuration,\n frame.keyframe,\n false,\n frame.catalogTrackName,\n frame.mpuSequenceNumber,\n frame.packetSequenceNumber,\n frame.packetSequenceNumbers,\n frame.repairedPacketSequenceNumbers,\n frame.authoredTiming,\n frame.deliveryProvenance,\n )\n this.#dumpEmit('recovered-drain', [frame])\n }\n }\n\n /** Flush a block's buffered frames as a multi-sample CMAF segment. */\n #flushBlock(sbn: number): void {\n const block = this.#blocks.get(sbn)\n if (!block || block.size === 0) {\n this.#blocks.delete(sbn)\n return\n }\n\n // Sort by ESI to restore original frame order\n const entries = [...block.entries()].sort((a, b) => a[0] - b[0])\n const sortedFrames = entries.map(([, f]) => f)\n this.#blocks.delete(sbn)\n\n if (!this.#claimSbnEmission(sbn)) return\n const frames = this.#applyVideoKeyframeGate(sortedFrames)\n if (!frames) return\n\n this.#dumpEmit('flushBlock', frames, sbn)\n this.#emitPerSample(frames)\n }\n\n /** Flush a block with fill frames for missing ESI slots (FEC timeout path). */\n #flushBlockWithFill(sbn: number): void {\n const block = this.#blocks.get(sbn)\n if (!block) return\n\n const k = this.#config.k\n const isVideo = this.#config.trackType === 'video'\n const firstAudioFrame = !isVideo\n ? [...block.values()].sort((a, b) => a.timestamp - b.timestamp)[0]?.data\n : undefined\n const audioConcealmentFrame = this.#lastDecodableAudioFrame ?? firstAudioFrame\n\n // Build full K-slot array: real frames + fill for missing\n const allFrames: BlockFrame[] = []\n let lastRealTs = -1\n let firstRealTs = -1\n\n // Find min/max timestamps from received frames to interpolate missing ones\n for (const [, f] of block) {\n if (firstRealTs < 0 || f.timestamp < firstRealTs) firstRealTs = f.timestamp\n if (f.timestamp > lastRealTs) lastRealTs = f.timestamp\n }\n const estimatedBlockDuration = block.size > 1\n ? (lastRealTs - firstRealTs) / (block.size - 1) * k\n : this.#frameDuration * k\n\n for (let esi = 0; esi < k; esi++) {\n const real = block.get(esi)\n if (real) {\n allFrames.push(real)\n } else {\n // Interpolate timestamp for missing slot\n const interpolatedTs = firstRealTs >= 0\n ? firstRealTs + Math.round(esi * estimatedBlockDuration / k)\n : (this.#lastEmittedTs >= 0\n ? this.#lastEmittedTs + this.#frameDuration * (esi + 1)\n : 0)\n allFrames.push({\n // A synthesized AAC access unit is not portable across every\n // AudioSpecificConfig. Repeat known-good encoded audio instead;\n // if this is the first block, use its earliest received sample.\n data: isVideo\n ? this.#fillFrame\n : (audioConcealmentFrame?.slice() ?? new Uint8Array(0)),\n timestamp: interpolatedTs,\n // Video: first fill is keyframe (IDR). Audio: all fill marked keyframe.\n keyframe: isVideo ? !block.has(0) && esi === 0 : true,\n })\n }\n }\n\n this.#blocks.delete(sbn)\n if (!this.#claimSbnEmission(sbn)) return\n const gatedFrames = this.#applyVideoKeyframeGate(allFrames)\n if (!gatedFrames) return\n\n this.#emitPerSample(gatedFrames)\n }\n}\n\nfunction isH264Codec(codec?: string): boolean {\n const c = codec?.toLowerCase()\n return c?.startsWith('avc1') === true || c?.startsWith('avc3') === true\n}\n\nfunction describeH264NalTypes(nals: Uint8Array[]): number[] {\n return nals.slice(0, 16).map((nal) => (\n nal[0] !== undefined ? nal[0] & 0x1f : -1\n ))\n}\n", "/**\n * CMAF batcher for MoQ group boundaries.\n *\n * Collects frames via addFrame() and emits a single multi-sample moof+mdat\n * segment on flush(). Designed to be called once per MoQ group so one\n * appendBuffer() call per group lands in MSE.\n *\n * Unlike CmafBlockAssembler (FEC-SBN keyed, for multicast), this class is\n * caller-driven \u2014 no SBN/ESI, no block timeouts. Call flush() when the\n * group's readFrame() loop returns done.\n *\n * Timestamps passed to addFrame() MUST be in the same timescale the caller\n * used to build the init segment (typically microseconds). The batcher does\n * not scale \u2014 it rebases per-instance (first sample \u2192 0) and passes\n * inter-frame deltas as sample durations.\n */\n\nimport { encodeMultiSampleSegment } from './mmtp-to-cmaf.js'\n\nexport interface CmafGroupBatcherConfig {\n trackType: 'video' | 'audio'\n /** ISO track ID. Must match the trackId used when building the init segment. */\n trackId: number\n /**\n * Duration (in the same units as timestamp) for the last sample of a group\n * with exactly one frame (no inter-frame delta to measure). Derive from\n * catalog: video = 1_000_000 / fps; audio = 1_000_000 * 1024 / sampleRate.\n */\n fallbackFrameDuration: number\n}\n\ninterface BufferedFrame {\n data: Uint8Array\n timestamp: number\n keyframe: boolean\n}\n\nexport class CmafGroupBatcher {\n readonly trackType: 'video' | 'audio'\n readonly #trackId: number\n readonly #fallbackFrameDuration: number\n\n #buffer: BufferedFrame[] = []\n #sequence = 1\n #baseTs = -1\n #disposed = false\n\n onSegment: ((segment: Uint8Array) => void) | null = null\n\n constructor(config: CmafGroupBatcherConfig) {\n if (!Number.isFinite(config.trackId) || config.trackId <= 0) {\n throw new Error(`CmafGroupBatcher: trackId must be a positive finite number (got ${config.trackId})`)\n }\n if (!Number.isFinite(config.fallbackFrameDuration) || config.fallbackFrameDuration <= 0) {\n throw new Error(\n `CmafGroupBatcher: fallbackFrameDuration must be positive and finite \u2014 derive from catalog (video=1e6/fps, audio=1e6*1024/sampleRate). Got ${config.fallbackFrameDuration}`,\n )\n }\n this.trackType = config.trackType\n this.#trackId = config.trackId\n this.#fallbackFrameDuration = config.fallbackFrameDuration\n }\n\n addFrame(data: Uint8Array, timestamp: number, keyframe: boolean): void {\n if (this.#disposed) return\n this.#buffer.push({ data, timestamp, keyframe })\n }\n\n flush(): void {\n if (this.#disposed) return\n const frames = this.#buffer\n if (frames.length === 0) return\n this.#buffer = []\n\n if (this.#baseTs < 0) this.#baseTs = frames[0].timestamp\n const relTs = frames[0].timestamp - this.#baseTs\n\n const samples = frames.map((f, i) => {\n let duration: number\n if (i < frames.length - 1) {\n duration = frames[i + 1].timestamp - f.timestamp\n } else {\n duration = i > 0 ? (f.timestamp - frames[i - 1].timestamp) : this.#fallbackFrameDuration\n }\n if (duration <= 0) duration = this.#fallbackFrameDuration\n return { data: f.data, duration, keyframe: f.keyframe }\n })\n\n const segment = encodeMultiSampleSegment(samples, relTs, this.#sequence++, this.#trackId)\n this.onSegment?.(segment)\n }\n\n reset(): void {\n this.#buffer = []\n this.#sequence = 1\n this.#baseTs = -1\n }\n\n dispose(): void {\n this.#disposed = true\n this.#buffer = []\n this.onSegment = null\n }\n}\n", "/**\n * TransportPool \u2014 ref-counted subscription management for per-track transport.\n *\n * Tracks which consumers (identified by trackName) hold references to which\n * subscriptions (identified by a string key). When the last consumer releases\n * a subscription, it is closed.\n */\n\n/** Transport subscription mode \u2014 matches TrackState in the state machine. */\nexport type SubscriptionMode = 'multicast' | 'amt' | 'moq'\n\n/** Direct Socket health report (IWA transport). */\nexport interface DirectSocketHealth {\n connected: boolean\n bytesReceived: number\n packetsReceived: number\n errors: number\n}\n\n/** Subscription handle returned by acquireSubscription. */\nexport interface Subscription {\n key: string\n mode: SubscriptionMode\n consumers: ReadonlySet<string>\n}\n\n/** Stats for a subscription. */\nexport interface SubscriptionStats {\n activeSubscriptions: number\n totalAcquires: number\n totalReleases: number\n}\n\n/** Options for acquiring a subscription. */\nexport interface AcquireOptions {\n /** Subscription key (e.g., \"(S,G,port)\" for multicast). */\n key: string\n /** Transport mode. */\n mode: SubscriptionMode\n /** Consumer identity (trackName). */\n consumer: string\n}\n\nexport interface TransportPoolOptions {\n /** Maximum concurrent subscriptions. 0 = unlimited. */\n maxSubscriptions?: number\n}\n\ntype FailedCallback = (sub: { key: string; consumers: Set<string>; error: unknown }) => void\n\nexport class TransportPool {\n readonly #subs = new Map<string, Subscription>()\n readonly #options: TransportPoolOptions\n #failedCb: FailedCallback | null = null\n #stats: SubscriptionStats = { activeSubscriptions: 0, totalAcquires: 0, totalReleases: 0 }\n\n constructor(options?: TransportPoolOptions) {\n this.#options = options ?? {}\n }\n\n /** Register callback for subscription failures. */\n onSubscriptionFailed(cb: FailedCallback): void {\n this.#failedCb = cb\n }\n\n /** Acquire a subscription for a consumer. Returns the subscription. */\n acquireSubscription(opts: AcquireOptions): Subscription\n acquireSubscription(key: string, consumer: string): Subscription\n acquireSubscription(keyOrOpts: string | AcquireOptions, consumer?: string): Subscription {\n const key = typeof keyOrOpts === 'string' ? keyOrOpts : keyOrOpts.key\n const cons = typeof keyOrOpts === 'string' ? consumer! : keyOrOpts.consumer\n const mode: SubscriptionMode = typeof keyOrOpts === 'object' ? keyOrOpts.mode : 'moq'\n\n let sub = this.#subs.get(key)\n if (!sub) {\n sub = { key, mode, consumers: new Set() }\n this.#subs.set(key, sub)\n this.#stats.activeSubscriptions++\n }\n ;(sub.consumers as Set<string>).add(cons)\n this.#stats.totalAcquires++\n return sub\n }\n\n /** Release a subscription for a consumer. Closes if last consumer. */\n releaseSubscription(key: string, consumer: string): void {\n const sub = this.#subs.get(key)\n if (!sub) return\n ;(sub.consumers as Set<string>).delete(consumer)\n this.#stats.totalReleases++\n if (sub.consumers.size === 0) {\n this.#subs.delete(key)\n this.#stats.activeSubscriptions--\n }\n }\n\n /** Release all subscriptions held by a consumer. */\n releaseAllForConsumer(consumer: string): void {\n for (const [key, sub] of this.#subs) {\n ;(sub.consumers as Set<string>).delete(consumer)\n if (sub.consumers.size === 0) {\n this.#subs.delete(key)\n this.#stats.activeSubscriptions--\n }\n }\n }\n\n /** Report a subscription failure. */\n reportFailure(key: string, error: unknown): void {\n const sub = this.#subs.get(key)\n if (sub) this.#failedCb?.({ key, consumers: sub.consumers as Set<string>, error })\n }\n\n /** Get stats. */\n get stats(): Readonly<SubscriptionStats> { return this.#stats }\n\n /** Clean up all subscriptions. */\n dispose(): void {\n this.#subs.clear()\n this.#failedCb = null\n this.#stats.activeSubscriptions = 0\n }\n\n /** Number of active subscriptions. */\n get size(): number {\n return this.#subs.size\n }\n}\n", "/**\n * FEC Block Processor\n *\n * Splits decoded FEC blocks (K symbols concatenated by WASM RaptorQ decoder)\n * into individual MFU fragments ready for reassembly and decoding.\n *\n * Extracted from moqtail.html's inline processFecBlock() for reuse across\n * both moq-lib and moqtail players. Pure function \u2014 no side effects.\n */\n\nimport {\n\tMMTP_EXT_TRAILER_HEADER_SIZE,\n\tMMTP_EXT_TYPE_ALTA_AUTH,\n\tMMTP_HEADER_SIZE,\n\tMPU_HEADER_SIZE,\n\textractMFUPayload,\n\tparseMPUHeader,\n\tparseMMTPHeader,\n} from \"@blockcast/mmt-base\";\n\n/** A single parsed symbol from a decoded FEC block */\nexport interface ParsedFecSymbol {\n\t/** Symbol index within the block (0..K-1) */\n\tindex: number;\n\t/** MMTP packet ID (1=video, 2=audio) */\n\tpacketId: number;\n\t/** MMTP packet sequence number (for dedup across delivery paths) */\n\tpacketSequenceNumber: number;\n\t/** MPU fragment_type: 0=init (ftyp+moov), 2=MFU data */\n\tfragmentType: number;\n\t/** MFU fragment ready for MFUReassembler.addFragment() */\n\tfragment: {\n\t\tmpuSequenceNumber: number;\n\t\tfragmentationIndicator: number;\n\t\tfragmentCounter: number;\n\t\tdata: Uint8Array;\n\t\ttimestamp: bigint;\n\t\trapFlag: boolean;\n\t\tpriority?: number;\n\t\tsampleNumber?: number;\n\t\tmovieFragmentSequenceNumber?: number;\n\t\tsampleOffset?: number;\n\t\titemId?: number;\n\t};\n\t/** Whether this symbol carries keyframe metadata (RAP or high MFU priority). */\n\tisKeyframe: boolean;\n\t/** Raw symbol data (needed for audio decode path) */\n\trawSymbol: Uint8Array;\n}\n\n/** Result of processing a decoded FEC block */\nexport interface ProcessFecBlockResult {\n\t/** All valid parsed symbols for the owning track, sorted by serial packet sequence. */\n\tsymbols: ParsedFecSymbol[];\n\t/** Count of symbols that failed to parse */\n\tfailedCount: number;\n\t/** Failure reasons keyed by category */\n\tfailReasons: Record<string, number>;\n}\n\nexport interface ProcessFecSymbolOptions {\n\t/** Symbol index within the block, used only for diagnostics. */\n\tindex?: number;\n\t/** Keep only symbols for this packetId. */\n\townerPacketId?: number;\n}\n\nfunction hasUniformPaddingFrom(data: Uint8Array, start: number): boolean {\n\tif (start >= data.length) return false;\n\tconst pad = data[start]!;\n\tif (pad !== 0x00 && pad !== 0xee) return false;\n\tfor (let i = start + 1; i < data.length; i++) {\n\t\tif (data[i] !== pad) return false;\n\t}\n\treturn true;\n}\n\nfunction maybeExtendAltaTrailer(symbolData: Uint8Array, packetEnd: number): number {\n\tif (packetEnd + MMTP_EXT_TRAILER_HEADER_SIZE > symbolData.length) return packetEnd;\n\tconst view = new DataView(\n\t\tsymbolData.buffer,\n\t\tsymbolData.byteOffset + packetEnd,\n\t\tsymbolData.byteLength - packetEnd,\n\t);\n\tconst extType = view.getUint16(0, false);\n\tif (extType !== MMTP_EXT_TYPE_ALTA_AUTH) return packetEnd;\n\n\tconst extLen = view.getUint16(2, false);\n\tconst trailerEnd = packetEnd + MMTP_EXT_TRAILER_HEADER_SIZE + extLen;\n\treturn trailerEnd <= symbolData.length ? trailerEnd : packetEnd;\n}\n\nfunction trimDecodedSourceSymbol(symbolData: Uint8Array): Uint8Array | null {\n\tif (symbolData.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null;\n\n\tconst headerLength = parseMMTPHeader(symbolData).headerLength;\n\tif (symbolData.length < headerLength + MPU_HEADER_SIZE) return null;\n\tconst payload = symbolData.subarray(headerLength);\n\tconst mpu = parseMPUHeader(payload);\n\tconst trailingMpuHeaderSize = MPU_HEADER_SIZE - 2;\n\tif (mpu.payloadLength < trailingMpuHeaderSize) return null;\n\n\t// Recovered source symbols are fixed-width and may be padded, but their wire\n\t// boundary is still the ISO length-after-field coordinate. ALTA may extend the\n\t// packet from exactly that point; no MFU-content or alternate-length guessing.\n\tconst declaredEnd = headerLength + 2 + mpu.payloadLength;\n\tif (declaredEnd > symbolData.length) return null;\n\tconst packetEnd = maybeExtendAltaTrailer(symbolData, declaredEnd);\n\tif (packetEnd < symbolData.length && !hasUniformPaddingFrom(symbolData, packetEnd)) {\n\t\treturn null;\n\t}\n\treturn symbolData.subarray(0, packetEnd);\n}\n\nfunction emptyProcessResult(): ProcessFecBlockResult {\n\treturn {\n\t\tsymbols: [],\n\t\tfailedCount: 0,\n\t\tfailReasons: {},\n\t};\n}\n\nfunction isMfuKeyframeMetadata(rapFlag: boolean, priority?: number): boolean {\n\treturn rapFlag || (priority ?? 0) > 0x80;\n}\n\nfunction comparePacketSequence(a: ParsedFecSymbol, b: ParsedFecSymbol): number {\n\t// MMTP packet_sequence_number is a wrapping u32. A recovered FEC block spans\n\t// far less than half the serial-number space, so the signed low 32 bits give\n\t// its authored order across 0xffff_ffff -> 0. Index makes duplicate sequence\n\t// values deterministic without relying on Array#sort stability.\n\tconst serialDelta = (a.packetSequenceNumber - b.packetSequenceNumber) | 0;\n\treturn serialDelta !== 0 ? serialDelta : a.index - b.index;\n}\n\nfunction resultFromSymbols(\n\tparsed: ParsedFecSymbol[],\n\tfailedCount: number,\n\tfailReasons: Record<string, number>,\n\townerPacketId?: number,\n): ProcessFecBlockResult {\n\t// Sort by wrapping packetSequenceNumber to fix FEC interleaving scramble.\n\t// Reassembler needs FI=1 before FI=2/3 for the same mpuSequenceNumber.\n\tparsed.sort(comparePacketSequence);\n\n\t// When ownerPacketId is set, only keep symbols matching the track that owns\n\t// this FEC block. Other packetIds (including 0 from zero-padded recovery slots)\n\t// are garbage and must be rejected to prevent decoder crashes.\n\tconst symbols = ownerPacketId != null\n\t\t? parsed.filter((s) => s.packetId === ownerPacketId)\n\t\t: parsed;\n\n\treturn { symbols, failedCount, failReasons };\n}\n\nexport function processFecSymbol(\n\tsymbolData: Uint8Array,\n\toptions?: ProcessFecSymbolOptions,\n): ProcessFecBlockResult {\n\tconst failReasons: Record<string, number> = {};\n\tlet failedCount = 0;\n\tconst bump = (reason: string) => {\n\t\tfailedCount++;\n\t\tfailReasons[reason] = (failReasons[reason] ?? 0) + 1;\n\t};\n\n\tif (!symbolData || symbolData.length === 0) return emptyProcessResult();\n\tif (symbolData.length < MMTP_HEADER_SIZE) {\n\t\tbump(\"too_short\");\n\t\treturn resultFromSymbols([], failedCount, failReasons, options?.ownerPacketId);\n\t}\n\tif (((symbolData[0]! >> 6) & 0x03) !== 0) {\n\t\tbump(\"bad_hdr\");\n\t\treturn resultFromSymbols([], failedCount, failReasons, options?.ownerPacketId);\n\t}\n\n\ttry {\n\t\tconst hdr = parseMMTPHeader(symbolData);\n\t\t// Only process MPU packets (packetType=0x00). Skip signaling/repair\n\t\t// that may appear in the block due to FEC interleaving.\n\t\tif ((hdr.packetType & 0x0f) !== 0x00) {\n\t\t\tbump(`type_0x${hdr.packetType.toString(16)}`);\n\t\t\treturn resultFromSymbols([], failedCount, failReasons, options?.ownerPacketId);\n\t\t}\n\n\t\tconst packetData = trimDecodedSourceSymbol(symbolData);\n\t\tif (!packetData) {\n\t\t\tbump(\"bad_length\");\n\t\t\treturn resultFromSymbols([], failedCount, failReasons, options?.ownerPacketId);\n\t\t}\n\n\t\t// WASM-decoded source symbols retain fecType bits from the original FFmpeg\n\t\t// packet (fecType=1 per draft-ramadan-moq-mmt-00 \u00A73.1), but the 4-byte\n\t\t// Source FEC Payload ID (ISO 23008-1 \u00A7C.5.2) was stripped before feeding\n\t\t// to add_source(). Tell extractMFUPayload to skip FEC PID subtraction so it\n\t\t// does not truncate video data.\n\t\tconst extraction = extractMFUPayload(packetData, { skipFecId: true });\n\t\tif (!extraction) {\n\t\t\tbump(\"extract_fail\");\n\t\t\treturn resultFromSymbols([], failedCount, failReasons, options?.ownerPacketId);\n\t\t}\n\t\tconst sym: ParsedFecSymbol = {\n\t\t\tindex: options?.index ?? 0,\n\t\t\tpacketId: hdr.packetId,\n\t\t\tpacketSequenceNumber: hdr.sequenceNumber,\n\t\t\tfragmentType: extraction.fragmentType,\n\t\t\tfragment: {\n\t\t\t\tmpuSequenceNumber: extraction.mpu.mpuSequenceNumber,\n\t\t\t\tfragmentationIndicator: extraction.mpu.fragmentationIndicator,\n\t\t\t\tfragmentCounter: extraction.mpu.fragmentCounter,\n\t\t\t\tdata: extraction.data,\n\t\t\t\ttimestamp: hdr.timestamp,\n\t\t\t\trapFlag: hdr.rapFlag,\n\t\t\t\tpriority: extraction.mfuDuHeader?.priority,\n\t\t\t\tsampleNumber: extraction.mfuDuHeader?.sampleNumber,\n\t\t\t\tmovieFragmentSequenceNumber: extraction.mfuDuHeader?.movieFragmentSequenceNumber,\n\t\t\t\tsampleOffset: extraction.mfuDuHeader?.offset,\n\t\t\t\titemId: extraction.itemId ?? undefined,\n\t\t\t},\n\t\t\tisKeyframe: isMfuKeyframeMetadata(\n\t\t\t\thdr.rapFlag,\n\t\t\t\textraction.mfuDuHeader?.priority,\n\t\t\t),\n\t\t\trawSymbol: packetData,\n\t\t};\n\n\t\treturn resultFromSymbols([sym], failedCount, failReasons, options?.ownerPacketId);\n\t} catch {\n\t\tbump(\"exception\");\n\t\treturn resultFromSymbols([], failedCount, failReasons, options?.ownerPacketId);\n\t}\n}\n\n/**\n * Process a decoded FEC block into individual MFU fragments.\n *\n * The WASM RaptorQ decoder returns ALL K source symbols concatenated\n * (K x symbolSize bytes). This function splits them apart, parses\n * each MMTP header, extracts MFU payload via shared extractMFUPayload,\n * and returns structured results sorted for reassembly.\n *\n * @param blockData - Concatenated K source symbols from WASM decoder\n * @param options.k - Number of source symbols (derived from blockData.length / symbolSize if omitted)\n * @param options.symbolSize - Symbol size in bytes (default 1320)\n */\nexport function processFecBlock(\n\tblockData: Uint8Array,\n\toptions?: { k?: number; symbolSize?: number; ownerPacketId?: number },\n): ProcessFecBlockResult {\n\tconst empty = emptyProcessResult();\n\n\tif (!blockData || blockData.length === 0) return empty;\n\n\tconst symbolSize = options?.symbolSize ?? 1320;\n\tconst k =\n\t\toptions?.k ?? (symbolSize > 0 ? Math.round(blockData.length / symbolSize) : 0);\n\tif (k === 0) return empty;\n\n\tconst failReasons: Record<string, number> = {};\n\tlet failedCount = 0;\n\tconst parsed: ParsedFecSymbol[] = [];\n\n\tfor (let i = 0; i < k; i++) {\n\t\tconst symStart = i * symbolSize;\n\t\tif (symStart >= blockData.length) break;\n\t\tconst symEnd = Math.min(symStart + symbolSize, blockData.length);\n\n\t\tconst single = processFecSymbol(blockData.subarray(symStart, symEnd), { index: i });\n\t\tfor (const [reason, count] of Object.entries(single.failReasons)) {\n\t\t\tfailReasons[reason] = (failReasons[reason] ?? 0) + count;\n\t\t}\n\t\tfailedCount += single.failedCount;\n\t\tparsed.push(...single.symbols);\n\t}\n\n\treturn resultFromSymbols(parsed, failedCount, failReasons, options?.ownerPacketId);\n}\n", "/**\n * RFC 6330 Object Transmission Information (OTI) utilities.\n *\n * Shared OTI encoding/decoding for both moq-lib and moqtail players.\n * Eliminates duplicated OTI byte manipulation across codebases.\n */\n\nimport { parseMoqRepairFrame } from \"@blockcast/mmt-base\";\n\nexport interface OTIParams {\n\t/** Transfer length F (total source block size in bytes) */\n\ttransferLength: number;\n\t/** Symbol size T (bytes per symbol) */\n\tsymbolSize: number;\n\t/** Number of source blocks Z */\n\tsourceBlocks: number;\n\t/** Number of sub-blocks N */\n\tsubBlocks: number;\n\t/** Symbol alignment Al */\n\talignment: number;\n}\n\n/**\n * Build a 12-byte RFC 6330 Common + Scheme-Specific OTI from parameters.\n *\n * Wire format (per RFC 6330 \u00A74.3.1 + \u00A74.3.3):\n * byte 0: F high byte (bit 32 \u2014 transfer length is 40-bit)\n * bytes 1-4: F low 32 bits (big-endian)\n * byte 5: reserved (0)\n * bytes 6-7: T (symbol size, big-endian)\n * byte 8: Z (source blocks)\n * bytes 9-10: N (sub-blocks, big-endian)\n * byte 11: Al (alignment)\n */\nexport function buildOTI(params: OTIParams): Uint8Array {\n\tconst {\n\t\ttransferLength: F,\n\t\tsymbolSize: T,\n\t\tsourceBlocks: Z,\n\t\tsubBlocks: N,\n\t\talignment: Al,\n\t} = params;\n\tvalidateOTIFields(F, T, Z, N, Al);\n\tconst oti = new Uint8Array(12);\n\t// F: 40-bit big-endian (byte 0 = high, bytes 1-4 = low 32)\n\toti[0] = (F / 0x100000000) & 0xFF;\n\toti[1] = (F >>> 24) & 0xFF;\n\toti[2] = (F >>> 16) & 0xFF;\n\toti[3] = (F >>> 8) & 0xFF;\n\toti[4] = F & 0xFF;\n\t// byte 5: reserved\n\toti[6] = (T >>> 8) & 0xFF;\n\toti[7] = T & 0xFF;\n\toti[8] = Z & 0xFF;\n\toti[9] = (N >>> 8) & 0xFF;\n\toti[10] = N & 0xFF;\n\toti[11] = Al & 0xFF;\n\treturn oti;\n}\n\n/**\n * Build OTI from catalog FEC config (K and T).\n * F = K \u00D7 T (interleaved encoder convention from FFmpeg moqenc_mmt.c).\n */\nexport function buildOTIFromCatalog(K: number, T: number): Uint8Array {\n\tif (!Number.isInteger(K) || K <= 0 || K > 56_403) {\n\t\tthrow new Error(`K must be an integer in 1..56403 (got ${K})`);\n\t}\n\treturn buildOTI({\n\t\ttransferLength: K * T,\n\t\tsymbolSize: T,\n\t\tsourceBlocks: 1,\n\t\tsubBlocks: 1,\n\t\talignment: 8,\n\t});\n}\n\n/**\n * Parse a 12-byte OTI into structured parameters.\n */\nexport function parseOTI(oti: Uint8Array): OTIParams & { K: number } {\n\tif (oti.length !== 12) throw new Error(`OTI must be exactly 12 bytes (got ${oti.length})`);\n\tif (oti[5] !== 0) throw new Error(`OTI reserved byte 5 must be zero (got ${oti[5]})`);\n\tconst F = ((oti[0] & 0xFF) * 0x100000000) +\n\t\t(((oti[1] << 24) | (oti[2] << 16) | (oti[3] << 8) | oti[4]) >>> 0);\n\tconst T = (oti[6] << 8) | oti[7];\n\tconst Z = oti[8];\n\tconst N = (oti[9] << 8) | oti[10];\n\tconst Al = oti[11];\n\tvalidateOTIFields(F, T, Z, N, Al);\n\tconst K = Math.ceil(Math.ceil(F / T) / Z);\n\treturn { transferLength: F, symbolSize: T, sourceBlocks: Z, subBlocks: N, alignment: Al, K };\n}\n\nfunction validateOTIFields(F: number, T: number, Z: number, N: number, Al: number): void {\n\tif (!Number.isSafeInteger(F) || F <= 0 || F > 0xffffffffff) {\n\t\tthrow new Error(`F must be a positive 40-bit integer (got ${F})`);\n\t}\n\tif (!Number.isInteger(T) || T <= 0 || T > 0xffff) {\n\t\tthrow new Error(`T must be an integer in 1..65535 (got ${T})`);\n\t}\n\tif (!Number.isInteger(Z) || Z <= 0 || Z > 0xff) {\n\t\tthrow new Error(`Z must be an integer in 1..255 (got ${Z})`);\n\t}\n\tif (!Number.isInteger(N) || N <= 0 || N > 0xffff) {\n\t\tthrow new Error(`N must be an integer in 1..65535 (got ${N})`);\n\t}\n\tif (!Number.isInteger(Al) || Al <= 0 || Al > 0xff || T % Al !== 0) {\n\t\tthrow new Error(`Al must be an integer in 1..255 that divides T (got Al=${Al}, T=${T})`);\n\t}\n\tconst K = Math.ceil(Math.ceil(F / T) / Z);\n\tif (K > 56_403) {\n\t\tthrow new Error(`OTI requires K=${K}, above the RFC 6330 systematic limit 56403`);\n\t}\n}\n\n/**\n * Resolve a catalog FEC block's interleave window in milliseconds.\n *\n * The catalog carries exactly ONE interleave field: `interleaveDepthMs`, the\n * full FEC block span in MILLISECONDS (draft-ramadan-moq-fec \u00A75.1). Receivers\n * derive the block count D with `deriveFecInterleaveDepth`.\n *\n * Absent or non-positive \u2192 undefined; callers treat that as unconfigured and\n * error rather than guess. The legacy `interleaveDepth` frame-count key and\n * the `interleaveMs` alias were removed in the BLO-14526 clean cut \u2014 producers\n * emit milliseconds only, and there is deliberately NO fallback read.\n */\nexport type FecInterleaveCatalog = {\n\tinterleaveDepthMs?: number;\n};\n\nexport function catalogFecInterleaveMs(\n\tfec: FecInterleaveCatalog | undefined,\n): number | undefined {\n\tconst ms = fec?.interleaveDepthMs;\n\treturn typeof ms === \"number\" && ms > 0 ? ms : undefined;\n}\n\n/**\n * Derive the integer FEC interleave depth D from a catalog block span and the\n * duration of one media unit.\n *\n * `interleaveDepthMs` is nearest-millisecond catalog signaling while media\n * unit durations are commonly fractional. Invert that quantization with\n * positive-value round-half-up semantics:\n *\n * D = max(1, floor(interleaveDepthMs / mediaUnitDurationMs + 0.5))\n *\n * `Math.ceil` is not equivalent: 4000 / 33.333 is slightly greater than 120,\n * but represents D=120 rather than D=121.\n */\nexport function deriveFecInterleaveDepth(\n\tinterleaveDepthMs: number,\n\tmediaUnitDurationMs: number,\n): number {\n\tif (!Number.isFinite(interleaveDepthMs) || interleaveDepthMs <= 0) {\n\t\tthrow new Error(`[FEC] interleaveDepthMs must be finite and > 0 (got ${interleaveDepthMs})`);\n\t}\n\tif (!Number.isFinite(mediaUnitDurationMs) || mediaUnitDurationMs <= 0) {\n\t\tthrow new Error(`[FEC] mediaUnitDurationMs must be finite and > 0 (got ${mediaUnitDurationMs})`);\n\t}\n\treturn Math.max(1, Math.floor(interleaveDepthMs / mediaUnitDurationMs + 0.5));\n}\n\n/**\n * Compute the full FEC block completion timeout from the interleave window.\n *\n * The catalog's `interleaveDepthMs` IS the full FEC block span in\n * milliseconds (draft-ramadan-moq-fec \u00A75.1): producers emit\n * D \u00D7 unit-duration, and every symbol of a block \u2014 source and repair \u2014\n * arrives within that span. The full-block timeout is therefore\n * K-INDEPENDENT:\n *\n * timeout = interleaveDepthMs * safetyFactor\n *\n * The retired per-symbol-window model multiplied the interleave value by\n * (K-1); under \u00A75.1 semantics that overshoots ~(K-1)\u00D7 (K=32 @ 133ms \u2192\n * ~12.4s instead of ~0.4s). A caller that genuinely needs a per-sub-block\n * deadline scales the block span by K_sub / K instead \u2014 do NOT reintroduce\n * (K-1) here.\n *\n * @param interleaveDepthMs - Full FEC block span in ms (catalog fec.interleaveDepthMs)\n * @param safetyFactor - Jitter margin (default 3.0)\n * @returns Timeout in milliseconds\n */\nexport function computeFecTimeout(\n\tinterleaveDepthMs: number,\n\tsafetyFactor: number = 3.0,\n): number {\n\tif (interleaveDepthMs <= 0) {\n\t\tthrow new Error(`[FEC] interleaveDepthMs must be > 0 (got ${interleaveDepthMs}ms). Fix encoder config.`);\n\t}\n\t// interleaveDepthMs IS the block span. K=4, D=4 at 30fps:\n\t// interleaveDepthMs = 133ms \u2192 timeout = ceil(133 * 3) = 399ms.\n\treturn Math.ceil(interleaveDepthMs * safetyFactor);\n}\n\n/**\n * Absolute deadline for one FEC block.\n *\n * deadline = firstAcceptedAtMs + interleaveDepthMs + reorderToleranceMs\n *\n * Two properties matter, and both are contract, not preference:\n *\n * 1. The anchor is an ABSOLUTE receiver-local timestamp of the first accepted\n * packet of the block -- not \"now when we noticed\", and not a duration a\n * caller starts counting at an unspecified moment. A relative timeout\n * cannot say WHEN a block was due, so it cannot yield playback latency and\n * it cannot be compared across two consumers that started their timers at\n * different instants.\n *\n * 2. The reorder/jitter allowance is ADDITIVE and SIGNALED, never a multiplier\n * on the span. Jitter is a property of the network path; the FEC block span\n * is a property of the encoder. Multiplying them couples two independent\n * quantities: at a 133ms video span a x3 factor grants 266ms of slack, and\n * at a 43ms audio span only 86ms -- for the same network. The tolerance\n * therefore arrives as its own catalog value and is added once.\n *\n * Both arguments are required. There is no default tolerance: a missing value\n * is an encoder/catalog bug and must surface at the boundary, not be papered\n * over with a constant here.\n *\n * @param firstAcceptedAtMs - Receiver-local ms timestamp of the block's first accepted packet\n * @param interleaveDepthMs - Full FEC block span in ms (catalog fec.interleaveDepthMs)\n * @param reorderToleranceMs - Declared jitter/reorder allowance in ms (catalog fec.reorderToleranceMs)\n * @returns Absolute receiver-local deadline in milliseconds\n */\nexport function fecBlockDeadlineMs(params: {\n\tfirstAcceptedAtMs: number;\n\tinterleaveDepthMs: number;\n\treorderToleranceMs: number;\n}): number {\n\tconst { firstAcceptedAtMs, interleaveDepthMs, reorderToleranceMs } = params;\n\tif (!Number.isFinite(firstAcceptedAtMs)) {\n\t\tthrow new Error(`[FEC] firstAcceptedAtMs must be finite (got ${firstAcceptedAtMs}).`);\n\t}\n\treturn firstAcceptedAtMs + fecBlockBudgetMs(interleaveDepthMs, reorderToleranceMs);\n}\n\n/**\n * Relative budget for one FEC block: span + declared tolerance.\n *\n * Only for consumers whose interface genuinely takes a duration (the WASM\n * reassembler's `timeoutMs`). Anything that can hold a timestamp should use\n * `fecBlockDeadlineMs` instead, so the anchor stays explicit and latency stays\n * extractable. This is deliberately NOT `span * factor` -- see the note on\n * `fecBlockDeadlineMs` for why the tolerance is additive.\n */\nexport function fecBlockBudgetMs(interleaveDepthMs: number, reorderToleranceMs: number): number {\n\t// Finite, not merely > 0: Infinity satisfies `> 0` and would yield an\n\t// infinite budget, which fecBlockDeadlineMs then turns into a deadline no\n\t// block can ever miss -- the eviction bound silently stops bounding. The\n\t// tolerance check below already required finiteness; this is the same bar\n\t// for the encoder's span.\n\tif (!Number.isFinite(interleaveDepthMs) || !(interleaveDepthMs > 0)) {\n\t\tthrow new Error(\n\t\t\t`[FEC] interleaveDepthMs must be a finite value > 0 (got ${interleaveDepthMs}ms). Fix encoder config.`,\n\t\t);\n\t}\n\tif (!(reorderToleranceMs >= 0) || !Number.isFinite(reorderToleranceMs)) {\n\t\tthrow new Error(\n\t\t\t`[FEC] reorderToleranceMs must be a finite value >= 0 (got ${reorderToleranceMs}ms). ` +\n\t\t\t\t`It is a catalog value (fec.reorderToleranceMs), not a local default.`,\n\t\t);\n\t}\n\treturn Math.ceil(interleaveDepthMs + reorderToleranceMs);\n}\n\n/**\n * Playback latency for a block, from the same anchor the deadline uses.\n *\n * latency = presentedAtMs - firstAcceptedAtMs\n *\n * Both timestamps are receiver-local, so this needs NO publisher/receiver\n * clock agreement. That is the point of anchoring the deadline absolutely:\n * publisher-stamped media time and receiver BCTM disagree by roughly the\n * quantity we are trying to measure, which makes any cross-host subtraction\n * unusable at a 33ms target. This subtraction is immune to that skew.\n *\n * It measures first-accepted-packet to presentation. It is NOT glass-to-glass\n * and must not be reported as such -- capture and encode sit outside both\n * timestamps.\n */\nexport function playbackLatencyMs(params: { presentedAtMs: number; firstAcceptedAtMs: number }): number {\n\tconst { presentedAtMs, firstAcceptedAtMs } = params;\n\tif (!Number.isFinite(presentedAtMs) || !Number.isFinite(firstAcceptedAtMs)) {\n\t\tthrow new Error(`[FEC] playback latency needs two finite timestamps (got ${presentedAtMs}, ${firstAcceptedAtMs}).`);\n\t}\n\tif (presentedAtMs < firstAcceptedAtMs) {\n\t\tthrow new Error(\n\t\t\t`[FEC] presentedAtMs ${presentedAtMs} precedes firstAcceptedAtMs ${firstAcceptedAtMs}; ` +\n\t\t\t\t`the two timestamps must come from one receiver-local clock.`,\n\t\t);\n\t}\n\treturn presentedAtMs - firstAcceptedAtMs;\n}\n\n/**\n * Extract FEC parameters from an MMTP repair packet.\n *\n * Per ISO/IEC 23008-1 Section C.5.3, the 13-byte Repair FEC Payload ID\n * follows the complete MMTP header, including optional C/X fields.\n *\n * Returns raw fields \u2014 caller passes directly to WASM add_repair(ss_start, ssb_length, rs_id, data).\n * No SBN/ESI derivation \u2014 the WASM decoder handles block assignment.\n */\nexport function parseRepairPacket(packet: Uint8Array): {\n\tssStart: number; ssbLength: number; rsId: number; repairData: Uint8Array;\n} | null {\n\tconst parsed = parseMoqRepairFrame(packet);\n\tif (!parsed) return null;\n\treturn {\n\t\tssStart: parsed.ssStart,\n\t\tssbLength: parsed.ssbLength,\n\t\trsId: parsed.rsId,\n\t\trepairData: parsed.symbolData,\n\t};\n}\n", "/**\n * FEC Block Mapper \u2014 \u00A78.3 Source Symbol ESI Derivation\n *\n * Maps between MoQ transport identifiers (group_id, object_id) and\n * FEC block coordinates (SBN, ESI, SS_ID) per draft-ramadan-moq-fec-00 \u00A78.3.\n *\n * Shared by both players (audio/video pacing) and IWA (visualization + override).\n *\n * The formulas:\n * D = max(1, floor(interleaveDepthMs / gopDurationMs + 0.5))\n * SBN = floor(G / D) // source block number\n * first_group = SBN * D\n * symbols_per_group = ceil(K / D)\n * ESI = (G - first_group) * symbols_per_group + O\n * SS_ID = SBN * K + ESI\n */\n\nimport { deriveFecInterleaveDepth } from \"./oti.js\"\n\n/** Configuration from catalog FEC parameters. */\nexport interface FecBlockConfig {\n /** Block time span from catalog fec.interleaveDepthMs (milliseconds). */\n interleaveDepthMs: number\n /** GOP/frame duration (milliseconds). Measured from actual MMTP timestamps. */\n gopDurationMs: number\n /** K \u2014 source symbols per block from catalog fec.sourceSymbols. */\n k: number\n}\n\n/** Result of mapping MoQ (group, object) \u2192 FEC coordinates. */\nexport interface FecCoordinates {\n /** Source Block Number */\n sbn: number\n /** Encoding Symbol ID within block (0..K-1 for source symbols) */\n esi: number\n /** Flat source symbol ID: SBN * K + ESI */\n ssId: number\n /** Which group within the block (0..D-1) */\n groupInBlock: number\n /** Total groups per block (D) */\n groupsPerBlock: number\n}\n\nexport class FecBlockMapper {\n /** Groups per FEC block, recovered with positive round-half-up semantics. */\n readonly d: number\n /** Source symbols contributed by each group: ceil(K / D) */\n readonly symbolsPerGroup: number\n /** Source symbols per block */\n readonly k: number\n /** Block time span (ms) \u2014 used as pacing threshold */\n readonly interleaveDepthMs: number\n /** GOP/frame duration (ms) */\n readonly gopDurationMs: number\n\n constructor(config: FecBlockConfig) {\n if (config.k <= 0) throw new Error('k must be > 0')\n this.k = config.k\n this.interleaveDepthMs = config.interleaveDepthMs\n this.gopDurationMs = config.gopDurationMs\n this.d = deriveFecInterleaveDepth(config.interleaveDepthMs, config.gopDurationMs)\n this.symbolsPerGroup = Math.ceil(config.k / this.d)\n }\n\n /**\n * \u00A78.3: MoQ (group_id, object_id) \u2192 FEC (SBN, ESI, SS_ID).\n *\n * For a source object with MoQ Group_ID G and Object_ID O.\n */\n derive(groupId: number, objectId: number): FecCoordinates {\n const sbn = Math.floor(groupId / this.d)\n const firstGroup = sbn * this.d\n const groupInBlock = groupId - firstGroup\n const esi = groupInBlock * this.symbolsPerGroup + objectId\n return {\n sbn,\n esi,\n ssId: sbn * this.k + esi,\n groupInBlock,\n groupsPerBlock: this.d,\n }\n }\n\n /**\n * \u00A78.3 inverse: SS_ID \u2192 (SBN, ESI).\n *\n * For MMTP over multicast UDP where the Source FEC Payload ID carries SS_ID directly.\n */\n fromSsId(ssId: number): { sbn: number; esi: number } {\n return {\n sbn: Math.floor(ssId / this.k),\n esi: ssId % this.k,\n }\n }\n\n /**\n * Pacing threshold: one full block duration = interleaveDepthMs.\n * The minimum buffer needed before FEC recovery can start.\n */\n get pacingThresholdMs(): number {\n return this.interleaveDepthMs\n }\n\n /** Is this group the first in a new FEC block? (SBN boundary) */\n isBlockBoundary(groupId: number): boolean {\n return groupId % this.d === 0\n }\n\n /** Block start/end group IDs for a given SBN. For IWA visualization. */\n blockRange(sbn: number): { firstGroup: number; lastGroup: number } {\n return {\n firstGroup: sbn * this.d,\n lastGroup: (sbn + 1) * this.d - 1,\n }\n }\n\n /** CMAF segment name for a block. Matches MoQ group naming convention. */\n segmentName(trackName: string, sbn: number): string {\n return `${trackName}/sbn_${sbn}`\n }\n\n /** Expected number of samples in a complete CMAF segment for this block. */\n get samplesPerBlock(): number {\n return this.k\n }\n}\n\n/**\n * Null/raw mode: no FEC. Every group is its own \"block\".\n * Used when catalog has no FEC config (ATSC raw, DVB dongles, no-FEC MMTP).\n * IWA uses this for passthrough visualization.\n */\nexport class NullBlockMapper extends FecBlockMapper {\n constructor(gopDurationMs: number) {\n if (gopDurationMs <= 0) throw new Error('NullBlockMapper: gopDurationMs must be > 0 (derive from catalog framerate or sampleRate)')\n // D=1, K=1: each group is a single-symbol block\n super({ interleaveDepthMs: gopDurationMs, gopDurationMs, k: 1 })\n }\n\n derive(groupId: number, objectId: number): FecCoordinates {\n return {\n sbn: groupId,\n esi: objectId,\n ssId: groupId,\n groupInBlock: 0,\n groupsPerBlock: 1,\n }\n }\n\n get pacingThresholdMs(): number {\n return 0\n }\n}\n\n/**\n * Object-level FEC (\u00A79.1): each MoQ group (frame) is its own FEC block.\n * K_actual = ceil(frame_bytes / symbolSize) \u2014 varies per block.\n *\n * SBN = group_id, ESI = object_id.\n * SS_ID = Group_ID * sourceSymbols + Object_ID (\u00A79.1.2).\n *\n * This mapper does NOT carry per-block K. Each block discovers its own K from:\n * 1. Clean reception: counting source objects per group\n * 2. Repair packets: RSB_length in Repair FEC Payload ID (\u00A77.1)\n *\n * Callers MUST pass per-block K to FecBlockManager.addSourcePacket/addRepairPacket.\n * Created once per stream, GC'd on stream close (Effect cleanup).\n */\nexport class ObjectBlockMapper extends FecBlockMapper {\n /** Catalog sourceSymbols (max K) \u2014 used ONLY for SS_ID encoding (\u00A79.1.2).\n * Actual per-block K varies: K_actual = ceil(frame_bytes / symbolSize). */\n readonly sourceSymbolsMax: number\n\n constructor(gopDurationMs: number, sourceSymbolsMax: number) {\n if (sourceSymbolsMax <= 0) throw new Error('ObjectBlockMapper: sourceSymbolsMax must be > 0 (from catalog)')\n // D=1, K=sourceSymbolsMax for SS_ID math only \u2014 actual block K varies\n super({ interleaveDepthMs: gopDurationMs, gopDurationMs, k: sourceSymbolsMax })\n this.sourceSymbolsMax = sourceSymbolsMax\n }\n\n /** SBN = group_id, ESI = object_id. SS_ID per \u00A79.1.2. */\n derive(groupId: number, objectId: number): FecCoordinates {\n return {\n sbn: groupId,\n esi: objectId,\n ssId: groupId * this.sourceSymbolsMax + objectId,\n groupInBlock: 0,\n groupsPerBlock: 1,\n }\n }\n\n /**\n * Decode SS_ID using block-specific K from repair metadata.\n * In object mode, the global K is meaningless \u2014 each block has different K.\n * Callers must pass blockK (from RSB_length in repair FEC Payload ID).\n */\n fromSsId(ssId: number, blockK?: number): { sbn: number; esi: number } {\n if (blockK && blockK > 0) {\n return {\n sbn: Math.floor(ssId / blockK),\n esi: ssId % blockK,\n }\n }\n // Fallback: treat ssId as raw ESI (within current block)\n return { sbn: 0, esi: ssId }\n }\n\n get pacingThresholdMs(): number {\n return this.gopDurationMs // one frame latency\n }\n\n isBlockBoundary(_groupId: number): boolean {\n return true // every group starts a new block\n }\n\n get samplesPerBlock(): number {\n return 0 // variable \u2014 not known until frame arrives\n }\n}\n\n/**\n * Create a FecBlockMapper from catalog FEC config + measured GOP duration.\n * Returns NullBlockMapper when FEC is not configured.\n * Returns ObjectBlockMapper when fecMode is 'object'.\n */\nexport function createMapper(\n fecConfig: { interleaveDepthMs?: number; sourceSymbols?: number; mode?: string } | undefined,\n gopDurationMs: number,\n): FecBlockMapper {\n if (!fecConfig || !fecConfig.sourceSymbols) {\n return new NullBlockMapper(gopDurationMs)\n }\n // extensions feature \u2014 see moqcast-draft docs/fec-extensions-material.md, not in public -00\n if (fecConfig.mode === 'object') {\n return new ObjectBlockMapper(gopDurationMs, fecConfig.sourceSymbols)\n }\n return new FecBlockMapper({\n interleaveDepthMs: fecConfig.interleaveDepthMs && fecConfig.interleaveDepthMs > 0\n ? fecConfig.interleaveDepthMs\n : gopDurationMs,\n gopDurationMs,\n k: fecConfig.sourceSymbols,\n })\n}\n\n/** A registered track with its mapper and time anchor. */\nexport interface BridgedTrack {\n mapper: FecBlockMapper\n /** Wall-clock time (ms) when group 0 of this track started. */\n epochMs: number\n}\n\n/**\n * Cross-track SBN alignment via wall-clock timestamps.\n *\n * Different physical layers have different FEC configs (WiFi K=4/80ms,\n * cellular K=32/4000ms, ATSC K=64/10000ms). Each track has its own\n * FecBlockMapper. The bridge converts between them using wall-clock\n * timestamps as the universal coordinate.\n */\nexport class FecTimestampBridge {\n #tracks = new Map<string, BridgedTrack>()\n\n /** Register a track with its FEC mapper and start epoch. */\n register(trackName: string, mapper: FecBlockMapper, epochMs: number): void {\n this.#tracks.set(trackName, { mapper, epochMs })\n }\n\n /** Remove a track. */\n unregister(trackName: string): void {\n this.#tracks.delete(trackName)\n }\n\n /** All registered track names. */\n get trackNames(): string[] {\n return [...this.#tracks.keys()]\n }\n\n /** Get the mapper for a specific track. */\n getMapper(trackName: string): FecBlockMapper | undefined {\n return this.#tracks.get(trackName)?.mapper\n }\n\n /**\n * Resolve a wall-clock timestamp to SBN on every registered track.\n * Returns a map of trackName \u2192 { sbn, groupInBlock, groupsPerBlock }.\n */\n resolve(wallClockMs: number): Map<string, { sbn: number; groupId: number; groupInBlock: number }> {\n const result = new Map<string, { sbn: number; groupId: number; groupInBlock: number }>()\n for (const [name, track] of this.#tracks) {\n const elapsedMs = wallClockMs - track.epochMs\n if (elapsedMs < 0) continue\n const groupId = Math.floor(elapsedMs / track.mapper.gopDurationMs)\n const coords = track.mapper.derive(groupId, 0)\n result.set(name, { sbn: coords.sbn, groupId, groupInBlock: coords.groupInBlock })\n }\n return result\n }\n\n /**\n * Wall-clock time range for a specific block on a specific track.\n */\n blockTimeRange(trackName: string, sbn: number): { startMs: number; endMs: number } | undefined {\n const track = this.#tracks.get(trackName)\n if (!track) return undefined\n const range = track.mapper.blockRange(sbn)\n return {\n startMs: track.epochMs + range.firstGroup * track.mapper.gopDurationMs,\n endMs: track.epochMs + (range.lastGroup + 1) * track.mapper.gopDurationMs,\n }\n }\n\n /**\n * Find which SBNs on other tracks overlap a given block on a source track.\n */\n overlappingBlocks(\n sourceTrack: string,\n sourceSbn: number,\n targetTrack: string,\n ): { firstSbn: number; lastSbn: number } | undefined {\n const source = this.#tracks.get(sourceTrack)\n const target = this.#tracks.get(targetTrack)\n if (!source || !target) return undefined\n const sourceRange = this.blockTimeRange(sourceTrack, sourceSbn)\n if (!sourceRange) return undefined\n const targetStartGroup = Math.floor((sourceRange.startMs - target.epochMs) / target.mapper.gopDurationMs)\n const targetEndGroup = Math.floor((sourceRange.endMs - target.epochMs) / target.mapper.gopDurationMs)\n const firstCoords = target.mapper.derive(Math.max(0, targetStartGroup), 0)\n const lastCoords = target.mapper.derive(Math.max(0, targetEndGroup), 0)\n return { firstSbn: firstCoords.sbn, lastSbn: lastCoords.sbn }\n }\n}\n", "/**\n * TrackSession \u2014 per-track transport state machine.\n * SessionManager \u2014 orchestrates all sessions + TransportPool.\n *\n * Implements Patent Claim 9 (draft-ramadan-moq-fec APPLICATION-1):\n * (a) subscribe unicast\n * (b) simultaneously join multicast\n * (c) render from unicast during join\n * (d) activate multicast on RAP, suppress unicast\n * (e) cross-transport FEC repair during transition\n * (f) hysteresis before switching away from multicast\n * (g) re-subscribe unicast on sustained failure\n *\n * PIM: manages transport state without inspecting packet contents.\n */\n\nimport {\n TransportPool,\n type Subscription,\n type SubscriptionMode,\n type AcquireOptions,\n type DirectSocketHealth,\n type TransportPoolOptions,\n} from './transport-pool.js'\nimport { deriveFecInterleaveDepth } from '@blockcast/mmt-fec'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type TrackState =\n | 'idle'\n | 'unicast'\n | 'unicast-fallback'\n | 'joining'\n | 'multicast'\n | 'amt'\n | 'failing'\n\nexport interface TrackSessionConfig {\n trackName: string\n trackType: 'video' | 'audio'\n packetId: number\n /** FEC interleave depth from catalog (ms). Used for hysteresis timer. */\n fecInterleaveMs: number\n /** FEC K from catalog. Used for audio worklet latency calculation. */\n fecK: number\n /** FEC repair symbols per block (P). NOT symbol size. */\n fecRepairCount: number\n /** FEC symbol size in bytes (T). Required for WASM decoder OTI. */\n fecSymbolSize: number\n /** MoQ repair track name from catalog FEC config. */\n repairTrackName?: string\n /** Frame samples from initData parsing (audio-init-parser.ts). */\n frameSamples?: number\n /** Audio sample rate from catalog. */\n sampleRate?: number\n /** Ordered transport preference: best first. */\n preferredTransports: SubscriptionMode[]\n /** Subscription key for multicast/AMT: \"(S,G,port)\". */\n multicastKey?: string\n /** MoQ track name for unicast subscription. */\n moqTrackName?: string\n /** AMT relay address (if AMT is in preference chain). */\n amtRelay?: string\n /** MoQ relay URL (for WebTransport subscription). */\n moqRelayUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// PIM callbacks (protocol-independent \u2014 transport state only)\n// ---------------------------------------------------------------------------\n\nexport interface SessionManagerCallbacks {\n /** Fired on every transport state transition. */\n onStateChange?(trackName: string, from: TrackState, to: TrackState): void\n /** Fired to suppress/unsuppress unicast for this track (transport arbitration). */\n onSuppress?(trackName: string, suppressed: boolean): void\n /**\n * Fired when a track sat in `joining` for SessionManager.takeoverDeadlineMs\n * without a RAP (BLO-9413/D9). The session has already returned to `unicast`\n * (nothing was suppressed, so nothing to restore) \u2014 the host should abandon\n * the multicast attempt (e.g. MediaRouter.failbackToMoq()) so the router\n * stops waiting for a keyframe that is never coming.\n */\n onTakeoverTimeout?(trackName: string): void\n}\n\n// ---------------------------------------------------------------------------\n// MMTP application-layer extension (FEC lifecycle, media resets)\n// ---------------------------------------------------------------------------\n\n/**\n * MMTP track events (discriminated union).\n * Each protocol layer defines its own event union \u2014 MMTP events cover\n * media resets, latency, and FEC lifecycle.\n */\nexport type MmtpTrackEvent =\n | { type: 'reset' }\n | { type: 'latency'; latencyMs: number }\n | { type: 'fec-setup' }\n | { type: 'fec-teardown' }\n\n/**\n * MMTP application-layer extension of SessionManagerCallbacks.\n * Single onTrackEvent dispatches all MMTP-specific events.\n * PIM consumers use the base SessionManagerCallbacks (no onTrackEvent).\n *\n * IWA can define its own subinterface with its own TrackEvent union\n * (e.g., IwaTrackEvent for socket health, AMT state) using the same pattern.\n */\nexport interface MmtpSessionCallbacks extends SessionManagerCallbacks {\n onTrackEvent?(trackName: string, event: MmtpTrackEvent, config: TrackSessionConfig): void\n}\n\n/**\n * Loss-hysteresis window covering the signaled interleave window plus jitter.\n *\n * This path lacks media cadence, so it intentionally does not estimate a full\n * source-plus-repair production horizon. FECManager uses\n * computeFecDeliveryWindowMs with media cadence when configuring block\n * inactivity deadlines.\n *\n * `fecK` no longer feeds the math; it is still validated as a catalog\n * well-formedness guard and kept in the signature for positional API compat\n * (removal is a follow-up with the next cross-repo pin bump).\n */\nexport function computeFecLossWindowMs(fecInterleaveMs: number, fecK: number, jitterBufferMs?: number): number {\n return computeFecDeliveryWindowMs({\n interleaveDepthMs: fecInterleaveMs,\n sourceSymbols: fecK,\n jitterBufferMs,\n })\n}\n\nexport interface FecDeliveryWindowConfig {\n interleaveDepthMs: number\n sourceSymbols: number\n repairSymbols?: number\n symbolSize?: number\n mediaUnitsPerSecond?: number\n bandwidthBps?: number\n /**\n * Jitter + one-way-delay + reorder budget in ms (draft-ramadan-moq-fec \u00A79),\n * sourced from the catalog `jitterBufferMs`. Added on top of the max-of-spans\n * core; it is NOT a multiplier. See the margin-policy note in the function.\n */\n jitterBufferMs?: number\n}\n\n/**\n * Catalog-derived silence window that covers interleave geometry plus the time\n * required to produce or serialize a complete source+repair block. The catalog\n * field remains D media units; this function does not feed mapper geometry.\n */\nexport function computeFecDeliveryWindowMs(\n config: FecDeliveryWindowConfig,\n): number {\n const fecInterleaveMs = config.interleaveDepthMs\n const fecK = config.sourceSymbols\n if (fecInterleaveMs <= 0) {\n throw new Error(\n `[TrackSession] fecInterleaveMs=${fecInterleaveMs}. ` +\n `Derive a positive FEC interleave window from the stream catalog.`\n )\n }\n if (fecK <= 0) {\n throw new Error(\n `[TrackSession] fecK=${fecK}. ` +\n `Derive a positive FEC source-symbol count from the stream catalog.`\n )\n }\n\n const spansMs = [fecInterleaveMs]\n if (config.mediaUnitsPerSecond && config.mediaUnitsPerSecond > 0) {\n const mediaUnitMs = 1000 / config.mediaUnitsPerSecond\n // interleaveDepthMs is integer catalog signaling while media-unit duration\n // is often fractional (for example 4/30s = 133.33ms). Recover D using the\n // producer's nearest-ms quantization, then cover every source and repair\n // round-robin cycle. A decoder cannot recover a partial MFU until the\n // repair tail arrives, so a K-only deadline expires the source fragments\n // before the final repair symbols that complete them.\n const interleaveUnits = deriveFecInterleaveDepth(fecInterleaveMs, mediaUnitMs)\n const deliverySymbols = fecK + Math.max(0, config.repairSymbols ?? 0)\n spansMs.push(deliverySymbols * interleaveUnits * mediaUnitMs)\n }\n if (\n config.symbolSize &&\n config.symbolSize > 0 &&\n config.bandwidthBps &&\n config.bandwidthBps > 0\n ) {\n const repairSymbols = Math.max(0, config.repairSymbols ?? 0)\n spansMs.push(\n ((fecK + repairSymbols) * config.symbolSize * 8 * 1000) /\n config.bandwidthBps,\n )\n }\n\n // draft-ramadan-moq-fec \u00A79: the recovery timeout is a DERIVED budget \u2014\n // max(interleave span, source+repair production horizon, block serialization) PLUS\n // an ADDITIVE margin for jitter + one-way delay + reorder. It MUST NOT be a\n // fixed multiple of the interleave span (this replaces the retired `* 3` /\n // `* 2` safety factors). The margin is the catalog `jitterBufferMs`.\n //\n // MARGIN-POLICY (operator sign-off \u2014 see PR): no catalog currently emits\n // `jitterBufferMs`. Rather than throw (which would break every existing\n // catalog) or invent a magic constant, the default margin is the un-rounded\n // interleave window \u2014 the spec's natural reorder-budget bound and already a\n // catalog value. Emit `jitterBufferMs` in the catalog to set it explicitly.\n const coreMs = Math.max(...spansMs)\n const marginMs =\n config.jitterBufferMs !== undefined && config.jitterBufferMs >= 0\n ? config.jitterBufferMs\n : fecInterleaveMs\n return Math.ceil(coreMs) + Math.ceil(marginMs)\n}\n\n/** Convert the shared FEC delivery deadline into an integer media timescale. */\nexport function computeFecDeliveryWindowMediaUnits(\n config: FecDeliveryWindowConfig,\n timestampTimescale: number,\n): number {\n if (!Number.isFinite(timestampTimescale) || timestampTimescale <= 0) {\n throw new Error(\n `[TrackSession] timestampTimescale=${timestampTimescale}. ` +\n 'Derive a positive media timescale from the container.',\n )\n }\n const deliveryWindow = Math.ceil(\n computeFecDeliveryWindowMs(config) * timestampTimescale / 1000,\n )\n if (!Number.isSafeInteger(deliveryWindow)) {\n throw new Error('[TrackSession] FEC delivery window exceeds the media timestamp range.')\n }\n return deliveryWindow\n}\n\n// ---------------------------------------------------------------------------\n// TrackSession\n// ---------------------------------------------------------------------------\n\nexport class TrackSession {\n readonly config: TrackSessionConfig\n #state: TrackState = 'idle'\n #subscription: Subscription | null = null\n #hysteresisTimer: ReturnType<typeof setTimeout> | null = null\n #joinDeadlineTimer: ReturnType<typeof setTimeout> | null = null\n #previousPrimary: SubscriptionMode | null = null\n #manager: SessionManager\n // Single suppression authority flag (BLO-9413 failback coherence): every\n // suppress this session fires is recorded here, and every path that leaves\n // a suppressed state unwinds through #unsuppress(). Before this flag,\n // unsubscribe() only un-suppressed from multicast/amt \u2014 a session torn down\n // from `failing` (exactly where a starving track sits) was re-subscribed to\n // unicast with the host's media still muted: MoQ playing into a dead sink.\n #suppressFired = false\n\n constructor(config: TrackSessionConfig, manager: SessionManager) {\n this.config = config\n this.#manager = manager\n }\n\n // -----------------------------------------------------------------------\n // State\n // -----------------------------------------------------------------------\n\n get state(): TrackState { return this.#state }\n get subscription(): Subscription | null { return this.#subscription }\n\n /** True while this session's onSuppress(true) is outstanding (no matching\n * onSuppress(false) yet). Test/assertion surface for failback coherence. */\n get isSuppressed(): boolean { return this.#suppressFired }\n\n /** Current active transport mode, or null if idle. */\n get transport(): SubscriptionMode | null {\n switch (this.#state) {\n case 'multicast': return 'multicast'\n case 'amt': return 'amt'\n case 'unicast': return 'moq'\n case 'unicast-fallback': return 'moq' // SSM bridge absent \u2014 MoQ unicast is the only source\n case 'joining': return 'moq' // MoQ still primary during join\n case 'failing': return this.#previousPrimary\n default: return null\n }\n }\n\n // -----------------------------------------------------------------------\n // Computed (no magic numbers)\n // -----------------------------------------------------------------------\n\n /** Audio worklet latency: max(fecInterleaveMs, frameDurationMs * fecK).\n * Returns undefined if frame duration unknown (no initData). */\n get audioLatencyMs(): number | undefined {\n const fd = this.frameDurationMs\n if (fd === undefined) {\n return this.config.fecInterleaveMs > 0 ? this.config.fecInterleaveMs : undefined\n }\n return Math.max(this.config.fecInterleaveMs, fd * this.config.fecK)\n }\n\n /** Frame duration in ms, derived from frameSamples + sampleRate. */\n get frameDurationMs(): number | undefined {\n const { frameSamples, sampleRate } = this.config\n if (!frameSamples || !sampleRate || sampleRate <= 0) return undefined\n return (frameSamples / sampleRate) * 1000\n }\n\n // -----------------------------------------------------------------------\n // Transitions (Patent Claim 9)\n // -----------------------------------------------------------------------\n\n /** idle \u2192 unicast: player subscribes to this track. */\n subscribe(): void {\n if (this.#state !== 'idle') return\n this.#transition('unicast')\n\n // Emit audio latency on first subscribe\n if (this.config.trackType === 'audio') {\n const latency = this.audioLatencyMs\n if (latency !== undefined) {\n this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'latency', latencyMs: latency }, this.config)\n }\n }\n }\n\n /** unicast \u2192 joining: multicast/AMT capability detected, join in progress. */\n joinStarted(): void {\n if (this.#state !== 'unicast') return\n this.#transition('joining')\n // FEC decoders must be ready before multicast data arrives (Claim 9e: cross-transport repair)\n this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'fec-setup' }, this.config)\n }\n\n /**\n * any non-fallback state \u2192 unicast-fallback. Used when the SSM bridge is\n * absent (vanilla Chromium / no extension) and the host falls back to a\n * MoQ unicast subscription per track. Terminal-ish: activateMulticast,\n * failbackAll, and transportFailed are no-ops while in this state.\n *\n * Fires fec-setup so the per-track repair worker spins up \u2014 repair packets\n * still flow over the MoQ repair-track subscription, so FEC source+repair\n * can converge as in the multicast path.\n *\n * Audio latency is NOT re-emitted here: subscribe() already fires latency\n * once on the idle \u2192 unicast edge, and enterUnicastFallback is only ever\n * reached from a non-idle state (SessionManager skips idle sessions).\n */\n enterUnicastFallback(): void {\n if (this.#state === 'unicast-fallback') return\n this.#clearHysteresis()\n this.#transition('unicast-fallback')\n // Suppress the redundant unicast media decoder. On the no-bridge path the\n // host feeds MoQ-unicast source packets into MulticastManager (the render\n // consumer); a second base-media subscribe (the @moq/watch decoder) would\n // split the single connection's QUIC flow-control budget and stall delivery\n // (BLO-8969). Fire suppress here, on the fallback edge, so the decoder is\n // gated off before it subscribes rather than after the relay forward task\n // has already wedged.\n this.#suppress()\n this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'fec-setup' }, this.config)\n }\n\n /**\n * unicast-fallback \u2192 unicast. Called when the SSM bridge appears mid-session\n * (extension installed after fallback engaged). Host first aborts the fallback\n * drainers, then transitions sessions back to unicast so the regular\n * multicast/AMT preference chain can take over via joinStarted().\n */\n exitUnicastFallback(): void {\n if (this.#state !== 'unicast-fallback') return\n this.#transition('unicast')\n // Un-suppress the unicast decoder: the bridge appeared mid-session, so the\n // host tears down the fallback drainers and the regular preference chain\n // (joinStarted -> joining -> rapReceived) will re-suppress on multicast RAP.\n this.#unsuppress()\n }\n\n /**\n * joining \u2192 multicast or amt: RAP received on the joined transport (Claim 9d).\n * Resets audio worklet, then suppresses MoQ.\n */\n rapReceived(mode: 'multicast' | 'amt'): void {\n if (this.#state !== 'joining') return\n this.#previousPrimary = mode\n this.#transition(mode)\n if (this.config.trackType === 'audio') {\n this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'reset' }, this.config)\n }\n this.#suppress()\n }\n\n /** multicast/amt \u2192 failing: loss detected, start hysteresis timer (Claim 9f). */\n lossDetected(): void {\n if (this.#state !== 'multicast' && this.#state !== 'amt') return\n this.#previousPrimary = this.#state as SubscriptionMode\n this.#transition('failing')\n\n // Hysteresis timer: sustained loss before failback (Claim 9f). The window\n // covers a complete FEC block plus safety margin: the catalog interleave\n // value IS the block span (interleaveDepthMs, \u00A75.1), scaled by the same\n // safety factor FECManager uses for its block timeout.\n const hysteresisMs = computeFecLossWindowMs(this.config.fecInterleaveMs, this.config.fecK)\n this.#hysteresisTimer = setTimeout(() => {\n this.hysteresisExpired()\n }, hysteresisMs)\n }\n\n /** failing \u2192 previous primary: loss recovered within hysteresis window. */\n lossRecovered(): void {\n if (this.#state !== 'failing') return\n this.#clearHysteresis()\n if (this.#previousPrimary) {\n this.#transition(this.#previousPrimary as TrackState)\n }\n }\n\n /**\n * Transport failed (AMT relay down, multicast interface gone).\n * Try next transport in preference chain.\n */\n transportFailed(): void {\n this.#clearHysteresis()\n // Unicast-fallback is terminal \u2014 the host owns the subscription and\n // SessionManager transport machinery does not arbitrate it.\n if (this.#state === 'unicast-fallback') return\n const current = this.#previousPrimary ?? this.transport\n const prefs = this.config.preferredTransports\n const currentIdx = current ? prefs.indexOf(current) : -1\n const nextMode = prefs[currentIdx + 1]\n\n if (nextMode) {\n // Failover to next in chain\n if (nextMode === 'moq') {\n this.#transition('unicast')\n this.#unsuppress()\n if (this.config.trackType === 'audio') {\n this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'reset' }, this.config)\n }\n } else {\n // Next is multicast or AMT \u2014 start joining. `joining` means MoQ is\n // primary again (see transport getter) \u2014 if the failed transport had\n // suppressed unicast, restore it for the join window or the viewer\n // watches black video until the next RAP (which may never come).\n this.#transition('joining')\n this.#unsuppress()\n }\n } else {\n // End of chain \u2014 fall back to unicast (Claim 9g)\n this.#transition('unicast')\n this.#unsuppress()\n if (this.config.trackType === 'audio') {\n this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'reset' }, this.config)\n }\n }\n }\n\n /** Hysteresis expired \u2192 failback to next transport (Claim 9g). */\n hysteresisExpired(): void {\n if (this.#state !== 'failing') return\n this.#clearHysteresis()\n this.transportFailed()\n }\n\n /** \u2192 idle: player unsubscribes / tab close / cleanup. */\n unsubscribe(): void {\n this.#clearHysteresis()\n if (this.#state !== 'idle') {\n this.#transition('idle')\n // Unwind any outstanding suppression regardless of which state we left.\n // The old `wasActive` check (multicast/amt only) leaked suppression when\n // tearing down from `failing` or `unicast-fallback` \u2014 failbackAll()'s\n // unsubscribe\u2192subscribe cycle then re-entered unicast with the host's\n // media still muted (BLO-9413 freeze: \"failing back\" logged, video dead).\n this.#unsuppress()\n }\n }\n\n /** True when SessionManager.enterUnicastFallback put this track into the\n * host-driven unicast subscription mode. Host can iterate sessions to\n * decide whether to keep its fallback drainer alive across MediaRouter\n * arbitration events. */\n get isUnicastFallback(): boolean {\n return this.#state === 'unicast-fallback'\n }\n\n dispose(): void {\n this.#clearHysteresis()\n this.#clearJoinDeadline()\n this.#state = 'idle'\n this.#subscription = null\n }\n\n // -----------------------------------------------------------------------\n // Internal\n // -----------------------------------------------------------------------\n\n #transition(to: TrackState): void {\n const from = this.#state\n if (from === to) return\n this.#state = to\n // Takeover deadline (BLO-9413/D9): every entry into `joining` arms it\n // (joinStarted AND transportFailed-failover both route through here);\n // every exit cancels it. Without this, a transport that accepts the\n // subscribe but never delivers a RAP parks the session in `joining`\n // forever (observed live in the Phase-0 headless repro).\n if (from === 'joining') this.#clearJoinDeadline()\n if (to === 'joining') this.#armJoinDeadline()\n // FEC teardown: leaving FEC-active states \u2192 non-FEC state\n const fecActive = (s: TrackState) =>\n s === 'joining' ||\n s === 'multicast' ||\n s === 'amt' ||\n s === 'failing' ||\n s === 'unicast-fallback'\n if (fecActive(from) && !fecActive(to)) {\n this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'fec-teardown' }, this.config)\n }\n this.#manager.callbacks?.onStateChange?.(this.config.trackName, from, to)\n }\n\n #clearHysteresis(): void {\n if (this.#hysteresisTimer !== null) {\n clearTimeout(this.#hysteresisTimer)\n this.#hysteresisTimer = null\n }\n }\n\n #armJoinDeadline(): void {\n this.#clearJoinDeadline()\n const deadlineMs = this.#manager.takeoverDeadlineMs\n if (deadlineMs <= 0) return // disabled\n this.#joinDeadlineTimer = setTimeout(() => {\n this.#joinDeadlineTimer = null\n if (this.#state !== 'joining') return\n // No RAP within the deadline: return to unicast. MoQ was never\n // suppressed in `joining`, so there is nothing to restore \u2014 this edge\n // must stay silent on onSuppress (asserted in tests).\n this.#transition('unicast')\n this.#manager.callbacks?.onTakeoverTimeout?.(this.config.trackName)\n }, deadlineMs)\n }\n\n #clearJoinDeadline(): void {\n if (this.#joinDeadlineTimer !== null) {\n clearTimeout(this.#joinDeadlineTimer)\n this.#joinDeadlineTimer = null\n }\n }\n\n /** Fire onSuppress(true) exactly once per suppression epoch. */\n #suppress(): void {\n if (this.#suppressFired) return\n this.#suppressFired = true\n this.#manager.callbacks?.onSuppress?.(this.config.trackName, true)\n }\n\n /** Unwind an outstanding suppression; no-op when none is outstanding. */\n #unsuppress(): void {\n if (!this.#suppressFired) return\n this.#suppressFired = false\n this.#manager.callbacks?.onSuppress?.(this.config.trackName, false)\n }\n}\n\n// ---------------------------------------------------------------------------\n// SessionManager\n// ---------------------------------------------------------------------------\n\nexport class SessionManager {\n readonly pool: TransportPool\n readonly sessions = new Map<string, TrackSession>()\n callbacks: SessionManagerCallbacks | null = null\n /**\n * Takeover deadline (BLO-9413/D9): max time a session may sit in `joining`\n * without a RAP before returning to `unicast` and firing onTakeoverTimeout.\n * MoQ is never suppressed during `joining`, so expiry is a silent abandon \u2014\n * playback never glitches. Hosts tune per stream (e.g. GOP length); 0\n * disables.\n */\n takeoverDeadlineMs = 5000\n\n /** Narrow to MMTP callbacks (onTrackEvent). Safe \u2014 optional chaining handles PIM-only consumers. */\n get mmtp(): MmtpSessionCallbacks | null {\n return this.callbacks as MmtpSessionCallbacks | null\n }\n\n constructor(poolOptions?: TransportPoolOptions) {\n this.pool = new TransportPool(poolOptions)\n\n // Wire pool failure \u2192 per-track failover\n this.pool.onSubscriptionFailed(sub => {\n for (const trackName of sub.consumers) {\n const session = this.sessions.get(trackName)\n if (session) session.transportFailed()\n }\n })\n }\n\n // -----------------------------------------------------------------------\n // Track management\n // -----------------------------------------------------------------------\n\n /** Add a track session from catalog config. */\n addTrack(config: TrackSessionConfig): TrackSession {\n if (this.sessions.has(config.trackName)) {\n throw new Error(`[SessionManager] Track \"${config.trackName}\" already exists`)\n }\n const session = new TrackSession(config, this)\n this.sessions.set(config.trackName, session)\n return session\n }\n\n /** Remove a track session. Releases subscription with grace period. */\n removeTrack(trackName: string): void {\n const session = this.sessions.get(trackName)\n if (!session) return\n session.unsubscribe()\n\n // Release pool subscription\n const subKey = session.config.multicastKey ?? session.config.moqTrackName\n if (subKey) this.pool.releaseSubscription(subKey, trackName)\n\n session.dispose()\n this.sessions.delete(trackName)\n }\n\n // -----------------------------------------------------------------------\n // Atomic multi-track operations\n // -----------------------------------------------------------------------\n\n /**\n * All tracks with multicast RAP \u2192 multicast state (atomic).\n * Called when MediaRouter fires onSourceSwitch('multicast').\n * Prevents split-brain between video and audio.\n *\n * A takeover deadline can return a track from `joining` to `unicast` while\n * the multicast socket keeps delivering packets. If a valid RAP arrives\n * later, allow that late-RAP edge to suppress unicast and promote multicast.\n */\n activateMulticast(): void {\n for (const [, session] of this.sessions) {\n if (session.state === 'unicast') {\n session.joinStarted()\n }\n if (session.state === 'joining') {\n session.rapReceived('multicast')\n }\n }\n }\n\n /**\n * All tracks with AMT RAP \u2192 amt state (atomic).\n */\n activateAmt(): void {\n for (const [, session] of this.sessions) {\n if (session.state === 'unicast') {\n session.joinStarted()\n }\n if (session.state === 'joining') {\n session.rapReceived('amt')\n }\n }\n }\n\n /**\n * All tracks \u2192 unicast (failback, atomic).\n * Called when MulticastManager fires onFailback().\n * unicast-fallback sessions are skipped \u2014 the host owns those subscriptions\n * and they must drain uninterrupted across transport-state events.\n *\n * Coherence invariant (BLO-9413): after this returns, every non-fallback\n * session is `unicast` (or `idle`) AND unsuppressed \u2014 unsubscribe() unwinds\n * any outstanding suppression regardless of the state it tears down from\n * (`failing` included; see TrackSession.#suppressFired).\n */\n failbackAll(): void {\n for (const [, session] of this.sessions) {\n if (\n session.state !== 'idle' &&\n session.state !== 'unicast' &&\n session.state !== 'unicast-fallback'\n ) {\n session.unsubscribe()\n session.subscribe() // re-enter unicast\n }\n }\n }\n\n /**\n * All tracks \u2192 unicast-fallback (atomic).\n * Called by the host when the multicast gateway is absent and\n * the player will subscribe to source tracks over MoQ unicast and feed\n * packets via MulticastManager.feedUnicastSource() (or manager.router\n * directly). Sessions in this state stay terminal until the host calls\n * unsubscribe() / disposes the SessionManager.\n */\n enterUnicastFallback(): void {\n for (const [, session] of this.sessions) {\n if (session.state === 'idle') continue\n session.enterUnicastFallback()\n }\n }\n\n /**\n * True if any session is currently in unicast-fallback. Hosts use this to\n * gate `multicastTrack.close()` on failback events \u2014 closing the shared\n * broadcast would kill the host's fallback drainers.\n */\n hasActiveFallback(): boolean {\n for (const [, session] of this.sessions) {\n if (session.isUnicastFallback) return true\n }\n return false\n }\n\n /**\n * All fallback tracks \u2192 unicast (atomic).\n * Called when the SSM bridge appears mid-session and the host has aborted\n * the fallback drainers. The session re-enters the regular preference chain\n * via joinStarted() (issued by the host immediately after).\n */\n exitUnicastFallback(): void {\n for (const [, session] of this.sessions) {\n if (session.isUnicastFallback) session.exitUnicastFallback()\n }\n }\n\n // -----------------------------------------------------------------------\n // Cleanup lifecycle\n // -----------------------------------------------------------------------\n\n /** Tab closed \u2014 release all subscriptions for this tab (grace period). */\n tabClosed(tabId: string): void {\n // All tracks owned by this tab \u2192 idle\n for (const [trackName, session] of this.sessions) {\n session.unsubscribe()\n this.pool.releaseAllForConsumer(trackName)\n }\n }\n\n /** Bridge WebTransport connection dropped \u2014 same as tab close. */\n bridgeDisconnected(): void {\n this.tabClosed('bridge')\n }\n\n /**\n * Track selection changed (user or ABR).\n * Old track released (grace period), new track subscribed.\n */\n trackSelectionChanged(oldTrackName: string, newConfig: TrackSessionConfig): void {\n this.removeTrack(oldTrackName)\n const session = this.addTrack(newConfig)\n session.subscribe()\n }\n\n // -----------------------------------------------------------------------\n // Query\n // -----------------------------------------------------------------------\n\n /** Get a track session by name. */\n getTrack(trackName: string): TrackSession | undefined {\n return this.sessions.get(trackName)\n }\n\n // -----------------------------------------------------------------------\n // Cleanup\n // -----------------------------------------------------------------------\n\n dispose(): void {\n for (const [, session] of this.sessions) {\n session.dispose()\n }\n this.sessions.clear()\n this.pool.dispose()\n this.callbacks = null\n }\n}\n", "/**\n * FEC Manager \u2014 codec-agnostic FEC decode service.\n *\n * Receives (packetId, sbn, esi, symbolData) tuples from any transport\n * (MMTP, LOC, etc.) and manages FEC block lifecycle: source counting,\n * WASM RaptorQ decode, block timeout, and recovery/failure notification.\n *\n * Extracted from MulticastManager to decouple FEC block logic from\n * MMTP packet parsing and frame routing.\n */\n\nimport { computeFecDeliveryWindowMs } from './track-session.js'\nimport { deriveFecInterleaveDepth } from '@blockcast/mmt-fec'\n\nimport type { MmtpDeliveryProvenance } from './mfu-reassembler.js'\nimport type {\n FecWorkerClient,\n FecTrackStats,\n FecBlockSnapshot,\n FrameMeta,\n FecWorkerErrorCode,\n} from '@blockcast/fec-worker'\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Minimum valid FEC interleave depth in milliseconds, per spec \u00A75.1\n * (the interactive/gaming floor; broadcast/ATSC 3.0 ranges up to 10000ms).\n *\n * A protocol constant, NOT a tuning knob. Values below this floor are almost\n * certainly a raw frame count (e.g. `2`) that a caller passed when no framerate\n * was available to convert to ms (common on audio tracks). Feeding such a value\n * into computeFecTimeout treats it as `2ms`, yielding a near-zero FEC block\n * timeout that abandons startup FEC blocks before their repair symbols arrive\n * (measured: moqtail startupFailed=4, shaka dropped=5.8% on startup). Timing\n * derivations MUST reject sub-floor values and fall back to the established\n * timeout instead of collapsing.\n */\nexport const MIN_INTERLEAVE_DEPTH_MS = 33\nconst WORKER_ERROR_LOG_CAP = 5\nconst WORKER_ERROR_KIND_CAP = 32\nconst WORKER_ERROR_OVERFLOW_KIND = 'other'\nconst STARTUP_REPAIR_WAIT_SOURCE_BOUNDARIES = 2\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Algorithm-agnostic FEC decoder backend interface.\n *\n * Implementations:\n * - RaptorQ WASM (mmt_wasm.js) \u2014 RFC 6330\n * - Reed-Solomon WASM \u2014 future\n * - XOR \u2014 future\n *\n * Source/repair symbol IDs follow ISO 23008-1 \u00A7C.5.2/C.5.3:\n * source: SS_ID (flat 32-bit symbol identifier)\n * repair: SS_Start + SSB_length + RS_ID (block-scoped repair coords)\n */\nexport type WasmFecDecoder = {\n add_source(ss_id: number, data: Uint8Array, ts: number): Uint8Array | null\n add_repair(ss_start: number, ssb_length: number, rs_id: number, data: Uint8Array, ts: number): Uint8Array | null\n configure(oti: Uint8Array): void\n cleanup(current_time: number): Uint8Array\n cleanup_blocks_detailed(sbns: Uint32Array): Uint8Array\n flush?(): void\n reset_stats?(): void\n}\n\n/** Algorithm-agnostic FEC stats snapshot. */\nexport interface FecStats {\n sourcePackets: number\n repairPackets: number\n duplicateSymbols: number\n blocksClean: number\n blocksRecovered: number\n blocksFailed: number\n greenFillFrames: number\n blocksComplete: number\n mixedPathBlocks: number\n recentBlockPathCounts: FecBlockPathCount[]\n k: number\n /** Of blocks with loss, what percentage were recovered (0-100). */\n recoveryRate: number\n /** What percentage of blocks had any loss (0-100). */\n packetLossRate: number\n /** Per-MMTP-packet-ID counters for the synchronous WASM decoder path. */\n tracks: FecPacketStats[]\n /** Latest Worker/WASM snapshot for each packet ID. */\n workers: Array<FecTrackStats & { packetId: number }>\n /** Main-thread repair symbols rejected before dispatch because their size did not match T. */\n repairSymbolSizeMismatches: number\n /** Bounded Worker error counts keyed by stable code, or message text for legacy errors. */\n workerErrors: Record<string, number>\n}\n\nexport interface FecPacketStats {\n packetId: number\n sourcePackets: number\n repairPackets: number\n duplicateSymbols: number\n blocksClean: number\n blocksRecovered: number\n blocksFailed: number\n greenFillFrames: number\n blocksComplete: number\n k: number\n recoveryRate: number\n packetLossRate: number\n}\n\nexport type FecSymbolSource = 'multicast' | 'moq'\n\nexport interface FecBlockPathCount {\n packetId: number\n sbn: number\n catalogTrackName?: string\n multicastSymbols: number\n moqSymbols: number\n complete: boolean\n}\n\ntype FecLifecycleTrace = {\n atMs: number\n event: 'repair-requested' | 'repair-arrived' | 'timeout-queued' | 'cleanup-ack' | 'recovered' | 'failed'\n packetId: number\n sbn: number\n catalogTrackName?: string\n sourceCount: number\n sourceEsis: number[]\n repairRsIds: number[]\n decoderPath: 'worker' | 'sync' | 'none'\n deadlineRemainingMs?: number\n pendingCleanup: boolean\n rsId?: number\n}\n\nexport interface FECManagerConfig {\n k?: number\n /** Symbol size in bytes (T). NOT repair count. */\n symbolSize?: number\n interleaveDepthMs?: number\n fps?: number\n /** Guarded block inactivity deadline derived from the full delivery geometry. */\n timeoutMs?: number\n}\n\ninterface ResolvedFECTrackConfig {\n k: number\n symbolSize: number\n interleaveDepthMs: number\n timeoutMs: number\n fps?: number\n}\n\ninterface MutableFecCounters {\n sourcePackets: number\n repairPackets: number\n duplicateSymbols: number\n blocksClean: number\n blocksRecovered: number\n blocksFailed: number\n greenFillFrames: number\n}\n\nfunction newFecCounters(): MutableFecCounters {\n return {\n sourcePackets: 0,\n repairPackets: 0,\n duplicateSymbols: 0,\n blocksClean: 0,\n blocksRecovered: 0,\n blocksFailed: 0,\n greenFillFrames: 0,\n }\n}\n\n// ---------------------------------------------------------------------------\n// FECManager\n// ---------------------------------------------------------------------------\n\nexport class FECManager {\n // Per-track WASM RaptorQ decoders, keyed by physical stream identity.\n #fecDecoders = new Map<string, { client: WasmFecDecoder; k: number }>()\n /** @deprecated Use setFecDecoder(packetId, ...). Backwards-compat shim. */\n\n // Per-track Worker-backed FEC decoders, keyed by physical stream identity.\n // Worker-backed tracks use async decode \u2014 recovered frames arrive via onFrame callback,\n // not synchronous return from feedSourceSymbol/feedRepairSymbol.\n #workerClients = new Map<string, FecWorkerClient>()\n // Worker setup can finish while an interleaved block is already collecting\n // on the sync decoder. Keep that block on its original decoder so later\n // source/repair symbols do not split across two RaptorQ states.\n #syncDecoderBlockOwners = new Map<string, WasmFecDecoder>()\n #decoderBlockOwners = new WeakMap<object, Map<number, {\n catalogTrackName?: string\n conflicted: boolean\n }>>()\n\n #fecK = 4\n #fecSymbolSize = 0\n #fecTimeoutMs = 500 // default 500ms; refined by setFramerate() when fps known\n #trackConfigs = new Map<number, ResolvedFECTrackConfig>()\n #fecStats = newFecCounters()\n #fecTrackStats = new Map<number, MutableFecCounters>()\n #fecWorkerStats = new Map<number, FecTrackStats>()\n #fecStatsObservers = new Set<(packetId: number, stats: FecTrackStats) => void>()\n\n // Block inactivity tracking: (packetId, SBN) -> timer. The deadline is\n // derived from catalog geometry and refreshed on progress; only an idle\n // block fails.\n #activeBlocks = new Map<string, ReturnType<typeof setTimeout>>()\n #activeBlockDeadlinesMs = new Map<string, number>()\n #activeBlockTimeoutOwners = new Map<string, object>()\n #lifecycleTrace: FecLifecycleTrace[] = []\n // Worker-backed timeouts enqueue cleanup behind all prior symbol messages.\n // Failure remains pending until cleanupComplete acknowledges that queue drain.\n #pendingWorkerCleanups = new Set<string>()\n #pendingWorkerCleanupClients = new Map<string, FecWorkerClient>()\n // Source symbol count per track-scoped block \u2014 marks complete when K symbols arrive.\n #blockSourceCounts = new Map<string, number>()\n #blockSourceEsis = new Map<string, Set<number>>()\n #blockRepairRsIds = new Map<string, Set<number>>()\n // Blocks already recovered \u2014 prevents late source symbols from restarting timeouts.\n // WASM removes blocks after recovery; late symbols create empty blocks returning null,\n // which would falsely start new timeouts without this guard.\n #completedBlocks = new Set<string>()\n #seenSymbols = new Set<string>()\n #blockPathCounts = new Map<string, FecBlockPathCount>()\n // Exact catalog identity observed on accepted symbols for each FEC block.\n // Conflicting identities collapse to null so recovery fails closed.\n #blockCatalogTrackNames = new Map<string, string | null>()\n // A receiver joins an already-running live stream in the middle of D\n // interleaved source blocks. This applies to both multicast and reliable MoQ:\n // a MoQ group boundary is not an FEC ESI=0 boundary. Those blocks are not\n // packet loss because their earlier ESIs predate the subscription. Ignore\n // each such block until ESI=0 establishes a complete boundary for the track.\n // After that boundary, a first observed ESI>0 is real delivery loss and must\n // continue through normal FEC recovery/failure accounting.\n #startupAlignedPackets = new Set<string>()\n #startupPartialBlocks = new Set<string>()\n // A live multicast join can observe an ESI=0 source boundary before the\n // repair lane catches up. Returning false here also drops that media packet\n // at MmtpRouter, so the readiness probe is multicast-only and bounded: an\n // actual multicast repair unlocks the next boundary, while a second source\n // boundary fails open. Reliable MoQ media continues through normal startup\n // alignment and retained MoQ repair cannot prove multicast readiness.\n #startupRequireRepairReady = false\n #startupRepairReadyPackets = new Set<string>()\n #startupRepairBoundaryCounts = new Map<string, number>()\n #startupRepairFailOpenPackets = new Set<string>()\n // SBNs that reached K source-symbol arrivals via trackBlock. Used by the\n // worker recovery suppression check (see onFrame handler below) so we only\n // suppress `recovered=true` emits for blocks that ALSO completed cleanly\n // via source symbols. Separating this from #completedBlocks (which is also\n // populated by every worker frame emit, by failed timeouts, and by external\n // callers) was the BLO-3323 L15 fix: the prior predicate caused blocksRecovered\n // to stay at 0 because the SBN was always in #completedBlocks by the time the\n // worker fired recovered=true.\n #cleanlyCompletedBlocks = new Set<string>()\n // D-28: blocks whose repair deadline already fired onRepairNeeded. At most\n // one repair request per block; entries are removed when the block\n // completes, recovers, or fails.\n #repairRequestedBlocks = new Set<string>()\n // D-28: repair-deadline timers refreshed by accepted source progress until\n // the first repair request. A `null` value marks a deadline that already\n // fired (never re-armed). At most K accepted source symbols can refresh the\n // horizon, so browser scheduling delay cannot create an early loss signal\n // and repair traffic cannot keep a block alive indefinitely.\n #repairDeadlines = new Map<string, ReturnType<typeof setTimeout> | null>()\n // Worker recovery emits all K source symbols. Track recovery once per block\n // while routing only ESI slots that were absent from the direct source path.\n #workerRecoveredBlocks = new Set<string>()\n\n // Framerate detection state\n #fpsDetected = false\n #fecFps?: number\n #fecInterleaveDepthMs = 0\n\n // Worker error log rate-limiter \u2014 bounds console.warn output and exported\n // error cardinality when the FEC Worker emits the same failure repeatedly\n // with volatile packet details (e.g. ssStart) in the message text.\n // Stable Worker codes aggregate those messages; uncoded/legacy messages are\n // capped into an `other` bucket. This also covers wasm-bindgen\n // \"recursive use of an object\" bubbled from add_source/add_repair during\n // overlapping message processing). Without this, Point 5 of the 7-point\n // validation harness \u2014 which caps headless warnings at 500 \u2014 fails even\n // though the errors are duplicates of a single underlying event.\n #workerErrorCounts = new Map<string, number>()\n #repairSymbolSizeMismatchCount = 0\n\n // Event callbacks \u2014 block lifecycle notifications for MmtpRouter wiring.\n // NOTE: For sync WASM path, recovery routing is handled by MmtpRouter from\n // feedSourceSymbol/feedRepairSymbol return values. For Worker path, recovery\n // arrives via onBlockRecovered callback.\n onBlockStarted:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null = null\n onBlockComplete:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null = null\n onBlockFailed:\n | ((sbn: number, packetId?: number, catalogTrackName?: string) => void)\n | null = null\n\n // D-28 on-demand repair callbacks. When onRepairNeeded is registered, each\n // block arms a one-shot repair deadline after the derived K-symbol source\n // horizon; a block still short of K when it fires requests a per-SBN MoQ\n // repair fetch upstream. The inactivity fail bound is unchanged either way \u2014\n // repair and failure are orthogonal timers, so wiring D-28 never delays\n // failure accounting. onRepairResolved fires when a repair-requested block\n // leaves the active set, with the outcome (\"completed\" after clean completion\n // or recovery, \"failed\" at the fail bound) so the upstream in-flight slot\n // (MulticastManager.clearRepairForBlock) can be released and logged.\n onRepairNeeded:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null = null\n onRepairResolved:\n | ((\n sbn: number,\n packetId: number,\n outcome: 'completed' | 'failed',\n catalogTrackName?: string,\n ) => void)\n | null = null\n\n // Worker-path callbacks \u2014 async recovery and stats from FecWorkerClient.\n onBlockRecovered: ((sbn: number, packetId: number, data: ArrayBuffer, meta: FrameMeta, catalogTrackName?: string) => void) | null = null\n onFecStatsUpdate: ((packetId: number, stats: FecTrackStats) => void) | null = null\n onGreenFill: ((sbn: number, packetId: number, data: ArrayBuffer) => void) | null = null\n\n #streamKey(packetId: number, catalogTrackName?: string): string {\n return catalogTrackName === undefined\n ? `${packetId}`\n : `${packetId}:${encodeURIComponent(catalogTrackName)}`\n }\n\n #belongsToPacket(key: string, packetId: number): boolean {\n return Number(key.split(':', 1)[0]) === packetId\n }\n\n #belongsToStream(key: string, packetId: number, catalogTrackName: string): boolean {\n if (!this.#belongsToPacket(key, packetId)) return false\n const parts = key.split(':')\n const encodedTrackName = encodeURIComponent(catalogTrackName)\n if (parts.length === 2) return parts[1] === encodedTrackName\n if (parts.length >= 3) return parts[2] === encodedTrackName\n return false\n }\n\n #packetIdsForStreamKeys(keys: Set<string>): number[] {\n return [...new Set([...keys].map((key) => Number(key.split(':', 1)[0])))]\n }\n\n #blockKey(sbn: number, packetId: number, catalogTrackName?: string): string {\n const base = `${packetId}:${sbn}`\n return catalogTrackName === undefined\n ? base\n : `${base}:${encodeURIComponent(catalogTrackName)}`\n }\n\n #parseBlockKey(key: string): { packetId: number; sbn: number; catalogTrackName?: string } {\n const [packetIdText, sbnText, ...catalogParts] = key.split(':')\n const catalogTrackName = catalogParts.length > 0\n ? decodeURIComponent(catalogParts.join(':'))\n : undefined\n return {\n packetId: Number(packetIdText) || 0,\n sbn: Number(sbnText) || 0,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n }\n }\n\n #decoderForPacket(packetId: number, catalogTrackName?: string) {\n return this.#fecDecoders.get(this.#streamKey(packetId, catalogTrackName))\n ?? this.#fecDecoders.get(this.#streamKey(packetId))\n }\n\n #workerForPacket(packetId: number, catalogTrackName?: string) {\n return this.#workerClients.get(this.#streamKey(packetId, catalogTrackName))\n ?? this.#workerClients.get(this.#streamKey(packetId))\n }\n\n #claimDecoderBlock(\n client: object,\n sbn: number,\n catalogTrackName?: string,\n ): boolean {\n let blocks = this.#decoderBlockOwners.get(client)\n if (!blocks) {\n blocks = new Map()\n this.#decoderBlockOwners.set(client, blocks)\n }\n const owner = blocks.get(sbn)\n if (!owner) {\n blocks.set(sbn, { catalogTrackName, conflicted: false })\n if (blocks.size > 512) {\n const stale = [...blocks.keys()].slice(0, 256)\n for (const key of stale) blocks.delete(key)\n }\n return true\n }\n if (owner.conflicted) return false\n if (owner.catalogTrackName === catalogTrackName) return true\n owner.conflicted = true\n return false\n }\n\n #decoderBlockCatalogTrackName(client: object, sbn: number): string | undefined {\n const owner = this.#decoderBlockOwners.get(client)?.get(sbn)\n return owner?.conflicted ? undefined : owner?.catalogTrackName\n }\n\n #resetDecoderBlockOwners(client: object, catalogTrackName: string): void {\n const blocks = this.#decoderBlockOwners.get(client)\n if (!blocks) return\n for (const [sbn, owner] of blocks) {\n if (owner.catalogTrackName === catalogTrackName || owner.conflicted) {\n blocks.delete(sbn)\n }\n }\n if (blocks.size === 0) this.#decoderBlockOwners.delete(client)\n }\n\n #discardFecDecoderClient(client: WasmFecDecoder): void {\n for (const [key, entry] of this.#fecDecoders) {\n if (entry.client === client) this.#fecDecoders.delete(key)\n }\n this.#decoderBlockOwners.delete(client)\n }\n\n #assertExclusiveDecoderClient(\n streamKey: string,\n client: object,\n registrations: Iterable<readonly [string, object]>,\n kind: 'decoder' | 'worker',\n ): void {\n for (const [registeredKey, registeredClient] of registrations) {\n if (registeredClient !== client || registeredKey === streamKey) continue\n throw new Error(\n `[FECManager] ${kind} client is already registered for stream ${registeredKey}; ` +\n `create one client per physical stream`,\n )\n }\n }\n\n #statsForPacket(packetId: number): MutableFecCounters {\n let stats = this.#fecTrackStats.get(packetId)\n if (!stats) {\n stats = newFecCounters()\n this.#fecTrackStats.set(packetId, stats)\n }\n return stats\n }\n\n #markPathBlockComplete(sbn: number, packetId: number, catalogTrackName?: string): void {\n const block = this.#blockPathCounts.get(this.#blockKey(sbn, packetId, catalogTrackName))\n if (block) block.complete = true\n }\n\n /** Cancel and forget a block's D-28 repair-deadline timer. */\n #clearRepairDeadline(key: string): void {\n const timer = this.#repairDeadlines.get(key)\n if (timer != null) clearTimeout(timer)\n this.#repairDeadlines.delete(key)\n }\n\n #requestRepair(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): void {\n const key = this.#blockKey(sbn, packetId, catalogTrackName)\n if (\n this.#completedBlocks.has(key) ||\n !this.onRepairNeeded ||\n this.#repairRequestedBlocks.has(key)\n ) return\n\n const deadline = this.#repairDeadlines.get(key)\n if (deadline) clearTimeout(deadline)\n this.#repairDeadlines.set(key, null)\n this.#repairRequestedBlocks.add(key)\n\n // Preserve the source-progress inactivity deadline and append exactly one\n // bounded repair-fetch grace. Once requested, noteBlockActivity() no\n // longer moves this terminal cleanup boundary.\n const config = this.#resolveTrackConfig(packetId)\n const graceMs = this.#repairGraceMs(config)\n const now = performance.now()\n const currentDeadline = this.#activeBlockDeadlinesMs.get(key) ?? now\n this.#armBlockTimeout(\n sbn,\n packetId,\n catalogTrackName,\n Math.max(1, Math.ceil(Math.max(now, currentDeadline) + graceMs - now)),\n )\n this.#traceLifecycle('repair-requested', sbn, packetId, catalogTrackName)\n if (catalogTrackName === undefined) {\n this.onRepairNeeded(sbn, packetId)\n } else {\n this.onRepairNeeded(sbn, packetId, catalogTrackName)\n }\n }\n\n #traceLifecycle(\n event: FecLifecycleTrace['event'],\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n rsId?: number,\n ): void {\n const key = this.#blockKey(sbn, packetId, catalogTrackName)\n const deadline = this.#activeBlockDeadlinesMs.get(key)\n const decoderPath = this.#syncDecoderBlockOwners.has(key)\n ? 'sync'\n : this.#workerForPacket(packetId, catalogTrackName)\n ? 'worker'\n : this.#decoderForPacket(packetId, catalogTrackName)\n ? 'sync'\n : 'none'\n this.#lifecycleTrace.push({\n atMs: Date.now(),\n event,\n packetId,\n sbn,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n sourceCount: this.#blockSourceCounts.get(key) ?? 0,\n sourceEsis: [...(this.#blockSourceEsis.get(key) ?? [])].sort((a, b) => a - b),\n repairRsIds: [...(this.#blockRepairRsIds.get(key) ?? [])].sort((a, b) => a - b),\n decoderPath,\n ...(deadline === undefined\n ? {}\n : { deadlineRemainingMs: Math.max(0, deadline - performance.now()) }),\n pendingCleanup: this.#pendingWorkerCleanups.has(key),\n ...(rsId === undefined ? {} : { rsId }),\n })\n if (this.#lifecycleTrace.length > 64) this.#lifecycleTrace.splice(0, 32)\n }\n\n #defaultTrackConfig(): ResolvedFECTrackConfig {\n return {\n k: this.#fecK,\n symbolSize: this.#fecSymbolSize,\n interleaveDepthMs: this.#fecInterleaveDepthMs,\n fps: this.#fecFps,\n timeoutMs: this.#fecTimeoutMs,\n }\n }\n\n #resolveTrackConfig(packetId: number): ResolvedFECTrackConfig {\n return this.#trackConfigs.get(packetId) ?? this.#defaultTrackConfig()\n }\n\n #computeTimeout(config: {\n k: number\n interleaveDepthMs: number\n fps?: number\n timeoutMs?: number\n }): number {\n if (config.timeoutMs && config.timeoutMs > 0) return config.timeoutMs\n // Only a valid \u00A75.1 ms interleave (>= MIN_INTERLEAVE_DEPTH_MS) drives the\n // timeout. A sub-floor value is a raw frame count (no fps to convert with);\n // fall back to the established #fecTimeoutMs rather than collapse to ~0.\n if (config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {\n return Math.max(\n this.#fecTimeoutMs,\n computeFecDeliveryWindowMs({\n interleaveDepthMs: config.interleaveDepthMs,\n sourceSymbols: config.k,\n mediaUnitsPerSecond: config.fps,\n }),\n )\n }\n return this.#fecTimeoutMs\n }\n\n #repairDeadlineMs(config: ResolvedFECTrackConfig): number {\n const halfFailBoundMs = Math.ceil(config.timeoutMs / 2)\n if (\n config.k > 0 &&\n config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS &&\n config.fps !== undefined &&\n Number.isFinite(config.fps) &&\n config.fps > 0\n ) {\n const mediaUnitMs = 1000 / config.fps\n const interleaveUnits = deriveFecInterleaveDepth(\n config.interleaveDepthMs,\n mediaUnitMs,\n )\n const sourceHorizonMs = Math.ceil(config.k * interleaveUnits * mediaUnitMs)\n if (sourceHorizonMs < config.timeoutMs) return sourceHorizonMs\n }\n return halfFailBoundMs\n }\n\n #repairGraceMs(config: ResolvedFECTrackConfig): number {\n return Math.max(1, config.timeoutMs - this.#repairDeadlineMs(config))\n }\n\n #armBlockTimeout(\n sbn: number,\n packetId: number,\n catalogTrackName: string | undefined,\n delayMs: number,\n ): void {\n const key = this.#blockKey(sbn, packetId, catalogTrackName)\n const current = this.#activeBlocks.get(key)\n if (current) clearTimeout(current)\n const boundedDelayMs = Math.max(1, Math.ceil(delayMs))\n this.#activeBlockDeadlinesMs.set(key, performance.now() + boundedDelayMs)\n const owner = {}\n this.#activeBlockTimeoutOwners.set(key, owner)\n const timer = setTimeout(() => {\n // clearTimeout() cannot retract a callback that the browser already\n // moved onto its task queue. Only the timer that still owns this\n // block's deadline may fail it or enqueue Worker cleanup.\n if (this.#activeBlockTimeoutOwners.get(key) !== owner) return\n this.#activeBlocks.delete(key)\n this.#activeBlockDeadlinesMs.delete(key)\n this.#activeBlockTimeoutOwners.delete(key)\n this.#traceLifecycle('timeout-queued', sbn, packetId, catalogTrackName)\n\n // A block that began on the synchronous decoder remains owned by that\n // decoder even if Worker setup completed later. Cleanup must follow the\n // same ownership rule as source and repair symbol dispatch.\n const syncOwner = this.#syncDecoderBlockOwners.get(key)\n if (syncOwner) {\n this.#routeSyncCleanup(syncOwner, sbn, packetId)\n this.#failBlock(sbn, packetId, false, catalogTrackName)\n return\n }\n\n const workerClient = this.#workerForPacket(packetId, catalogTrackName)\n if (workerClient) {\n this.#pendingWorkerCleanups.add(key)\n this.#pendingWorkerCleanupClients.set(key, workerClient)\n workerClient.cleanup(sbn)\n } else {\n const decoder = this.#decoderForPacket(packetId, catalogTrackName)?.client\n if (decoder) {\n this.#routeSyncCleanup(decoder, sbn, packetId)\n this.#failBlock(sbn, packetId, false, catalogTrackName)\n } else {\n this.#failBlock(sbn, packetId, false, catalogTrackName)\n }\n }\n }, boundedDelayMs)\n this.#activeBlocks.set(key, timer)\n }\n\n #upsertTrackConfig(packetId: number, config: FECManagerConfig): ResolvedFECTrackConfig {\n const previous = this.#trackConfigs.get(packetId) ?? this.#defaultTrackConfig()\n const timingChanged =\n config.k !== undefined ||\n config.interleaveDepthMs !== undefined ||\n config.fps !== undefined\n const next: ResolvedFECTrackConfig = {\n k: config.k ?? previous.k,\n symbolSize:\n config.symbolSize != null && config.symbolSize > 0\n ? config.symbolSize\n : previous.symbolSize,\n interleaveDepthMs: config.interleaveDepthMs ?? previous.interleaveDepthMs,\n fps: config.fps ?? previous.fps,\n timeoutMs: config.timeoutMs ?? (timingChanged ? 0 : previous.timeoutMs),\n }\n next.timeoutMs = this.#computeTimeout(next)\n this.#trackConfigs.set(packetId, next)\n return next\n }\n\n #normalizeSymbolData(packetId: number, data: Uint8Array): Uint8Array {\n const symbolSize = this.symbolSizeForPacket(packetId)\n if (symbolSize <= 0 || data.byteLength === symbolSize) return data\n const normalized = new Uint8Array(symbolSize)\n normalized.set(data.subarray(0, symbolSize))\n return normalized\n }\n\n #blockKeysFor(sbn: number, packetId?: number, catalogTrackName?: string): string[] {\n if (packetId != null && catalogTrackName !== undefined) {\n return [this.#blockKey(sbn, packetId, catalogTrackName)]\n }\n\n const keys = new Set<string>()\n for (const source of [\n this.#activeBlocks,\n this.#blockSourceCounts,\n this.#blockCatalogTrackNames,\n ]) {\n for (const key of source.keys()) {\n const block = this.#parseBlockKey(key)\n if (\n block.sbn === sbn &&\n (packetId === undefined || block.packetId === packetId)\n ) keys.add(key)\n }\n }\n for (const source of [this.#completedBlocks, this.#cleanlyCompletedBlocks]) {\n for (const key of source) {\n const block = this.#parseBlockKey(key)\n if (\n block.sbn === sbn &&\n (packetId === undefined || block.packetId === packetId)\n ) keys.add(key)\n }\n }\n\n if (packetId !== undefined) {\n if (keys.size === 0) return [this.#blockKey(sbn, packetId)]\n return keys.size === 1 ? [...keys] : []\n }\n return keys.size > 0 ? [...keys] : [this.#blockKey(sbn, 0, catalogTrackName)]\n }\n\n // -----------------------------------------------------------------------\n // Accessors\n // -----------------------------------------------------------------------\n\n /** Internal state for diagnostics \u2014 not part of the public stats API. */\n get diagnostics() {\n return {\n timeoutMs: this.#fecTimeoutMs,\n activeBlocks: this.#activeBlocks.size,\n completedBlocks: this.#completedBlocks.size,\n pendingCounts: Object.fromEntries(this.#blockSourceCounts),\n fpsDetected: this.#fpsDetected,\n interleaveDepthMs: this.#fecInterleaveDepthMs,\n startupAlignedPackets: this.#packetIdsForStreamKeys(this.#startupAlignedPackets),\n startupPartialBlocks: this.#startupPartialBlocks.size,\n startupRepairReadyPackets: this.#packetIdsForStreamKeys(this.#startupRepairReadyPackets),\n startupRepairFailOpenPackets: this.#packetIdsForStreamKeys(this.#startupRepairFailOpenPackets),\n pendingWorkerCleanups: this.#pendingWorkerCleanups.size,\n repairTrackedBlocks: this.#blockRepairRsIds.size,\n lifecycleTrace: this.#lifecycleTrace.map((entry) => ({ ...entry })),\n }\n }\n\n get fecStats(): FecStats {\n const s = this.#fecStats\n const affected = s.blocksRecovered + s.blocksFailed\n const total = s.blocksClean + affected\n const tracks = [...this.#fecTrackStats.entries()]\n .sort(([left], [right]) => left - right)\n .map(([packetId, stats]) => {\n const trackAffected = stats.blocksRecovered + stats.blocksFailed\n const trackTotal = stats.blocksClean + trackAffected\n return {\n ...stats,\n packetId,\n blocksComplete: trackTotal,\n k: this.#resolveTrackConfig(packetId).k,\n recoveryRate: trackAffected > 0\n ? (stats.blocksRecovered / trackAffected) * 100\n : 100,\n packetLossRate: trackTotal > 0 ? (trackAffected / trackTotal) * 100 : 0,\n }\n })\n return {\n ...s,\n blocksComplete: total,\n mixedPathBlocks: Array.from(this.#blockPathCounts.values()).filter(\n (block) => block.multicastSymbols > 0 && block.moqSymbols > 0,\n ).length,\n recentBlockPathCounts: Array.from(this.#blockPathCounts.values()).slice(-24),\n k: this.#fecK,\n // True recovery rate: of blocks that HAD loss, how many were recovered?\n recoveryRate: affected > 0 ? (s.blocksRecovered / affected) * 100 : 100,\n packetLossRate: total > 0 ? (affected / total) * 100 : 0,\n tracks,\n workers: [...this.#fecWorkerStats.entries()]\n .sort(([left], [right]) => left - right)\n .map(([packetId, stats]) => ({\n ...stats,\n config: {\n ...stats.config,\n resolution: stats.config.resolution ? { ...stats.config.resolution } : undefined,\n },\n blocks: stats.blocks.map((block) => ({ ...block })),\n decoder: stats.decoder ? { ...stats.decoder } : undefined,\n altaFailedDetails: stats.altaFailedDetails?.map((detail) => ({ ...detail })),\n packetId,\n })),\n repairSymbolSizeMismatches: this.#repairSymbolSizeMismatchCount,\n workerErrors: Object.fromEntries(this.#workerErrorCounts),\n }\n }\n\n /**\n * Observe Worker-backed per-track snapshots without taking ownership of the\n * legacy single-callback slot. Infrastructure diagnostics and a player may\n * therefore consume the same source event without wrapping or replacing one\n * another's callback.\n */\n addFecStatsObserver(\n observer: (packetId: number, stats: FecTrackStats) => void,\n ): () => void {\n this.#fecStatsObservers.add(observer)\n return () => this.#fecStatsObservers.delete(observer)\n }\n\n /** Start a new multicast transport join without discarding track/decoder state. */\n beginMulticastJoin(): void {\n this.#startupAlignedPackets.clear()\n this.#startupPartialBlocks.clear()\n this.#startupRequireRepairReady = true\n this.#startupRepairReadyPackets.clear()\n this.#startupRepairBoundaryCounts.clear()\n this.#startupRepairFailOpenPackets.clear()\n }\n\n /** Begin a fresh decode epoch for one physical stream. */\n resetTrack(packetId: number, catalogTrackName: string): void {\n if (!Number.isInteger(packetId) || packetId < 0) {\n throw new Error(`[FECManager] invalid packetId for reset: ${packetId}`)\n }\n if (!catalogTrackName) {\n throw new Error('[FECManager] catalogTrackName is required for track reset')\n }\n const resetMap = <T>(map: Map<string, T>): void => {\n for (const key of map.keys()) {\n if (!this.#belongsToStream(key, packetId, catalogTrackName)) continue\n map.delete(key)\n }\n }\n const resetTimerMap = (\n map: Map<string, ReturnType<typeof setTimeout> | null>,\n ): void => {\n for (const [key, timer] of map) {\n if (!this.#belongsToStream(key, packetId, catalogTrackName)) continue\n if (timer != null) clearTimeout(timer)\n map.delete(key)\n }\n }\n const resetSet = (set: Set<string>): void => {\n for (const key of set) {\n if (this.#belongsToStream(key, packetId, catalogTrackName)) set.delete(key)\n }\n }\n\n resetTimerMap(this.#activeBlocks)\n resetTimerMap(this.#repairDeadlines)\n resetMap(this.#activeBlockDeadlinesMs)\n resetMap(this.#activeBlockTimeoutOwners)\n resetMap(this.#pendingWorkerCleanupClients)\n resetMap(this.#blockSourceCounts)\n resetMap(this.#blockSourceEsis)\n resetMap(this.#blockRepairRsIds)\n resetMap(this.#blockPathCounts)\n resetMap(this.#blockCatalogTrackNames)\n resetMap(this.#syncDecoderBlockOwners)\n resetMap(this.#startupRepairBoundaryCounts)\n resetSet(this.#pendingWorkerCleanups)\n resetSet(this.#completedBlocks)\n resetSet(this.#cleanlyCompletedBlocks)\n resetSet(this.#repairRequestedBlocks)\n resetSet(this.#workerRecoveredBlocks)\n resetSet(this.#startupAlignedPackets)\n resetSet(this.#startupPartialBlocks)\n resetSet(this.#startupRepairReadyPackets)\n resetSet(this.#startupRepairFailOpenPackets)\n resetSet(this.#seenSymbols)\n\n const decoderClients = new Set<WasmFecDecoder>()\n for (const [key, entry] of this.#fecDecoders) {\n if (this.#belongsToStream(key, packetId, catalogTrackName)) {\n decoderClients.add(entry.client)\n }\n }\n const clients = new Set<object>()\n for (const client of decoderClients) {\n if (\n typeof client.flush === 'function' &&\n typeof client.reset_stats === 'function'\n ) {\n try {\n client.flush()\n client.reset_stats()\n clients.add(client)\n } catch {\n this.#discardFecDecoderClient(client)\n }\n } else {\n this.#discardFecDecoderClient(client)\n }\n }\n for (const [key, client] of this.#workerClients) {\n if (!this.#belongsToStream(key, packetId, catalogTrackName)) continue\n client.reset()\n clients.add(client)\n }\n for (const client of clients) this.#resetDecoderBlockOwners(client, catalogTrackName)\n }\n\n /** Record a transport symbol and reject later duplicates of the same packetId/SBN/ESI. */\n feedSymbolFromSource(\n packetId: number,\n sbn: number,\n esi: number,\n _data: Uint8Array,\n source: FecSymbolSource,\n catalogTrackName?: string,\n ): boolean {\n const blockKey = this.#blockKey(sbn, packetId, catalogTrackName)\n const streamKey = this.#streamKey(packetId, catalogTrackName)\n const k = this.kForPacket(packetId)\n const isSourceSymbol = esi >= 0 && esi < k\n const isStartupPartial = this.#startupPartialBlocks.has(blockKey)\n const awaitingMulticastRepair =\n this.#startupRequireRepairReady &&\n source === 'multicast' &&\n !this.#startupRepairReadyPackets.has(streamKey) &&\n !this.#startupRepairFailOpenPackets.has(streamKey)\n if (awaitingMulticastRepair) {\n if (!isSourceSymbol) {\n this.#startupRepairReadyPackets.add(streamKey)\n this.#startupRepairBoundaryCounts.delete(streamKey)\n this.#markStartupPartial(blockKey)\n return false\n }\n\n if (esi === 0) {\n const boundaries = (this.#startupRepairBoundaryCounts.get(streamKey) ?? 0) + 1\n this.#startupRepairBoundaryCounts.set(streamKey, boundaries)\n if (boundaries >= STARTUP_REPAIR_WAIT_SOURCE_BOUNDARIES) {\n this.#startupRepairBoundaryCounts.delete(streamKey)\n this.#startupRepairFailOpenPackets.add(streamKey)\n } else {\n this.#markStartupPartial(blockKey)\n return false\n }\n } else {\n this.#markStartupPartial(blockKey)\n return false\n }\n }\n if (isSourceSymbol && esi === 0) {\n // ESI=0 establishes a complete source boundary for this SBN. Remove\n // only this block: other interleaved SBNs may still be join partials.\n this.#startupPartialBlocks.delete(blockKey)\n this.#startupAlignedPackets.add(streamKey)\n } else if (\n isStartupPartial ||\n !this.#startupAlignedPackets.has(streamKey)\n ) {\n this.#markStartupPartial(blockKey)\n return false\n }\n\n // A completed block remains authoritative after its individual symbol\n // identities age out of the bounded #seenSymbols cache. Without this\n // guard, a delayed copy from the other transport can recreate the block\n // inside a Worker decoder; that orphan never receives the full source and\n // repair set, so cleanup later reports a false failure/green fill.\n if (this.#completedBlocks.has(blockKey)) {\n this.#fecStats.duplicateSymbols++\n this.#statsForPacket(packetId).duplicateSymbols++\n return false\n }\n\n const symbolKey = `${blockKey}:${esi}`\n if (this.#seenSymbols.has(symbolKey)) {\n this.#fecStats.duplicateSymbols++\n this.#statsForPacket(packetId).duplicateSymbols++\n return false\n }\n this.#seenSymbols.add(symbolKey)\n\n const counts = this.#blockPathCounts.get(blockKey) ?? {\n packetId,\n sbn,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n multicastSymbols: 0,\n moqSymbols: 0,\n complete: false,\n }\n if (source === 'multicast') counts.multicastSymbols++\n else counts.moqSymbols++\n this.#blockPathCounts.set(blockKey, counts)\n this.#observeBlockCatalogTrackName(blockKey, catalogTrackName)\n\n if (this.#seenSymbols.size > 4096) {\n this.#seenSymbols = new Set(Array.from(this.#seenSymbols).slice(-2048))\n }\n if (this.#blockPathCounts.size > 512) {\n this.#blockPathCounts = new Map(Array.from(this.#blockPathCounts).slice(-256))\n }\n if (this.#blockCatalogTrackNames.size > 512) {\n this.#blockCatalogTrackNames = new Map(\n Array.from(this.#blockCatalogTrackNames).slice(-256),\n )\n }\n\n return true\n }\n\n #observeBlockCatalogTrackName(blockKey: string, catalogTrackName?: string): void {\n if (catalogTrackName === undefined) return\n if (!this.#blockCatalogTrackNames.has(blockKey)) {\n this.#blockCatalogTrackNames.set(blockKey, catalogTrackName)\n return\n }\n if (this.#blockCatalogTrackNames.get(blockKey) !== catalogTrackName) {\n this.#blockCatalogTrackNames.set(blockKey, null)\n }\n }\n\n /** Exact catalog identity retained for async recovery, if unambiguous. */\n catalogTrackNameForBlock(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): string | undefined {\n if (catalogTrackName !== undefined) {\n return this.#blockCatalogTrackNames.get(\n this.#blockKey(sbn, packetId, catalogTrackName),\n ) ?? undefined\n }\n const names = new Set<string>()\n for (const [key, name] of this.#blockCatalogTrackNames) {\n const block = this.#parseBlockKey(key)\n if (block.packetId === packetId && block.sbn === sbn && name !== null) names.add(name)\n }\n return names.size === 1 ? [...names][0] : undefined\n }\n\n #markStartupPartial(blockKey: string): void {\n this.#startupPartialBlocks.add(blockKey)\n if (this.#startupPartialBlocks.size > 128) {\n this.#startupPartialBlocks = new Set(\n Array.from(this.#startupPartialBlocks).slice(-64),\n )\n }\n }\n\n /** Per-block state snapshot for FecBlockGrid visualization.\n * Returns the most recent blocks (active + recently completed). */\n getBlockStates(): Array<{\n sbn: number;\n packetId?: number;\n state: 'collecting' | 'complete' | 'failed';\n sourceCount: number;\n k: number;\n progress: number;\n }> {\n const blocks: Array<{ sbn: number; packetId?: number; state: 'collecting' | 'complete' | 'failed'; sourceCount: number; k: number; progress: number }> = [];\n // Active blocks (still collecting symbols)\n for (const [key] of this.#activeBlocks) {\n const { packetId, sbn } = this.#parseBlockKey(key);\n const count = this.#blockSourceCounts.get(key) ?? 0;\n const k = this.kForPacket(packetId);\n blocks.push({ sbn, packetId, state: 'collecting', sourceCount: count, k, progress: k > 0 ? Math.min(count / k, 1) : 0 });\n }\n // Recently completed blocks (last 16)\n const completed = [...this.#completedBlocks].slice(-16);\n for (const key of completed) {\n const { packetId, sbn } = this.#parseBlockKey(key);\n if (!this.#activeBlocks.has(key)) {\n const k = this.kForPacket(packetId);\n blocks.push({ sbn, packetId, state: 'complete', sourceCount: k, k, progress: 1 });\n }\n }\n blocks.sort((a, b) => a.sbn - b.sbn || (a.packetId ?? 0) - (b.packetId ?? 0));\n return blocks.slice(-24); // cap at 24 most recent\n }\n\n /** The raw WASM decoder for stats access (first registered decoder) */\n get wasmDecoder(): WasmFecDecoder | null {\n return this.#fecDecoders.values().next().value?.client ?? null\n }\n\n get k(): number { return this.#fecK }\n kForPacket(packetId: number): number { return this.#resolveTrackConfig(packetId).k }\n /** FEC symbol size in bytes. */\n get symbolSize(): number { return this.#fecSymbolSize }\n symbolSizeForPacket(packetId: number): number { return this.#resolveTrackConfig(packetId).symbolSize }\n /** FEC interleave window in milliseconds (0 = no interleaving). */\n get interleaveDepthMs(): number { return this.#fecInterleaveDepthMs }\n interleaveDepthMsForPacket(packetId: number): number { return this.#resolveTrackConfig(packetId).interleaveDepthMs }\n /** Resolved per-track block fail bound in milliseconds. */\n timeoutMsForPacket(packetId: number): number { return this.#resolveTrackConfig(packetId).timeoutMs }\n /** Resolved per-track D-28 repair request deadline in milliseconds. */\n repairDeadlineMsForPacket(packetId: number): number {\n return this.#repairDeadlineMs(this.#resolveTrackConfig(packetId))\n }\n /** Remaining fetch budget for a specific repair-requested block. */\n repairFetchTimeoutMsForBlock(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): number {\n const deadlineMs = this.#activeBlockDeadlinesMs.get(\n this.#blockKey(sbn, packetId, catalogTrackName),\n )\n if (deadlineMs !== undefined) {\n return Math.max(1, Math.ceil(deadlineMs - performance.now()))\n }\n const trackConfig = this.#resolveTrackConfig(packetId)\n return Math.max(1, trackConfig.timeoutMs - this.#repairDeadlineMs(trackConfig))\n }\n\n // -----------------------------------------------------------------------\n // Configuration\n // -----------------------------------------------------------------------\n\n /** Configure FEC parameters. */\n configure(config: FECManagerConfig): void {\n if (config.k != null) this.#fecK = config.k\n if (config.symbolSize != null && config.symbolSize > 0) this.#fecSymbolSize = config.symbolSize\n if (config.interleaveDepthMs != null) this.#fecInterleaveDepthMs = config.interleaveDepthMs\n if (config.fps != null && Number.isFinite(config.fps) && config.fps > 0) {\n this.#fecFps = config.fps\n }\n // interleaveDepthMs is in milliseconds per spec \u00A75.1.\n // Valid range: 33ms (interactive/gaming) to 10000ms (broadcast ATSC 3.0).\n // Warn if value looks like frame count instead of ms (common misconfiguration).\n if (config.interleaveDepthMs != null && config.interleaveDepthMs > 0 && config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) {\n console.warn(`[FECManager] interleaveDepthMs=${config.interleaveDepthMs} looks like frame count, not milliseconds (\u00A75.1 requires ms). Expected range: ${MIN_INTERLEAVE_DEPTH_MS}-10000ms.`)\n }\n if (config.timeoutMs != null && config.timeoutMs > 0) {\n this.#fecTimeoutMs = config.timeoutMs\n } else if (\n config.interleaveDepthMs != null &&\n config.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS\n ) {\n this.#fecTimeoutMs = computeFecDeliveryWindowMs({\n interleaveDepthMs: config.interleaveDepthMs,\n sourceSymbols: this.#fecK,\n symbolSize: this.#fecSymbolSize,\n mediaUnitsPerSecond: config.fps,\n })\n }\n // A shared multicast parser receives every packetId. Keep existing track\n // deadlines at least as wide as the selected video delivery envelope so a\n // short audio interleave cannot expire during browser/AMT scheduling gaps.\n for (const [packetId, track] of this.#trackConfigs) {\n if (track.timeoutMs < this.#fecTimeoutMs) {\n this.#trackConfigs.set(packetId, { ...track, timeoutMs: this.#fecTimeoutMs })\n }\n }\n }\n\n /** Configure FEC parameters for one packetId without changing legacy defaults. */\n configureTrack(packetId: number, config: FECManagerConfig): void {\n this.#upsertTrackConfig(packetId, config)\n }\n\n /** Register a FEC decoder for a specific packetId (track). */\n setFecDecoder(\n packetId: number,\n client: WasmFecDecoder,\n k: number,\n config?: FECManagerConfig,\n catalogTrackName?: string,\n ): void {\n const streamKey = this.#streamKey(packetId, catalogTrackName)\n this.#assertExclusiveDecoderClient(\n streamKey,\n client,\n [...this.#fecDecoders].map(([key, entry]) => [key, entry.client] as const),\n 'decoder',\n )\n const trackConfig = this.#upsertTrackConfig(packetId, { ...config, k })\n this.#fecDecoders.set(streamKey, { client, k: trackConfig.k })\n }\n\n /**\n * Register a Worker-backed FEC decoder for a specific packetId (track).\n *\n * Worker-backed tracks decode asynchronously \u2014 feedSourceSymbol/feedRepairSymbol\n * return null immediately, and recovered frames arrive via the onBlockRecovered callback.\n *\n * Takes precedence over sync WASM decoders for the same packetId.\n */\n setFecWorker(\n packetId: number,\n client: FecWorkerClient,\n config: FECManagerConfig,\n catalogTrackName?: string,\n ): void {\n const k = config?.k\n if (k === undefined || !Number.isFinite(k) || k <= 0) {\n throw new Error(`[FECManager] packetId=${packetId} Worker requires an explicit positive k`)\n }\n const symbolSize = config.symbolSize\n if (symbolSize === undefined || !Number.isFinite(symbolSize) || symbolSize <= 0) {\n throw new Error(\n `[FECManager] packetId=${packetId} Worker requires an explicit positive symbolSize`,\n )\n }\n const streamKey = this.#streamKey(packetId, catalogTrackName)\n this.#assertExclusiveDecoderClient(\n streamKey,\n client,\n this.#workerClients,\n 'worker',\n )\n if (config) this.#upsertTrackConfig(packetId, config)\n const workerK = this.#resolveTrackConfig(packetId).k\n this.#workerClients.set(streamKey, client)\n\n client.onFrame((data: ArrayBuffer, meta: FrameMeta) => {\n if (meta.recovered) {\n // Recovered Worker events represent one exact source-symbol position.\n // Block-completion accounting needs that exact ESI, and counting an\n // ESI-less event as recovered would report healthy FEC while dropping\n // the media. Enforce the Worker contract before any recovery metric.\n if (!Number.isInteger(meta.esi) || meta.esi! < 0 || meta.esi! >= workerK) {\n const kind = 'recovered-symbol-missing-esi'\n const seen = this.#workerErrorCounts.get(kind) ?? 0\n this.#workerErrorCounts.set(kind, seen + 1)\n if (seen < WORKER_ERROR_LOG_CAP) {\n console.warn(\n `[FECManager] Dropping recovered symbol without valid ESI: pid=${packetId} sbn=${meta.sbn} esi=${String(meta.esi)}`,\n )\n }\n return\n }\n const esi = meta.esi!\n\n // Suppress duplicate routing when the worker fires recovered=true\n // for a block ALREADY CLEANLY COMPLETED by K source symbols.\n //\n // BLO-3323 L15: predicate was previously `#completedBlocks.has(sbn)`,\n // but `#completedBlocks` is populated by 5 paths (clean K-arrival,\n // failed timeout, every worker frame emit via markBlockCompleted below,\n // external markBlockCompleted, and cancelBlockTimeout). The first\n // worker frame for a block adds the SBN, so subsequent recovered=true\n // emits for the same block were always suppressed \u2192 blocksRecovered\n // stayed at 0 even when recovery actually fired. Switching to the\n // narrower #cleanlyCompletedBlocks set (populated only by trackBlock\n // when count >= K) preserves the original dedup intent (BLO-3035\n // layer 4 \u2014 don't double-route a block already received via source)\n // while letting genuinely-recovered blocks bump the counter.\n const recoveredCatalogTrackName = catalogTrackName\n ?? this.#decoderBlockCatalogTrackName(client, meta.sbn)\n if (!this.#claimDecoderBlock(client, meta.sbn, recoveredCatalogTrackName)) return\n const blockKey = this.#blockKey(meta.sbn, packetId, recoveredCatalogTrackName)\n const cleanlyCompleted = this.#cleanlyCompletedBlocks.has(blockKey)\n if (!cleanlyCompleted) {\n if (!this.#workerRecoveredBlocks.has(blockKey)) {\n this.#traceLifecycle(\n 'recovered',\n meta.sbn,\n packetId,\n recoveredCatalogTrackName,\n )\n this.#workerRecoveredBlocks.add(blockKey)\n this.#fecStats.blocksRecovered++\n this.#statsForPacket(packetId).blocksRecovered++\n if (this.#workerRecoveredBlocks.size > 1000) {\n const stale = Array.from(this.#workerRecoveredBlocks).slice(0, 500)\n for (const key of stale) this.#workerRecoveredBlocks.delete(key)\n }\n }\n\n // RaptorQ returns the full K-symbol block, not only erased symbols.\n // The direct path already parsed every source ESI in #seenSymbols;\n // routing those symbols again injects duplicate MFU fragments and can\n // leave a real FI=3 tail followed by duplicate FI=2 fragments.\n const symbolKey = `${blockKey}:${esi}`\n if (!this.#seenSymbols.has(symbolKey)) {\n this.#seenSymbols.add(symbolKey)\n this.onBlockRecovered?.(\n meta.sbn,\n packetId,\n data,\n meta,\n recoveredCatalogTrackName,\n )\n }\n }\n\n // The worker emits recovered source symbols in ESI order. Complete the\n // block only after the final slot so assemblers receive every missing\n // symbol before their completion callback flushes buffered media.\n if (\n esi >= workerK - 1 &&\n !this.#completedBlocks.has(blockKey)\n ) {\n this.cancelBlockTimeout(meta.sbn, packetId, recoveredCatalogTrackName)\n if (recoveredCatalogTrackName === undefined) {\n this.onBlockComplete?.(meta.sbn, packetId)\n } else {\n this.onBlockComplete?.(\n meta.sbn,\n packetId,\n recoveredCatalogTrackName,\n )\n }\n }\n } else {\n const completedCatalogTrackName = catalogTrackName\n ?? this.#decoderBlockCatalogTrackName(client, meta.sbn)\n this.markBlockCompleted(meta.sbn, packetId, completedCatalogTrackName)\n }\n })\n\n client.onSnapshot((stats: FecTrackStats) => {\n this.#fecWorkerStats.set(packetId, stats)\n try {\n this.onFecStatsUpdate?.(packetId, stats)\n } catch (error) {\n console.warn('[FECManager] FEC stats callback failed:', error)\n }\n for (const observer of this.#fecStatsObservers) {\n try {\n observer(packetId, stats)\n } catch (error) {\n console.warn('[FECManager] FEC stats observer failed:', error)\n }\n }\n })\n\n client.onGreenFill((sbn: number, data: ArrayBuffer) => {\n this.#fecStats.greenFillFrames++\n this.#statsForPacket(packetId).greenFillFrames++\n this.onGreenFill?.(sbn, packetId, data)\n })\n\n client.onCleanupComplete((sbn: number) => {\n const cleanupKeys = [...this.#pendingWorkerCleanups].filter((key) => {\n const block = this.#parseBlockKey(key)\n return block.packetId === packetId &&\n block.sbn === sbn &&\n this.#pendingWorkerCleanupClients.get(key) === client\n })\n for (const key of cleanupKeys) {\n this.#pendingWorkerCleanups.delete(key)\n this.#pendingWorkerCleanupClients.delete(key)\n if (this.#completedBlocks.has(key)) continue\n const block = this.#parseBlockKey(key)\n this.#traceLifecycle(\n 'cleanup-ack',\n sbn,\n packetId,\n block.catalogTrackName,\n )\n this.#failBlock(sbn, packetId, false, block.catalogTrackName)\n }\n })\n\n client.onError((msg: string, code?: FecWorkerErrorCode) => {\n const requestedKind = code ?? msg\n const kind =\n this.#workerErrorCounts.has(requestedKind) ||\n this.#workerErrorCounts.size < WORKER_ERROR_KIND_CAP - 1\n ? requestedKind\n : WORKER_ERROR_OVERFLOW_KIND\n const seen = this.#workerErrorCounts.get(kind) ?? 0\n this.#workerErrorCounts.set(kind, seen + 1)\n if (seen < WORKER_ERROR_LOG_CAP) {\n console.warn('[FECManager] Worker error:', msg)\n } else if (seen === WORKER_ERROR_LOG_CAP) {\n console.warn(\n `[FECManager] Worker error (suppressing further copies of ${kind}): ${msg}`,\n )\n }\n })\n }\n\n /** Called by player when framerate is known (from decoder output). Refines FEC block timeout. */\n setFramerate(fps: number): void {\n if (this.#fpsDetected || fps <= 0) return\n this.#fpsDetected = true\n this.#fecFps = fps\n // Only a valid \u00A75.1 ms interleave refines the timeout; a sub-floor value is\n // a raw frame count and would collapse it (see MIN_INTERLEAVE_DEPTH_MS).\n if (this.#fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS) {\n this.#fecTimeoutMs = computeFecDeliveryWindowMs({\n interleaveDepthMs: this.#fecInterleaveDepthMs,\n sourceSymbols: this.#fecK,\n symbolSize: this.#fecSymbolSize,\n mediaUnitsPerSecond: fps,\n })\n }\n for (const [packetId, config] of this.#trackConfigs) {\n if (config.fps !== undefined || config.interleaveDepthMs < MIN_INTERLEAVE_DEPTH_MS) continue\n this.#trackConfigs.set(packetId, {\n ...config,\n fps,\n timeoutMs: Math.max(\n this.#fecTimeoutMs,\n computeFecDeliveryWindowMs({\n interleaveDepthMs: config.interleaveDepthMs,\n sourceSymbols: config.k,\n symbolSize: config.symbolSize,\n mediaUnitsPerSecond: fps,\n }),\n ),\n })\n }\n }\n\n // -----------------------------------------------------------------------\n // Symbol feeding (codec-agnostic)\n // -----------------------------------------------------------------------\n\n /**\n * Feed a source symbol to the WASM FEC decoder.\n *\n * Does NOT track block source counts or fire lifecycle events \u2014 call\n * `trackBlock()` separately BEFORE this method (MmtpRouter does this).\n *\n * @returns Recovered block data if WASM completed the block, else null.\n * Caller is responsible for routing recovered data and updating\n * block completion state via `markBlockCompleted()`.\n */\n feedSourceSymbol(\n packetId: number,\n ssId: number,\n symbolData: Uint8Array,\n alta?: { auth: Uint8Array; payloadEnd: number },\n catalogTrackName?: string,\n ): Uint8Array | null {\n this.#fecStats.sourcePackets++\n this.#statsForPacket(packetId).sourcePackets++\n const fecData = this.#normalizeSymbolData(packetId, symbolData)\n const sbn = Math.floor(ssId / this.kForPacket(packetId))\n const blockKey = this.#blockKey(sbn, packetId, catalogTrackName)\n const syncBlockOwner = this.#syncDecoderBlockOwners.get(blockKey)\n\n // Worker-backed track: dispatch async, return null (recovery via onFrame callback)\n const workerClient = this.#workerForPacket(packetId, catalogTrackName)\n if (workerClient && !syncBlockOwner) {\n if (!this.#claimDecoderBlock(workerClient, sbn, catalogTrackName)) return null\n // Create clean ArrayBuffer copy \u2014 prevents transferring a shared underlying buffer\n // when symbolData is a view into a larger packet (T-03-06)\n const buf = fecData.buffer.slice(\n fecData.byteOffset,\n fecData.byteOffset + fecData.byteLength,\n ) as ArrayBuffer\n // Pass the flat SS_ID through to the worker. The WASM decoder derives\n // SBN/ESI internally per ISO 23008-1 \u00A7C.5.2; the WASM add_source\n // signature is (ss_id, data, timestamp) \u2014 pre-computing sbn/esi on this\n // side produces wrong WASM argument alignment and silently breaks\n // block assembly.\n if (alta) {\n // Copy auth into a fresh ArrayBuffer to transfer zero-copy. The\n // underlying buffer of the parsed trailer view belongs to the wire\n // packet, which may be transferred separately or reused.\n const authBuf = alta.auth.buffer.slice(\n alta.auth.byteOffset,\n alta.auth.byteOffset + alta.auth.byteLength,\n ) as ArrayBuffer\n workerClient.feedSource(ssId, buf, performance.now(), {\n auth: authBuf,\n payloadEnd: alta.payloadEnd,\n })\n } else {\n workerClient.feedSource(ssId, buf, performance.now())\n }\n return null\n }\n\n // Synchronous WASM path.\n const entry = this.#decoderForPacket(packetId, catalogTrackName)\n const client = syncBlockOwner ?? entry?.client\n if (!client) return null\n if (!this.#claimDecoderBlock(client, sbn, catalogTrackName)) return null\n this.#syncDecoderBlockOwners.set(blockKey, client)\n\n let recovered: Uint8Array | null = null\n try {\n recovered = client.add_source(ssId, fecData, performance.now())\n } catch {\n // WASM RefCell panic \u2014 decoder instance shared with multicast repair path\n }\n if (recovered || this.#completedBlocks.has(blockKey)) {\n this.#syncDecoderBlockOwners.delete(blockKey)\n }\n\n return recovered\n }\n\n /**\n * Feed a repair symbol to the WASM FEC decoder.\n *\n * @returns Recovered block data if repair completed the block, else null.\n * Caller is responsible for routing recovered data and updating\n * block completion state via `markBlockCompleted()`.\n */\n feedRepairSymbol(\n packetId: number,\n ssStart: number,\n ssbLength: number,\n rsId: number,\n repairData: Uint8Array,\n catalogTrackName?: string,\n ): Uint8Array | null {\n this.#fecStats.repairPackets++\n this.#statsForPacket(packetId).repairPackets++\n const symbolSize = this.symbolSizeForPacket(packetId)\n if (symbolSize > 0 && repairData.byteLength !== symbolSize) {\n this.#repairSymbolSizeMismatchCount++\n if (this.#repairSymbolSizeMismatchCount <= 5 || this.#repairSymbolSizeMismatchCount % 100 === 0) {\n console.warn(\n `[FECManager] Dropping repair symbol with invalid size: got ${repairData.byteLength}B, expected ${symbolSize}B (packetId=${packetId}, ssStart=${ssStart}, ssbLength=${ssbLength}, rsId=${rsId})`,\n )\n }\n return null\n }\n const fecData = repairData\n\n // Repair symbols are block activity: refresh the inactivity fail bound so\n // a block being actively repaired over MoQ is not failed mid-recovery.\n // The multicast in-band path already does this at the router; this covers\n // the MoQ backfill path, which bypasses the router's packet accounting.\n // ssbLength is SSB_length = K (ISO 23008-1 \u00A7C.5.3), so ssStart/ssbLength\n // is the block number. The legacy feedSymbol shape passes ssbLength=1\n // with flat symbol ids \u2014 its ratio is not a block number, so skip it.\n if (ssbLength > 1) {\n this.noteBlockActivity(\n Math.floor(ssStart / ssbLength),\n packetId,\n catalogTrackName,\n )\n }\n\n const sbn = ssbLength > 0 ? Math.floor(ssStart / ssbLength) : 0\n const blockKey = this.#blockKey(sbn, packetId, catalogTrackName)\n if (this.#completedBlocks.has(blockKey)) return null\n const syncBlockOwner = this.#syncDecoderBlockOwners.get(blockKey)\n let repairIds = this.#blockRepairRsIds.get(blockKey)\n if (!repairIds) {\n repairIds = new Set()\n this.#blockRepairRsIds.set(blockKey, repairIds)\n }\n repairIds.add(rsId)\n if (this.#repairRequestedBlocks.has(blockKey)) {\n this.#traceLifecycle('repair-arrived', sbn, packetId, catalogTrackName, rsId)\n }\n\n // Worker-backed track: dispatch async, return null (recovery via onFrame callback)\n const workerClient = this.#workerForPacket(packetId, catalogTrackName)\n if (workerClient && !syncBlockOwner) {\n if (!this.#claimDecoderBlock(workerClient, sbn, catalogTrackName)) return null\n // Create clean ArrayBuffer copy (T-03-06)\n const buf = fecData.buffer.slice(\n fecData.byteOffset,\n fecData.byteOffset + fecData.byteLength,\n ) as ArrayBuffer\n // Forward the wire-level Repair FEC Payload ID (ISO 23008-1 \u00A7C.5.3)\n // directly. WASM add_repair signature is\n // (ss_start, ssb_length, rs_id, data, timestamp) \u2014 pre-computing\n // (sbn, esi) on this side misaligns WASM args and produces 0% recovery.\n workerClient.feedRepair(ssStart, ssbLength, rsId, buf, performance.now())\n return null\n }\n\n // Synchronous WASM path.\n const entry = this.#decoderForPacket(packetId, catalogTrackName)\n const client = syncBlockOwner ?? entry?.client\n if (!client) return null\n if (!this.#claimDecoderBlock(client, sbn, catalogTrackName)) return null\n this.#syncDecoderBlockOwners.set(blockKey, client)\n\n let recovered: Uint8Array | null = null\n try {\n recovered = client.add_repair(ssStart, ssbLength, rsId, fecData, performance.now())\n } catch {\n // WASM add_repair failure \u2014 block will timeout and be marked failed\n }\n if (recovered) this.#syncDecoderBlockOwners.delete(blockKey)\n\n return recovered\n }\n\n #routeSyncCleanup(\n decoder: WasmFecDecoder,\n sbn: number,\n packetId: number,\n ): void {\n let framed: Uint8Array\n try {\n framed = decoder.cleanup_blocks_detailed(Uint32Array.of(sbn))\n } catch (error) {\n console.warn(`[FECManager] Scoped cleanup failed for SBN ${sbn}:`, error)\n return\n }\n\n const view = new DataView(framed.buffer, framed.byteOffset, framed.byteLength)\n let offset = 0\n while (offset < framed.byteLength) {\n if (framed.byteLength - offset < 8) {\n console.warn(`[FECManager] Scoped cleanup returned a truncated record for SBN ${sbn}`)\n return\n }\n const fillSbn = view.getUint32(offset, false)\n const length = view.getUint32(offset + 4, false)\n offset += 8\n if (length > framed.byteLength - offset) {\n console.warn(`[FECManager] Scoped cleanup returned a truncated payload for SBN ${sbn}`)\n return\n }\n const data = framed.slice(offset, offset + length).buffer as ArrayBuffer\n offset += length\n this.#fecStats.greenFillFrames++\n this.#statsForPacket(packetId).greenFillFrames++\n this.onGreenFill?.(fillSbn, packetId, data)\n }\n }\n\n /**\n * Unified symbol feeder. Dispatches to feedSourceSymbol or feedRepairSymbol.\n */\n feedSymbol(packetId: number, symbolId: number, data: Uint8Array, isRepair: boolean): Uint8Array | null {\n if (isRepair) {\n return this.feedRepairSymbol(packetId, symbolId, 1, symbolId, data)\n }\n return this.feedSourceSymbol(packetId, symbolId, data)\n }\n\n // -----------------------------------------------------------------------\n // Block lifecycle\n // -----------------------------------------------------------------------\n\n /**\n * Track a source symbol for an FEC block. Refreshes the inactivity deadline\n * on each symbol and marks the block clean when K source symbols arrive.\n */\n trackBlock(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n esi?: number,\n ): void {\n const key = this.#blockKey(sbn, packetId, catalogTrackName)\n const trackConfig = this.#resolveTrackConfig(packetId)\n const k = trackConfig.k\n\n // Skip blocks already recovered \u2014 late source symbols must not restart timeouts\n if (this.#completedBlocks.has(key)) return\n\n // Count source symbols per block\n const count = (this.#blockSourceCounts.get(key) ?? 0) + 1\n this.#blockSourceCounts.set(key, count)\n if (Number.isInteger(esi) && esi! >= 0 && esi! < k) {\n let sourceEsis = this.#blockSourceEsis.get(key)\n if (!sourceEsis) {\n sourceEsis = new Set()\n this.#blockSourceEsis.set(key, sourceEsis)\n }\n sourceEsis.add(esi!)\n }\n if (count === 1) {\n if (catalogTrackName === undefined) {\n this.onBlockStarted?.(sbn, packetId)\n } else {\n this.onBlockStarted?.(sbn, packetId, catalogTrackName)\n }\n }\n\n // When K source symbols received, block is clean (no loss, no FEC needed).\n if (k > 0 && count >= k) {\n this.#fecStats.blocksClean++\n this.#statsForPacket(packetId).blocksClean++\n this.cancelBlockTimeout(sbn, packetId, catalogTrackName)\n this.#blockSourceCounts.delete(key)\n this.#blockSourceEsis.delete(key)\n this.#blockRepairRsIds.delete(key)\n this.#completedBlocks.add(key)\n // BLO-3323 L15: track which SBNs reached K source-symbol arrivals so the\n // worker recovery suppression can distinguish \"block completed by source\"\n // (legitimately suppress duplicate recovery) from \"block touched by any\n // prior emit / timeout / external mark\" (do NOT suppress recovery).\n this.#cleanlyCompletedBlocks.add(key)\n this.#markPathBlockComplete(sbn, packetId, catalogTrackName)\n if (catalogTrackName === undefined) {\n this.onBlockComplete?.(sbn, packetId)\n } else {\n this.onBlockComplete?.(sbn, packetId, catalogTrackName)\n }\n // MmtpRouter feeds the K-th source immediately after this callback.\n // Defer cleanup so that synchronous feed selects the same decoder; this\n // also clears blocks whose K-th packet was too large for decoder input.\n queueMicrotask(() => this.#syncDecoderBlockOwners.delete(key))\n return\n }\n\n this.noteBlockActivity(sbn, packetId, catalogTrackName, true)\n\n // Source ESI is intentionally not a repair trigger. Multicast delivery is\n // unordered, so K-1 may precede an interior symbol in a clean block. The\n // progress-refreshed source horizon above is the earliest loss signal.\n }\n\n /** Refresh deadlines after accepted block activity. */\n noteBlockActivity(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n sourceProgress = false,\n ): void {\n const key = this.#blockKey(sbn, packetId, catalogTrackName)\n\n // Late symbols must not resurrect a block that already completed or failed.\n if (this.#completedBlocks.has(key)) return\n // The worker cleanup command is already queued after all symbols that made\n // the inactivity deadline. Symbols arriving after that boundary are late\n // and must not arm a second timer before the acknowledgement resolves it.\n if (this.#pendingWorkerCleanups.has(key)) return\n\n // #requestRepair appends one bounded grace to the active deadline. Source\n // or repair symbols may still complete the block, but cannot extend it.\n if (this.#repairRequestedBlocks.has(key)) return\n\n const trackConfig = this.#resolveTrackConfig(packetId)\n const timeoutMs = trackConfig.timeoutMs\n if (timeoutMs <= 0) return\n\n // D-28: accepted source progress refreshes the pre-request horizon. There\n // are at most K such arrivals; repair traffic never refreshes this timer.\n const repairDeadline = this.#repairDeadlines.get(key)\n if (\n this.onRepairNeeded &&\n (repairDeadline === undefined || sourceProgress)\n ) {\n if (repairDeadline != null) clearTimeout(repairDeadline)\n this.#repairDeadlines.set(\n key,\n setTimeout(() => {\n this.#requestRepair(sbn, packetId, catalogTrackName)\n }, this.#repairDeadlineMs(trackConfig)),\n )\n }\n\n this.#armBlockTimeout(sbn, packetId, catalogTrackName, timeoutMs)\n }\n\n #failBlock(\n sbn: number,\n packetId: number,\n countGreenFill: boolean,\n catalogTrackName?: string,\n ): void {\n const key = this.#blockKey(sbn, packetId, catalogTrackName)\n this.#traceLifecycle('failed', sbn, packetId, catalogTrackName)\n this.#clearRepairDeadline(key)\n const activeTimer = this.#activeBlocks.get(key)\n if (activeTimer) {\n clearTimeout(activeTimer)\n this.#activeBlocks.delete(key)\n }\n this.#activeBlockDeadlinesMs.delete(key)\n if (this.#repairRequestedBlocks.delete(key)) {\n if (catalogTrackName === undefined) {\n this.onRepairResolved?.(sbn, packetId, 'failed')\n } else {\n this.onRepairResolved?.(sbn, packetId, 'failed', catalogTrackName)\n }\n }\n this.#blockSourceCounts.delete(key)\n this.#blockSourceEsis.delete(key)\n this.#blockRepairRsIds.delete(key)\n this.#activeBlockTimeoutOwners.delete(key)\n this.#syncDecoderBlockOwners.delete(key)\n this.#completedBlocks.add(key)\n this.#markPathBlockComplete(sbn, packetId, catalogTrackName)\n this.#fecStats.blocksFailed++\n this.#statsForPacket(packetId).blocksFailed++\n if (countGreenFill) {\n this.#fecStats.greenFillFrames++\n this.#statsForPacket(packetId).greenFillFrames++\n }\n if (catalogTrackName === undefined) {\n this.onBlockFailed?.(sbn, packetId)\n } else {\n this.onBlockFailed?.(sbn, packetId, catalogTrackName)\n }\n }\n\n /** Check whether a block has already been completed (clean or recovered). */\n isBlockCompleted(sbn: number, packetId?: number, catalogTrackName?: string): boolean {\n return this.#blockKeysFor(sbn, packetId, catalogTrackName).some((key) => this.#completedBlocks.has(key))\n }\n\n /** Mark a block as completed externally (e.g., after routing recovered data). */\n markBlockCompleted(sbn: number, packetId?: number, catalogTrackName?: string): void {\n for (const key of this.#blockKeysFor(sbn, packetId, catalogTrackName)) {\n this.#completedBlocks.add(key)\n this.#pendingWorkerCleanups.delete(key)\n this.#pendingWorkerCleanupClients.delete(key)\n const block = this.#parseBlockKey(key)\n this.#markPathBlockComplete(block.sbn, block.packetId, block.catalogTrackName)\n this.#clearRepairDeadline(key)\n if (this.#repairRequestedBlocks.delete(key)) {\n if (block.catalogTrackName === undefined) {\n this.onRepairResolved?.(block.sbn, block.packetId, 'completed')\n } else {\n this.onRepairResolved?.(\n block.sbn,\n block.packetId,\n 'completed',\n block.catalogTrackName,\n )\n }\n }\n this.#blockSourceCounts.delete(key)\n this.#blockSourceEsis.delete(key)\n this.#blockRepairRsIds.delete(key)\n const timer = this.#activeBlocks.get(key)\n if (timer) {\n clearTimeout(timer)\n this.#activeBlocks.delete(key)\n }\n this.#activeBlockDeadlinesMs.delete(key)\n this.#activeBlockTimeoutOwners.delete(key)\n }\n }\n\n /** Increment blocksRecovered stat (called by MmtpRouter after routing recovery). */\n countRecovery(packetId?: number): void {\n this.#fecStats.blocksRecovered++\n if (packetId != null) this.#statsForPacket(packetId).blocksRecovered++\n }\n\n /**\n * Strict delivery provenance for one FEC block.\n *\n * A recovered symbol is a function of every symbol that entered its block,\n * so it can only be attributed to multicast when NO symbol in that block\n * arrived over MoQ. Repair symbols routinely arrive over reliable QUIC\n * (`MmtpRouter.feedRepairPacket`), so a block that recovered with MoQ help\n * produced bytes that never crossed a multicast socket.\n *\n * An untracked block returns 'unknown' rather than a guess: absence of\n * evidence is not evidence of multicast.\n */\n blockDeliveryProvenance(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): MmtpDeliveryProvenance {\n const counts = this.blockPathCount(sbn, packetId, catalogTrackName)\n if (!counts) return 'unknown'\n if (counts.moqSymbols > 0) return 'mixed'\n if (counts.multicastSymbols > 0) return 'multicast'\n return 'unknown'\n }\n\n /** Per-path symbol counts for one block (D-28 gate logging), if tracked. */\n blockPathCount(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): FecBlockPathCount | null {\n return this.#blockPathCounts.get(\n this.#blockKey(sbn, packetId, catalogTrackName),\n ) ?? null\n }\n\n /** Cancel block timeout (block completed via clean reception or FEC recovery). */\n cancelBlockTimeout(sbn: number, packetId?: number, catalogTrackName?: string): void {\n for (const key of this.#blockKeysFor(sbn, packetId, catalogTrackName)) {\n this.#completedBlocks.add(key)\n this.#pendingWorkerCleanups.delete(key)\n this.#pendingWorkerCleanupClients.delete(key)\n const block = this.#parseBlockKey(key)\n this.#markPathBlockComplete(block.sbn, block.packetId, block.catalogTrackName)\n this.#clearRepairDeadline(key)\n if (this.#repairRequestedBlocks.delete(key)) {\n if (block.catalogTrackName === undefined) {\n this.onRepairResolved?.(block.sbn, block.packetId, 'completed')\n } else {\n this.onRepairResolved?.(\n block.sbn,\n block.packetId,\n 'completed',\n block.catalogTrackName,\n )\n }\n }\n this.#blockSourceCounts.delete(key)\n this.#blockSourceEsis.delete(key)\n this.#blockRepairRsIds.delete(key)\n const timer = this.#activeBlocks.get(key)\n if (timer) {\n clearTimeout(timer)\n this.#activeBlocks.delete(key)\n }\n this.#activeBlockDeadlinesMs.delete(key)\n this.#activeBlockTimeoutOwners.delete(key)\n }\n // Trim old entries to prevent unbounded growth (keep last ~500 blocks)\n if (this.#completedBlocks.size > 1000) {\n const iter = this.#completedBlocks.values()\n for (let i = 0; i < 500; i++) iter.next()\n // Sets iterate in insertion order \u2014 first 500 are oldest\n const keep = new Set<string>()\n for (const v of iter) keep.add(v)\n this.#completedBlocks = keep\n }\n }\n\n // -----------------------------------------------------------------------\n // Cleanup\n // -----------------------------------------------------------------------\n\n dispose(): void {\n for (const timer of this.#activeBlocks.values()) clearTimeout(timer)\n this.#activeBlocks.clear()\n this.#activeBlockDeadlinesMs.clear()\n this.#activeBlockTimeoutOwners.clear()\n this.#pendingWorkerCleanups.clear()\n this.#pendingWorkerCleanupClients.clear()\n for (const timer of this.#repairDeadlines.values()) {\n if (timer != null) clearTimeout(timer)\n }\n this.#repairDeadlines.clear()\n this.#blockSourceCounts.clear()\n this.#blockSourceEsis.clear()\n this.#blockRepairRsIds.clear()\n this.#completedBlocks.clear()\n this.#cleanlyCompletedBlocks.clear()\n this.#repairRequestedBlocks.clear()\n this.#workerRecoveredBlocks.clear()\n this.#syncDecoderBlockOwners.clear()\n this.#startupAlignedPackets.clear()\n this.#startupPartialBlocks.clear()\n this.#startupRequireRepairReady = false\n this.#startupRepairReadyPackets.clear()\n this.#startupRepairBoundaryCounts.clear()\n this.#startupRepairFailOpenPackets.clear()\n this.#seenSymbols.clear()\n this.#blockPathCounts.clear()\n this.#blockCatalogTrackNames.clear()\n this.#trackConfigs.clear()\n this.#fecDecoders.clear()\n // Dispose Worker clients (T-03-07: prevent Worker leak)\n for (const client of new Set(this.#workerClients.values())) client.dispose()\n this.#workerClients.clear()\n }\n}\n", "export type MfuReassemblyPolicy =\n | ReliableMoqReassemblyPolicy\n | { transport: 'datagram'; deadlineMs: number }\n\nexport interface ReliableMoqReassemblyPolicy {\n transport: 'reliable-moq'\n startsAtRandomAccessGroup?: true\n}\n\nexport type MmtpTrackType = 'video' | 'audio' | null\n\nexport interface ReliableMoqReassemblyOptions {\n /**\n * The transport starts at a complete independently decodable media group.\n * The parser still requires an observed RAP before decoder startup.\n */\n startsAtRandomAccessGroup?: boolean\n}\n\nfunction validateReliableMoqOptionShape(value: Record<string, unknown>): void {\n const unsupported = Object.keys(value).filter(\n key => key !== 'transport' && key !== 'startsAtRandomAccessGroup',\n )\n if (unsupported.length > 0) {\n if (unsupported.includes('maxRacingGroups')) {\n throw new Error(\n '[MFU reassembly] maxRacingGroups was removed; reliable MoQ completion is transport-owned',\n )\n }\n throw new Error(\n `[MFU reassembly] unsupported reliable MoQ option(s): ${unsupported.join(', ')}`,\n )\n }\n const startsAtRandomAccessGroup = value['startsAtRandomAccessGroup']\n if (\n typeof startsAtRandomAccessGroup !== 'boolean' &&\n startsAtRandomAccessGroup !== undefined\n ) {\n throw new Error('[MFU reassembly] startsAtRandomAccessGroup must be boolean')\n }\n}\n\nexport function reliableMoqMfuReassembly(\n options: ReliableMoqReassemblyOptions = {},\n): ReliableMoqReassemblyPolicy {\n if (\n arguments.length > 1 ||\n typeof options !== 'object' ||\n options === null ||\n Array.isArray(options)\n ) {\n throw new Error(\n '[MFU reassembly] the positional maxRacingGroups API was removed; pass a reliable MoQ options object',\n )\n }\n validateReliableMoqOptionShape(options as Record<string, unknown>)\n return options.startsAtRandomAccessGroup\n ? {\n transport: 'reliable-moq',\n startsAtRandomAccessGroup: true,\n }\n : { transport: 'reliable-moq' }\n}\n\nfunction assertValidDatagramDeadline(deadlineMs: unknown): asserts deadlineMs is number {\n if (typeof deadlineMs !== 'number' || !Number.isFinite(deadlineMs) || deadlineMs <= 0) {\n throw new Error('[MFU reassembly] datagram deadlineMs must be a finite number > 0')\n }\n}\n\nexport function datagramMfuReassembly(deadlineMs: number): MfuReassemblyPolicy {\n assertValidDatagramDeadline(deadlineMs)\n return { transport: 'datagram', deadlineMs }\n}\n\n/** Validate the complete runtime shape even when a caller bypasses the policy factories. */\nexport function assertValidMfuReassemblyPolicy(policy: MfuReassemblyPolicy): void {\n if (typeof policy !== 'object' || policy === null || Array.isArray(policy)) {\n throw new Error('[MFU reassembly] policy must be an object')\n }\n\n const value = policy as unknown as Record<string, unknown>\n if (value['transport'] === 'reliable-moq') {\n validateReliableMoqOptionShape(value)\n if (value['startsAtRandomAccessGroup'] === false) {\n throw new Error(\n '[MFU reassembly] startsAtRandomAccessGroup must be true when present in a policy',\n )\n }\n return\n }\n if (value['transport'] === 'datagram') {\n const unsupported = Object.keys(value).filter(\n key => key !== 'transport' && key !== 'deadlineMs',\n )\n if (unsupported.length > 0) {\n throw new Error(\n `[MFU reassembly] unsupported datagram option(s): ${unsupported.join(', ')}`,\n )\n }\n assertValidDatagramDeadline(value['deadlineMs'])\n return\n }\n throw new Error(\n `[MFU reassembly] unsupported transport: ${String(value['transport'])}`,\n )\n}\n\nexport function mfuReassemblyTimeoutMs(policy: MfuReassemblyPolicy): number | null {\n assertValidMfuReassemblyPolicy(policy)\n return policy.transport === 'reliable-moq' ? null : policy.deadlineMs\n}\n\n/** Resolve the parser reorder window without inferring reliable-stream completion. */\nexport function mmtpFrameReorderWindowFrames(\n policy: MfuReassemblyPolicy,\n configuredWindow = 0,\n): number {\n assertValidMfuReassemblyPolicy(policy)\n if (!Number.isFinite(configuredWindow) || configuredWindow < 0) {\n throw new Error('[MFU reassembly] configuredWindow must be a finite number >= 0')\n }\n\n const requested = Math.ceil(configuredWindow)\n if (policy.transport === 'reliable-moq') {\n if (requested > 0) {\n throw new Error(\n '[MFU reassembly] reliable MoQ paths do not accept a frame reorder window in the parser',\n )\n }\n return 0\n }\n return requested\n}\n", "/**\n * Strict ISO-BMFF timing extraction for the timed-MFU browser path.\n *\n * This intentionally implements only the current single-track MMT profile:\n * one media track in FT=0, and one matching traf/trun in each FT=1. It does\n * not infer timing from packet arrival, MMTP send timestamps, or cadence.\n */\n\nconst U64_MAX = 0xffff_ffff_ffff_ffffn\nconst I64_MIN = -0x8000_0000_0000_0000n\nconst I64_MAX = 0x7fff_ffff_ffff_ffffn\nconst MICROSECONDS_PER_SECOND = 1_000_000n\n\nconst TFHD_BASE_DATA_OFFSET_PRESENT = 0x000001\nconst TFHD_SAMPLE_DESCRIPTION_INDEX_PRESENT = 0x000002\nconst TFHD_DEFAULT_SAMPLE_DURATION_PRESENT = 0x000008\nconst TFHD_DEFAULT_SAMPLE_SIZE_PRESENT = 0x000010\nconst TFHD_DEFAULT_SAMPLE_FLAGS_PRESENT = 0x000020\nconst TFHD_DURATION_IS_EMPTY = 0x010000\nconst TFHD_DEFAULT_BASE_IS_MOOF = 0x020000\nconst TFHD_KNOWN_FLAGS =\n TFHD_BASE_DATA_OFFSET_PRESENT |\n TFHD_SAMPLE_DESCRIPTION_INDEX_PRESENT |\n TFHD_DEFAULT_SAMPLE_DURATION_PRESENT |\n TFHD_DEFAULT_SAMPLE_SIZE_PRESENT |\n TFHD_DEFAULT_SAMPLE_FLAGS_PRESENT |\n TFHD_DURATION_IS_EMPTY |\n TFHD_DEFAULT_BASE_IS_MOOF\n\nconst TRUN_DATA_OFFSET_PRESENT = 0x000001\nconst TRUN_FIRST_SAMPLE_FLAGS_PRESENT = 0x000004\nconst TRUN_SAMPLE_DURATION_PRESENT = 0x000100\nconst TRUN_SAMPLE_SIZE_PRESENT = 0x000200\nconst TRUN_SAMPLE_FLAGS_PRESENT = 0x000400\nconst TRUN_SAMPLE_CTS_OFFSET_PRESENT = 0x000800\nconst TRUN_KNOWN_FLAGS =\n TRUN_DATA_OFFSET_PRESENT |\n TRUN_FIRST_SAMPLE_FLAGS_PRESENT |\n TRUN_SAMPLE_DURATION_PRESENT |\n TRUN_SAMPLE_SIZE_PRESENT |\n TRUN_SAMPLE_FLAGS_PRESENT |\n TRUN_SAMPLE_CTS_OFFSET_PRESENT\n\nexport interface IsoBmffTrackTiming {\n trackId: number\n timescale: number\n defaultSampleDuration?: number\n defaultSampleSize?: number\n}\n\nexport interface IsoBmffSampleTiming {\n /** Exact decode time in mdhd timescale units. */\n decodeTime: bigint\n /** Exact presentation time in mdhd timescale units, including signed CTS. */\n presentationTime: bigint\n /** Exact authored duration in mdhd timescale units. */\n duration: number\n timescale: number\n /** Exact elementary-stream sample size declared by trun/tfhd/trex. */\n size: number\n /** Byte position of the sample relative to the beginning of the moof. */\n bytePosition: bigint\n}\n\nexport interface IsoBmffMovieFragmentTiming {\n movieFragmentSequenceNumber: number\n samples: readonly IsoBmffSampleTiming[]\n}\n\ninterface IsoBox {\n type: string\n start: number\n bodyStart: number\n end: number\n}\n\ninterface FullBox {\n version: number\n flags: number\n bodyStart: number\n}\n\ninterface ParsedTrack {\n trackId: number\n handlerType: string\n timescale: number\n hasEdits: boolean\n}\n\ninterface ParsedTfhd {\n trackId: number\n defaultSampleDuration?: number\n defaultSampleSize?: number\n}\n\ninterface MdatLayout {\n dataStart: bigint\n declaredEnd: bigint\n}\n\nfunction view(data: Uint8Array): DataView {\n return new DataView(data.buffer, data.byteOffset, data.byteLength)\n}\n\nfunction boxType(data: Uint8Array, offset: number): string {\n return String.fromCharCode(\n data[offset]!,\n data[offset + 1]!,\n data[offset + 2]!,\n data[offset + 3]!,\n )\n}\n\nfunction readU64(data: Uint8Array, offset: number): bigint | null {\n if (offset < 0 || offset + 8 > data.length) return null\n const dataView = view(data)\n return (BigInt(dataView.getUint32(offset, false)) << 32n) |\n BigInt(dataView.getUint32(offset + 4, false))\n}\n\nfunction parseBox(data: Uint8Array, start: number, limit: number): IsoBox | null {\n if (start < 0 || limit > data.length || start + 8 > limit) return null\n const dataView = view(data)\n const shortSize = dataView.getUint32(start, false)\n let headerSize = 8\n let size: number\n if (shortSize === 0) {\n size = limit - start\n } else if (shortSize === 1) {\n if (start + 16 > limit) return null\n const extendedSize = readU64(data, start + 8)\n if (extendedSize === null || extendedSize > BigInt(Number.MAX_SAFE_INTEGER)) return null\n headerSize = 16\n size = Number(extendedSize)\n } else {\n size = shortSize\n }\n if (size < headerSize || start + size > limit) return null\n return {\n type: boxType(data, start + 4),\n start,\n bodyStart: start + headerSize,\n end: start + size,\n }\n}\n\nfunction parseBoxes(data: Uint8Array, start: number, end: number): IsoBox[] | null {\n if (start < 0 || end < start || end > data.length) return null\n const boxes: IsoBox[] = []\n let offset = start\n while (offset < end) {\n const box = parseBox(data, offset, end)\n if (!box || box.end <= offset) return null\n boxes.push(box)\n offset = box.end\n }\n return offset === end ? boxes : null\n}\n\nfunction children(data: Uint8Array, parent: IsoBox): IsoBox[] | null {\n return parseBoxes(data, parent.bodyStart, parent.end)\n}\n\nfunction exactlyOne(boxes: readonly IsoBox[], type: string): IsoBox | null {\n const matches = boxes.filter(box => box.type === type)\n return matches.length === 1 ? matches[0]! : null\n}\n\nfunction parseFullBox(data: Uint8Array, box: IsoBox): FullBox | null {\n if (box.bodyStart + 4 > box.end) return null\n const versionAndFlags = view(data).getUint32(box.bodyStart, false)\n return {\n version: versionAndFlags >>> 24,\n flags: versionAndFlags & 0x00ff_ffff,\n bodyStart: box.bodyStart + 4,\n }\n}\n\nfunction parseTkhdTrackId(data: Uint8Array, box: IsoBox): number | null {\n const full = parseFullBox(data, box)\n if (!full || full.flags & ~0x00000f) return null\n const bodyLength = box.end - full.bodyStart\n const trackIdOffset = full.version === 0\n ? full.bodyStart + 8\n : full.version === 1\n ? full.bodyStart + 16\n : -1\n const expectedBodyLength = full.version === 0 ? 80 : full.version === 1 ? 92 : -1\n if (trackIdOffset < 0 || bodyLength !== expectedBodyLength) return null\n return view(data).getUint32(trackIdOffset, false)\n}\n\nfunction parseMdhdTimescale(data: Uint8Array, box: IsoBox): number | null {\n const full = parseFullBox(data, box)\n if (!full || full.flags !== 0) return null\n const bodyLength = box.end - full.bodyStart\n const timescaleOffset = full.version === 0\n ? full.bodyStart + 8\n : full.version === 1\n ? full.bodyStart + 16\n : -1\n const expectedBodyLength = full.version === 0 ? 20 : full.version === 1 ? 32 : -1\n if (timescaleOffset < 0 || bodyLength !== expectedBodyLength) return null\n return view(data).getUint32(timescaleOffset, false)\n}\n\nfunction parseHandlerType(data: Uint8Array, box: IsoBox): string | null {\n const full = parseFullBox(data, box)\n if (!full || full.version !== 0 || full.flags !== 0 || full.bodyStart + 20 > box.end) {\n return null\n }\n return boxType(data, full.bodyStart + 4)\n}\n\nfunction editListHasEntries(data: Uint8Array, box: IsoBox): boolean | null {\n const full = parseFullBox(data, box)\n if (!full || full.flags !== 0 || (full.version !== 0 && full.version !== 1)) return null\n if (full.bodyStart + 4 > box.end) return null\n const entryCount = view(data).getUint32(full.bodyStart, false)\n const entrySize = full.version === 0 ? 12 : 20\n const expectedEnd = full.bodyStart + 4 + entryCount * entrySize\n if (!Number.isSafeInteger(expectedEnd) || expectedEnd !== box.end) return null\n return entryCount !== 0\n}\n\nfunction parseTrack(data: Uint8Array, trak: IsoBox): ParsedTrack | null {\n const trakChildren = children(data, trak)\n if (!trakChildren) return null\n const tkhd = exactlyOne(trakChildren, 'tkhd')\n const mdia = exactlyOne(trakChildren, 'mdia')\n if (!tkhd || !mdia) return null\n const trackId = parseTkhdTrackId(data, tkhd)\n if (trackId === null || trackId === 0) return null\n\n const mdiaChildren = children(data, mdia)\n if (!mdiaChildren) return null\n const mdhd = exactlyOne(mdiaChildren, 'mdhd')\n const hdlr = exactlyOne(mdiaChildren, 'hdlr')\n if (!mdhd || !hdlr) return null\n const timescale = parseMdhdTimescale(data, mdhd)\n const handlerType = parseHandlerType(data, hdlr)\n if (timescale === null || timescale === 0 || handlerType === null) return null\n\n const edtsBoxes = trakChildren.filter(box => box.type === 'edts')\n if (edtsBoxes.length > 1) return null\n let hasEdits = false\n if (edtsBoxes.length === 1) {\n const edtsChildren = children(data, edtsBoxes[0]!)\n if (!edtsChildren) return null\n const editLists = edtsChildren.filter(box => box.type === 'elst')\n if (editLists.length > 1) return null\n if (editLists.length === 1) {\n const result = editListHasEntries(data, editLists[0]!)\n if (result === null) return null\n hasEdits = result\n }\n }\n\n return { trackId, handlerType, timescale, hasEdits }\n}\n\nfunction parseTrex(data: Uint8Array, box: IsoBox): IsoBmffTrackTiming | null {\n const full = parseFullBox(data, box)\n if (!full || full.version !== 0 || full.flags !== 0 || box.end - full.bodyStart !== 20) {\n return null\n }\n const dataView = view(data)\n const trackId = dataView.getUint32(full.bodyStart, false)\n const defaultSampleDuration = dataView.getUint32(full.bodyStart + 8, false)\n const defaultSampleSize = dataView.getUint32(full.bodyStart + 12, false)\n return {\n trackId,\n timescale: 0,\n ...(defaultSampleDuration === 0 ? {} : { defaultSampleDuration }),\n ...(defaultSampleSize === 0 ? {} : { defaultSampleSize }),\n }\n}\n\nexport function parseIsoBmffInitializationTiming(\n data: Uint8Array,\n): IsoBmffTrackTiming | null {\n const topLevel = parseBoxes(data, 0, data.length)\n if (!topLevel) return null\n const ftyp = exactlyOne(topLevel, 'ftyp')\n const moov = exactlyOne(topLevel, 'moov')\n if (!ftyp || !moov || ftyp.start > moov.start) return null\n if (topLevel.some(box => box.type === 'moof' || box.type === 'mdat')) return null\n\n const moovChildren = children(data, moov)\n if (!moovChildren) return null\n const parsedTracks: ParsedTrack[] = []\n for (const trak of moovChildren.filter(box => box.type === 'trak')) {\n const parsed = parseTrack(data, trak)\n if (!parsed) return null\n parsedTracks.push(parsed)\n }\n const mediaTracks = parsedTracks.filter(\n track => track.handlerType === 'vide' || track.handlerType === 'soun',\n )\n if (mediaTracks.length !== 1) return null\n const mediaTrack = mediaTracks[0]!\n if (mediaTrack.hasEdits) return null\n\n const mvex = exactlyOne(moovChildren, 'mvex')\n if (!mvex) return null\n const mvexChildren = children(data, mvex)\n if (!mvexChildren) return null\n const trexEntries: IsoBmffTrackTiming[] = []\n for (const trexBox of mvexChildren.filter(box => box.type === 'trex')) {\n const trex = parseTrex(data, trexBox)\n if (!trex) return null\n trexEntries.push(trex)\n }\n const matchingTrex = trexEntries.filter(trex => trex.trackId === mediaTrack.trackId)\n if (matchingTrex.length !== 1) return null\n const trex = matchingTrex[0]!\n return {\n trackId: mediaTrack.trackId,\n timescale: mediaTrack.timescale,\n ...(trex.defaultSampleDuration === undefined\n ? {}\n : { defaultSampleDuration: trex.defaultSampleDuration }),\n ...(trex.defaultSampleSize === undefined\n ? {}\n : { defaultSampleSize: trex.defaultSampleSize }),\n }\n}\n\nfunction parseMfhdSequence(data: Uint8Array, box: IsoBox): number | null {\n const full = parseFullBox(data, box)\n if (!full || full.version !== 0 || full.flags !== 0 || box.end - full.bodyStart !== 4) {\n return null\n }\n return view(data).getUint32(full.bodyStart, false)\n}\n\nfunction parseTfhd(data: Uint8Array, box: IsoBox): ParsedTfhd | null {\n const full = parseFullBox(data, box)\n if (!full || full.version !== 0 || (full.flags & ~TFHD_KNOWN_FLAGS) !== 0) return null\n if ((full.flags & TFHD_BASE_DATA_OFFSET_PRESENT) !== 0 ||\n (full.flags & TFHD_DEFAULT_BASE_IS_MOOF) === 0 ||\n (full.flags & TFHD_DURATION_IS_EMPTY) !== 0) {\n return null\n }\n const dataView = view(data)\n let offset = full.bodyStart\n if (offset + 4 > box.end) return null\n const trackId = dataView.getUint32(offset, false)\n offset += 4\n if ((full.flags & TFHD_SAMPLE_DESCRIPTION_INDEX_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n offset += 4\n }\n let defaultSampleDuration: number | undefined\n if ((full.flags & TFHD_DEFAULT_SAMPLE_DURATION_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n defaultSampleDuration = dataView.getUint32(offset, false)\n offset += 4\n }\n let defaultSampleSize: number | undefined\n if ((full.flags & TFHD_DEFAULT_SAMPLE_SIZE_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n defaultSampleSize = dataView.getUint32(offset, false)\n offset += 4\n }\n if ((full.flags & TFHD_DEFAULT_SAMPLE_FLAGS_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n offset += 4\n }\n if (offset !== box.end) return null\n return {\n trackId,\n ...(defaultSampleDuration === undefined ? {} : { defaultSampleDuration }),\n ...(defaultSampleSize === undefined ? {} : { defaultSampleSize }),\n }\n}\n\nfunction parseTfdt(data: Uint8Array, box: IsoBox): bigint | null {\n const full = parseFullBox(data, box)\n if (!full || full.flags !== 0) return null\n if (full.version === 0 && box.end - full.bodyStart === 4) {\n return BigInt(view(data).getUint32(full.bodyStart, false))\n }\n if (full.version === 1 && box.end - full.bodyStart === 8) {\n return readU64(data, full.bodyStart)\n }\n return null\n}\n\nfunction parseMdatLayout(data: Uint8Array, offset: number): MdatLayout | null {\n if (offset < 0 || offset + 8 > data.length || boxType(data, offset + 4) !== 'mdat') return null\n const dataView = view(data)\n const shortSize = dataView.getUint32(offset, false)\n let headerSize = 8\n let declaredSize: bigint\n if (shortSize === 0) {\n return null\n } else if (shortSize === 1) {\n const extended = readU64(data, offset + 8)\n if (extended === null) return null\n headerSize = 16\n declaredSize = extended\n } else {\n declaredSize = BigInt(shortSize)\n }\n if (declaredSize < BigInt(headerSize) || declaredSize < BigInt(data.length - offset)) {\n return null\n }\n const declaredEnd = BigInt(offset) + declaredSize\n if (declaredEnd > U64_MAX) return null\n return { dataStart: BigInt(offset + headerSize), declaredEnd }\n}\n\nfunction resolvedDefault(\n entryValue: number | undefined,\n tfhdValue: number | undefined,\n trexValue: number | undefined,\n): number | null {\n const value = entryValue ?? tfhdValue ?? trexValue\n return value === undefined || value === 0 ? null : value\n}\n\nfunction parseTrunSamples(\n data: Uint8Array,\n box: IsoBox,\n moofStart: number,\n baseDecodeTime: bigint,\n tfhd: ParsedTfhd,\n track: IsoBmffTrackTiming,\n mdat: MdatLayout,\n maxSamples: number,\n): IsoBmffSampleTiming[] | null {\n const full = parseFullBox(data, box)\n if (!full || (full.version !== 0 && full.version !== 1) ||\n (full.flags & ~TRUN_KNOWN_FLAGS) !== 0 ||\n (full.flags & TRUN_DATA_OFFSET_PRESENT) === 0 ||\n ((full.flags & TRUN_FIRST_SAMPLE_FLAGS_PRESENT) !== 0 &&\n (full.flags & TRUN_SAMPLE_FLAGS_PRESENT) !== 0)) {\n return null\n }\n const dataView = view(data)\n let offset = full.bodyStart\n if (offset + 8 > box.end) return null\n const sampleCount = dataView.getUint32(offset, false)\n offset += 4\n if (sampleCount === 0 || sampleCount > maxSamples) return null\n const relativeDataOffset = dataView.getInt32(offset, false)\n offset += 4\n if (relativeDataOffset < 0) return null\n const dataOffset = BigInt(relativeDataOffset)\n const absoluteDataOffset = BigInt(moofStart) + dataOffset\n if (absoluteDataOffset > BigInt(data.length) || absoluteDataOffset < mdat.dataStart) return null\n if ((full.flags & TRUN_FIRST_SAMPLE_FLAGS_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n offset += 4\n }\n\n let decodeTime = baseDecodeTime\n let bytePosition = dataOffset\n const samples: IsoBmffSampleTiming[] = []\n for (let index = 0; index < sampleCount; index++) {\n let entryDuration: number | undefined\n if ((full.flags & TRUN_SAMPLE_DURATION_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n entryDuration = dataView.getUint32(offset, false)\n offset += 4\n }\n let entrySize: number | undefined\n if ((full.flags & TRUN_SAMPLE_SIZE_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n entrySize = dataView.getUint32(offset, false)\n offset += 4\n }\n if ((full.flags & TRUN_SAMPLE_FLAGS_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n offset += 4\n }\n let compositionOffset = 0\n if ((full.flags & TRUN_SAMPLE_CTS_OFFSET_PRESENT) !== 0) {\n if (offset + 4 > box.end) return null\n compositionOffset = full.version === 1\n ? dataView.getInt32(offset, false)\n : dataView.getUint32(offset, false)\n offset += 4\n }\n if (offset > box.end) return null\n\n const duration = resolvedDefault(\n entryDuration,\n tfhd.defaultSampleDuration,\n track.defaultSampleDuration,\n )\n const size = resolvedDefault(entrySize, tfhd.defaultSampleSize, track.defaultSampleSize)\n if (duration === null || size === null) return null\n const sampleEnd = bytePosition + BigInt(size)\n const absoluteSampleEnd = BigInt(moofStart) + sampleEnd\n if (absoluteSampleEnd > mdat.declaredEnd || absoluteSampleEnd > U64_MAX) return null\n const presentationTime = decodeTime + BigInt(compositionOffset)\n if (presentationTime < I64_MIN || presentationTime > I64_MAX) return null\n samples.push({\n decodeTime,\n presentationTime,\n duration,\n timescale: track.timescale,\n size,\n bytePosition,\n })\n decodeTime += BigInt(duration)\n if (decodeTime > U64_MAX) return null\n bytePosition = sampleEnd\n }\n return offset === box.end ? samples : null\n}\n\nexport function parseIsoBmffMovieFragmentTiming(\n data: Uint8Array,\n track: IsoBmffTrackTiming,\n maxSamples: number,\n): IsoBmffMovieFragmentTiming | null {\n if (!Number.isInteger(maxSamples) || maxSamples <= 0) return null\n const first = parseBox(data, 0, data.length)\n if (!first) return null\n const moof = first.type === 'styp'\n ? parseBox(data, first.end, data.length)\n : first\n if (!moof || moof.type !== 'moof' || moof.end >= data.length) return null\n const moofChildren = children(data, moof)\n if (!moofChildren) return null\n const mfhd = exactlyOne(moofChildren, 'mfhd')\n const traf = exactlyOne(moofChildren, 'traf')\n if (!mfhd || !traf) return null\n const movieFragmentSequenceNumber = parseMfhdSequence(data, mfhd)\n if (movieFragmentSequenceNumber === null || movieFragmentSequenceNumber === 0) return null\n\n const trafChildren = children(data, traf)\n if (!trafChildren) return null\n const tfhdBox = exactlyOne(trafChildren, 'tfhd')\n const tfdtBox = exactlyOne(trafChildren, 'tfdt')\n const trunBox = exactlyOne(trafChildren, 'trun')\n if (!tfhdBox || !tfdtBox || !trunBox) return null\n const tfhd = parseTfhd(data, tfhdBox)\n const baseDecodeTime = parseTfdt(data, tfdtBox)\n if (!tfhd || tfhd.trackId !== track.trackId || baseDecodeTime === null) return null\n const mdat = parseMdatLayout(data, moof.end)\n if (!mdat) return null\n const samples = parseTrunSamples(\n data,\n trunBox,\n moof.start,\n baseDecodeTime,\n tfhd,\n track,\n mdat,\n maxSamples,\n )\n if (!samples) return null\n return { movieFragmentSequenceNumber, samples }\n}\n\nfunction sameTrack(a: IsoBmffTrackTiming, b: IsoBmffTrackTiming): boolean {\n return a.trackId === b.trackId &&\n a.timescale === b.timescale &&\n a.defaultSampleDuration === b.defaultSampleDuration &&\n a.defaultSampleSize === b.defaultSampleSize\n}\n\nfunction sameSample(a: IsoBmffSampleTiming, b: IsoBmffSampleTiming): boolean {\n return a.decodeTime === b.decodeTime &&\n a.presentationTime === b.presentationTime &&\n a.duration === b.duration &&\n a.timescale === b.timescale &&\n a.size === b.size &&\n a.bytePosition === b.bytePosition\n}\n\ntype PendingMovieFragment = Array<IsoBmffSampleTiming | null>\n\n/** Bounded per-track FT=0/FT=1 timing state. */\nexport class IsoBmffTimingResolver {\n #track: IsoBmffTrackTiming | null = null\n #fragments = new Map<string, PendingMovieFragment>()\n #pendingSamples = 0\n\n constructor(readonly maxPendingSamples: number) {\n if (!Number.isInteger(maxPendingSamples) || maxPendingSamples <= 0) {\n throw new Error('[IsoBmffTimingResolver] maxPendingSamples must be a positive integer')\n }\n }\n\n get pendingSampleCount(): number {\n return this.#pendingSamples\n }\n\n addInitialization(data: Uint8Array): boolean {\n const timing = parseIsoBmffInitializationTiming(data)\n if (!timing) return false\n if (this.#track) return sameTrack(this.#track, timing)\n this.#track = timing\n return true\n }\n\n addMovieFragment(\n mpuSequenceNumber: number,\n data: Uint8Array,\n additionalSampleLimit = this.maxPendingSamples,\n ): boolean {\n if (!Number.isInteger(additionalSampleLimit) || additionalSampleLimit < 0) return false\n if (!this.#track || !isU32(mpuSequenceNumber)) return false\n const parsed = parseIsoBmffMovieFragmentTiming(\n data,\n this.#track,\n this.maxPendingSamples,\n )\n if (!parsed) return false\n const key = fragmentKey(mpuSequenceNumber, parsed.movieFragmentSequenceNumber)\n const existing = this.#fragments.get(key)\n if (existing) {\n return existing.length === parsed.samples.length && existing.every(\n (sample, index) => sample !== null && sameSample(sample, parsed.samples[index]!),\n )\n }\n if (parsed.samples.length > additionalSampleLimit) return false\n if (this.#pendingSamples + parsed.samples.length > this.maxPendingSamples) return false\n this.#fragments.set(key, [...parsed.samples])\n this.#pendingSamples += parsed.samples.length\n return true\n }\n\n sampleTiming(\n mpuSequenceNumber: number,\n movieFragmentSequenceNumber: number,\n sampleNumber: number,\n ): IsoBmffSampleTiming | null {\n if (!isU32(mpuSequenceNumber) || !isU32(movieFragmentSequenceNumber) ||\n movieFragmentSequenceNumber === 0 || !isU32(sampleNumber) || sampleNumber === 0) {\n return null\n }\n const samples = this.#fragments.get(\n fragmentKey(mpuSequenceNumber, movieFragmentSequenceNumber),\n )\n return samples?.[sampleNumber - 1] ?? null\n }\n\n consumeSample(\n mpuSequenceNumber: number,\n movieFragmentSequenceNumber: number,\n sampleNumber: number,\n expected: IsoBmffSampleTiming,\n ): boolean {\n const key = fragmentKey(mpuSequenceNumber, movieFragmentSequenceNumber)\n const samples = this.#fragments.get(key)\n const index = sampleNumber - 1\n const sample = samples?.[index]\n if (!samples || !sample || !sameSample(sample, expected)) return false\n samples[index] = null\n this.#pendingSamples--\n if (samples.every(value => value === null)) this.#fragments.delete(key)\n return true\n }\n\n reset(): void {\n this.#track = null\n this.#fragments.clear()\n this.#pendingSamples = 0\n }\n}\n\nfunction fragmentKey(mpuSequenceNumber: number, movieFragmentSequenceNumber: number): string {\n return `${mpuSequenceNumber}:${movieFragmentSequenceNumber}`\n}\n\nfunction isU32(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value <= 0xffff_ffff\n}\n\n/** Convert exact media ticks to microseconds without first narrowing the ticks. */\nexport function mediaTicksToMicroseconds(ticks: bigint, timescale: number): number | null {\n if (!Number.isInteger(timescale) || timescale <= 0) return null\n const numerator = ticks * MICROSECONDS_PER_SECOND\n const scale = BigInt(timescale)\n const whole = numerator / scale\n const remainder = numerator % scale\n if (whole < BigInt(Number.MIN_SAFE_INTEGER) || whole > BigInt(Number.MAX_SAFE_INTEGER)) {\n return null\n }\n const result = Number(whole) + Number(remainder) / timescale\n return Number.isFinite(result) ? result : null\n}\n", "import {\n parseMMTPHeader,\n extractMFUPayload,\n extractFecPayloadId,\n hasSourceFecPayloadId,\n extractAltaTrailer,\n PACKET_TYPE_MPU,\n PACKET_TYPE_REPAIR,\n} from '@blockcast/mmt-base'\nimport {\n MFUReassembler,\n MfuReassemblyError,\n type MfuReassemblerBackend,\n type MmtpSourcePath,\n type MmtpDeliveryProvenance,\n type ReassembledMFU,\n} from './mfu-reassembler.js'\nimport {\n SampleCombiner,\n SampleCombineError,\n type CombinedSample,\n type SampleCombinerOptions,\n type SampleCombinerStats,\n} from './sample-combiner.js'\nimport { classifyH264Keyframe, mmtpTimestampToMicros } from './codec-utils.js'\nimport {\n mmtpFrameReorderWindowFrames,\n mfuReassemblyTimeoutMs,\n type MfuReassemblyPolicy,\n} from './mfu-reassembly-policy.js'\nimport {\n IsoBmffTimingResolver,\n mediaTicksToMicroseconds,\n type IsoBmffSampleTiming,\n} from './iso-bmff-timing.js'\nimport type { AuthoredTiming } from './authored-timing.js'\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface TrackInfo {\n type: 'video' | 'audio'\n codec: string\n /** Catalog/in-band frame cadence available to downstream media stages. */\n framerate?: number\n /** Catalog MMTP group duration available to downstream media stages. */\n groupDurationMs?: number\n}\n\nexport interface MmtpParserConfig {\n tracks?: Map<number, TrackInfo>\n reorderWindow?: number\n /** MFU reassembly timeout (ms). Derive from FEC interleaveDepthMs or frame duration.\n * Pass null for live clock-free operation; memory remains bounded by maxBuffered.\n * Defaults to 1000ms if not provided. */\n mfuTimeoutMs?: number | null\n /** Explicit transport policy. Reliable MoQ is clock-free; datagrams require\n * a catalog/in-band-derived deadline. Mutually exclusive with mfuTimeoutMs. */\n reassemblyPolicy?: MfuReassemblyPolicy\n /** Max concurrent active MFUs. A new first fragment is rejected at the bound. */\n maxBufferedMfus?: number\n /**\n * Max subsample data units held for one partitioned sample (moqcast #37).\n * A sample needs `ceil(sampleSize / (256 * maxPayload))` data units; at the\n * ~1300-byte payloads this producer emits, one data unit spans ~327 KB, so\n * the default admits a ~5 MB sample \u2014 beyond any 8K intra frame \u2014 while\n * still bounding the per-insert overlap scan. Raise it only alongside a\n * larger authored frame size, never to paper over a lost data unit.\n */\n maxSubsampleSegments?: number\n /** Alternate backend. The TypeScript and Rust/WASM implementations enforce\n * the same authored active-DU contract. */\n reassembler?: MfuReassemblerBackend\n /** Default track info for auto-discovered packetIds.\n * When the parser receives an MMTP packet with an unregistered packetId,\n * it creates a new track entry using this info. Required for correct\n * keyframe detection with HEVC/AV1/VP9 (without it, defaults to H.264). */\n defaultTrack?: TrackInfo\n}\n\n/** Structured result from parseHeader(). Used by MmtpRouter for two-phase parsing:\n * 1. parseHeader() \u2192 get packetId + fecPayloadId for FEC feeding\n * 2. feed() \u2192 MFU extraction, reassembly, keyframe detection, emit */\nexport interface PacketInfo {\n packetId: number\n packetType: number\n timestamp: bigint\n /** MMTP timestamp converted to monotonic microseconds for pacing/sync users. */\n timestampUs: number\n sequenceNumber: number\n rapFlag: boolean\n fecType: number\n /** FEC source symbol ID (from 4-byte FEC Payload ID at packet end). Null if no FEC. */\n fecPayloadId: { ssId: number } | null\n /** True if this is a repair packet (packetType=0x03). */\n isRepair: boolean\n}\n\nexport interface FrameMeta {\n /** Authored ISO-BMFF presentation time converted to microseconds. */\n timestamp: number\n /** Authored ISO-BMFF decode time converted to microseconds. */\n decodeTimestamp?: number\n /** Authored ISO-BMFF sample duration converted to microseconds. */\n duration?: number\n /** Exact authored decode time in mediaTimescale units. */\n decodeTime?: bigint\n /** Exact authored presentation time in mediaTimescale units. */\n presentationTime?: bigint\n /** Exact authored duration in mediaTimescale units. */\n durationTicks?: number\n /** mdhd media timescale for the exact timing fields. */\n mediaTimescale?: number\n /** Exact authored ISO-BMFF timing carried without narrowing to microseconds. */\n authoredTiming?: AuthoredTiming\n keyframe: boolean\n packetId: number\n trackType: 'video' | 'audio' | null\n packetSequenceNumber?: number\n /** Every authoritative MMTP source-packet sequence that formed this frame. */\n packetSequenceNumbers?: number[]\n /** Subset of packetSequenceNumbers reconstructed by FEC. */\n repairedPacketSequenceNumbers?: number[]\n /** Immutable catalog identity supplied by the owning ingress subscription. */\n catalogTrackName?: string\n /** Raw MMTP short-NTP timestamp retained for adapters that own the timeline. */\n mmtpTimestamp?: bigint\n mpuSequenceNumber?: number\n movieFragmentSequenceNumber?: number\n sampleNumber?: number\n fragmentCount?: number\n rapFlag?: boolean\n priority?: number\n recovered?: boolean\n /** Starts a new publisher/source epoch for ordering and timestamp state. */\n sourceDiscontinuity?: boolean\n /** Source of the accepted FI=1/FI=0 frame path. Absent for recovery. */\n source?: MmtpSourcePath\n /**\n * Strict whole-frame delivery provenance. Unlike `source`, which names the\n * head fragment's transport for routing, this is folded over every\n * contributing fragment (and, for FEC-recovered frames, over the transports\n * that supplied the block's symbols). Only 'multicast' is evidence of\n * multicast-exclusive delivery. Absent means no producer on this path\n * recorded provenance \u2014 consumers MUST treat that as unproven, never as\n * multicast.\n */\n deliveryProvenance?: MmtpDeliveryProvenance\n}\n\nexport interface MmtpReorderStats {\n droppedBeforeStartupKeyframe: number\n droppedIncompleteStartupInterval: number\n droppedStale: number\n forcedWindowFlushes: number\n droppedOnLiveDispose: number\n publisherResets: number\n droppedOnPublisherReset: number\n bufferedFrames: number\n}\n\nexport interface MmtpReorderFrameDiagnostics {\n timestampUs: number\n keyframe: boolean\n sequenceNumber: number\n packetSequenceNumber?: number\n movieFragmentSequenceNumber?: number\n sampleNumber?: number\n fragmentCount?: number\n}\n\nexport interface MmtpReorderStreamDiagnostics {\n packetId: number\n bufferedFrames: number\n lastEmitted: MmtpReorderFrameDiagnostics | null\n startupKeyframe: MmtpReorderFrameDiagnostics | null\n head: MmtpReorderFrameDiagnostics[]\n tail: MmtpReorderFrameDiagnostics[]\n keyframes: MmtpReorderFrameDiagnostics[]\n}\n\nexport type MmtpParserRecord =\n | {\n kind: 'repair' | 'non-media'\n packet: PacketInfo\n byteLength: number\n }\n | {\n kind: 'init' | 'fragment'\n packet: PacketInfo\n data: Uint8Array\n mpuSequenceNumber: number\n fragmentationIndicator: number\n fragmentCounter: number\n }\n | {\n kind: 'pending'\n packet: PacketInfo\n mpuSequenceNumber: number\n fragmentationIndicator: number\n fragmentCounter: number\n }\n | {\n kind: 'rejected'\n packet: PacketInfo\n mpuSequenceNumber: number\n fragmentationIndicator: number\n fragmentCounter: number\n error: {\n /**\n * Either a single data unit's own fragment chain was rejected, or\n * combining reassembled data units into a sample was (moqcast #37).\n * Both are fail-closed drops of the same packet, so they share this\n * record; the code says which class it came from.\n */\n code: MfuReassemblyError['code'] | SampleCombineError['code']\n message: string\n }\n }\n | {\n kind: 'frame'\n packet: PacketInfo\n data: Uint8Array\n meta: FrameMeta\n mpuSequenceNumber: number\n fragmentCount: number\n rapFlag: boolean\n priority?: number\n }\n\ntype FrameCallback = (data: Uint8Array, meta: FrameMeta) => void\ntype InitCallback = (data: Uint8Array, packetId: number, catalogTrackName?: string) => void\n\nexport function isMfuKeyframeMetadata(rapFlag: boolean, priority?: number): boolean {\n return rapFlag || (priority ?? 0) > 0x80\n}\n\n/** Per-packet detached ALTA authenticator trailer (08-07 Path 2).\n * - `auth`: the authenticator bytes (length-prefixed at packet end).\n * - `payloadEnd`: byte offset within the source packet where signed\n * payload ends \u2014 consumers reconstruct `payload = packet[0..payloadEnd]`\n * for verify_detached. */\nexport interface ParsedAltaTrailer {\n auth: Uint8Array\n payloadEnd: number\n}\n\n/** Source-symbol exposure for FEC consumers.\n *\n * Fires from {@link MmtpContainerParser.feed} once per FEC-protected source\n * packet \u2014 after parseHeader confirms fec_payload_id is present, before MFU\n * extraction.\n *\n * Arguments:\n * - `packetId` \u2013 MMTP header `packet_id` (track identifier)\n * - `ssId` \u2013 flat Source Symbol ID from the 4-byte fec_payload_id trailer\n * - `rawSymbol` \u2013 packet bytes minus the 4-byte fec_payload_id trailer.\n * NOT zero-padded to FEC symbol size \u2014 consumer pads if needed\n * (FEC symbol size is FEC-config, not parser-config).\n * - `alta` \u2013 detached ALTA authenticator if present, else undefined\n *\n * Designed so consumers (player, Shaka shim, upstream moqtail) can wire\n * source-symbol delivery to whatever FEC pipeline they own, without depending\n * on libmmt's MulticastManager/MmtpRouter/FECManager stack. */\ntype SourceSymbolCallback = (\n packetId: number,\n ssId: number,\n rawSymbol: Uint8Array,\n alta: ParsedAltaTrailer | undefined,\n) => void\n\nexport interface ParseHeaderOptions {\n /** Recovered source symbols already had the trailing Source FEC Payload ID stripped. */\n skipFecPayloadId?: boolean\n /** Mark frames emitted from this packet as FEC-recovered. */\n recovered?: boolean\n /** Exact catalog track bound to this packet by its ingress subscription. */\n catalogTrackName?: string\n /** Source transport for parser-owned frame attribution. */\n source?: MmtpSourcePath\n /**\n * Explicit strict provenance for the fragments this packet produces.\n * The router sets it for FEC-recovered packets, which carry no `source`\n * but whose FEC block's symbol provenance is known.\n */\n deliveryProvenance?: MmtpDeliveryProvenance\n}\n\n// \u2500\u2500 FrameSequencer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface PendingFrame {\n data: Uint8Array\n meta: FrameMeta\n seq: number\n}\n\ninterface SequencerStream {\n buf: PendingFrame[]\n lastEmitted: PendingFrame | null\n}\n\nfunction reorderFrameDiagnostics(frame: PendingFrame): MmtpReorderFrameDiagnostics {\n return {\n timestampUs: frame.meta.timestamp,\n keyframe: frame.meta.keyframe,\n sequenceNumber: frame.seq,\n ...(frame.meta.packetSequenceNumber === undefined\n ? {}\n : { packetSequenceNumber: frame.meta.packetSequenceNumber }),\n ...(frame.meta.movieFragmentSequenceNumber === undefined\n ? {}\n : { movieFragmentSequenceNumber: frame.meta.movieFragmentSequenceNumber }),\n ...(frame.meta.sampleNumber === undefined ? {} : { sampleNumber: frame.meta.sampleNumber }),\n ...(frame.meta.fragmentCount === undefined\n ? {}\n : { fragmentCount: frame.meta.fragmentCount }),\n }\n}\n\ntype SequencerStat = Exclude<keyof MmtpReorderStats, 'bufferedFrames'>\n\nfunction serialSequenceDelta(a: number, b: number): number {\n return (a - b) | 0\n}\n\nfunction compareFrameOrder(\n a: PendingFrame,\n b: PendingFrame,\n): number {\n if (a.meta.decodeTime !== undefined && b.meta.decodeTime !== undefined &&\n a.meta.mediaTimescale === b.meta.mediaTimescale) {\n if (a.meta.decodeTime < b.meta.decodeTime) return -1\n if (a.meta.decodeTime > b.meta.decodeTime) return 1\n } else {\n const decodeDelta = (a.meta.decodeTimestamp ?? a.meta.timestamp) -\n (b.meta.decodeTimestamp ?? b.meta.timestamp)\n if (decodeDelta !== 0) return decodeDelta\n }\n const movieDelta = serialSequenceDelta(\n a.meta.movieFragmentSequenceNumber ?? 0,\n b.meta.movieFragmentSequenceNumber ?? 0,\n )\n if (movieDelta !== 0) return movieDelta\n const sampleDelta = (a.meta.sampleNumber ?? 0) - (b.meta.sampleNumber ?? 0)\n return sampleDelta !== 0 ? sampleDelta : serialSequenceDelta(a.seq, b.seq)\n}\n\nclass FrameSequencer {\n #streams = new Map<number, SequencerStream>()\n #windowSize: number\n #emit: (data: Uint8Array, meta: FrameMeta) => void\n #recordStat: (stat: SequencerStat, count?: number) => void\n\n constructor(\n windowSize: number,\n emit: (data: Uint8Array, meta: FrameMeta) => void,\n recordStat: (stat: SequencerStat, count?: number) => void = () => {},\n ) {\n this.#windowSize = windowSize\n this.#emit = emit\n this.#recordStat = recordStat\n }\n\n push(data: Uint8Array, meta: FrameMeta, seq: number): void {\n if (this.#windowSize <= 0) {\n this.#emit(data, meta)\n return\n }\n const frame: PendingFrame = { data, meta: { ...meta }, seq }\n const stream = this.#streamFor(meta.packetId)\n if (meta.sourceDiscontinuity) {\n this.#recordStat('publisherResets')\n this.#recordStat('droppedOnPublisherReset', stream.buf.length)\n stream.buf = []\n stream.lastEmitted = null\n frame.meta.sourceDiscontinuity = true\n }\n if (this.#isStale(stream, frame)) {\n this.#recordStat('droppedStale')\n return\n }\n if (meta.keyframe && stream.lastEmitted === null) {\n // The first RAP establishes decoder sync. Discard any pre-RAP media and\n // emit it immediately so startup does not pay the reorder-window delay.\n this.#recordStat('droppedBeforeStartupKeyframe', stream.buf.length)\n stream.buf = []\n this.#emitFrame(stream, frame)\n return\n }\n let lo = 0, hi = stream.buf.length\n while (lo < hi) {\n const mid = (lo + hi) >>> 1\n if (compareFrameOrder(stream.buf[mid]!, frame) < 0) lo = mid + 1\n else hi = mid\n }\n stream.buf.splice(lo, 0, frame)\n while (stream.buf.length > this.#windowSize) this.#flushHead(stream)\n }\n\n flush(): void {\n for (const packetId of [...this.#streams.keys()].sort((a, b) => a - b)) {\n const stream = this.#streams.get(packetId)\n while (stream && stream.buf.length > 0) this.#flushHead(stream)\n }\n }\n\n get buffered(): number {\n let total = 0\n for (const stream of this.#streams.values()) total += stream.buf.length\n return total\n }\n\n get diagnostics(): Omit<MmtpReorderStreamDiagnostics, 'packetId'>[] {\n return [...this.#streams.values()].map(stream => ({\n bufferedFrames: stream.buf.length,\n lastEmitted: stream.lastEmitted ? reorderFrameDiagnostics(stream.lastEmitted) : null,\n startupKeyframe: null,\n head: stream.buf.slice(0, 4).map(reorderFrameDiagnostics),\n tail: stream.buf.slice(-4).map(reorderFrameDiagnostics),\n keyframes: stream.buf.filter(frame => frame.meta.keyframe).slice(0, 8)\n .map(reorderFrameDiagnostics),\n }))\n }\n\n discard(): void {\n for (const stream of this.#streams.values()) {\n this.#recordStat('droppedOnLiveDispose', stream.buf.length)\n stream.buf = []\n }\n this.#streams.clear()\n }\n\n #streamFor(packetId: number): SequencerStream {\n let stream = this.#streams.get(packetId)\n if (!stream) {\n stream = { buf: [], lastEmitted: null }\n this.#streams.set(packetId, stream)\n }\n return stream\n }\n\n #flushHead(stream: SequencerStream): boolean {\n const f = stream.buf.shift()\n if (!f) return false\n return this.#emitFrame(stream, f)\n }\n\n #isStale(stream: SequencerStream, frame: PendingFrame): boolean {\n return stream.lastEmitted !== null &&\n compareFrameOrder(frame, stream.lastEmitted) <= 0\n }\n\n #emitFrame(stream: SequencerStream, frame: PendingFrame): boolean {\n if (this.#isStale(stream, frame)) {\n this.#recordStat('droppedStale')\n return false\n }\n stream.lastEmitted = frame\n this.#emit(frame.data, frame.meta)\n return true\n }\n}\n\n// \u2500\u2500 fMP4 box detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst FMPU_BOX_TYPES = new Set(['ftyp', 'moov', 'moof'])\n\nfunction isFmp4Box(data: Uint8Array): boolean {\n if (data.length < 8) return false\n const type = String.fromCharCode(data[4]!, data[5]!, data[6]!, data[7]!)\n return FMPU_BOX_TYPES.has(type)\n}\n\ninterface TimingStreamState {\n packetId: number\n resolver: IsoBmffTimingResolver\n}\n\n// \u2500\u2500 MmtpContainerParser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MmtpContainerParser {\n #tracks = new Map<number, TrackInfo>()\n #defaultTrack: TrackInfo | null = null\n #reassembler: MfuReassemblerBackend\n /**\n * Combines subsample data units back into whole samples (moqcast #37), one\n * combiner per parser stream. Per-stream is required, not an optimization:\n * {@link SampleCombiner} identifies a sample by\n * `(catalogTrackName, mpu, movieFragment, sampleNumber)`, so two streams\n * that carry no catalog identity would collide on every sample number and\n * each would see the other's data units as duplicates of its own. The\n * parser already isolates timing and reordering by this same stream key.\n *\n * Inert until a producer partitions \u2014 which it does the moment a frame\n * exceeds one data unit's 256-payload capacity (~327 KB here, routinely\n * exceeded by 4K/8K intra frames).\n */\n #sampleCombiners = new Map<string, SampleCombiner>()\n #sampleCombinerOptions: SampleCombinerOptions\n /** Counters folded in from combiners dropped by a track reset, so the\n * totals below stay monotonic across a publisher change. */\n #retiredSampleCombinerStats: SampleCombinerStats = {\n combinedSamples: 0,\n passthroughSamples: 0,\n timedOutSamples: 0,\n evictedSamples: 0,\n }\n #timingStreams = new Map<string, TimingStreamState>()\n #maxPendingTimingSamples: number\n #pendingTimingSamples = 0\n #sequencers = new Map<string, { packetId: number; sequencer: FrameSequencer }>()\n #reorderWindow = 0\n #reassemblyPolicy: MfuReassemblyPolicy | null = null\n #frameCb: FrameCallback | null = null\n #initCb: InitCallback | null = null\n #sourceSymbolCb: SourceSymbolCallback | null = null\n #trackFrameCbs = new Map<number, FrameCallback>()\n #reorderStats: Omit<MmtpReorderStats, 'bufferedFrames'> = {\n droppedBeforeStartupKeyframe: 0,\n droppedIncompleteStartupInterval: 0,\n droppedStale: 0,\n forcedWindowFlushes: 0,\n droppedOnLiveDispose: 0,\n publisherResets: 0,\n droppedOnPublisherReset: 0,\n }\n /** Streams (keyed by physical parser stream, value is packetId) that have\n * observed their first RAP under a reliable-moq policy. Reliable MoQ\n * resolves to a zero parser reorder window (transport ordering is\n * protocol-owned), so #sequencerFor returns null and its own startup gate\n * never runs; this map preserves an order-independent \"discard delta\n * frames before the first RAP\" gate for that path without reintroducing\n * reordering into the parser. */\n #reliableMoqStartupGate = new Map<string, number>()\n #disposed = false\n\n #streamKey(packetId: number, catalogTrackName?: string): string {\n return JSON.stringify([packetId, catalogTrackName ?? null])\n }\n\n #sampleCombinerFor(streamKey: string): SampleCombiner {\n let combiner = this.#sampleCombiners.get(streamKey)\n if (combiner === undefined) {\n combiner = new SampleCombiner(this.#sampleCombinerOptions)\n this.#sampleCombiners.set(streamKey, combiner)\n }\n return combiner\n }\n\n /** Drop one stream's combiner, folding its counters into the retained\n * totals so a reset cannot make the exposed stats run backwards. */\n #dropSampleCombiner(streamKey: string): void {\n const combiner = this.#sampleCombiners.get(streamKey)\n if (combiner === undefined) return\n const stats = combiner.stats\n this.#retiredSampleCombinerStats.combinedSamples += stats.combinedSamples\n this.#retiredSampleCombinerStats.passthroughSamples += stats.passthroughSamples\n this.#retiredSampleCombinerStats.timedOutSamples += stats.timedOutSamples\n this.#retiredSampleCombinerStats.evictedSamples += stats.evictedSamples\n this.#sampleCombiners.delete(streamKey)\n }\n\n constructor(config?: MmtpParserConfig) {\n if (config?.reassemblyPolicy && config.mfuTimeoutMs !== undefined) {\n throw new Error('[MmtpContainerParser] use reassemblyPolicy or mfuTimeoutMs, not both')\n }\n const reassemblyPolicy = config?.reassemblyPolicy ?? null\n const requestedReorderWindow = config?.reorderWindow ?? 0\n if (!Number.isFinite(requestedReorderWindow) || requestedReorderWindow < 0) {\n throw new Error('[MmtpContainerParser] reorderWindow must be a finite number >= 0')\n }\n const normalizedReorderWindow = Math.ceil(requestedReorderWindow)\n const requestedMfuTimeout = reassemblyPolicy\n ? mfuReassemblyTimeoutMs(reassemblyPolicy)\n : config?.mfuTimeoutMs\n if (reassemblyPolicy) {\n mmtpFrameReorderWindowFrames(reassemblyPolicy, normalizedReorderWindow)\n }\n if (\n requestedMfuTimeout !== undefined &&\n requestedMfuTimeout !== null &&\n requestedMfuTimeout <= 0\n ) {\n throw new Error('[MmtpContainerParser] mfuTimeoutMs must be > 0')\n }\n const maxPendingTimingSamples = config?.maxBufferedMfus ?? 100\n if (!Number.isInteger(maxPendingTimingSamples) || maxPendingTimingSamples <= 0) {\n throw new Error('[MmtpContainerParser] maxBufferedMfus must be a positive integer')\n }\n this.#maxPendingTimingSamples = maxPendingTimingSamples\n\n const maxSubsampleSegments = config?.maxSubsampleSegments ?? 16\n if (!Number.isInteger(maxSubsampleSegments) || maxSubsampleSegments <= 0) {\n throw new Error('[MmtpContainerParser] maxSubsampleSegments must be a positive integer')\n }\n\n // Everything above only reads `config` \u2014 the full policy/window\n // combination is validated before this point, so it is now safe to\n // mutate this instance or an injected, caller-owned reassembler.\n this.#reassemblyPolicy = reassemblyPolicy\n if (config?.reassembler) {\n this.#reassembler = config.reassembler\n // An injected backend owns its existing transport policy unless this\n // parser was given an explicit policy/legacy timeout override.\n if (requestedMfuTimeout !== undefined) {\n this.#reassembler.timeoutMs = requestedMfuTimeout\n }\n } else {\n this.#reassembler = new MFUReassembler({\n timeoutMs: requestedMfuTimeout === undefined ? 1000 : requestedMfuTimeout,\n ...(config?.maxBufferedMfus === undefined ? {} : { maxBuffered: config.maxBufferedMfus }),\n })\n }\n this.#reorderWindow = normalizedReorderWindow\n this.#sampleCombinerOptions = {\n maxBuffered: maxPendingTimingSamples,\n // The parser resolves every timed sample's authored size from FT=1\n // before its data units reach the combiner, so \"no size yet\" always\n // means the size has not arrived -- never that the host declines to\n // supply one. Waiting is therefore correct: treating an offset-zero\n // data unit as a whole sample in that window would truncate a\n // partitioned sample instead of completing it.\n requireAuthoredSize: true,\n maxSegmentsPerSample: maxSubsampleSegments,\n }\n this.#defaultTrack = config?.defaultTrack ?? null\n if (this.#defaultTrack) this.#validateReassemblyPolicy()\n if (config?.tracks) {\n for (const [id, info] of config.tracks) {\n this.#validateReassemblyPolicy()\n this.#tracks.set(id, info)\n }\n }\n }\n\n /** The MFU reassembler that buffers fragmented MFUs fed via feed().\n * Exposed so a co-operating FEC-recovery layer (MmtpRouter) feeds recovered\n * fragments into the SAME reassembler: a frame split between source-received\n * and FEC-recovered fragments must share one buffer to complete (BLO-8151).\n * Before this was shared, recovered video symbols reported \"0 emitted,\n * pending-reassembly=N\" because they landed in a separate instance. */\n get reassembler(): MfuReassemblerBackend {\n return this.#reassembler\n }\n\n /**\n * Subsample-combining counters (moqcast #37). `combinedSamples` counts\n * samples reassembled from two or more data units and `passthroughSamples`\n * those that arrived whole; the two are deliberately separate, so a\n * producer that silently stopped partitioning cannot hide behind a single\n * \"samples delivered\" total. This is the typed acceptance signal for\n * large-frame playback \u2014 never parse the console for it.\n */\n get sampleCombinerStats(): Readonly<SampleCombinerStats> {\n const total = { ...this.#retiredSampleCombinerStats }\n for (const combiner of this.#sampleCombiners.values()) {\n const stats = combiner.stats\n total.combinedSamples += stats.combinedSamples\n total.passthroughSamples += stats.passthroughSamples\n total.timedOutSamples += stats.timedOutSamples\n total.evictedSamples += stats.evictedSamples\n }\n return total\n }\n\n get reorderStats(): Readonly<MmtpReorderStats> {\n let bufferedFrames = 0\n for (const { sequencer } of this.#sequencers.values()) bufferedFrames += sequencer.buffered\n return { ...this.#reorderStats, bufferedFrames }\n }\n\n get reorderDiagnostics(): ReadonlyArray<MmtpReorderStreamDiagnostics> {\n const streams: MmtpReorderStreamDiagnostics[] = []\n for (const { packetId, sequencer } of this.#sequencers.values()) {\n for (const diagnostics of sequencer.diagnostics) streams.push({ packetId, ...diagnostics })\n }\n return streams\n }\n\n /**\n * Phase 1 of two-phase parsing. Returns structured header info for FEC feeding.\n * Call this before feed() when you need to extract FEC payload ID and feed\n * symbols to FECManager before MFU extraction.\n * Returns null for malformed packets.\n */\n parseHeader(packet: Uint8Array, options?: ParseHeaderOptions): PacketInfo | null {\n try {\n const header = parseMMTPHeader(packet)\n const isRepair = header.packetType === PACKET_TYPE_REPAIR\n const fecPayloadId =\n !options?.skipFecPayloadId &&\n !isRepair && hasSourceFecPayloadId(packet)\n ? extractFecPayloadId(packet)\n : null\n return {\n packetId: header.packetId,\n packetType: header.packetType,\n timestamp: header.timestamp,\n timestampUs: mmtpTimestampToMicros(header.timestamp),\n sequenceNumber: header.sequenceNumber,\n rapFlag: header.rapFlag,\n fecType: header.fecType,\n fecPayloadId,\n isRepair,\n }\n } catch {\n return null\n }\n }\n\n /** Feed a complete MMTP packet. For two-phase use, call parseHeader() first. */\n feed(packet: Uint8Array, options?: ParseHeaderOptions): MmtpParserRecord | null {\n if (this.#disposed) return null\n\n const info = this.parseHeader(packet, options)\n if (!info) return null\n\n if (info.packetType !== PACKET_TYPE_MPU) {\n return {\n kind: info.isRepair ? 'repair' : 'non-media',\n packet: info,\n byteLength: packet.byteLength,\n }\n }\n\n // \u2500\u2500 FEC source-symbol exposure \u2500\u2500\n // Fire onSourceSymbol BEFORE MFU extraction so the consumer-side FEC\n // pipeline (e.g. FecWorkerClient.feedSource) starts as early as possible.\n // Source-only: parseHeader already gates fecPayloadId on !isRepair.\n if (info.fecPayloadId !== null && this.#sourceSymbolCb) {\n // Strip the 4-byte fec_payload_id trailer per ISO 23008-1 \u00A7C.5.2.\n // Symbol-size zero-padding is the consumer's responsibility \u2014 FEC\n // symbol size is a FEC-config concern, not a parser concern.\n const rawSymbol = packet.subarray(0, packet.length - 4)\n const altaTrailer = extractAltaTrailer(packet)\n const alta = altaTrailer\n ? { auth: altaTrailer.auth, payloadEnd: altaTrailer.signedEnd }\n : undefined\n try {\n this.#sourceSymbolCb(info.packetId, info.fecPayloadId.ssId, rawSymbol, alta)\n } catch {\n // Source-symbol consumer threw \u2014 never block MFU extraction on\n // FEC-pipeline errors. The frame path stays viable; the consumer\n // is responsible for its own error surfacing.\n }\n }\n\n const extraction = extractMFUPayload(packet, {\n skipFecId: options?.skipFecPayloadId,\n })\n if (!extraction) return null\n\n const { mpu, fragmentType, data, mfuDuHeader } = extraction\n if (mpu.timedFlag !== 1) return null\n const streamKey = this.#streamKey(info.packetId, options?.catalogTrackName)\n\n if (fragmentType === 0) {\n if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0) return null\n const resolver = this.#timingResolver(streamKey, info.packetId, true)\n if (!resolver?.addInitialization(data)) return null\n this.#initCb?.(data, info.packetId, options?.catalogTrackName)\n return {\n kind: 'init',\n packet: info,\n data,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentationIndicator: mpu.fragmentationIndicator,\n fragmentCounter: mpu.fragmentCounter,\n }\n }\n\n if (fragmentType === 1) {\n if (mpu.fragmentationIndicator !== 0 || mpu.fragmentCounter !== 0) return null\n const resolver = this.#timingResolver(streamKey, info.packetId, false)\n if (!resolver) return null\n const pendingBefore = resolver.pendingSampleCount\n const remaining = this.#maxPendingTimingSamples - this.#pendingTimingSamples\n if (!resolver.addMovieFragment(mpu.mpuSequenceNumber, data, remaining)) return null\n this.#pendingTimingSamples += resolver.pendingSampleCount - pendingBefore\n return {\n kind: 'fragment',\n packet: info,\n data,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentationIndicator: mpu.fragmentationIndicator,\n fragmentCounter: mpu.fragmentCounter,\n }\n }\n if (fragmentType !== 2) return null\n\n const resolver = this.#timingResolver(streamKey, info.packetId, false)\n if (!resolver) return null\n if (!Number.isSafeInteger(data.byteLength) || data.byteLength <= 0) return null\n if (mpu.fragmentationIndicator === 0 || mpu.fragmentationIndicator === 1) {\n if (!mfuDuHeader || mfuDuHeader.movieFragmentSequenceNumber === 0 ||\n mfuDuHeader.sampleNumber === 0) {\n return null\n }\n const headTiming = resolver.sampleTiming(\n mpu.mpuSequenceNumber,\n mfuDuHeader.movieFragmentSequenceNumber,\n mfuDuHeader.sampleNumber,\n )\n if (!headTiming) return null\n // A data unit's authored base offset is its position within the sample\n // (moqcast #37): 0 for a whole-sample DU, nonzero for one subsample DU\n // among siblings. Sizes below are therefore relative to that base, not\n // to the sample's start -- comparing a subsample DU against the whole\n // sample's size is what dropped every partitioned 4K/8K frame.\n const offset = mfuDuHeader.offset\n if (!Number.isSafeInteger(offset) || offset < 0 || offset >= headTiming.size) return null\n const remaining = headTiming.size - offset\n // FI=0 carries its data unit whole, in a single payload. Only a\n // partitioned sample's FINAL data unit can be shorter than a full data\n // unit -- every earlier one spans exactly 256 payloads and so is always\n // fragmented -- which makes a whole-carried DU exactly the bytes left\n // from its base. At offset 0 this is the pre-amendment check unchanged.\n if (mpu.fragmentationIndicator === 0 && data.byteLength !== remaining) return null\n if (mpu.fragmentationIndicator === 1 && data.byteLength >= remaining) return null\n\n // Publish the authored size before this data unit can complete, so the\n // combiner never has to guess whether an offset-zero DU is a whole\n // sample. Registering can itself complete a sample whose data units all\n // arrived while the size was still unknown; that sample is delivered\n // through routeFrame like any other, and this packet's own fragment\n // continues below.\n const readyOnRegister = this.#sampleCombinerFor(streamKey).registerAuthoredSize(\n options?.catalogTrackName,\n mpu.mpuSequenceNumber,\n mfuDuHeader.movieFragmentSequenceNumber,\n mfuDuHeader.sampleNumber,\n headTiming.size,\n )\n if (readyOnRegister) {\n this.#emitCombinedSample(readyOnRegister, info, resolver, options?.recovered === true)\n }\n }\n\n let assembled: ReassembledMFU | null\n try {\n assembled = this.#reassembler.addFragment({\n fragmentationIndicator: mpu.fragmentationIndicator,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentCounter: mpu.fragmentCounter,\n data,\n timestamp: info.timestamp,\n rapFlag: info.rapFlag,\n priority: mfuDuHeader?.priority,\n sampleNumber: mfuDuHeader?.sampleNumber,\n movieFragmentSequenceNumber: mfuDuHeader?.movieFragmentSequenceNumber,\n sampleOffset: mfuDuHeader?.offset,\n packetSequenceNumber: info.sequenceNumber,\n recovered: options?.recovered === true,\n source: options?.source,\n deliveryProvenance: options?.deliveryProvenance,\n catalogTrackName: options?.catalogTrackName,\n packetId: info.packetId,\n })\n } catch (error) {\n if (!(error instanceof MfuReassemblyError)) throw error\n console.warn(\n `[MmtpContainerParser] rejected MFU fragment: code=${error.code} ` +\n `mpu=${mpu.mpuSequenceNumber} fi=${mpu.fragmentationIndicator} ` +\n `counter=${mpu.fragmentCounter}`,\n )\n return {\n kind: 'rejected',\n packet: info,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentationIndicator: mpu.fragmentationIndicator,\n fragmentCounter: mpu.fragmentCounter,\n error: { code: error.code, message: error.message },\n }\n }\n\n if (!assembled) {\n return {\n kind: 'pending',\n packet: info,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentationIndicator: mpu.fragmentationIndicator,\n fragmentCounter: mpu.fragmentCounter,\n }\n }\n\n const movieFragmentSequenceNumber = assembled.movieFragmentSequenceNumber\n const sampleNumber = assembled.sampleNumber\n if (movieFragmentSequenceNumber === undefined || sampleNumber === undefined) return null\n // Resolve authored timing BEFORE the combiner sees this data unit. An\n // assembled data unit whose sample identity has no authored timing is not\n // a sample this stream declared, so it must be dropped outright -- letting\n // it buffer would occupy a combiner slot and could evict a legitimate\n // partitioned sample waiting on a sibling.\n if (!resolver.sampleTiming(\n assembled.mpuSequenceNumber,\n movieFragmentSequenceNumber,\n sampleNumber,\n )) return null\n if (assembled.mpuSequenceNumber !== mpu.mpuSequenceNumber) return null\n\n // One reassembled data unit is not necessarily one whole sample. A\n // partitioned sample completes only once every sibling data unit has\n // arrived and they are byte-adjacent from zero to the authored size; an\n // unpartitioned sample passes straight through with no buffering.\n let combined: CombinedSample | null\n try {\n combined = this.#sampleCombinerFor(streamKey).addMfu(assembled)\n } catch (error) {\n if (!(error instanceof SampleCombineError)) throw error\n console.warn(\n `[MmtpContainerParser] rejected subsample data unit: code=${error.code} ` +\n `mpu=${assembled.mpuSequenceNumber} sample=${sampleNumber} ` +\n `offset=${assembled.sampleOffset ?? 0}`,\n )\n return {\n kind: 'rejected',\n packet: info,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentationIndicator: mpu.fragmentationIndicator,\n fragmentCounter: mpu.fragmentCounter,\n error: { code: error.code, message: error.message },\n }\n }\n if (!combined) {\n return {\n kind: 'pending',\n packet: info,\n mpuSequenceNumber: mpu.mpuSequenceNumber,\n fragmentationIndicator: mpu.fragmentationIndicator,\n fragmentCounter: mpu.fragmentCounter,\n }\n }\n\n return this.#emitCombinedSample(combined, info, resolver, options?.recovered === true)\n }\n\n /**\n * Deliver one combined sample: resolve its authored timing, build frame\n * metadata, retire the timing entry, and route it. Shared by the ordinary\n * completion path and by the rarer completion that falls out of registering\n * an authored size for data units that arrived before it.\n */\n #emitCombinedSample(\n combined: CombinedSample,\n info: PacketInfo,\n resolver: IsoBmffTimingResolver,\n recovered: boolean,\n ): MmtpParserRecord | null {\n const timing = resolver.sampleTiming(\n combined.mpuSequenceNumber,\n combined.movieFragmentSequenceNumber,\n combined.sampleNumber,\n )\n if (!timing) return null\n // The combiner already completes against the registered authored size;\n // re-checking here keeps the unpartitioned passthrough path fail-closed\n // on the same invariant rather than trusting it by construction.\n if (combined.data.byteLength !== timing.size) return null\n\n const meta = this.#buildFrameMeta(\n combined,\n info.packetId,\n info.sequenceNumber,\n timing,\n recovered,\n )\n if (!meta) return null\n if (!resolver.consumeSample(\n combined.mpuSequenceNumber,\n combined.movieFragmentSequenceNumber,\n combined.sampleNumber,\n timing,\n )) return null\n this.#pendingTimingSamples--\n this.routeFrame(combined.data, meta)\n return {\n kind: 'frame',\n packet: info,\n data: combined.data,\n meta,\n mpuSequenceNumber: combined.mpuSequenceNumber,\n fragmentCount: combined.fragmentCount,\n rapFlag: combined.rapFlag,\n ...(combined.priority === undefined ? {} : { priority: combined.priority }),\n }\n }\n\n onInit(cb: InitCallback): void {\n this.#initCb = cb\n }\n\n onFrame(cb: FrameCallback): void {\n this.#frameCb = cb\n }\n\n /** Register a per-packet FEC source-symbol callback. Fires for each MMTP\n * source MPU packet that carries a Source FEC Payload ID (ISO 23008-1 \u00A7C.5.2).\n *\n * Consumer responsibilities:\n * - Zero-pad `rawSymbol` to the FEC `symbolSize` if shorter (FEC config is\n * out-of-band \u2014 typically from the CMSF catalog's `fec.symbolSize`).\n * - Decide oversized handling (rawSymbol.length > symbolSize is not FEC-\n * decodable; consumer typically skips that block's source-feed).\n * - Look up the right FEC consumer (e.g. FecWorkerClient) by packetId.\n *\n * Designed for spec-clean reuse: Shaka, upstream moqtail, or any consumer\n * that uses MmtpContainerParser can wire FEC source delivery without\n * pulling in MulticastManager/MmtpRouter/FECManager. */\n onSourceSymbol(cb: SourceSymbolCallback): void {\n this.#sourceSymbolCb = cb\n }\n\n onTrackFrame(packetId: number, cb: FrameCallback): void {\n this.#trackFrameCbs.set(packetId, cb)\n }\n\n setTrack(packetId: number, info: TrackInfo): void {\n this.#validateReassemblyPolicy()\n for (const [key, entry] of this.#sequencers) {\n if (entry.packetId !== packetId) continue\n entry.sequencer.flush()\n this.#sequencers.delete(key)\n }\n for (const [key, state] of this.#timingStreams) {\n if (state.packetId === packetId) this.#dropTimingStream(key)\n }\n for (const key of [...this.#sampleCombiners.keys()]) {\n if ((JSON.parse(key) as [number, string | null])[0] === packetId) {\n this.#dropSampleCombiner(key)\n }\n }\n for (const [key, gatedPacketId] of this.#reliableMoqStartupGate) {\n if (gatedPacketId === packetId) this.#reliableMoqStartupGate.delete(key)\n }\n this.#tracks.set(packetId, info)\n }\n\n /** Drop one physical stream's ordering and MFU state for retained replay. */\n resetTrack(packetId: number, catalogTrackName: string): void {\n if (!catalogTrackName) {\n throw new Error('[MmtpContainerParser] catalogTrackName is required for track reset')\n }\n for (const [key, entry] of this.#sequencers) {\n if (key !== this.#streamKey(packetId, catalogTrackName)) continue\n entry.sequencer.discard()\n this.#sequencers.delete(key)\n }\n const streamKey = this.#streamKey(packetId, catalogTrackName)\n this.#dropTimingStream(streamKey)\n this.#reliableMoqStartupGate.delete(streamKey)\n this.#reassembler.resetStream(catalogTrackName)\n // Drop half-combined samples too: a partitioned sample whose siblings\n // straddle the reset would otherwise complete against a stale authored\n // size and hand the decoder bytes from two different publisher epochs.\n this.#dropSampleCombiner(streamKey)\n }\n\n setMfuTimeoutMs(ms: number | null): void {\n if (ms !== null && ms <= 0) throw new Error('[MmtpContainerParser] mfuTimeoutMs must be > 0')\n this.#reassembler.timeoutMs = ms\n }\n\n setReassemblyPolicy(policy: MfuReassemblyPolicy): void {\n const timeoutMs = mfuReassemblyTimeoutMs(policy)\n this.#validateReassemblyPolicy(policy)\n this.#flushSequencers()\n this.#reassemblyPolicy = policy\n this.#reliableMoqStartupGate.clear()\n this.setMfuTimeoutMs(timeoutMs)\n }\n\n setReorderWindow(windowSize: number): void {\n if (!Number.isFinite(windowSize) || windowSize < 0) {\n throw new Error('[MmtpContainerParser] reorderWindow must be a finite number >= 0')\n }\n const nextWindow = Math.ceil(windowSize)\n this.#validateReassemblyPolicy(this.#reassemblyPolicy, nextWindow)\n this.#flushSequencers()\n this.#reorderWindow = nextWindow\n if (this.#reorderWindow <= 0) this.#sequencers.clear()\n }\n\n /** Route an already-assembled frame through the same reorder/callback path\n * used by source packets. FEC recovery uses this to merge recovered frames\n * with direct source frames before the decoder sees them. */\n routeFrame(data: Uint8Array, meta: FrameMeta): void {\n const seq = meta.packetSequenceNumber\n const sequencer = this.#sequencerFor(meta.packetId, meta.trackType, meta.catalogTrackName)\n if (sequencer && seq !== undefined && Number.isFinite(seq)) {\n sequencer.push(data, meta, seq)\n return\n }\n if (this.#reassemblyPolicy?.transport === 'reliable-moq' &&\n meta.trackType === 'video' &&\n !this.#passReliableMoqStartupGate(meta)) {\n return\n }\n this.#emitFrame(data, meta)\n }\n\n /**\n * Claim and return the authored timing for a recovered sample. Recovery does\n * not pass through the ordinary `feed()` completion path, so it must retire\n * the same bounded resolver entry once the router accepts the recovered\n * sample. A missing entry is a fail-closed signal: callers must not replace\n * it with an MMTP send timestamp or cadence guess.\n */\n consumeAuthoredTimingFor(\n packetId: number,\n mpuSequenceNumber: number,\n movieFragmentSequenceNumber: number | undefined,\n sampleNumber: number | undefined,\n catalogTrackName?: string,\n ): AuthoredTiming | null {\n if (movieFragmentSequenceNumber === undefined || sampleNumber === undefined) return null\n const resolver = this.#timingResolver(\n this.#streamKey(packetId, catalogTrackName),\n packetId,\n false,\n )\n const timing = resolver?.sampleTiming(\n mpuSequenceNumber,\n movieFragmentSequenceNumber,\n sampleNumber,\n )\n if (!timing || !resolver || !resolver.consumeSample(\n mpuSequenceNumber,\n movieFragmentSequenceNumber,\n sampleNumber,\n timing,\n )) return null\n this.#pendingTimingSamples--\n if (this.#pendingTimingSamples < 0) {\n throw new Error('[MmtpContainerParser] pending timing sample accounting underflow')\n }\n return {\n decodeTime: timing.decodeTime,\n presentationTime: timing.presentationTime,\n durationTicks: timing.duration,\n mediaTimescale: timing.timescale,\n }\n }\n\n /** Order-independent reliable-moq startup gate: discard delta frames for a\n * stream until its first RAP is observed, without buffering/reordering. */\n #passReliableMoqStartupGate(meta: FrameMeta): boolean {\n const streamKey = this.#streamKey(meta.packetId, meta.catalogTrackName)\n if (meta.sourceDiscontinuity) this.#reliableMoqStartupGate.delete(streamKey)\n if (this.#reliableMoqStartupGate.has(streamKey)) return true\n if (!meta.keyframe) {\n this.#reorderStats.droppedBeforeStartupKeyframe++\n return false\n }\n this.#reliableMoqStartupGate.set(streamKey, meta.packetId)\n return true\n }\n\n /** Emit all ordered frames currently buffered by the parser. Finite-stream\n * consumers may call this before dispose(); live cancellation should not. */\n flush(): void {\n if (this.#disposed) return\n this.#flushSequencers()\n }\n\n dispose(): void {\n this.#disposed = true\n for (const { sequencer } of this.#sequencers.values()) sequencer.discard()\n this.#sequencers.clear()\n this.#timingStreams.clear()\n this.#pendingTimingSamples = 0\n this.#reliableMoqStartupGate.clear()\n this.#reassembler.reset()\n this.#frameCb = null\n this.#initCb = null\n this.#trackFrameCbs.clear()\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #timingResolver(\n streamKey: string,\n packetId: number,\n create: boolean,\n ): IsoBmffTimingResolver | null {\n const existing = this.#timingStreams.get(streamKey)\n if (existing) return existing.resolver\n if (!create) return null\n const resolver = new IsoBmffTimingResolver(this.#maxPendingTimingSamples)\n this.#timingStreams.set(streamKey, { packetId, resolver })\n return resolver\n }\n\n #dropTimingStream(streamKey: string): void {\n const state = this.#timingStreams.get(streamKey)\n if (!state) return\n this.#timingStreams.delete(streamKey)\n this.#pendingTimingSamples -= state.resolver.pendingSampleCount\n if (this.#pendingTimingSamples < 0) {\n throw new Error('[MmtpContainerParser] pending timing sample accounting underflow')\n }\n }\n\n #validateReassemblyPolicy(\n policy: MfuReassemblyPolicy | null = this.#reassemblyPolicy,\n reorderWindow = this.#reorderWindow,\n ): void {\n if (!policy) return\n mmtpFrameReorderWindowFrames(policy, reorderWindow)\n }\n\n #buildFrameMeta(\n assembled: CombinedSample,\n packetId: number,\n seq: number,\n timing: IsoBmffSampleTiming,\n recovered = false,\n ): FrameMeta | null {\n const { data, timestamp: mmtpTimestamp, rapFlag, priority } = assembled\n\n if (isFmp4Box(data)) return null\n const presentationTimestamp = mediaTicksToMicroseconds(\n timing.presentationTime,\n timing.timescale,\n )\n const decodeTimestamp = mediaTicksToMicroseconds(timing.decodeTime, timing.timescale)\n const duration = mediaTicksToMicroseconds(BigInt(timing.duration), timing.timescale)\n if (presentationTimestamp === null || decodeTimestamp === null || duration === null ||\n duration <= 0) {\n return null\n }\n\n let track = this.#tracks.get(packetId)\n if (!track && this.#defaultTrack) {\n track = this.#defaultTrack\n this.#tracks.set(packetId, track)\n }\n const codec = track?.codec ?? ''\n\n const keyframe = MmtpContainerParser.isKeyframe(data, isMfuKeyframeMetadata(rapFlag, priority), codec)\n\n const meta: FrameMeta = {\n timestamp: presentationTimestamp,\n decodeTimestamp,\n duration,\n decodeTime: timing.decodeTime,\n presentationTime: timing.presentationTime,\n durationTicks: timing.duration,\n mediaTimescale: timing.timescale,\n authoredTiming: {\n decodeTime: timing.decodeTime,\n presentationTime: timing.presentationTime,\n durationTicks: timing.duration,\n mediaTimescale: timing.timescale,\n },\n keyframe,\n packetId,\n trackType: track?.type ?? null,\n packetSequenceNumber: seq,\n ...(assembled.packetSequenceNumbers === undefined\n ? {}\n : { packetSequenceNumbers: assembled.packetSequenceNumbers }),\n ...(assembled.repairedPacketSequenceNumbers === undefined\n ? {}\n : { repairedPacketSequenceNumbers: assembled.repairedPacketSequenceNumbers }),\n ...(assembled.catalogTrackName === undefined\n ? {}\n : { catalogTrackName: assembled.catalogTrackName }),\n mmtpTimestamp,\n mpuSequenceNumber: assembled.mpuSequenceNumber,\n ...(assembled.movieFragmentSequenceNumber === undefined\n ? {}\n : { movieFragmentSequenceNumber: assembled.movieFragmentSequenceNumber }),\n ...(assembled.sampleNumber === undefined ? {} : { sampleNumber: assembled.sampleNumber }),\n fragmentCount: assembled.fragmentCount,\n rapFlag,\n ...(priority === undefined ? {} : { priority }),\n ...(assembled.source === undefined ? {} : { source: assembled.source }),\n deliveryProvenance: assembled.deliveryProvenance,\n }\n if (recovered || (assembled.repairedPacketSequenceNumbers?.length ?? 0) > 0) {\n meta.recovered = true\n }\n\n return meta\n }\n\n #sequencerFor(\n packetId: number,\n _trackType: FrameMeta['trackType'],\n catalogTrackName?: string,\n ): FrameSequencer | null {\n const windowSize = this.#reassemblyPolicy\n ? mmtpFrameReorderWindowFrames(this.#reassemblyPolicy, this.#reorderWindow)\n : this.#reorderWindow\n if (windowSize <= 0) return null\n const streamKey = this.#streamKey(packetId, catalogTrackName)\n let sequencer = this.#sequencers.get(streamKey)?.sequencer\n if (!sequencer) {\n sequencer = new FrameSequencer(windowSize, (data, meta) => {\n this.#emitFrame(data, meta)\n }, (stat, count = 1) => {\n this.#reorderStats[stat] += count\n })\n this.#sequencers.set(streamKey, { packetId, sequencer })\n }\n return sequencer\n }\n\n #flushSequencers(): void {\n for (const { sequencer } of this.#sequencers.values()) sequencer.flush()\n this.#sequencers.clear()\n }\n\n /** Authored presentation timestamp of the last emitted frame, in microseconds. */\n lastFrameTimestamp: number | undefined = undefined\n\n #emitFrame(data: Uint8Array, meta: FrameMeta): void {\n this.lastFrameTimestamp = meta.timestamp\n const trackCb = this.#trackFrameCbs.get(meta.packetId)\n trackCb?.(data, meta)\n this.#frameCb?.(data, meta)\n }\n\n // \u2500\u2500 Static utilities \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n static isKeyframe(data: Uint8Array, rapFlag: boolean, codec = ''): boolean {\n const payloadKeyframe = MmtpContainerParser.#scanForKeyframe(data, codec)\n if (payloadKeyframe !== undefined) return payloadKeyframe\n return rapFlag\n }\n\n static #scanForKeyframe(data: Uint8Array, codec: string): boolean | undefined {\n const c = codec.toLowerCase()\n if (data.length === 0) return undefined\n\n if (c.startsWith('av01')) {\n return scanAv1ObusForKeyframe(data)\n }\n\n if (c.startsWith('vp09') || c.startsWith('vp8')) {\n return (data[0]! & 0x04) === 0\n }\n\n const isHevc = c.startsWith('hev1') || c.startsWith('hvc1')\n if (isHevc) {\n if (data.length < 5) return undefined\n if (startsWithAnnexB(data)) {\n return MmtpContainerParser.#scanAnnexBForKeyframe(data, true)\n }\n return MmtpContainerParser.#scanLengthPrefixedHevcForKeyframe(data)\n }\n\n if (c === '' || c.startsWith('avc')) {\n return classifyH264Keyframe(data, { allowPartialAvcc: true })\n }\n\n return undefined\n }\n\n static #scanLengthPrefixedHevcForKeyframe(data: Uint8Array): boolean | undefined {\n let sawNal = false\n let offset = 0\n while (offset + 4 < data.length) {\n const nalLen =\n (data[offset]! << 24) |\n (data[offset + 1]! << 16) |\n (data[offset + 2]! << 8) |\n data[offset + 3]!\n offset += 4\n if (nalLen <= 0) return sawNal ? false : undefined\n // The router's sync probe may see only the first fragment of a large\n // length-prefixed NAL. The length can exceed this packet, but the NAL\n // header is still enough to classify IRAP/IDR and open the sync gate.\n if (offset >= data.length) return sawNal ? false : undefined\n sawNal = true\n const hevcType = (data[offset]! >> 1) & 0x3f\n if (hevcType >= 16 && hevcType <= 21) return true\n if (offset + nalLen > data.length) break\n offset += nalLen\n }\n return sawNal ? false : undefined\n }\n\n static #scanAnnexBForKeyframe(data: Uint8Array, isHevc: boolean): boolean | undefined {\n let offset = 0\n let sawNal = false\n while (offset < data.length) {\n const start = findAnnexBStart(data, offset)\n if (!start) return sawNal ? false : undefined\n const headerOffset = start.offset + start.length\n if (headerOffset >= data.length) return sawNal ? false : undefined\n sawNal = true\n if (isHevc) {\n if (headerOffset + 1 >= data.length) return sawNal ? false : undefined\n const hevcType = (data[headerOffset]! >> 1) & 0x3f\n if (hevcType >= 16 && hevcType <= 21) return true\n } else {\n const nalType = data[headerOffset]! & 0x1f\n if (nalType === 5) return true\n }\n offset = headerOffset + 1\n }\n return sawNal ? false : undefined\n }\n}\n\nfunction scanAv1ObusForKeyframe(data: Uint8Array): boolean | undefined {\n let offset = 0\n let sawSequenceHeader = false\n let sawFrame = false\n\n while (offset < data.length) {\n const obu = readAv1Obu(data, offset)\n if (!obu) return sawFrame ? false : undefined\n offset = obu.nextOffset\n\n if (obu.type === 1) {\n sawSequenceHeader = true\n continue\n }\n if (obu.type !== 3 && obu.type !== 6) continue\n\n sawFrame = true\n if (!sawSequenceHeader) {\n continue\n }\n\n const keyframe = readAv1FrameHeaderKeyframe(data.subarray(obu.payloadOffset, obu.payloadEnd))\n if (keyframe !== undefined) return keyframe\n }\n\n return sawFrame ? false : undefined\n}\n\nfunction readAv1Obu(\n data: Uint8Array,\n offset: number,\n): { type: number; payloadOffset: number; payloadEnd: number; nextOffset: number } | null {\n if (offset >= data.length) return null\n const header = data[offset]!\n if ((header & 0x80) !== 0 || (header & 0x01) !== 0) return null\n\n const type = (header >> 3) & 0x0f\n const extensionFlag = (header & 0x04) !== 0\n const hasSizeField = (header & 0x02) !== 0\n let cursor = offset + 1\n if (extensionFlag) cursor += 1\n if (cursor > data.length) return null\n\n let payloadSize = data.length - cursor\n if (hasSizeField) {\n const leb = readLeb128(data, cursor)\n if (!leb) return null\n payloadSize = leb.value\n cursor = leb.nextOffset\n }\n\n const payloadEnd = cursor + payloadSize\n if (payloadEnd > data.length) return null\n return { type, payloadOffset: cursor, payloadEnd, nextOffset: payloadEnd }\n}\n\nfunction readLeb128(data: Uint8Array, offset: number): { value: number; nextOffset: number } | null {\n let value = 0\n for (let i = 0; i < 8; i++) {\n if (offset + i >= data.length) return null\n const byte = data[offset + i]!\n value += (byte & 0x7f) * (2 ** (i * 7))\n if ((byte & 0x80) === 0) return { value, nextOffset: offset + i + 1 }\n }\n return null\n}\n\nfunction readAv1FrameHeaderKeyframe(payload: Uint8Array): boolean | undefined {\n if (payload.length === 0) return undefined\n const first = payload[0]!\n const showExistingFrame = (first & 0x80) !== 0\n if (showExistingFrame) return false\n const frameType = (first >> 5) & 0x03\n return frameType === 0\n}\n\nfunction startsWithAnnexB(data: Uint8Array): boolean {\n return (\n (data.length >= 4 && data[0] === 0 && data[1] === 0 && data[2] === 1) ||\n (data.length >= 5 && data[0] === 0 && data[1] === 0 && data[2] === 0 && data[3] === 1)\n )\n}\n\nfunction findAnnexBStart(data: Uint8Array, offset: number): { offset: number; length: 3 | 4 } | null {\n for (let i = offset; i + 3 <= data.length; i++) {\n if (data[i] !== 0 || data[i + 1] !== 0) continue\n if (data[i + 2] === 1) return { offset: i, length: 3 }\n if (i + 3 < data.length && data[i + 2] === 0 && data[i + 3] === 1) {\n return { offset: i, length: 4 }\n }\n }\n return null\n}\n", "/**\n * MMTP packet processing and frame routing layer.\n *\n * Delegates MMTP header parsing, MFU extraction, reassembly, and keyframe\n * detection to MmtpContainerParser while owning FEC orchestration, sync\n * gating, CMAF assembly, and callback routing.\n *\n * Extracted from MulticastManager \u2014 this module owns MMTP-layer concerns:\n * FEC symbol feeding, sync gating, MSE segment assembly, and frame routing.\n * It does NOT own network I/O, watchdog timers, or multicast join/leave.\n *\n * @packageDocumentation\n */\n\nimport { parseMMTPHeader } from \"@blockcast/mmt-base\";\nimport { MfuReassemblyError } from \"./mfu-reassembler.js\";\nimport type {\n MfuReassemblerBackend,\n MmtpSourcePath,\n MmtpDeliveryProvenance,\n} from \"./mfu-reassembler.js\";\nimport type { MfuReassemblyPolicy } from \"./mfu-reassembly-policy.js\";\nimport { mmtpTimestampToMicros } from \"./codec-utils.js\";\nimport { scaleAuthoredTiming, type AuthoredTiming } from \"./authored-timing.js\";\nimport {\n processFecBlock,\n processFecSymbol,\n parseRepairPacket,\n FecBlockMapper,\n type ProcessFecBlockResult,\n} from \"@blockcast/mmt-fec\";\nimport {\n CmafBlockAssembler,\n type BlockAssemblerConfig,\n type CmafBlockSample,\n} from \"./cmaf-block-assembler.js\";\nimport { MmtpContainerParser, isMfuKeyframeMetadata } from \"./mmtp-container-parser.js\";\nimport { type TrackRole } from \"./carp-catalog.js\";\nimport type {\n ManifestAuthMode,\n ManifestObjectIdentity,\n ManifestTrackContext,\n ManifestVerifier,\n} from \"@blockcast/mmt-manifest-verify\";\n\n// \u2500\u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Keyframe loss timeout slack factor.\n *\n * Derivation: The keyframe loss timer must fire after one full keyframe group\n * has been missed. Group duration must come from explicit catalog timing; FEC\n * sourceSymbols is a block width, not a GOP length. Multicast jitter means the\n * last packet of a group may arrive up to ~0.5 group durations late (network\n * reordering + FEC recovery latency). The total timeout is therefore:\n *\n * timeout = group_duration * (1.0 + 0.5_jitter) = group_duration * 1.5\n *\n * The 0.5 jitter margin covers: (a) FEC interleave recovery window (up to D\n * frames), (b) network reordering within one interleave depth, and (c) OS\n * socket buffer draining latency. This is tight enough to detect loss within\n * one missing group while avoiding false positives from normal jitter.\n */\nexport const KEYFRAME_LOST_TIMEOUT_SLACK = 1.5;\n\nfunction isRouterDebugEnabled(): boolean {\n const globalDebug = (globalThis as { __MMTP_ROUTER_DEBUG?: unknown }).__MMTP_ROUTER_DEBUG;\n if (globalDebug === true || globalDebug === \"true\" || globalDebug === \"1\") return true;\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n ?.env;\n return env?.[\"MMTP_ROUTER_DEBUG\"] === \"1\" || env?.[\"MMTP_ROUTER_DEBUG\"] === \"true\";\n}\nfunction debugRouter(message: string): void {\n if (isRouterDebugEnabled()) console.debug(message);\n}\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type RoutedFrameMeta = {\n timestamp: number;\n keyframe: boolean;\n /** Exact ISO-BMFF timing in the media track's native timescale. */\n authoredTiming?: AuthoredTiming;\n /** Transport that supplied the accepted FI=1 head (or complete FI=0 frame). */\n source?: MmtpSourcePath;\n /**\n * Whole-frame delivery verdict folded across every contributing fragment.\n *\n * Distinct from `source` on purpose. `source` answers a routing question \u2014\n * which transport's copy is this \u2014 and deliberately lets the head fragment\n * decide. This answers an evidence question: did every byte of this frame\n * physically arrive over multicast? A frame can be routed as 'multicast'\n * and still be 'mixed'.\n */\n deliveryProvenance?: MmtpDeliveryProvenance;\n packetId?: number;\n packetSequenceNumber?: number;\n /** Every authoritative MMTP source-packet sequence that formed this frame. */\n packetSequenceNumbers?: number[];\n /** Subset of packetSequenceNumbers reconstructed by FEC. */\n repairedPacketSequenceNumbers?: number[];\n /** Native MMTP MPU group coordinate. */\n mpuSequenceNumber?: number;\n /** Immutable catalog identity supplied by the owning ingress subscription. */\n catalogTrackName?: string;\n recovered?: boolean;\n /**\n * True when this frame opens playback after the router suppressed earlier\n * video deltas while waiting for a sync keyframe. Downstream decoders can\n * use this to treat the following dependency chain as provisional.\n */\n sourceDiscontinuity?: boolean;\n};\n\nexport interface MmtpIngressContext {\n tracks: readonly Readonly<{ name: string; packetId: number }>[];\n}\n\nfunction catalogTrackNameForPacket(\n ingress: MmtpIngressContext | undefined,\n packetId: number,\n): string | undefined {\n if (!ingress) return undefined;\n const names = new Set(\n ingress.tracks\n .filter((track) => track.packetId === packetId && track.name.length > 0)\n .map((track) => track.name),\n );\n return names.size === 1 ? names.values().next().value : undefined;\n}\n\nfunction hasAmbiguousCatalogTrackName(\n ingress: MmtpIngressContext | undefined,\n packetId: number,\n): boolean {\n if (!ingress) return false;\n const names = new Set(\n ingress.tracks\n .filter((track) => track.packetId === packetId && track.name.length > 0)\n .map((track) => track.name),\n );\n return names.size > 1;\n}\n\nfunction ingressHasPacketId(\n ingress: MmtpIngressContext | undefined,\n packetId: number,\n): boolean {\n // Direct/legacy callers have no catalog-backed ingress policy to enforce.\n // MulticastManager supplies a populated context whenever packetIds are selected.\n return ingress === undefined || ingress.tracks.some((track) => track.packetId === packetId);\n}\n\n/** Callback for decoded video/audio frames. */\nexport type FrameCallback = (data: Uint8Array, meta: RoutedFrameMeta) => void;\n\n/** Metadata accompanying an accepted raw MMTP source packet. */\nexport type RoutedPacketMeta = {\n packetId: number;\n packetSequenceNumber: number;\n /** True when FEC reconstructed this source packet. */\n recovered: boolean;\n /** True when the packet has no trailing Source FEC Payload ID. */\n skipFecPayloadId: boolean;\n /** Immutable catalog identity supplied by the owning ingress subscription. */\n catalogTrackName?: string;\n /**\n * Strict provenance for the fragments this packet produces. Set for\n * FEC-recovered packets, which carry no `source` of their own but inherit\n * their FEC block's symbol provenance.\n */\n deliveryProvenance?: MmtpDeliveryProvenance;\n};\n\n/** Callback for accepted raw MMTP source packets. */\nexport type TrackPacketCallback = (packet: Uint8Array, meta: RoutedPacketMeta) => void;\n\ntype PendingRecoveredVideoFrame = {\n data: Uint8Array;\n meta: RoutedFrameMeta;\n};\n\ntype PendingRecoveredAudioFrame = {\n data: Uint8Array;\n meta: RoutedFrameMeta;\n};\n\ntype DirectAudioCursor = {\n timestamp: number;\n sequenceNumber: number;\n};\n\nconst DIRECT_RECOVERED_VIDEO_QUEUE_LIMIT = 512;\nconst DIRECT_RECOVERED_AUDIO_QUEUE_LIMIT = 512;\n\ntype ParserDiagnostics = {\n packets: number;\n frames: number;\n initSegments: number;\n noFragment: number;\n fragments: number;\n reassembled: number;\n pendingBuffers: number;\n pendingFragments: number;\n pendingSamples?: unknown[];\n lastPacketLength: number;\n lastSequenceNumber: number;\n lastFecSsId?: number;\n lastFrameTimestamp?: number;\n directAudioSourceEmitted: number;\n directAudioSourceRegressions: number;\n directAudioRecoveredQueued: number;\n directAudioRecoveredDrained: number;\n directAudioRecoveredStaleDropped: number;\n};\n\n/**\n * FECManager interface \u2014 the contract MmtpRouter expects for FEC\n * source/repair symbol processing and block lifecycle events.\n *\n * Concrete implementation: `FECManager` class in `./fec-manager.ts`.\n * MmtpRouter only interacts with FEC through this interface.\n */\nexport interface FECManagerLike {\n /** Feed a source symbol to the FEC decoder for a given track.\n * Returns recovered block bytes if recovery completes, else null.\n *\n * Optional `alta` carries the detached ALTA authenticator extracted from\n * the MMTP header-extension trailer (08-07 Path 2) \u2014 present on signed\n * source packets, absent on relay-reassembled / unsigned streams. When\n * provided, the fec-worker runs verify_detached instead of counting the\n * symbol as `altaPending`. */\n feedSourceSymbol(\n packetId: number,\n ssId: number,\n symbolData: Uint8Array,\n alta?: { auth: Uint8Array; payloadEnd: number },\n catalogTrackName?: string,\n ): Uint8Array | null;\n\n /** Feed a repair symbol to the FEC decoder for a given track.\n * Returns recovered block bytes if recovery completes, else null. */\n feedRepairSymbol(\n packetId: number,\n ssStart: number,\n ssbLength: number,\n rsId: number,\n repairData: Uint8Array,\n catalogTrackName?: string,\n ): Uint8Array | null;\n\n /** Record a transport symbol for first-arrival-wins dedup and path counters. */\n feedSymbolFromSource(\n packetId: number,\n sbn: number,\n esi: number,\n data: Uint8Array,\n source: \"multicast\" | \"moq\",\n catalogTrackName?: string,\n ): boolean;\n\n /** Strict provenance for a block: 'multicast' only when no symbol arrived\n * over MoQ. Recovered bytes inherit it, since a recovered symbol is a\n * function of every symbol that entered the block. */\n blockDeliveryProvenance?(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): MmtpDeliveryProvenance;\n\n /** Track a source symbol arrival for block completion counting.\n * Refreshes the inactivity deadline and marks clean when K arrive. */\n trackBlock(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n esi?: number,\n ): void;\n\n /** Refresh the block inactivity deadline after accepted repair progress. */\n noteBlockActivity?(sbn: number, packetId: number, catalogTrackName?: string): void;\n\n /** Cancel block timeout (block completed via clean or FEC recovery). */\n cancelBlockTimeout(sbn: number, packetId?: number, catalogTrackName?: string): void;\n\n /** Check if a block is already completed (clean or recovered). */\n isBlockCompleted(sbn: number, packetId?: number, catalogTrackName?: string): boolean;\n\n /** Mark a block as completed externally (after routing recovered data). */\n markBlockCompleted(sbn: number, packetId?: number, catalogTrackName?: string): void;\n\n /** Increment blocksRecovered stat (called after routing recovery). */\n countRecovery(packetId?: number): void;\n\n /** FEC source symbol count K per block. */\n readonly k: number;\n kForPacket?(packetId: number): number;\n\n /** FEC symbol size T in bytes (0 = unknown / no FEC). */\n readonly symbolSize: number;\n symbolSizeForPacket?(packetId: number): number;\n\n /** Update framerate \u2014 used to refine FEC block timeout. */\n setFramerate(fps: number): void;\n\n /** Per-block state snapshot for FecBlockGrid visualization. */\n getBlockStates(): Array<{\n sbn: number;\n packetId?: number;\n state: \"collecting\" | \"complete\" | \"failed\";\n sourceCount: number;\n k: number;\n progress: number;\n }>;\n\n /** FEC statistics snapshot. */\n readonly fecStats: {\n sourcePackets: number;\n repairPackets: number;\n blocksClean: number;\n blocksRecovered: number;\n blocksFailed: number;\n greenFillFrames: number;\n };\n\n // \u2500\u2500 Event hooks (set by MmtpRouter in constructor) \u2500\u2500\n\n /** Called when a block receives its first source symbol. */\n onBlockStarted:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null;\n\n /** Called when a block completes cleanly (all K sources arrived). */\n onBlockComplete:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null;\n\n /** Called when a block times out (unrecoverable). */\n onBlockFailed:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null;\n\n /** Dispose timers and state. */\n dispose(): void;\n}\n\n// \u2500\u2500\u2500 MmtpRouter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ManifestIdentityResolutionInput {\n packetId: number;\n sbn: number;\n esi: number;\n catalogTrackName?: string;\n recovered: boolean;\n sourceSymbolFormat: \"full-mmtp-packet\" | \"fec-padded-recovery\";\n}\n\nexport type ManifestIdentityResolver = (\n input: ManifestIdentityResolutionInput,\n) => ManifestObjectIdentity | null;\n\nexport class MmtpRouter {\n readonly #parser: MmtpContainerParser;\n // Source AND FEC-recovered MFU fragments share ONE reassembler \u2014 the parser's\n // (see #parser.reassembler). A keyframe split between source-received and\n // FEC-recovered fragments must reassemble in a single buffer (BLO-8151).\n readonly #fecManager: FECManagerLike | null;\n #defaultFecMapper: FecBlockMapper | null;\n #fecMappersByPacketId = new Map<number, FecBlockMapper>();\n readonly #manifestVerifier: ManifestVerifier | null;\n readonly #ownsManifestVerifier: boolean;\n readonly #resolveManifestIdentity:\n | ((\n packetId: number,\n sbn: number,\n esi: number,\n catalogTrackName?: string,\n ) => ManifestObjectIdentity | null)\n | null;\n readonly #resolveManifestIdentityForRoute: ManifestIdentityResolver | null;\n #manifestRecoveryQueues = new Map<\n string,\n Array<{\n blockId: number;\n result: ProcessFecBlockResult;\n ownerPacketId: number;\n catalogTrackName?: string;\n }>\n >();\n\n // Temp state set by #processMMTP before calling parser.feed(),\n // read by the parser's onFrame callback for CMAF assembly + dedup.\n #currentFecInfo:\n | { sbn: number; esi: number; oversized?: boolean }\n | undefined;\n #currentSeqNum = 0;\n\n // Track type registry: which packetIds are video vs audio (from catalog)\n #videoPacketIds = new Set<number>();\n #audioPacketIds = new Set<number>();\n #catalogTrackNamesByPacketId = new Map<number, Set<string>>();\n\n // Per-track codec string for keyframe detection (e.g. 'avc1.42001e', 'av01.0.04M.08')\n #trackCodecs = new Map<number, string>();\n\n // Per-track frame callbacks keyed by packetId \u2014 used for ABR / multi-track routing\n #trackCallbacks = new Map<\n number,\n (data: Uint8Array, meta: { timestamp: number; keyframe: boolean }) => void\n >();\n #trackPacketCallbacks = new Map<number, TrackPacketCallback>();\n // Cached resolved callbacks: per-track + legacy composite per packetId.\n // Invalidated when any callback is registered (onTrackFrame, onVideoFrame, onAudioFrame).\n #resolvedCallbacks = new Map<number, FrameCallback>();\n\n // Per-ingress-stream sync flag: suppress direct frame emission until the first complete\n // FEC block confirms we've joined mid-stream cleanly. Prevents corrupt partial\n // frames from reaching the decoder on multicast mid-stream join. Catalog\n // identity keeps renditions that reuse a packet ID from opening each other's gate.\n #trackSynced = new Set<string>();\n #droppedPreSyncVideo = new Set<string>();\n\n // Dedup: packetId-scoped packetSequenceNumbers already received via direct source path.\n // Recovered FEC blocks contain all K source symbols, so skip direct-path\n // duplicates even when the source frame is still buffered by parser reordering.\n #sourcePktSeqs = new Set<string>();\n #routedPacketSeqs = new Set<string>();\n\n // Live WebCodecs callbacks cannot accept late recovered inter-frame video.\n // MSE recovery routes through CmafBlockAssembler and can reorder by block; the\n // direct callback path has to preserve decode order before frames leave libmmt.\n #latestVideoSeqByStream = new Map<string, number>();\n #lastDirectSourceVideoTsByStream = new Map<string, number>();\n #lastDirectEmittedVideoTsByStream = new Map<string, number>();\n #pendingDirectRecoveredVideoByStream = new Map<string, Map<string, PendingRecoveredVideoFrame>>();\n // AudioDecoder input must remain monotonic too. A repair that completes after\n // the live source cursor passed its AAC access unit is useful for FEC\n // accounting, but injecting it into the decoder can stall PCM output.\n #lastDirectSourceAudioCursorByStream = new Map<string, DirectAudioCursor>();\n #lastDirectEmittedAudioCursorByStream = new Map<string, DirectAudioCursor>();\n #pendingDirectRecoveredAudioByStream = new Map<string, Map<string, PendingRecoveredAudioFrame>>();\n\n // Per-track CMAF block assemblers for MSE mode. When active, frames are buffered per-SBN\n // and emitted as multi-sample CMAF segments on block completion instead of per-frame.\n #mseAssemblers = new Map<string, CmafBlockAssembler>();\n\n // Callbacks (legacy \u2014 used when per-track callbacks are not registered)\n #onVideoFrame: FrameCallback | null = null;\n #onAudioFrame: FrameCallback | null = null;\n\n // Init segment (ftyp+moov) from MMTP MPU metadata \u2014 needed for MSE SourceBuffer\n #onInitSegment: ((data: Uint8Array) => void) | null = null;\n\n // Per-track init segment callbacks keyed by packetId\n #trackInitCallbacks = new Map<number, (data: Uint8Array) => void>();\n // Track whether init has been delivered per physical stream \u2014 prevent duplicates\n #trackInitDelivered = new Set<string>();\n\n // FEC block lifecycle \u2014 backend decides failed handling: WebCodecs=freeze, MSE=green fill\n #onBlockStarted: ((sbn: number, packetId: number) => void) | null = null;\n /** Subscriber for unrecoverable FEC blocks. Mirrors #onBlockStarted so\n * players can drive decoder recovery when a block times out; the internal\n * MSE/delta-role handling below stays independent of this subscriber. */\n #onBlockFailed:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null = null;\n // \u2500\u2500 Phase 14: per-role packetId dispatch (D-26, D-27, D-28) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** Maps packetId \u2192 TrackRole. Default: 'single' when absent (legacy path). */\n #packetIdRoles: Map<number, TrackRole> = new Map();\n /** Explicit catalog ownership for dedicated repair-track packet IDs. */\n #repairOwnersByPacketId = new Map<number, Set<number>>();\n /** Callback fired when a delta-track FEC block completes or fails. */\n #onDeltaBlockCompleteCb:\n | ((packetId: number, failed: boolean) => void)\n | null = null;\n\n // Debug counters\n #pidCounts: Record<number, number> = {};\n #repairPidCounts: Record<number, number> = {};\n #moqRepairPidCounts: Record<number, number> = {};\n #moqRepairOwnerCounts: Record<number, number> = {};\n #parserDiagnostics = new Map<string, ParserDiagnostics>();\n\n // \u2500\u2500 Keyframe loss detection state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** Catalog-derived keyframe loss timeout (ms). null = not configured (throws on video track registration). */\n #keyframeLostTimeoutMs: number | null;\n /** Active keyframe loss timer handle. Single timer per video track \u2014 cleared on each rapFlag and on dispose(). */\n #keyframeLostTimer: ReturnType<typeof setTimeout> | null = null;\n /** Monotonic counter incremented on each emitted video keyframe. Tracks current GOP position. */\n #lastKeyframeGroupId = 0;\n /** Callback fired when no complete keyframe is emitted within keyframeLostTimeoutMs. Argument: next expected groupId. */\n #onKeyframeLost: ((expectedGroupId: number) => void) | null = null;\n\n #disposed = false;\n\n constructor(config?: {\n fecManager?: FECManagerLike;\n fecMapper?: FecBlockMapper;\n /** MFU reassembly timeout (ms) for FEC recovery path. Derive from FEC interleaveDepthMs. */\n mfuTimeoutMs?: number;\n reassemblyPolicy?: MfuReassemblyPolicy;\n reassembler?: MfuReassemblerBackend;\n maxBufferedMfus?: number;\n /** Catalog-derived keyframe loss timeout (ms). Derive from explicit\n * keyframe/group duration; do not use FEC sourceSymbols as GOP length. */\n keyframeLostTimeoutMs?: number;\n manifestVerifier?: ManifestVerifier;\n /** Existing behavior is router ownership; shared browser plans opt into caller ownership. */\n manifestVerifierOwnership?: \"caller\" | \"router\";\n resolveManifestIdentity?: (\n packetId: number,\n sbn: number,\n esi: number,\n catalogTrackName?: string,\n ) => ManifestObjectIdentity | null;\n resolveManifestIdentityForRoute?: ManifestIdentityResolver;\n /** Internal terminal-failure sink owned by the composing transport. */\n onBlockFailed?: (\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ) => void;\n }) {\n this.#keyframeLostTimeoutMs = config?.keyframeLostTimeoutMs ?? null;\n this.#fecManager = config?.fecManager ?? null;\n this.#defaultFecMapper = config?.fecMapper ?? null;\n this.#manifestVerifier = config?.manifestVerifier ?? null;\n this.#ownsManifestVerifier = config?.manifestVerifierOwnership !== \"caller\";\n this.#resolveManifestIdentity = config?.resolveManifestIdentity ?? null;\n this.#resolveManifestIdentityForRoute = config?.resolveManifestIdentityForRoute ?? null;\n\n // Create MMTP parser \u2014 handles MFU extraction, reassembly, keyframe detection.\n // Its MFUReassembler is the single reassembler for BOTH the source path\n // (parser.feed) and the FEC-recovery path (#routeRecoveredBlock), so a frame\n // split across both completes (BLO-8151). mfuTimeoutMs threads the\n // catalog-derived timeout through.\n this.#parser = new MmtpContainerParser({\n mfuTimeoutMs: config?.mfuTimeoutMs,\n reassemblyPolicy: config?.reassemblyPolicy,\n reassembler: config?.reassembler,\n maxBufferedMfus: config?.maxBufferedMfus,\n });\n\n // Wire parser init callback \u2192 router's init delivery (dedup).\n this.#parser.onInit((data, packetId, catalogTrackName) => {\n this.#diagFor(packetId, catalogTrackName).initSegments++;\n this.#deliverInit(packetId, data, catalogTrackName);\n });\n\n // Wire parser frame callback \u2192 router's callback routing + CMAF assembly.\n this.#parser.onFrame((data, meta) => {\n const packetId = meta.packetId;\n const diag = this.#diagFor(packetId, meta.catalogTrackName);\n diag.frames++;\n diag.lastFrameTimestamp = meta.timestamp;\n const isVideo = this.#classifyPacketId(packetId) === true;\n const frameMeta: RoutedFrameMeta = {\n timestamp: meta.timestamp,\n keyframe: isVideo ? meta.keyframe : true,\n packetId,\n packetSequenceNumber: meta.packetSequenceNumber ?? this.#currentSeqNum,\n ...(meta.authoredTiming === undefined\n ? {}\n : { authoredTiming: meta.authoredTiming }),\n ...(meta.packetSequenceNumbers === undefined\n ? {}\n : { packetSequenceNumbers: meta.packetSequenceNumbers }),\n ...(meta.repairedPacketSequenceNumbers === undefined\n ? {}\n : { repairedPacketSequenceNumbers: meta.repairedPacketSequenceNumbers }),\n ...(meta.mpuSequenceNumber === undefined\n ? {}\n : { mpuSequenceNumber: meta.mpuSequenceNumber }),\n ...(meta.catalogTrackName === undefined\n ? {}\n : { catalogTrackName: meta.catalogTrackName }),\n };\n if (meta.source) frameMeta.source = meta.source;\n // Forward whenever present, not when truthy: 'unknown' is a verdict the\n // parser reached, not a value to drop.\n if (meta.deliveryProvenance !== undefined)\n frameMeta.deliveryProvenance = meta.deliveryProvenance;\n if (meta.recovered) frameMeta.recovered = true;\n const sourceDiscontinuity = this.#opensFecSync(\n packetId,\n frameMeta.keyframe,\n frameMeta.catalogTrackName,\n );\n\n // Route to MSE assembler if active for this track.\n const assembler = this.#mseAssemblerFor(packetId, frameMeta.catalogTrackName);\n if (assembler) {\n const fecInfo = this.#currentFecInfo;\n if (fecInfo && !fecInfo.oversized) {\n assembler.addFrame(\n fecInfo.sbn,\n fecInfo.esi,\n data,\n frameMeta.timestamp,\n frameMeta.keyframe,\n frameMeta.catalogTrackName,\n frameMeta.mpuSequenceNumber,\n frameMeta.packetSequenceNumber,\n frameMeta.packetSequenceNumbers,\n frameMeta.repairedPacketSequenceNumbers,\n frameMeta.authoredTiming,\n frameMeta.deliveryProvenance,\n );\n } else {\n assembler.addBypassFrame(\n data,\n frameMeta.timestamp,\n frameMeta.keyframe,\n frameMeta.catalogTrackName,\n frameMeta.mpuSequenceNumber,\n frameMeta.packetSequenceNumber,\n frameMeta.packetSequenceNumbers,\n frameMeta.repairedPacketSequenceNumbers,\n frameMeta.authoredTiming,\n frameMeta.deliveryProvenance,\n );\n }\n }\n\n if (!this.#shouldEmitFrame(\n packetId,\n frameMeta.keyframe,\n frameMeta.catalogTrackName,\n )) return;\n if (sourceDiscontinuity) frameMeta.sourceDiscontinuity = true;\n\n this.#noteEmittedKeyframe(packetId, frameMeta.keyframe);\n\n // The emitted-sequence high-water mark (#noteEmittedVideoSeq) is recorded\n // at the actual emit inside #emitDirectVideo, not here \u2014 a recovered frame\n // can be queued ahead of the source cursor, so it must track real emits.\n\n // Route to per-track or legacy callback. Even when an MSE block-assembler\n // is registered, this callback still fires \u2014 downstream consumers\n // (`mc.onVideoFrame` \u2192 takeover detection, multicast frame counter,\n // `confirmVideo`/`confirmAudio`) need every reassembled frame. The\n // SourceBuffer-write side of this callback path is suppressed inside\n // `MseBackend.feedVideo`/`feedAudio` when the assembler owns the track,\n // which is configured via `MseBackend.setAssemblerOwns('video' | 'audio')`.\n // Prevents the dual-CMAF-timeline collision (BLO-3035 layer 3).\n const cb = this.#getFrameCallback(packetId);\n if (isVideo && !assembler) {\n if (frameMeta.recovered) {\n this.#routeDirectRecoveredVideo(packetId, data, frameMeta, cb);\n } else {\n this.#routeDirectSourceVideo(packetId, data, frameMeta, cb);\n }\n } else if (!isVideo && !assembler) {\n if (frameMeta.recovered) {\n this.#routeDirectRecoveredAudio(packetId, data, frameMeta, cb);\n } else {\n this.#routeDirectSourceAudio(packetId, data, frameMeta, cb);\n }\n } else {\n cb?.(data, frameMeta);\n }\n });\n\n // Wire up FECManager block lifecycle events.\n // NOTE: Recovery routing is handled from feedSourceSymbol/feedRepairSymbol\n // return values in #processMMTP \u2014 no onBlockRecovered event needed.\n if (this.#fecManager) {\n this.#fecManager.onBlockStarted = (\n sbn: number,\n packetId: number,\n _catalogTrackName?: string,\n ) => {\n this.#onBlockStarted?.(sbn, packetId);\n };\n\n this.#fecManager.onBlockComplete = (\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ) => {\n this.#mseAssemblerFor(packetId, catalogTrackName)?.completeBlock(sbn);\n // Phase 14 D-26: fan-out to delta callback when delta-role packetId completes.\n if (\n this.#isCatalogTrackUnambiguous(packetId, catalogTrackName) &&\n this.#packetIdRoles.get(packetId) === \"delta\"\n ) {\n this.#onDeltaBlockCompleteCb?.(packetId, false); // failed=false\n }\n };\n\n this.#fecManager.onBlockFailed = (\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ) => {\n this.#mseAssemblerFor(packetId, catalogTrackName)?.failBlock(sbn);\n if (\n this.#isCatalogTrackUnambiguous(packetId, catalogTrackName) &&\n this.#packetIdRoles.get(packetId) === \"delta\"\n ) {\n this.#onDeltaBlockCompleteCb?.(packetId, true); // failed=true\n }\n config?.onBlockFailed?.(sbn, packetId, catalogTrackName);\n this.#onBlockFailed?.(sbn, packetId, catalogTrackName);\n };\n }\n }\n\n // \u2500\u2500 Accessors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n get fecManager(): FECManagerLike | null {\n return this.#fecManager;\n }\n\n get reassembler(): MfuReassemblerBackend {\n return this.#parser.reassembler;\n }\n\n getDiagnostics(): Record<number, ParserDiagnostics> {\n const out: Record<number, ParserDiagnostics> = {};\n for (const [key, diag] of this.#parserDiagnostics) {\n const packetId = this.#packetIdFromStreamKey(key);\n const existing = out[packetId];\n if (!existing) {\n out[packetId] = { ...diag };\n continue;\n }\n const latest =\n diag.lastSequenceNumber >= existing.lastSequenceNumber ? diag : existing;\n out[packetId] = {\n packets: existing.packets + diag.packets,\n frames: existing.frames + diag.frames,\n initSegments: existing.initSegments + diag.initSegments,\n noFragment: existing.noFragment + diag.noFragment,\n fragments: existing.fragments + diag.fragments,\n reassembled: existing.reassembled + diag.reassembled,\n pendingBuffers: existing.pendingBuffers + diag.pendingBuffers,\n pendingFragments: existing.pendingFragments + diag.pendingFragments,\n directAudioSourceEmitted:\n existing.directAudioSourceEmitted + diag.directAudioSourceEmitted,\n directAudioSourceRegressions:\n existing.directAudioSourceRegressions + diag.directAudioSourceRegressions,\n directAudioRecoveredQueued:\n existing.directAudioRecoveredQueued + diag.directAudioRecoveredQueued,\n directAudioRecoveredDrained:\n existing.directAudioRecoveredDrained + diag.directAudioRecoveredDrained,\n directAudioRecoveredStaleDropped:\n existing.directAudioRecoveredStaleDropped + diag.directAudioRecoveredStaleDropped,\n pendingSamples: [\n ...((existing.pendingSamples as unknown[]) ?? []),\n ...((diag.pendingSamples as unknown[]) ?? []),\n ],\n lastPacketLength: latest.lastPacketLength,\n lastSequenceNumber: latest.lastSequenceNumber,\n ...(latest.lastFecSsId === undefined ? {} : { lastFecSsId: latest.lastFecSsId }),\n ...(latest.lastFrameTimestamp === undefined\n ? {}\n : { lastFrameTimestamp: latest.lastFrameTimestamp }),\n };\n }\n return out;\n }\n\n // \u2500\u2500 Track configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Register callback for keyframe loss detection.\n * Fires when no complete keyframe is emitted within keyframeLostTimeoutMs.\n * Argument: next expected groupId (lastKeyframeGroupId + 1). */\n onKeyframeLost(cb: (expectedGroupId: number) => void): void {\n this.#onKeyframeLost = cb;\n }\n\n /**\n * Set the keyframe loss timeout after construction.\n * Must be called BEFORE setPacketIdType('video').\n * Derive from explicit keyframe/group duration:\n * Math.ceil(groupDurationMs * KEYFRAME_LOST_TIMEOUT_SLACK).\n */\n setKeyframeLostTimeoutMs(ms: number): void {\n this.#keyframeLostTimeoutMs = ms;\n }\n\n /** Update MFU reassembly timeout. Source and FEC-recovery paths share the\n * parser's single reassembler (BLO-8151), so one update covers both. */\n setMfuTimeoutMs(ms: number): void {\n if (ms <= 0) throw new Error(\"[MmtpRouter] mfuTimeoutMs must be > 0\");\n this.#parser.setMfuTimeoutMs(ms);\n }\n\n /** Update parser frame reordering for unordered multicast delivery.\n * Callers derive this from the catalog FEC block span (\u00A75.1 \u2014 the\n * interleave value IS the span): positive round-half-up of span / frameMs. */\n setReorderWindow(windowSize: number): void {\n this.#parser.setReorderWindow(windowSize);\n }\n\n /** Register MMTP packetId \u2192 track type mapping.\n * Call BEFORE packets arrive to ensure correct routing from the first packet.\n * If keyframeLostTimeoutMs is not provided, Phase 13 keyframe repair is\n * disabled (video still routes/decodes normally). framerate is OPTIONAL per\n * draft-ietf-moq-catalogformat-01 \u00A73.2.23, so we can't require the timeout. */\n setPacketIdType(packetId: number, trackType: \"video\" | \"audio\"): void {\n if (trackType === \"video\" && this.#keyframeLostTimeoutMs === null) {\n console.warn(\n \"[MmtpRouter] keyframeLostTimeoutMs not configured \u2014 Phase 13 keyframe \" +\n \"repair disabled for this track. Set it via setKeyframeLostTimeoutMs() \" +\n \"or provide in constructor config to enable: \" +\n \"Math.ceil(groupDurationMs * KEYFRAME_LOST_TIMEOUT_SLACK).\",\n );\n }\n if (trackType === \"video\") this.#videoPacketIds.add(packetId);\n else this.#audioPacketIds.add(packetId);\n // Sync track info to parser for codec-aware keyframe detection.\n const codec = this.#trackCodecs.get(packetId) ?? \"\";\n this.#parser.setTrack(packetId, { type: trackType, codec });\n }\n\n /** Register codec string for a packetId \u2014 enables codec-aware keyframe detection.\n * Without this, only H.264 IDR (NAL type 5) is detected as keyframe fallback.\n * AV1 (OBU_SEQUENCE_HEADER) and VP9 (frame_type bit) require the codec string. */\n setTrackCodec(packetId: number, codec: string): void {\n this.#trackCodecs.set(packetId, codec);\n // Sync to parser. Determine track type from existing registration.\n const trackType = this.#videoPacketIds.has(packetId)\n ? (\"video\" as const)\n : this.#audioPacketIds.has(packetId)\n ? (\"audio\" as const)\n : null;\n if (trackType) {\n this.#parser.setTrack(packetId, { type: trackType, codec });\n }\n }\n\n // \u2500\u2500 Frame callbacks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** @deprecated Use onTrackFrame(packetId, cb) for per-track routing */\n onVideoFrame(cb: FrameCallback): void {\n this.#onVideoFrame = cb;\n this.#resolvedCallbacks.clear();\n }\n\n /** @deprecated Use onTrackFrame(packetId, cb) for per-track routing */\n onAudioFrame(cb: FrameCallback): void {\n this.#onAudioFrame = cb;\n this.#resolvedCallbacks.clear();\n }\n\n /** Register a frame callback for a specific MMTP packetId (track).\n * When a legacy callback (onVideoFrame/onAudioFrame) also exists for this\n * packetId's type, both fire: per-track first, then legacy. This ensures\n * element.ts wiring (confirmAudio, feedMulticast) continues working when\n * TrackPipeline registers per-track sources. */\n onTrackFrame(\n packetId: number,\n cb: (\n data: Uint8Array,\n meta: {\n timestamp: number;\n keyframe: boolean;\n packetId?: number;\n packetSequenceNumber?: number;\n },\n ) => void,\n ): void {\n this.#trackCallbacks.set(packetId, cb);\n this.#resolvedCallbacks.clear();\n }\n\n /** Register a raw MMTP source-packet callback for one track.\n *\n * Direct packets retain their original bytes, including a Source FEC Payload\n * ID when present. FEC-recovered packets are the recovered source-symbol\n * bytes and set `skipFecPayloadId` because that trailer is not part of the\n * protected symbol. Repair packets are never delivered here.\n */\n onTrackPacket(packetId: number, cb: TrackPacketCallback): void {\n this.#trackPacketCallbacks.set(packetId, cb);\n }\n\n /** Register an init segment callback (ftyp+moov from MMTP MPU metadata). */\n onInitSegment(cb: (data: Uint8Array) => void): void {\n this.#onInitSegment = cb;\n }\n\n /** Register an init segment callback for a specific MMTP packetId.\n * Called when FT=0 init arrives via direct source or FEC recovery. */\n onTrackInit(packetId: number, cb: (data: Uint8Array) => void): void {\n this.#trackInitCallbacks.set(packetId, cb);\n }\n\n // \u2500\u2500 MSE integration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Create a CMAF block assembler for MSE mode on a specific track.\n * When active, source frames are buffered per FEC block and emitted as\n * multi-sample CMAF segments instead of per-frame callbacks. */\n enableMseAssembler(\n packetId: number,\n config: Omit<BlockAssemblerConfig, \"k\">,\n catalogTrackName?: string,\n ): CmafBlockAssembler {\n const k = this.#kForPacket(packetId);\n const assemblerKey = this.#mseAssemblerKey(packetId, catalogTrackName);\n const previous = this.#mseAssemblers.get(assemblerKey);\n if (previous) {\n previous.flush();\n previous.dispose();\n }\n const assembler = new CmafBlockAssembler({ ...config, k });\n this.#mseAssemblers.set(assemblerKey, assembler);\n console.log(\n `[MmtpRouter] MSE assembler enabled for packetId=${packetId}${catalogTrackName === undefined ? \"\" : ` track=${catalogTrackName}`} (K=${k}, ${config.trackType})`,\n );\n return assembler;\n }\n\n /** Register a segment callback for a track's MSE assembler.\n * Called with complete moof+mdat CMAF segments on block completion. */\n onMseSegment(packetId: number, cb: (segment: Uint8Array) => void, catalogTrackName?: string): void {\n const assembler = this.#mseAssemblers.get(this.#mseAssemblerKey(packetId, catalogTrackName));\n if (assembler) assembler.onSegment = cb;\n }\n\n /** Register a raw-sample callback for a track's MSE assembler.\n * Use this when the caller already owns the fMP4/MSE timeline and only wants\n * FEC recovery, keyframe gating, and dedup from the assembler. */\n onMseSample(packetId: number, cb: (sample: CmafBlockSample) => void, catalogTrackName?: string): void {\n const assembler = this.#mseAssemblers.get(this.#mseAssemblerKey(packetId, catalogTrackName));\n if (assembler) assembler.onSample = cb;\n }\n\n /** Register an init segment callback for a track's MSE assembler. */\n onMseInitSegment(packetId: number, cb: (init: Uint8Array) => void, catalogTrackName?: string): void {\n const assembler = this.#mseAssemblers.get(this.#mseAssemblerKey(packetId, catalogTrackName));\n if (assembler) assembler.onInitSegment = cb;\n }\n\n /** Build and emit init segment for a track's MSE assembler from catalog config. */\n emitMseInit(packetId: number, catalogTrackName?: string): void {\n this.#mseAssemblers.get(this.#mseAssemblerKey(packetId, catalogTrackName))?.emitInit();\n }\n\n /** Mark a video track as synchronized by an external path.\n * This opens the MMTP/FEC pre-sync gates without waiting for a multicast\n * keyframe, for MSE timelines already seeded by MoQ with the same track. */\n markExternalVideoSync(packetId?: number, catalogTrackName?: string): void {\n const packetIds =\n packetId === undefined\n ? [...this.#videoPacketIds]\n : this.#videoPacketIds.has(packetId)\n ? [packetId]\n : [];\n for (const id of packetIds) {\n const streamKey = this.#mediaStreamKey(id, catalogTrackName);\n this.#trackSynced.add(streamKey);\n this.#droppedPreSyncVideo.delete(streamKey);\n this.#mseAssemblerFor(id, catalogTrackName)?.markVideoSynced();\n }\n }\n\n // \u2500\u2500 FEC event handler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Register callback for FEC blocks when the first source symbol arrives. */\n onFecBlockStarted(cb: (sbn: number, packetId: number) => void): void {\n this.#onBlockStarted = cb;\n }\n\n /** Register callback for FEC blocks that time out unrecoverably. Players use\n * this to recover a decoder that was starved by the lost block. */\n onFecBlockFailed(\n cb: (sbn: number, packetId: number, catalogTrackName?: string) => void,\n ): void {\n this.#onBlockFailed = cb;\n }\n\n // \u2500\u2500 Phase 14: per-role packetId dispatch (D-26, D-27, D-28) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Register a TrackRole for a specific packetId.\n *\n * D-26: Enables per-role block-complete fanout \u2014 delta-role packetIds fire\n * onDeltaBlockComplete when their FEC block completes or fails.\n * Composite callback preserved: legacy onVideoFrame/onAudioFrame still fire\n * (MEMORY.md \"prevents callback stealing\").\n *\n * Valid roles: 'base' | 'delta' | 'repair' | 'single'.\n * Throws on unknown role (error on bad init, CLAUDE.md).\n */\n setPacketIdRole(packetId: number, role: TrackRole): void {\n const VALID_ROLES: readonly TrackRole[] = [\n \"base\",\n \"delta\",\n \"repair\",\n \"single\",\n ];\n if (!VALID_ROLES.includes(role)) {\n throw new Error(\n `[MmtpRouter] setPacketIdRole: unknown role '${role}'. Valid: ${VALID_ROLES.join(\", \")}`,\n );\n }\n this.#packetIdRoles.set(packetId, role);\n }\n\n /** Replace catalog-derived repair ownership before a catalog reconfiguration. */\n clearRepairPacketIdOwners(): void {\n this.#repairOwnersByPacketId.clear();\n }\n\n /** Bind one dedicated repair-track PID to its authoritative media PID. */\n setRepairPacketIdOwner(repairPacketId: number, ownerPacketId: number): void {\n const owners = this.#repairOwnersByPacketId.get(repairPacketId) ?? new Set<number>();\n owners.add(ownerPacketId);\n this.#repairOwnersByPacketId.set(repairPacketId, owners);\n }\n\n /**\n * Register callback fired when a delta-track FEC block completes or fails.\n *\n * @param cb - (packetId, failed) \u2014 packetId identifies the delta track,\n * failed=false means clean/recovered, failed=true means unrecoverable.\n *\n * Called from both onBlockComplete (failed=false) and onBlockFailed (failed=true)\n * for every packetId registered with role='delta' via setPacketIdRole().\n */\n onDeltaBlockComplete(cb: (packetId: number, failed: boolean) => void): void {\n this.#onDeltaBlockCompleteCb = cb;\n }\n\n // \u2500\u2500 Packet processing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Update framerate \u2014 delegates to FECManager for timeout refinement. */\n setFramerate(fps: number): void {\n this.#fecManager?.setFramerate(fps);\n }\n\n /** Set FEC block mapper (created when FEC config becomes known via configureFec). */\n setFecMapper(mapper: FecBlockMapper, packetId?: number): void {\n if (packetId == null) {\n this.#defaultFecMapper = mapper;\n return;\n }\n this.#fecMappersByPacketId.set(packetId, mapper);\n }\n\n registerManifestTrack(context: ManifestTrackContext): void {\n if (!this.#manifestVerifier) throw new Error(\"manifest verifier is not configured\");\n this.#manifestVerifier.registerTrack(context);\n }\n\n feedManifestFrame(trackName: string, frame: Uint8Array): void {\n this.#manifestVerifier?.ingestManifestFrame(trackName, frame);\n }\n\n setManifestAuthMode(mode: ManifestAuthMode): void {\n this.#manifestVerifier?.setMode(mode);\n if (mode !== \"enforce\") {\n for (const queue of this.#manifestRecoveryQueues.values()) {\n for (const item of queue) {\n this.#routeProcessedFecSymbols(\n item.blockId,\n item.result,\n item.ownerPacketId,\n item.catalogTrackName,\n );\n }\n }\n this.#manifestRecoveryQueues.clear();\n }\n }\n\n /**\n * Main entry point: feed a raw MMTP packet for processing.\n *\n * Source packets are played immediately AND fed to FECManager for recovery.\n * Repair packets are fed to FECManager only (no direct playback).\n */\n /**\n * Feed one MMTP source packet.\n *\n * `source` is REQUIRED. It used to default to \"multicast\", which meant any\n * caller that forgot it silently attributed its bytes to a multicast socket\n * they may never have crossed \u2014 and that attribution is what the physical-\n * delivery evidence path is built on. Absence is not multicast; say which.\n */\n feedPacket(\n packet: Uint8Array,\n source: \"multicast\" | \"moq\",\n manifestIdentity?: ManifestObjectIdentity,\n ingress?: MmtpIngressContext,\n ): void {\n if (this.#disposed) return;\n this.#processMMTP(packet, source, manifestIdentity, false, ingress);\n }\n\n /**\n * Feed a repair packet from MoQ (reliable QUIC) \u2014 parses MMTP repair\n * header and delegates to FECManager for FEC recovery.\n */\n feedRepairPacket(\n packet: Uint8Array,\n ownerPacketIdHint?: number,\n catalogTrackName?: string,\n ): void {\n if (this.#disposed) return;\n\n const parsed = parseRepairPacket(packet);\n if (!parsed) return;\n\n // A dedicated repair track can carry a packet ID that differs from the\n // media packet ID. The subscribing track supplies the authoritative owner.\n const header = packet.length >= 4 ? parseMMTPHeader(packet) : null;\n const repairPacketId = header?.packetId;\n if (repairPacketId == null) return;\n\n const hintedOwner =\n ownerPacketIdHint != null && this.#classifyPacketId(ownerPacketIdHint) !== null\n ? ownerPacketIdHint\n : null;\n // A MoQ repair subscription owns a media track explicitly. Dedicated\n // repair PIDs must carry that hint; only same-PID repair is self-describing.\n const ownerPacketId = hintedOwner ??\n (this.#classifyPacketId(repairPacketId) !== null ? repairPacketId : null);\n if (ownerPacketId === null) {\n debugRouter(\n `[MmtpRouter] Dropping MoQ repair packet pid=${repairPacketId}: owner hint is required`,\n );\n }\n\n if (ownerPacketId != null && this.#fecManager) {\n this.#moqRepairPidCounts[repairPacketId] =\n (this.#moqRepairPidCounts[repairPacketId] || 0) + 1;\n this.#moqRepairOwnerCounts[ownerPacketId] =\n (this.#moqRepairOwnerCounts[ownerPacketId] || 0) + 1;\n const pidCount = this.#moqRepairPidCounts[repairPacketId]!;\n const ownerCount = this.#moqRepairOwnerCounts[ownerPacketId]!;\n if (pidCount <= 2 || pidCount % 500 === 0) {\n console.log(\n `[MOQ-REPAIR] pid=${repairPacketId} repairOwner=${ownerPacketId} pidCount=${pidCount} ownerCount=${ownerCount} ssbLength=${parsed.ssbLength} len=${packet.length}`,\n );\n }\n\n const repairSbn = this.#deriveRepairSbn(\n parsed.ssStart,\n parsed.ssbLength,\n ownerPacketId,\n );\n const repairEsi = this.#kForPacket(ownerPacketId) + parsed.rsId;\n\n if (\n !this.#feedFecTransportSymbol(\n ownerPacketId,\n repairSbn,\n repairEsi,\n parsed.repairData,\n \"moq\",\n catalogTrackName,\n )\n ) return;\n\n // NOTE: Do NOT call trackBlock for repair packets (same as multicast repair path).\n const recovered = catalogTrackName === undefined\n ? this.#fecManager.feedRepairSymbol(\n ownerPacketId,\n parsed.ssStart,\n parsed.ssbLength,\n parsed.rsId,\n parsed.repairData,\n )\n : this.#fecManager.feedRepairSymbol(\n ownerPacketId,\n parsed.ssStart,\n parsed.ssbLength,\n parsed.rsId,\n parsed.repairData,\n catalogTrackName,\n );\n if (recovered) {\n const alreadyCompleted = catalogTrackName === undefined\n ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId)\n : this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);\n if (catalogTrackName === undefined) {\n this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);\n } else {\n this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId, catalogTrackName);\n }\n if (!alreadyCompleted) {\n this.#fecManager.countRecovery(ownerPacketId);\n if (catalogTrackName === undefined) {\n this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);\n } else {\n this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId, catalogTrackName);\n }\n this.#routeRecoveredBlock(\n repairSbn,\n recovered,\n ownerPacketId,\n parsed.ssbLength,\n catalogTrackName,\n );\n }\n }\n }\n }\n\n // \u2500\u2500 SBN/ESI derivation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n #kForPacket(packetId?: number): number {\n // Same packet-scoped rule as #deriveSbnEsi: once the packet ID is known,\n // only its explicitly registered mapper may stand in for the FEC\n // manager's per-packet K \u2014 never the default single-track mapper.\n if (packetId != null)\n return (\n this.#fecManager?.kForPacket?.(packetId) ??\n this.#fecMappersByPacketId.get(packetId)?.k ??\n this.#fecManager?.k ??\n 4\n );\n return this.#defaultFecMapper?.k ?? this.#fecManager?.k ?? 4;\n }\n\n #symbolSizeForPacket(packetId?: number): number {\n if (packetId != null) return this.#fecManager?.symbolSizeForPacket?.(packetId) ?? this.#fecManager?.symbolSize ?? 0;\n return this.#fecManager?.symbolSize ?? 0;\n }\n\n /** Derive SBN/ESI from SS_ID using packet-scoped FEC geometry. */\n #deriveSbnEsi(ssId: number, packetId?: number): { sbn: number; esi: number } {\n // A default mapper describes legacy single-track geometry. Once a packet\n // ID is known, only its explicitly registered mapper may override the\n // FECManager's packet-scoped K. Falling back to the default video mapper\n // here maps audio source symbols with video K while repair SBNs correctly\n // use the audio SSB_length, splitting one block across two coordinates.\n const mapper = packetId == null\n ? this.#defaultFecMapper\n : this.#fecMappersByPacketId.get(packetId) ?? null;\n if (mapper) return mapper.fromSsId(ssId);\n const k = this.#kForPacket(packetId);\n return { sbn: Math.floor(ssId / k), esi: ssId % k };\n }\n\n #deriveRepairSbn(ssStart: number, ssbLength: number, packetId?: number): number {\n if (ssbLength > 0) return Math.floor(ssStart / ssbLength);\n return this.#deriveSbnEsi(ssStart, packetId).sbn;\n }\n\n #resolveRepairOwnerPacketId(\n packetId: number,\n ingress?: MmtpIngressContext,\n ): number | null {\n if (this.#classifyPacketId(packetId) !== null) return packetId;\n\n if (this.#packetIdRoles.get(packetId) !== \"repair\") {\n debugRouter(\n `[MmtpRouter] Dropping repair packet pid=${packetId}: physical PID is unregistered`,\n );\n return null;\n }\n\n const registeredOwners = this.#repairOwnersByPacketId.get(packetId) ?? new Set<number>();\n const owners = ingress === undefined\n ? [...registeredOwners]\n : [...registeredOwners].filter((ownerPacketId) =>\n ingressHasPacketId(ingress, ownerPacketId));\n if (owners.length === 1) return owners[0]!;\n\n debugRouter(\n `[MmtpRouter] Dropping repair packet pid=${packetId}: catalog owner is ${owners.length === 0 ? \"absent\" : \"ambiguous\"}`,\n );\n return null;\n }\n\n /** Route a recovered FEC block through the parser + dedup pipeline.\n * Sync WASM decoders return K fixed-width source symbols concatenated. */\n routeRecoveredBlock(\n blockId: number,\n data: Uint8Array,\n packetId: number,\n catalogTrackName?: string,\n ): void {\n this.#routeRecoveredBlock(blockId, data, packetId, undefined, catalogTrackName);\n }\n\n /** Route a single recovered source symbol through the parser + dedup pipeline.\n * Worker-backed decoders emit one padded source symbol per frame event. */\n routeRecoveredSymbol(\n blockId: number,\n data: Uint8Array,\n packetId: number,\n esi = 0,\n catalogTrackName?: string,\n ): void {\n const result = processFecSymbol(data, { ownerPacketId: packetId, index: esi });\n this.#routeVerifiedFecSymbols(blockId, result, packetId, catalogTrackName);\n }\n\n // \u2500\u2500 Keyframe timer cancellation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Cancel the active keyframe loss timer.\n * Call on failbackToMoq to prevent a stale timer from firing\n * onKeyframeLost after multicast is no longer the active source. */\n cancelKeyframeLostTimer(): void {\n if (this.#keyframeLostTimer) {\n clearTimeout(this.#keyframeLostTimer);\n this.#keyframeLostTimer = null;\n }\n }\n\n // \u2500\u2500 Dispose \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Emit parser frames buffered for a finite input before teardown. */\n flush(): void {\n if (this.#disposed) return;\n this.#parser.flush();\n }\n\n /** Drop one rendition's parser/router/CMAF epoch without touching peers. */\n resetTrack(packetId: number, catalogTrackName: string): void {\n if (this.#disposed) return;\n this.#parser.resetTrack(packetId, catalogTrackName);\n const streamKey = this.#mediaStreamKey(packetId, catalogTrackName);\n this.#trackSynced.delete(streamKey);\n this.#droppedPreSyncVideo.delete(streamKey);\n this.#latestVideoSeqByStream.delete(streamKey);\n this.#lastDirectSourceVideoTsByStream.delete(streamKey);\n this.#lastDirectEmittedVideoTsByStream.delete(streamKey);\n this.#pendingDirectRecoveredVideoByStream.delete(streamKey);\n this.#lastDirectSourceAudioCursorByStream.delete(streamKey);\n this.#lastDirectEmittedAudioCursorByStream.delete(streamKey);\n this.#pendingDirectRecoveredAudioByStream.delete(streamKey);\n this.#manifestRecoveryQueues.delete(streamKey);\n for (const packetSeqs of [this.#sourcePktSeqs, this.#routedPacketSeqs]) {\n for (const key of packetSeqs) {\n try {\n const [keyPacketId, keyTrackName] = JSON.parse(key);\n if (keyPacketId === packetId && keyTrackName === catalogTrackName) {\n packetSeqs.delete(key);\n }\n } catch {\n // Internal keys are JSON tuples; malformed legacy keys do not belong\n // to a named physical stream and are left untouched.\n }\n }\n }\n const assemblersToReset = new Set<CmafBlockAssembler>();\n const exactAssembler = this.#mseAssemblers.get(streamKey);\n if (exactAssembler) assemblersToReset.add(exactAssembler);\n const selectedAssembler = this.#mseAssemblerFor(packetId, catalogTrackName);\n if (selectedAssembler) assemblersToReset.add(selectedAssembler);\n for (const assembler of assemblersToReset) assembler.reset();\n this.#trackInitDelivered.delete(streamKey);\n this.#parserDiagnostics.delete(streamKey);\n }\n\n /** Live teardown is drop-only. Finite inputs must opt into a final flush. */\n dispose(options: { flush?: boolean } = {}): void {\n if (options.flush) this.flush();\n this.#disposed = true;\n this.cancelKeyframeLostTimer();\n this.#parser.dispose();\n this.#sourcePktSeqs.clear();\n this.#routedPacketSeqs.clear();\n this.#trackPacketCallbacks.clear();\n this.#latestVideoSeqByStream.clear();\n this.#lastDirectSourceVideoTsByStream.clear();\n this.#lastDirectEmittedVideoTsByStream.clear();\n this.#pendingDirectRecoveredVideoByStream.clear();\n this.#lastDirectSourceAudioCursorByStream.clear();\n this.#lastDirectEmittedAudioCursorByStream.clear();\n this.#pendingDirectRecoveredAudioByStream.clear();\n this.#trackSynced.clear();\n this.#droppedPreSyncVideo.clear();\n this.#trackInitDelivered.clear();\n this.#repairPidCounts = {};\n this.#moqRepairPidCounts = {};\n this.#moqRepairOwnerCounts = {};\n this.#parserDiagnostics.clear();\n this.#catalogTrackNamesByPacketId.clear();\n this.#repairOwnersByPacketId.clear();\n if (this.#ownsManifestVerifier) this.#manifestVerifier?.dispose();\n this.#manifestRecoveryQueues.clear();\n for (const assembler of this.#mseAssemblers.values()) assembler.dispose();\n this.#mseAssemblers.clear();\n }\n\n // \u2500\u2500 Private: keyframe loss detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Arm (or re-arm) the keyframe loss timer.\n * Clears any existing timer and sets a new one for keyframeLostTimeoutMs.\n * On timeout, fires onKeyframeLost with the next expected groupId. */\n #armKeyframeLostTimer(): void {\n if (this.#keyframeLostTimer) {\n clearTimeout(this.#keyframeLostTimer);\n }\n const expectedNext = this.#lastKeyframeGroupId + 1;\n this.#keyframeLostTimer = setTimeout(() => {\n this.#keyframeLostTimer = null;\n this.#onKeyframeLost?.(expectedNext);\n }, this.#keyframeLostTimeoutMs!);\n }\n\n #noteEmittedKeyframe(packetId: number, keyframe: boolean): void {\n if (!keyframe) return;\n if (!this.#videoPacketIds.has(packetId)) return;\n if (this.#keyframeLostTimeoutMs === null) return;\n this.#lastKeyframeGroupId++;\n this.#armKeyframeLostTimer();\n }\n\n // \u2500\u2500 Private: MMTP processing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Classify a packetId from the catalog. Returns true=video, false=audio, null=unknown (drop). */\n #classifyPacketId(packetId: number): boolean | null {\n if (this.#videoPacketIds.has(packetId)) return true;\n if (this.#audioPacketIds.has(packetId)) return false;\n return null;\n }\n\n /** Resolve callback for a packetId (cached per-packetId, invalidated on registration).\n * When a per-track callback is registered AND a legacy callback exists,\n * fires BOTH: per-track first, then legacy. This ensures element.ts wiring\n * (confirmAudio, feedMulticast) continues to fire even when TrackPipeline\n * registers per-track sources.\n * Returns null for unknown packetIds \u2014 callers must drop the packet. */\n #getFrameCallback(packetId: number): FrameCallback | null {\n const cached = this.#resolvedCallbacks.get(packetId);\n if (cached) return cached;\n\n const perTrack = this.#trackCallbacks.get(packetId);\n const classification = this.#classifyPacketId(packetId);\n const legacy =\n classification === true\n ? this.#onVideoFrame\n : classification === false\n ? this.#onAudioFrame\n : null;\n\n let resolved: FrameCallback | null;\n if (perTrack && legacy) {\n // Composite: fire both so per-track routing AND legacy confirm/feedMulticast work.\n resolved = (data, meta) => {\n perTrack(data, meta);\n legacy(data, meta);\n };\n } else {\n resolved = perTrack ?? legacy ?? null;\n }\n\n if (resolved) this.#resolvedCallbacks.set(packetId, resolved);\n return resolved;\n }\n\n /** Deliver init segment for a track (deduped per physical stream). */\n #deliverInit(packetId: number, data: Uint8Array, catalogTrackName?: string): void {\n const streamKey = this.#mediaStreamKey(packetId, catalogTrackName);\n if (this.#trackInitDelivered.has(streamKey)) return;\n if (data.byteLength <= 8) return;\n this.#trackInitDelivered.add(streamKey);\n const cb = this.#trackInitCallbacks.get(packetId);\n if (cb) {\n cb(data);\n } else if (this.#onInitSegment) {\n this.#onInitSegment(data);\n }\n console.log(\n `[MmtpRouter] Init delivered for packetId=${packetId}${catalogTrackName === undefined ? \"\" : ` track=${catalogTrackName}`} (${data.byteLength}B)`,\n );\n }\n\n /**\n * Core MMTP packet processing \u2014 source packets \u2192 play + FEC, repair \u2192 FEC only.\n * Both source and repair feed FECManager so RaptorQ has enough symbols (source + repair >= K).\n *\n * Two-phase parsing via MmtpContainerParser:\n * 1. parseHeader() \u2192 extract packetId + fecPayloadId for FEC feeding\n * 2. feed() \u2192 MFU extraction, reassembly, keyframe detection, emit via callbacks\n */\n #processMMTP(\n packet: Uint8Array,\n source: \"multicast\" | \"moq\",\n manifestIdentity?: ManifestObjectIdentity,\n manifestAdmitted = false,\n ingress?: MmtpIngressContext,\n ): void {\n const info = this.#parser.parseHeader(packet);\n if (!info) return;\n if (source === \"multicast\" && !ingressHasPacketId(ingress, info.packetId)) {\n debugRouter(\n `[MmtpRouter] Dropping multicast packet pid=${info.packetId}: absent from ingress tracks`,\n );\n return;\n }\n const packetRole = this.#packetIdRoles.get(info.packetId);\n if (\n source === \"multicast\" &&\n this.#classifyPacketId(info.packetId) === null &&\n (!info.isRepair || packetRole !== \"repair\")\n ) {\n debugRouter(\n `[MmtpRouter] Dropping multicast packet pid=${info.packetId}: physical PID is unregistered`,\n );\n return;\n }\n if (!info.isRepair && hasAmbiguousCatalogTrackName(ingress, info.packetId)) {\n return;\n }\n\n if (!info.isRepair && this.#manifestVerifier && !manifestAdmitted) {\n let identity = manifestIdentity;\n if (!identity && info.fecPayloadId && (this.#resolveManifestIdentityForRoute || this.#resolveManifestIdentity)) {\n const coordinates = this.#deriveSbnEsi(info.fecPayloadId.ssId, info.packetId);\n const catalogTrackName = catalogTrackNameForPacket(ingress, info.packetId);\n identity = (this.#resolveManifestIdentityForRoute\n ? this.#resolveManifestIdentityForRoute({\n packetId: info.packetId,\n sbn: coordinates.sbn,\n esi: coordinates.esi,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n recovered: false,\n sourceSymbolFormat: \"full-mmtp-packet\",\n })\n : this.#resolveManifestIdentity!(\n info.packetId,\n coordinates.sbn,\n coordinates.esi,\n catalogTrackName,\n )) ?? undefined;\n }\n if (identity) {\n identity = {\n ...identity,\n packetId: info.packetId,\n ...(info.fecPayloadId ? { ssId: info.fecPayloadId.ssId } : {}),\n };\n const authenticated = info.fecPayloadId ? packet.subarray(0, packet.length - 4) : packet;\n this.#manifestVerifier.admitObject(identity, authenticated, () => {\n if (!this.#disposed) this.#processMMTP(packet, source, identity, true, ingress);\n });\n return;\n }\n if (\n this.#manifestVerifier.config.mode === \"enforce\" &&\n this.#manifestVerifier.config.onMissingManifest === \"stall\"\n ) {\n console.warn(`[MmtpRouter] manifest identity absent; stalling packetId=${info.packetId}`);\n return;\n }\n }\n\n if (info.isRepair) {\n // Repair packet \u2014 per ISO 23008-1 \u00A7C.5.3, carries (SS_Start, SSB_length, RS_ID).\n const parsed = parseRepairPacket(packet);\n if (!parsed) return;\n\n const ownerPacketId = this.#resolveRepairOwnerPacketId(info.packetId, ingress);\n if (ownerPacketId != null && this.#fecManager) {\n if (hasAmbiguousCatalogTrackName(ingress, ownerPacketId)) return;\n this.#repairPidCounts[info.packetId] = (this.#repairPidCounts[info.packetId] || 0) + 1;\n const count = this.#repairPidCounts[info.packetId]!;\n if (count <= 2 || count % 500 === 0) {\n console.log(\n `[MC-PKT] pid=${info.packetId} repairOwner=${ownerPacketId} type=${info.packetType} count=${count} ssbLength=${parsed.ssbLength} len=${packet.length}`,\n );\n }\n\n // NOTE: Do NOT call trackBlock for repair packets. Only source symbols\n // should count toward block completion \u2014 repair inflates blocksClean stats\n // and masks blocks that actually needed FEC recovery.\n const repairSbn = this.#deriveRepairSbn(\n parsed.ssStart,\n parsed.ssbLength,\n ownerPacketId,\n );\n const repairEsi = this.#kForPacket(ownerPacketId) + parsed.rsId;\n\n if (\n !this.#feedFecTransportSymbol(\n ownerPacketId,\n repairSbn,\n repairEsi,\n parsed.repairData,\n source,\n catalogTrackNameForPacket(ingress, ownerPacketId),\n )\n ) return;\n\n // Repair symbols are block progress too. The deadline measures\n // inactivity, not total block lifetime.\n const repairCatalogTrackName = catalogTrackNameForPacket(ingress, ownerPacketId);\n if (repairCatalogTrackName === undefined) {\n this.#fecManager.noteBlockActivity?.(repairSbn, ownerPacketId);\n } else {\n this.#fecManager.noteBlockActivity?.(\n repairSbn,\n ownerPacketId,\n repairCatalogTrackName,\n );\n }\n\n const recovered = repairCatalogTrackName === undefined\n ? this.#fecManager.feedRepairSymbol(\n ownerPacketId,\n parsed.ssStart,\n parsed.ssbLength,\n parsed.rsId,\n parsed.repairData,\n )\n : this.#fecManager.feedRepairSymbol(\n ownerPacketId,\n parsed.ssStart,\n parsed.ssbLength,\n parsed.rsId,\n parsed.repairData,\n repairCatalogTrackName,\n );\n if (recovered) {\n const alreadyCompleted = repairCatalogTrackName === undefined\n ? this.#fecManager.isBlockCompleted(repairSbn, ownerPacketId)\n : this.#fecManager.isBlockCompleted(\n repairSbn,\n ownerPacketId,\n repairCatalogTrackName,\n );\n if (repairCatalogTrackName === undefined) {\n this.#fecManager.cancelBlockTimeout(repairSbn, ownerPacketId);\n } else {\n this.#fecManager.cancelBlockTimeout(\n repairSbn,\n ownerPacketId,\n repairCatalogTrackName,\n );\n }\n // Skip routing if block was already completed via source path (all K sources\n // arrived). The parser's feed() already played every frame.\n if (!alreadyCompleted) {\n this.#fecManager.countRecovery(ownerPacketId);\n if (repairCatalogTrackName === undefined) {\n this.#fecManager.markBlockCompleted(repairSbn, ownerPacketId);\n } else {\n this.#fecManager.markBlockCompleted(\n repairSbn,\n ownerPacketId,\n repairCatalogTrackName,\n );\n }\n this.#routeRecoveredBlock(\n repairSbn,\n recovered,\n ownerPacketId,\n parsed.ssbLength,\n catalogTrackNameForPacket(ingress, ownerPacketId),\n );\n }\n }\n }\n } else {\n // Drop source packets with unknown packetId \u2014 not in catalog.\n if (this.#classifyPacketId(info.packetId) === null) return;\n\n // Debug: count packets per packetId\n this.#pidCounts[info.packetId] = (this.#pidCounts[info.packetId] || 0) + 1;\n const count = this.#pidCounts[info.packetId]!;\n if (count <= 2 || count % 500 === 0) {\n console.log(\n `[MC-PKT] pid=${info.packetId} type=${info.packetType} count=${count} len=${packet.length}`,\n );\n }\n\n // Source packet \u2014 parse for playback first, then feed FEC only if the\n // source block did not complete cleanly. Feeding FEC before parser/feed\n // lets a source-triggered recovery race ahead of the source timeline.\n let fecInfo:\n | { sbn: number; esi: number; oversized?: boolean }\n | undefined;\n let sourceFec:\n | {\n ssId: number;\n sbn: number;\n symbolData: Uint8Array;\n }\n | null = null;\n let sourceSymbolAccepted = false;\n let sourceSymbolTrackable = false;\n const catalogTrackName = catalogTrackNameForPacket(ingress, info.packetId);\n if (catalogTrackName !== undefined) {\n const names = this.#catalogTrackNamesByPacketId.get(info.packetId) ?? new Set();\n names.add(catalogTrackName);\n this.#catalogTrackNamesByPacketId.set(info.packetId, names);\n }\n\n if (this.#fecManager && info.fecPayloadId) {\n const fecId = info.fecPayloadId;\n const symbolSize = this.#symbolSizeForPacket(info.packetId);\n\n // The mmtp-packet source symbol protects the complete MMTP packet\n // before SS_ID is appended, self-delimiting and zero-padded to T.\n const rawSymbol = packet.subarray(0, packet.length - 4);\n const oversized = symbolSize > 0 && rawSymbol.length > symbolSize;\n const coords = this.#deriveSbnEsi(fecId.ssId, info.packetId);\n\n // NOTE: trackBlock() is deferred to AFTER #feedViaParser (below).\n // Reason: trackBlock synchronously fires onBlockComplete when the\n // K-th source symbol arrives, which synchronously calls\n // CmafBlockAssembler.completeBlock \u2192 #flushBlock \u2192 emit. If\n // trackBlock fires BEFORE parser.feed, the K-th packet's\n // parser.onFrame \u2192 assembler.addFrame happens AFTER flushBlock\n // already drained the block \u2014 the K-th frame is stranded in a\n // newly-allocated empty block that never gets a completeBlock\n // event again, because that SBN is already in fecManager's\n // #completedBlocks set (BLO-3035 layer 4).\n //\n // Complete-packet symbols larger than T cannot be decoded but still\n // represent a source-symbol arrival, so trackBlock counts them below.\n\n if (oversized) {\n // Oversized packet \u2014 not FEC-encoded by FFmpeg, skip FEC feed.\n // Still emitted via parser.feed() below (direct path).\n sourceSymbolAccepted = this.#feedFecTransportSymbol(\n info.packetId,\n coords.sbn,\n coords.esi,\n rawSymbol,\n source,\n catalogTrackName,\n );\n sourceSymbolTrackable = sourceSymbolAccepted;\n } else {\n let symbolData: Uint8Array;\n if (symbolSize > 0 && rawSymbol.length < symbolSize) {\n symbolData = new Uint8Array(symbolSize);\n symbolData.set(rawSymbol);\n } else {\n symbolData = rawSymbol;\n }\n\n sourceSymbolAccepted = this.#feedFecTransportSymbol(\n info.packetId,\n coords.sbn,\n coords.esi,\n symbolData,\n source,\n catalogTrackName,\n );\n sourceSymbolTrackable = sourceSymbolAccepted;\n if (sourceSymbolAccepted) {\n sourceFec = { ssId: fecId.ssId, sbn: coords.sbn, symbolData };\n }\n }\n\n // Compute FEC block info for the MSE assembler.\n fecInfo = { sbn: coords.sbn, esi: coords.esi, oversized };\n }\n\n // Parse only the first copy of an FEC source symbol. A late direct copy\n // of a worker-recovered fragment has the same SBN/ESI and fragment\n // counter; feeding it again can make the MFU buffer non-contiguous even\n // when FI=1..3 were already complete. Non-FEC packets still always flow.\n if (!this.#fecManager || !info.fecPayloadId || sourceSymbolAccepted) {\n this.#noteSourcePacketSeq(\n info.packetId,\n info.sequenceNumber,\n catalogTrackName,\n );\n this.#routeTrackPacket(packet, info.packetId, info.sequenceNumber, {\n recovered: false,\n skipFecPayloadId: false,\n catalogTrackName,\n });\n this.#feedViaParser(packet, info, fecInfo, {\n source,\n catalogTrackName,\n });\n }\n\n // Deferred trackBlock (BLO-3035 layer 4 race fix). Fire AFTER\n // #feedViaParser \u2192 parser.feed \u2192 addFrame, so the K-th source\n // packet's frame is buffered in CmafBlockAssembler.#blocks BEFORE\n // trackBlock synchronously triggers onBlockComplete \u2192 completeBlock\n // \u2192 #flushBlock. Without this ordering, the K-th frame was lost on\n // every block \u2014 only K-1 frames per block were ever emitted to MSE.\n // Only first-arriving symbols count toward completion or playback.\n if (\n this.#fecManager &&\n info.fecPayloadId &&\n sourceSymbolAccepted &&\n sourceSymbolTrackable\n ) {\n const { sbn, esi } = this.#deriveSbnEsi(\n info.fecPayloadId.ssId,\n info.packetId,\n );\n if (this.#classifyPacketId(info.packetId) !== null) {\n if (catalogTrackName === undefined) {\n this.#fecManager.trackBlock(sbn, info.packetId, undefined, esi);\n } else {\n this.#fecManager.trackBlock(\n sbn,\n info.packetId,\n catalogTrackName,\n esi,\n );\n }\n }\n }\n\n // Only now feed source symbols into FEC. The decoder must still receive\n // the K-th source symbol after trackBlock() marks a clean block complete:\n // Worker-backed decoders use that symbol for their own block lifecycle\n // and stats. Suppress any synchronous recovered-block return when the\n // manager already completed the block, because every source frame was\n // parsed and emitted above.\n if (this.#fecManager && sourceFec) {\n const alreadyCompleted = catalogTrackName === undefined\n ? this.#fecManager.isBlockCompleted(sourceFec.sbn, info.packetId)\n : this.#fecManager.isBlockCompleted(\n sourceFec.sbn,\n info.packetId,\n catalogTrackName,\n );\n const recovered = catalogTrackName === undefined\n ? this.#fecManager.feedSourceSymbol(\n info.packetId,\n sourceFec.ssId,\n sourceFec.symbolData,\n undefined,\n )\n : this.#fecManager.feedSourceSymbol(\n info.packetId,\n sourceFec.ssId,\n sourceFec.symbolData,\n undefined,\n catalogTrackName,\n );\n if (recovered) {\n if (!alreadyCompleted && this.#classifyPacketId(info.packetId) !== null) {\n if (catalogTrackName === undefined) {\n this.#fecManager.cancelBlockTimeout(sourceFec.sbn, info.packetId);\n } else {\n this.#fecManager.cancelBlockTimeout(\n sourceFec.sbn,\n info.packetId,\n catalogTrackName,\n );\n }\n }\n // Only route recovered blocks that needed actual repair.\n if (!alreadyCompleted) {\n this.#fecManager.countRecovery(info.packetId);\n if (catalogTrackName === undefined) {\n this.#fecManager.markBlockCompleted(sourceFec.sbn, info.packetId);\n } else {\n this.#fecManager.markBlockCompleted(\n sourceFec.sbn,\n info.packetId,\n catalogTrackName,\n );\n }\n this.#routeRecoveredBlock(\n sourceFec.sbn,\n recovered,\n info.packetId,\n undefined,\n catalogTrackName,\n );\n }\n }\n }\n }\n }\n\n /**\n * Set temp context and delegate to parser.feed() for MFU extraction,\n * reassembly, keyframe detection, and frame emission via callbacks.\n */\n #feedViaParser(\n packet: Uint8Array,\n info: {\n packetId: number;\n sequenceNumber: number;\n fecPayloadId?: { ssId: number } | null;\n },\n fecInfo?: { sbn: number; esi: number; oversized?: boolean },\n parserOptions?: {\n skipFecPayloadId?: boolean;\n recovered?: boolean;\n source?: MmtpSourcePath;\n deliveryProvenance?: MmtpDeliveryProvenance;\n catalogTrackName?: string;\n },\n ): void {\n const diag = this.#diagFor(info.packetId, parserOptions?.catalogTrackName);\n const before = this.#parser.reassembler.getStatus();\n diag.packets++;\n diag.lastPacketLength = packet.byteLength;\n diag.lastSequenceNumber = info.sequenceNumber;\n if (info.fecPayloadId) diag.lastFecSsId = info.fecPayloadId.ssId;\n this.#currentFecInfo = fecInfo;\n this.#currentSeqNum = info.sequenceNumber;\n try {\n this.#parser.feed(packet, {\n skipFecPayloadId: parserOptions?.skipFecPayloadId,\n recovered: parserOptions?.recovered,\n source: parserOptions?.source,\n deliveryProvenance: parserOptions?.deliveryProvenance,\n catalogTrackName: parserOptions?.catalogTrackName,\n });\n } finally {\n this.#currentFecInfo = undefined;\n }\n const after = this.#parser.reassembler.getStatus();\n const fragmentDelta = after.stats.totalFragments - before.stats.totalFragments;\n const reassembledDelta = after.stats.reassembledMFUs - before.stats.reassembledMFUs;\n if (fragmentDelta > 0) diag.fragments += fragmentDelta;\n else diag.noFragment++;\n if (reassembledDelta > 0) diag.reassembled += reassembledDelta;\n diag.pendingBuffers = after.bufferedMFUs;\n diag.pendingFragments = after.pendingFragments;\n diag.pendingSamples = after.pendingSamples;\n }\n\n #feedFecTransportSymbol(\n packetId: number,\n sbn: number,\n esi: number,\n data: Uint8Array,\n source: MmtpSourcePath,\n catalogTrackName?: string,\n ): boolean {\n const fecManager = this.#fecManager;\n if (!fecManager) return false;\n return catalogTrackName === undefined\n ? fecManager.feedSymbolFromSource(packetId, sbn, esi, data, source)\n : fecManager.feedSymbolFromSource(\n packetId,\n sbn,\n esi,\n data,\n source,\n catalogTrackName,\n );\n }\n\n #diagFor(packetId: number, catalogTrackName?: string): ParserDiagnostics {\n const streamKey = this.#mediaStreamKey(packetId, catalogTrackName);\n let diag = this.#parserDiagnostics.get(streamKey);\n if (!diag) {\n diag = {\n packets: 0,\n frames: 0,\n initSegments: 0,\n noFragment: 0,\n fragments: 0,\n reassembled: 0,\n pendingBuffers: 0,\n pendingFragments: 0,\n lastPacketLength: 0,\n lastSequenceNumber: 0,\n directAudioSourceEmitted: 0,\n directAudioSourceRegressions: 0,\n directAudioRecoveredQueued: 0,\n directAudioRecoveredDrained: 0,\n directAudioRecoveredStaleDropped: 0,\n };\n this.#parserDiagnostics.set(streamKey, diag);\n }\n return diag;\n }\n\n #shouldEmitFrame(\n packetId: number,\n keyframe: boolean,\n catalogTrackName?: string,\n ): boolean {\n if (!this.#fecManager) return true;\n if (!this.#videoPacketIds.has(packetId)) return true;\n const streamKey = this.#mediaStreamKey(packetId, catalogTrackName);\n if (this.#trackSynced.has(streamKey)) return true;\n if (!keyframe) {\n this.#droppedPreSyncVideo.add(streamKey);\n return false;\n }\n this.#trackSynced.add(streamKey);\n this.#droppedPreSyncVideo.delete(streamKey);\n return true;\n }\n\n #opensFecSync(\n packetId: number,\n keyframe: boolean,\n catalogTrackName?: string,\n ): boolean {\n const streamKey = this.#mediaStreamKey(packetId, catalogTrackName);\n return !!this.#fecManager &&\n this.#videoPacketIds.has(packetId) &&\n !this.#trackSynced.has(streamKey) &&\n this.#droppedPreSyncVideo.has(streamKey) &&\n keyframe;\n }\n\n #noteEmittedVideoSeq(streamKey: string, packetSequenceNumber: number | undefined): void {\n if (packetSequenceNumber === undefined || !Number.isFinite(packetSequenceNumber)) return;\n const latest = this.#latestVideoSeqByStream.get(streamKey);\n if (latest === undefined || packetSequenceNumber > latest) {\n this.#latestVideoSeqByStream.set(streamKey, packetSequenceNumber);\n }\n }\n\n #noteSourcePacketSeq(\n packetId: number,\n packetSequenceNumber: number | undefined,\n catalogTrackName?: string,\n ): void {\n if (packetSequenceNumber === undefined || !Number.isFinite(packetSequenceNumber)) return;\n this.#sourcePktSeqs.add(\n this.#packetSeqKey(packetId, packetSequenceNumber, catalogTrackName),\n );\n // Eviction: keep the most recent 2000 entries (covers ~62 blocks at K=32/30fps).\n // FEC recovery completes within the catalog-derived holdback window, so older\n // sequence numbers cannot be useful for duplicate suppression.\n if (this.#sourcePktSeqs.size > 4000) {\n const iter = this.#sourcePktSeqs.values();\n for (let i = 0; i < 2000; i++) iter.next();\n const keep = new Set<string>();\n for (const v of iter) keep.add(v);\n this.#sourcePktSeqs = keep;\n }\n }\n\n #routeTrackPacket(\n packet: Uint8Array,\n packetId: number,\n packetSequenceNumber: number,\n meta: Pick<\n RoutedPacketMeta,\n \"recovered\" | \"skipFecPayloadId\" | \"catalogTrackName\" | \"deliveryProvenance\"\n >,\n ): void {\n if (!Number.isFinite(packetSequenceNumber)) return;\n const callback = this.#trackPacketCallbacks.get(packetId);\n if (!callback) return;\n const key = this.#packetSeqKey(packetId, packetSequenceNumber, meta.catalogTrackName);\n if (this.#routedPacketSeqs.has(key)) return;\n this.#routedPacketSeqs.add(key);\n if (this.#routedPacketSeqs.size > 4000) {\n const iter = this.#routedPacketSeqs.values();\n for (let i = 0; i < 2000; i++) iter.next();\n const keep = new Set<string>();\n for (const value of iter) keep.add(value);\n this.#routedPacketSeqs = keep;\n }\n callback(packet, {\n packetId,\n packetSequenceNumber,\n recovered: meta.recovered,\n skipFecPayloadId: meta.skipFecPayloadId,\n ...(meta.catalogTrackName === undefined\n ? {}\n : { catalogTrackName: meta.catalogTrackName }),\n ...(meta.deliveryProvenance === undefined\n ? {}\n : { deliveryProvenance: meta.deliveryProvenance }),\n });\n }\n\n #isStaleRecoveredVideo(\n packetId: number,\n packetSequenceNumber: number,\n catalogTrackName?: string,\n ): boolean {\n const latest = this.#latestVideoSeqByStream.get(\n this.#mediaStreamKey(packetId, catalogTrackName),\n );\n return latest !== undefined && packetSequenceNumber <= latest;\n }\n\n #mediaStreamKey(packetId: number, catalogTrackName?: string): string {\n return JSON.stringify([packetId, catalogTrackName ?? null]);\n }\n\n #packetIdFromStreamKey(key: string): number {\n try {\n const decoded = JSON.parse(key);\n if (Array.isArray(decoded) && typeof decoded[0] === \"number\") return decoded[0];\n } catch {\n // Internal keys are JSON tuples; malformed legacy keys should not happen.\n }\n return 0;\n }\n\n #mseAssemblerKey(packetId: number, catalogTrackName?: string): string {\n return this.#mediaStreamKey(packetId, catalogTrackName);\n }\n\n #mseAssemblerFor(\n packetId: number,\n catalogTrackName?: string,\n ): CmafBlockAssembler | undefined {\n const exact = this.#mseAssemblers.get(\n this.#mseAssemblerKey(packetId, catalogTrackName),\n );\n if (exact || catalogTrackName === undefined) return exact;\n if (!this.#isCatalogTrackUnambiguous(packetId, catalogTrackName)) return undefined;\n return this.#mseAssemblers.get(this.#mseAssemblerKey(packetId));\n }\n\n #isCatalogTrackUnambiguous(\n packetId: number,\n catalogTrackName?: string,\n ): boolean {\n if (catalogTrackName === undefined) return true;\n const names = this.#catalogTrackNamesByPacketId.get(packetId);\n return names?.size === 1 && names.has(catalogTrackName);\n }\n\n #resolveRecoveredTiming(\n packetId: number,\n mpuSequenceNumber: number,\n movieFragmentSequenceNumber: number | undefined,\n sampleNumber: number | undefined,\n mmtpTimestamp: bigint,\n catalogTrackName?: string,\n ): { authoredTiming?: AuthoredTiming; timestamp: number } | null {\n // Non-timed MFUs do not carry a movie-fragment/sample identity. They are\n // outside the ISO-BMFF presentation timeline, so retain the raw MMTP\n // timestamp path used by the untimed router contract.\n if (movieFragmentSequenceNumber === undefined || sampleNumber === undefined) {\n return { timestamp: mmtpTimestampToMicros(Number(mmtpTimestamp)) };\n }\n\n // Claim the resolver entry before scaling it. The claim is the single\n // consume operation for a recovered sample; doing lookup \u2192 scale \u2192 claim\n // would leave an invalid entry live forever when scaling rejects it.\n const authoredTiming = this.#parser.consumeAuthoredTimingFor(\n packetId,\n mpuSequenceNumber,\n movieFragmentSequenceNumber,\n sampleNumber,\n catalogTrackName,\n );\n // Recovery is accepted only when the timed stream's FT=1 metadata names\n // this exact sample. MMTP send timestamps describe transport arrival, not\n // the authored presentation timeline, so they are never a fallback.\n if (authoredTiming === null) return null;\n const scaled = scaleAuthoredTiming(authoredTiming, 1_000_000);\n if (scaled === null) return null;\n return { authoredTiming, timestamp: scaled.timestamp };\n }\n\n #packetSeqKey(\n packetId: number,\n packetSequenceNumber: number,\n catalogTrackName?: string,\n ): string {\n return JSON.stringify([packetId, catalogTrackName ?? null, packetSequenceNumber]);\n }\n\n #pendingRecoveredKey(meta: RoutedFrameMeta): string {\n return `${meta.timestamp}:${meta.packetSequenceNumber ?? \"\"}`;\n }\n\n #pendingRecoveredQueue(streamKey: string): Map<string, PendingRecoveredVideoFrame> {\n let queue = this.#pendingDirectRecoveredVideoByStream.get(streamKey);\n if (!queue) {\n queue = new Map();\n this.#pendingDirectRecoveredVideoByStream.set(streamKey, queue);\n }\n return queue;\n }\n\n #comparePendingRecoveredVideo(\n a: PendingRecoveredVideoFrame,\n b: PendingRecoveredVideoFrame,\n ): number {\n if (a.meta.timestamp !== b.meta.timestamp) return a.meta.timestamp - b.meta.timestamp;\n const aSeq = a.meta.packetSequenceNumber ?? Number.MAX_SAFE_INTEGER;\n const bSeq = b.meta.packetSequenceNumber ?? Number.MAX_SAFE_INTEGER;\n return aSeq - bSeq;\n }\n\n #emitDirectVideo(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n cb: FrameCallback | null,\n ): boolean {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n const lastEmitted = this.#lastDirectEmittedVideoTsByStream.get(streamKey);\n if (lastEmitted !== undefined && meta.timestamp <= lastEmitted) return false;\n this.#lastDirectEmittedVideoTsByStream.set(streamKey, meta.timestamp);\n // Record the emitted sequence number HERE \u2014 at the real emit \u2014 not when a\n // recovered frame is queued. #routeDirectRecoveredVideo may enqueue a frame\n // that is ahead of the source cursor; noting its seq before it drains would\n // make #isStaleRecoveredVideo drop an earlier gap frame that arrives later\n // (it would look <= the queued-but-unemitted seq). The high-water mark must\n // reflect what actually reached the decoder callback.\n this.#noteEmittedVideoSeq(streamKey, meta.packetSequenceNumber);\n cb?.(data, meta);\n return true;\n }\n\n #drainDirectRecoveredVideoUpTo(\n streamKey: string,\n packetId: number,\n horizonTs: number,\n cb: FrameCallback | null,\n ): void {\n const queue = this.#pendingDirectRecoveredVideoByStream.get(streamKey);\n if (!queue || queue.size === 0) return;\n\n const ready = [...queue.values()]\n .filter(frame => frame.meta.timestamp <= horizonTs)\n .sort((a, b) => this.#comparePendingRecoveredVideo(a, b));\n\n for (const frame of ready) {\n queue.delete(this.#pendingRecoveredKey(frame.meta));\n this.#emitDirectVideo(packetId, frame.data, frame.meta, cb);\n }\n\n if (queue.size === 0) {\n this.#pendingDirectRecoveredVideoByStream.delete(streamKey);\n }\n }\n\n #enqueueDirectRecoveredVideo(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n ): void {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n const lastEmitted = this.#lastDirectEmittedVideoTsByStream.get(streamKey);\n if (lastEmitted !== undefined && meta.timestamp <= lastEmitted) return;\n\n const queue = this.#pendingRecoveredQueue(streamKey);\n queue.set(this.#pendingRecoveredKey(meta), { data, meta: { ...meta } });\n\n if (queue.size > DIRECT_RECOVERED_VIDEO_QUEUE_LIMIT) {\n const keep = [...queue.entries()]\n .sort((a, b) => this.#comparePendingRecoveredVideo(a[1], b[1]))\n .slice(-Math.floor(DIRECT_RECOVERED_VIDEO_QUEUE_LIMIT / 2));\n queue.clear();\n for (const [key, frame] of keep) queue.set(key, frame);\n }\n }\n\n #routeDirectSourceVideo(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n cb: FrameCallback | null,\n ): void {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n const lastSourceTs = this.#lastDirectSourceVideoTsByStream.get(streamKey);\n if (lastSourceTs === undefined || meta.timestamp > lastSourceTs) {\n this.#lastDirectSourceVideoTsByStream.set(streamKey, meta.timestamp);\n }\n\n // Recovered FEC may arrive before the source stream reaches the same media\n // time. Drain those gap fills immediately before the source frame that proves\n // the live cursor caught up, preserving the decoder dependency chain.\n this.#drainDirectRecoveredVideoUpTo(streamKey, packetId, meta.timestamp, cb);\n this.#emitDirectVideo(packetId, data, meta, cb);\n }\n\n #routeDirectRecoveredVideo(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n cb: FrameCallback | null,\n ): void {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n this.#enqueueDirectRecoveredVideo(packetId, data, meta);\n const lastSourceTs = this.#lastDirectSourceVideoTsByStream.get(streamKey);\n this.#drainDirectRecoveredVideoUpTo(\n streamKey,\n packetId,\n lastSourceTs ?? Number.POSITIVE_INFINITY,\n cb,\n );\n }\n\n #pendingRecoveredAudioQueue(streamKey: string): Map<string, PendingRecoveredAudioFrame> {\n let queue = this.#pendingDirectRecoveredAudioByStream.get(streamKey);\n if (!queue) {\n queue = new Map();\n this.#pendingDirectRecoveredAudioByStream.set(streamKey, queue);\n }\n return queue;\n }\n\n #directAudioCursor(meta: RoutedFrameMeta): DirectAudioCursor {\n return {\n timestamp: meta.timestamp,\n // A recovered frame without transport sequence metadata must drain before\n // an equal-timestamp source horizon instead of remaining queued forever.\n sequenceNumber: meta.packetSequenceNumber ?? Number.MIN_SAFE_INTEGER,\n };\n }\n\n #compareDirectAudioCursor(a: DirectAudioCursor, b: DirectAudioCursor): number {\n if (a.timestamp !== b.timestamp) return a.timestamp - b.timestamp;\n return a.sequenceNumber - b.sequenceNumber;\n }\n\n #emitDirectAudio(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n cb: FrameCallback | null,\n ): boolean {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n const cursor = this.#directAudioCursor(meta);\n const lastEmitted = this.#lastDirectEmittedAudioCursorByStream.get(streamKey);\n if (\n lastEmitted !== undefined &&\n this.#compareDirectAudioCursor(cursor, lastEmitted) <= 0\n ) return false;\n this.#lastDirectEmittedAudioCursorByStream.set(streamKey, cursor);\n cb?.(data, meta);\n return true;\n }\n\n #emitDirectSourceAudio(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n cb: FrameCallback | null,\n ): void {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n const cursor = this.#directAudioCursor(meta);\n const lastEmitted = this.#lastDirectEmittedAudioCursorByStream.get(streamKey);\n const diag = this.#diagFor(packetId, meta.catalogTrackName);\n diag.directAudioSourceEmitted++;\n if (\n lastEmitted !== undefined &&\n this.#compareDirectAudioCursor(cursor, lastEmitted) <= 0\n ) {\n // Source packets already passed transport/parser validation. Preserve\n // them even when unordered multicast completion moves the callback\n // cursor backward; only recovered frames are optional stale gap fills.\n diag.directAudioSourceRegressions++;\n } else {\n this.#lastDirectEmittedAudioCursorByStream.set(streamKey, cursor);\n }\n cb?.(data, meta);\n }\n\n #drainDirectRecoveredAudioUpTo(\n streamKey: string,\n packetId: number,\n horizon: DirectAudioCursor,\n cb: FrameCallback | null,\n ): void {\n const queue = this.#pendingDirectRecoveredAudioByStream.get(streamKey);\n if (!queue || queue.size === 0) return;\n\n const ready = [...queue.values()]\n .filter(frame => this.#compareDirectAudioCursor(\n this.#directAudioCursor(frame.meta),\n horizon,\n ) <= 0)\n .sort((a, b) => this.#compareDirectAudioCursor(\n this.#directAudioCursor(a.meta),\n this.#directAudioCursor(b.meta),\n ));\n\n for (const frame of ready) {\n queue.delete(this.#pendingRecoveredKey(frame.meta));\n if (this.#emitDirectAudio(packetId, frame.data, frame.meta, cb)) {\n this.#diagFor(packetId, frame.meta.catalogTrackName)\n .directAudioRecoveredDrained++;\n } else {\n this.#diagFor(packetId, frame.meta.catalogTrackName)\n .directAudioRecoveredStaleDropped++;\n }\n }\n\n if (queue.size === 0) {\n this.#pendingDirectRecoveredAudioByStream.delete(streamKey);\n }\n }\n\n #routeDirectSourceAudio(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n cb: FrameCallback | null,\n ): void {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n const cursor = this.#directAudioCursor(meta);\n const lastSource = this.#lastDirectSourceAudioCursorByStream.get(streamKey);\n if (\n lastSource === undefined ||\n this.#compareDirectAudioCursor(cursor, lastSource) > 0\n ) {\n this.#lastDirectSourceAudioCursorByStream.set(streamKey, cursor);\n }\n this.#drainDirectRecoveredAudioUpTo(streamKey, packetId, cursor, cb);\n this.#emitDirectSourceAudio(packetId, data, meta, cb);\n }\n\n #routeDirectRecoveredAudio(\n packetId: number,\n data: Uint8Array,\n meta: RoutedFrameMeta,\n cb: FrameCallback | null,\n ): void {\n const streamKey = this.#mediaStreamKey(packetId, meta.catalogTrackName);\n const cursor = this.#directAudioCursor(meta);\n const lastEmitted = this.#lastDirectEmittedAudioCursorByStream.get(streamKey);\n if (\n lastEmitted !== undefined &&\n this.#compareDirectAudioCursor(cursor, lastEmitted) <= 0\n ) {\n this.#diagFor(packetId, meta.catalogTrackName)\n .directAudioRecoveredStaleDropped++;\n return;\n }\n\n const queue = this.#pendingRecoveredAudioQueue(streamKey);\n queue.set(this.#pendingRecoveredKey(meta), { data, meta: { ...meta } });\n this.#diagFor(packetId, meta.catalogTrackName).directAudioRecoveredQueued++;\n if (queue.size > DIRECT_RECOVERED_AUDIO_QUEUE_LIMIT) {\n const keep = [...queue.entries()]\n .sort((a, b) => this.#compareDirectAudioCursor(\n this.#directAudioCursor(a[1].meta),\n this.#directAudioCursor(b[1].meta),\n ))\n .slice(-Math.floor(DIRECT_RECOVERED_AUDIO_QUEUE_LIMIT / 2));\n queue.clear();\n for (const [key, frame] of keep) queue.set(key, frame);\n }\n\n const lastSource = this.#lastDirectSourceAudioCursorByStream.get(streamKey);\n if (lastSource !== undefined) {\n this.#drainDirectRecoveredAudioUpTo(streamKey, packetId, lastSource, cb);\n }\n }\n\n // \u2500\u2500 Private: FEC recovery routing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Route WASM-recovered block through appropriate path based on container kind. */\n #routeRecoveredBlock(\n blockId: number,\n recovered: Uint8Array,\n ownerPacketId?: number,\n blockK?: number,\n catalogTrackName?: string,\n ): void {\n // MMTP path: use processFecBlock for MMTP-wrapped recovery.\n // Recovered symbols are individual MMTP packets that may be fragments\n // (FI=1/2/3) of a larger MFU. Route through the reassembler so\n // multi-packet MFUs (e.g., keyframes spanning 15+ packets) are\n // properly assembled before reaching the decoder.\n // In object mode, blockK comes from repair RSB_length (per-block variable K).\n const k = blockK ?? this.#kForPacket(ownerPacketId);\n const packetSymbolSize = this.#symbolSizeForPacket(ownerPacketId);\n const symbolSize = packetSymbolSize > 0 ? packetSymbolSize : undefined;\n const result = processFecBlock(recovered, {\n k,\n symbolSize,\n ownerPacketId,\n });\n this.#routeVerifiedFecSymbols(blockId, result, ownerPacketId, catalogTrackName);\n }\n\n #routeVerifiedFecSymbols(\n blockId: number,\n result: ProcessFecBlockResult,\n ownerPacketId?: number,\n catalogTrackName?: string,\n ): void {\n if (!this.#manifestVerifier || ownerPacketId == null) {\n this.#routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName);\n return;\n }\n if (!this.#resolveManifestIdentity && !this.#resolveManifestIdentityForRoute) {\n console.warn(`[MmtpRouter] manifest identity resolver unavailable; recovered media is fail-open for packetId=${ownerPacketId} block=${blockId}`);\n this.#routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName);\n return;\n }\n const queueKey = this.#mediaStreamKey(ownerPacketId, catalogTrackName);\n const queue = this.#manifestRecoveryQueues.get(queueKey) ?? [];\n queue.push({ blockId, result, ownerPacketId, catalogTrackName });\n this.#manifestRecoveryQueues.set(queueKey, queue);\n if (queue.length > 1) return;\n const drain = (): void => {\n const next = queue[0];\n if (!next) {\n this.#manifestRecoveryQueues.delete(queueKey);\n return;\n }\n this.#admitRecoveredResult(\n next.blockId,\n next.result,\n next.ownerPacketId,\n next.catalogTrackName,\n () => {\n queue.shift();\n drain();\n },\n );\n };\n drain();\n }\n\n #admitRecoveredResult(\n blockId: number,\n result: ProcessFecBlockResult,\n ownerPacketId: number,\n catalogTrackName: string | undefined,\n done: () => void,\n ): void {\n const verifier = this.#manifestVerifier;\n const resolveIdentity = this.#resolveManifestIdentity;\n const resolveIdentityForRoute = this.#resolveManifestIdentityForRoute;\n if (!verifier) {\n this.#routeProcessedFecSymbols(blockId, result, ownerPacketId, catalogTrackName);\n done();\n return;\n }\n if (!resolveIdentity && !resolveIdentityForRoute) throw new Error(\"manifest identity resolver disappeared during admission\");\n const symbols = result.symbols;\n const admitNext = (index: number): void => {\n const symbol = symbols[index];\n if (!symbol) {\n done();\n return;\n }\n const identity = resolveIdentityForRoute\n ? resolveIdentityForRoute({\n packetId: ownerPacketId,\n sbn: blockId,\n esi: symbol.index,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n recovered: true,\n sourceSymbolFormat: \"fec-padded-recovery\",\n })\n : resolveIdentity!(ownerPacketId, blockId, symbol.index, catalogTrackName);\n if (!identity) {\n console.warn(`[MmtpRouter] manifest identity unresolved; recovered symbol is fail-open for packetId=${ownerPacketId} block=${blockId} esi=${symbol.index}`);\n this.#routeProcessedFecSymbols(\n blockId,\n { ...result, symbols: [symbol] },\n ownerPacketId,\n catalogTrackName,\n );\n admitNext(index + 1);\n return;\n }\n const admission = verifier.admitObject(\n {\n ...identity,\n packetId: ownerPacketId,\n ssId: blockId * this.#kForPacket(ownerPacketId) + symbol.index,\n },\n symbol.rawSymbol,\n () => {\n if (!this.#disposed) {\n this.#routeProcessedFecSymbols(\n blockId,\n { ...result, symbols: [symbol] },\n ownerPacketId,\n catalogTrackName,\n );\n }\n admitNext(index + 1);\n },\n );\n if (admission.action === \"drop\") admitNext(index + 1);\n };\n admitNext(0);\n }\n\n #routeProcessedFecSymbols(\n blockId: number,\n result: ProcessFecBlockResult,\n ownerPacketId?: number,\n catalogTrackName?: string,\n ): void {\n // Route recovered symbols to the track's callback. The ownerPacketId tells us\n // which track this block belongs to \u2014 resolves per-track, then legacy fallback.\n if (ownerPacketId == null) return; // no track association\n if (this.#classifyPacketId(ownerPacketId) === null) return; // unknown track\n\n // A recovered symbol is a function of EVERY symbol that entered its block,\n // so its provenance is the block's, not the media path's. Repair symbols\n // routinely arrive over reliable QUIC (`feedRepairPacket`), so a block that\n // recovered with MoQ help produced bytes that never crossed a multicast\n // socket. Without this, recovered frames reached the decoder carrying no\n // attribution at all and were counted as multicast by default.\n // A FEC manager that cannot report block provenance yields 'unknown', not\n // a guess: these bytes are then never counted as physical-delivery\n // evidence. `FECManagerLike` is a pluggable structural interface, so an\n // implementation without this method must degrade, not throw.\n const deliveryProvenance: MmtpDeliveryProvenance =\n this.#fecManager?.blockDeliveryProvenance?.(\n blockId,\n ownerPacketId,\n catalogTrackName,\n ) ?? 'unknown';\n\n for (const sym of result.symbols) {\n this.#routeTrackPacket(sym.rawSymbol, ownerPacketId, sym.packetSequenceNumber, {\n recovered: true,\n skipFecPayloadId: true,\n catalogTrackName,\n deliveryProvenance,\n });\n }\n\n // Feed recovered metadata before routing data. FT=0 initializes the timing\n // resolver and FT=1 publishes the sample table that recovered FT=2 data\n // must resolve against. The parser's callbacks dedupe repeated init\n // delivery, while keeping this path in lockstep with direct source input.\n for (const sym of result.symbols) {\n if (sym.fragmentType !== 0 && sym.fragmentType !== 1) continue;\n this.#feedViaParser(\n sym.rawSymbol,\n { packetId: ownerPacketId, sequenceNumber: sym.packetSequenceNumber },\n undefined,\n {\n skipFecPayloadId: true,\n recovered: true,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n },\n );\n }\n\n // MSE assembler path: route recovered block as pre-assembled frames (skip FT=0)\n // BLO-3035 layer 3: recovered symbols are RAW MFU fragments, not complete\n // frames. A keyframe can span multiple packets (FI=1/2/3); pushing each\n // fragment as a \"frame\" produces garbage moof+mdat that fails decode.\n // Mirror the non-MSE path's logic: FI=0 \u2192 emit single-packet MFU directly\n // (after dedup against source path), FI=1/2/3 \u2192 feed reassembler, emit when\n // the complete frame is available. Take the keyframe bit from the assembled\n // frame's rapFlag (with `#detectKeyframe` as a bitstream fallback for\n // publishers that don't set rapFlag) \u2014 `sym.isKeyframe` is just rapFlag\n // from a single fragment's header (see `block-processor.ts` where\n // `isKeyframe = hdr.rapFlag`) and isn't meaningful for the reassembled-\n // frame keyframe decision when FI\u22600.\n const assembler = this.#mseAssemblerFor(ownerPacketId, catalogTrackName);\n if (assembler) {\n const isVideoTrack = this.#classifyPacketId(ownerPacketId) === true;\n const frames: Array<{\n data: Uint8Array;\n timestamp: number;\n keyframe: boolean;\n authoredTiming?: AuthoredTiming;\n packetSequenceNumber?: number;\n packetSequenceNumbers?: number[];\n repairedPacketSequenceNumbers?: number[];\n mpuSequenceNumber?: number;\n catalogTrackName?: string;\n deliveryProvenance?: MmtpDeliveryProvenance;\n }> = [];\n let dedupedAgainstSource = 0;\n let droppedBeforeSync = 0;\n for (const sym of result.symbols) {\n if (sym.fragmentType === 0) continue; // init already delivered above\n const fi = sym.fragment.fragmentationIndicator;\n if (fi === 0) {\n // Single-packet MFU. Dedup against source path so frames aren't\n // emitted twice. The direct source frame may still be buffered by the\n // parser reorder window, so use source-received sequence numbers.\n if (this.#sourcePktSeqs.has(\n this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName),\n )) {\n dedupedAgainstSource++;\n continue;\n }\n const keyframe = isVideoTrack\n ? this.#detectKeyframe(\n ownerPacketId,\n sym.fragment.data,\n isMfuKeyframeMetadata(sym.fragment.rapFlag, sym.fragment.priority),\n )\n : sym.fragment.rapFlag;\n if (!this.#shouldEmitFrame(ownerPacketId, keyframe, catalogTrackName)) {\n droppedBeforeSync++;\n continue;\n }\n const recoveredTiming = this.#resolveRecoveredTiming(\n ownerPacketId,\n sym.fragment.mpuSequenceNumber,\n sym.fragment.movieFragmentSequenceNumber,\n sym.fragment.sampleNumber,\n sym.fragment.timestamp,\n catalogTrackName,\n );\n if (!recoveredTiming) continue;\n frames.push({\n data: sym.fragment.data,\n timestamp: recoveredTiming.timestamp,\n keyframe,\n authoredTiming: recoveredTiming.authoredTiming,\n packetSequenceNumber: sym.packetSequenceNumber,\n packetSequenceNumbers: [sym.packetSequenceNumber],\n repairedPacketSequenceNumbers: [sym.packetSequenceNumber],\n mpuSequenceNumber: sym.fragment.mpuSequenceNumber,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n // A single-packet MFU needs the block verdict just as much as a\n // reassembled one. The multi-fragment branch below gets it via the\n // reassembler fold (`assembled.deliveryProvenance`); this branch\n // never touches the reassembler, so it must carry the block-level\n // verdict directly or the sample reaches MSE unattributed.\n deliveryProvenance,\n });\n this.#noteEmittedKeyframe(ownerPacketId, keyframe);\n } else {\n // Multi-packet MFU. Feed to reassembler; emit only when complete.\n // A decoded block contains every source symbol, including fragments\n // already accepted on the direct path. Re-feeding those fragments as\n // recovered would replace their original attribution in the shared\n // reassembler and overstate repairedPacketSequenceNumbers.\n if (this.#sourcePktSeqs.has(\n this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName),\n )) {\n dedupedAgainstSource++;\n continue;\n }\n let assembled;\n try {\n assembled = this.#parser.reassembler.addFragment({\n fragmentationIndicator: fi,\n mpuSequenceNumber: sym.fragment.mpuSequenceNumber,\n fragmentCounter: sym.fragment.fragmentCounter,\n data: sym.fragment.data,\n timestamp: sym.fragment.timestamp,\n rapFlag: sym.fragment.rapFlag,\n sampleNumber: sym.fragment.sampleNumber,\n movieFragmentSequenceNumber: sym.fragment.movieFragmentSequenceNumber,\n sampleOffset: sym.fragment.sampleOffset,\n priority: sym.fragment.priority,\n packetSequenceNumber: sym.packetSequenceNumber,\n recovered: true,\n // Without this the fold sees a fragment with neither `source`\n // nor a verdict, returns 'unknown', and the block-level finding\n // computed above is lost before the frame reaches any consumer.\n deliveryProvenance,\n packetId: ownerPacketId,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n });\n } catch (error) {\n if (!(error instanceof MfuReassemblyError)) throw error;\n console.warn(\n `[MmtpRouter] dropped rejected recovered MFU fragment: block=${blockId} ` +\n `packetId=${ownerPacketId} code=${error.code}`,\n );\n continue;\n }\n if (assembled) {\n const keyframe = isVideoTrack\n ? this.#detectKeyframe(\n ownerPacketId,\n assembled.data,\n isMfuKeyframeMetadata(assembled.rapFlag, assembled.priority),\n )\n : assembled.rapFlag;\n if (!this.#shouldEmitFrame(\n ownerPacketId,\n keyframe,\n assembled.catalogTrackName,\n )) {\n droppedBeforeSync++;\n continue;\n }\n const recoveredCatalogTrackName = assembled.catalogTrackName ?? catalogTrackName;\n const recoveredTiming = this.#resolveRecoveredTiming(\n ownerPacketId,\n assembled.mpuSequenceNumber,\n assembled.movieFragmentSequenceNumber,\n assembled.sampleNumber,\n assembled.timestamp,\n recoveredCatalogTrackName,\n );\n if (!recoveredTiming) continue;\n frames.push({\n data: assembled.data,\n timestamp: recoveredTiming.timestamp,\n keyframe,\n authoredTiming: recoveredTiming.authoredTiming,\n // The singular delivery identity is the fragment that completed\n // and delivered the MFU, matching the callback/parser path.\n packetSequenceNumber: sym.packetSequenceNumber,\n ...(assembled.packetSequenceNumbers === undefined\n ? {}\n : { packetSequenceNumbers: assembled.packetSequenceNumbers }),\n ...(assembled.repairedPacketSequenceNumbers === undefined\n ? {}\n : {\n repairedPacketSequenceNumbers:\n assembled.repairedPacketSequenceNumbers,\n }),\n mpuSequenceNumber: assembled.mpuSequenceNumber,\n ...(assembled.catalogTrackName === undefined\n ? {}\n : { catalogTrackName: assembled.catalogTrackName }),\n deliveryProvenance: assembled.deliveryProvenance,\n });\n this.#noteEmittedKeyframe(ownerPacketId, keyframe);\n }\n }\n }\n if (frames.length > 0) {\n assembler.addRecoveredBlock(blockId, frames);\n debugRouter(\n `[MmtpRouter] Block ${blockId}: ${result.symbols.length} symbols \u2192 ${frames.length} reassembled frames \u2192 MSE assembler (packetId=${ownerPacketId}, dedupedAgainstSource=${dedupedAgainstSource}, droppedBeforeSync=${droppedBeforeSync})`,\n );\n } else if (result.symbols.length > 0) {\n // Recovery yielded symbols but no complete frames emerged \u2014 either all\n // FI=0 were source-path duplicates, or all FI=1/2/3 are still mid-\n // reassembly. Surface this so an operator debugging \"FEC recovered N\n // symbols but no new frames rendered\" has a signal.\n debugRouter(\n `[MmtpRouter] Block ${blockId}: ${result.symbols.length} symbols recovered \u2192 0 emitted (dedupedAgainstSource=${dedupedAgainstSource}, droppedBeforeSync=${droppedBeforeSync}, pending-reassembly=${result.symbols.length - dedupedAgainstSource - droppedBeforeSync}, packetId=${ownerPacketId})`,\n );\n }\n return;\n }\n\n const cb = this.#getFrameCallback(ownerPacketId);\n const isVideo = this.#classifyPacketId(ownerPacketId) === true;\n let droppedBeforeSync = 0;\n let droppedStale = 0;\n let dedupedAgainstSource = 0;\n\n for (const sym of result.symbols) {\n // FT=0 init already delivered above \u2014 skip data routing\n if (sym.fragmentType === 0) continue;\n\n if (isVideo) {\n const fi = sym.fragment.fragmentationIndicator;\n if (fi === 0) {\n // Dedup: skip FI=0 frames already received via direct source path.\n // The reassembler deduplicates FI=1/2/3 fragments via #emitted, but\n // FI=0 (single-packet MFU) bypasses it. Without this check, recovery\n // can re-emit a source frame that is only held in the reorder buffer,\n // producing duplicate timestamps when the source path later flushes.\n if (this.#sourcePktSeqs.has(\n this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName),\n )) {\n dedupedAgainstSource++;\n continue;\n }\n if (this.#isStaleRecoveredVideo(\n ownerPacketId,\n sym.packetSequenceNumber,\n catalogTrackName,\n )) {\n droppedStale++;\n continue;\n }\n\n const d = sym.fragment.data;\n const keyframe = this.#detectKeyframe(\n ownerPacketId,\n d,\n isMfuKeyframeMetadata(sym.fragment.rapFlag, sym.fragment.priority),\n );\n const recoveredTiming = this.#resolveRecoveredTiming(\n ownerPacketId,\n sym.fragment.mpuSequenceNumber,\n sym.fragment.movieFragmentSequenceNumber,\n sym.fragment.sampleNumber,\n sym.fragment.timestamp,\n catalogTrackName,\n );\n if (!recoveredTiming) continue;\n this.#parser.routeFrame(d, {\n timestamp: recoveredTiming.timestamp,\n authoredTiming: recoveredTiming.authoredTiming,\n keyframe,\n packetId: ownerPacketId,\n trackType: \"video\",\n packetSequenceNumber: sym.packetSequenceNumber,\n packetSequenceNumbers: [sym.packetSequenceNumber],\n repairedPacketSequenceNumbers: [sym.packetSequenceNumber],\n mpuSequenceNumber: sym.fragment.mpuSequenceNumber,\n recovered: true,\n deliveryProvenance,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n });\n } else {\n // Fragment of a multi-packet MFU \u2014 must go through reassembler\n if (this.#sourcePktSeqs.has(\n this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName),\n )) {\n dedupedAgainstSource++;\n continue;\n }\n let assembled;\n try {\n assembled = this.#parser.reassembler.addFragment({\n fragmentationIndicator: fi,\n mpuSequenceNumber: sym.fragment.mpuSequenceNumber,\n fragmentCounter: sym.fragment.fragmentCounter,\n data: sym.fragment.data,\n timestamp: sym.fragment.timestamp,\n rapFlag: sym.fragment.rapFlag,\n sampleNumber: sym.fragment.sampleNumber,\n movieFragmentSequenceNumber: sym.fragment.movieFragmentSequenceNumber,\n sampleOffset: sym.fragment.sampleOffset,\n priority: sym.fragment.priority,\n packetSequenceNumber: sym.packetSequenceNumber,\n recovered: true,\n // Without this the fold sees a fragment with neither `source`\n // nor a verdict, returns 'unknown', and the block-level finding\n // computed above is lost before the frame reaches any consumer.\n deliveryProvenance,\n packetId: ownerPacketId,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n });\n } catch (error) {\n if (!(error instanceof MfuReassemblyError)) throw error;\n console.warn(\n `[MmtpRouter] dropped rejected recovered MFU fragment: block=${blockId} ` +\n `packetId=${ownerPacketId} code=${error.code}`,\n );\n continue;\n }\n if (assembled) {\n if (this.#isStaleRecoveredVideo(\n ownerPacketId,\n sym.packetSequenceNumber,\n assembled.catalogTrackName,\n )) {\n droppedStale++;\n continue;\n }\n const keyframe = this.#detectKeyframe(\n ownerPacketId,\n assembled.data,\n isMfuKeyframeMetadata(assembled.rapFlag, assembled.priority),\n );\n const recoveredCatalogTrackName = assembled.catalogTrackName ?? catalogTrackName;\n const recoveredTiming = this.#resolveRecoveredTiming(\n ownerPacketId,\n assembled.mpuSequenceNumber,\n assembled.movieFragmentSequenceNumber,\n assembled.sampleNumber,\n assembled.timestamp,\n recoveredCatalogTrackName,\n );\n if (!recoveredTiming) continue;\n this.#parser.routeFrame(assembled.data, {\n timestamp: recoveredTiming.timestamp,\n authoredTiming: recoveredTiming.authoredTiming,\n keyframe,\n packetId: ownerPacketId,\n trackType: \"video\",\n packetSequenceNumber: sym.packetSequenceNumber,\n ...(assembled.packetSequenceNumbers === undefined\n ? {}\n : { packetSequenceNumbers: assembled.packetSequenceNumbers }),\n ...(assembled.repairedPacketSequenceNumbers === undefined\n ? {}\n : {\n repairedPacketSequenceNumbers:\n assembled.repairedPacketSequenceNumbers,\n }),\n mpuSequenceNumber: assembled.mpuSequenceNumber,\n recovered: true,\n deliveryProvenance,\n ...(assembled.catalogTrackName === undefined\n ? {}\n : { catalogTrackName: assembled.catalogTrackName }),\n });\n }\n }\n } else {\n // Audio recovery: dedup single-packet MFUs already emitted via direct source path\n // (parity with video FI=0 path at line 993). Prevents re-delivering audio frames\n // with duplicate timestamps \u2014 doubled samples in the FIFO ring buffer corrupt the\n // 1kHz test tone (pitch/energy instability in FFT spectrum).\n if (this.#sourcePktSeqs.has(\n this.#packetSeqKey(ownerPacketId, sym.packetSequenceNumber, catalogTrackName),\n )) {\n dedupedAgainstSource++;\n continue;\n }\n const recoveredTiming = this.#resolveRecoveredTiming(\n ownerPacketId,\n sym.fragment.mpuSequenceNumber,\n sym.fragment.movieFragmentSequenceNumber,\n sym.fragment.sampleNumber,\n sym.fragment.timestamp,\n catalogTrackName,\n );\n if (recoveredTiming === null) continue;\n const meta = {\n timestamp: recoveredTiming.timestamp,\n authoredTiming: recoveredTiming.authoredTiming,\n keyframe: true,\n packetId: ownerPacketId,\n packetSequenceNumber: sym.packetSequenceNumber,\n packetSequenceNumbers: [sym.packetSequenceNumber],\n repairedPacketSequenceNumbers: [sym.packetSequenceNumber],\n mpuSequenceNumber: sym.fragment.mpuSequenceNumber,\n recovered: true,\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n };\n this.#routeDirectRecoveredAudio(\n ownerPacketId,\n sym.fragment.data,\n meta,\n cb,\n );\n }\n }\n if (result.symbols.length > 0) {\n debugRouter(\n `[MmtpRouter] Block ${blockId}: ${result.symbols.length} symbols recovered (packetId=${ownerPacketId}, dedupedAgainstSource=${dedupedAgainstSource}, droppedBeforeSync=${droppedBeforeSync}, droppedStale=${droppedStale})`,\n );\n }\n }\n\n // \u2500\u2500 Private: keyframe detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Codec-aware keyframe detection from bitstream data.\n * H.264 IDR (NAL type 5/7), AV1 OBU_SEQUENCE_HEADER (type 1), VP9 frame_type bit. */\n #detectKeyframe(\n packetId: number,\n data: Uint8Array,\n rapFlag = false,\n ): boolean {\n const codec = this.#trackCodecs.get(packetId) ?? \"\";\n return MmtpContainerParser.isKeyframe(data, rapFlag, codec);\n }\n}\n", "import type {\n MFUFragment,\n MfuReassemblerBackend,\n PendingMfuDebug,\n ReassembledMFU,\n ReassemblerStats,\n} from './mfu-reassembler.js'\nimport {\n isMfuReassemblyErrorCode,\n MfuReassemblyError,\n} from './mfu-reassembler.js'\n\nexport interface MmtWasmFragment {\n with_sample_identity(movieFragmentSequence: number, sampleNumber: number, offset: number): void\n set_item_id(itemId: number): void\n set_packet_delivery?(packetSequenceNumber: number, recovered: boolean): void\n set_priority(priority: number): void\n set_attribution(attribution: number): void\n set_stream_identity(streamIdentity: number): void\n}\n\nexport interface MmtWasmReassembledMfu {\n readonly mpu_sequence_number: number\n readonly timestamp: number\n readonly fragment_count: number\n readonly rap_flag: boolean\n readonly priority?: number\n readonly sample_number?: number\n readonly movie_fragment_sequence_number?: number\n readonly packet_sequence_numbers?: Uint32Array | number[]\n readonly repaired_packet_sequence_numbers?: Uint32Array | number[]\n readonly attribution?: number\n readonly stream_identity?: number\n readonly data: Uint8Array\n free(): void\n}\n\nexport interface MmtWasmStats {\n readonly total_fragments: bigint | number\n readonly reassembled_mfus: bigint | number\n readonly timed_out_mfus: bigint | number\n readonly evicted_memory_limit: bigint | number\n readonly out_of_order_fragments: bigint | number\n readonly duplicate_fragments: bigint | number\n free?: () => void\n}\n\nexport interface MmtWasmReassembler {\n add_fragment(now: number, fragment: MmtWasmFragment): MmtWasmReassembledMfu | undefined\n cleanup_timeouts(now: number): void\n set_timeout(timeoutMs: number): void\n set_clock_free(): void\n next_timeout_at(): number | undefined\n flush(): void\n reset_stream(streamIdentity: number): void\n reset(): void\n stats(): MmtWasmStats\n readonly buffered_count: number\n readonly pending_fragment_count: number\n}\n\nexport interface MmtWasmBindings {\n MfuFragmentJs: new (\n mpuSequenceNumber: number,\n fragmentationIndicator: number,\n fragmentCounter: number,\n data: Uint8Array,\n timestamp: number,\n rapFlag: boolean,\n ) => MmtWasmFragment\n MmtReassembler: {\n new (timeoutMs: number, maxBuffered: number): MmtWasmReassembler\n clock_free(maxBuffered: number): MmtWasmReassembler\n }\n}\n\nexport interface WasmMfuReassemblerOptions {\n bindings: MmtWasmBindings\n timeoutMs: number | null\n maxBuffered?: number\n now?: () => number\n}\n\nfunction asNumber(value: bigint | number): number {\n return typeof value === 'bigint' ? Number(value) : value\n}\n\nfunction timestampU32(timestamp: bigint): number {\n return Number(BigInt.asUintN(32, timestamp))\n}\n\n/** Thin host adapter over mmt-core's Rust/WASM IFC reassembler. */\nexport class WasmMfuReassembler implements MfuReassemblerBackend {\n readonly #bindings: MmtWasmBindings\n readonly #core: MmtWasmReassembler\n readonly #now: () => number\n #timeoutMs: number | null\n #cleanupTimer: ReturnType<typeof setTimeout> | null = null\n #nextStreamIdentity = 1\n #streamIdentityByKey = new Map<string, number>()\n #streamNameByIdentity = new Map<number, string>()\n #coreTotalFragments = 0\n #rejectedBeforeCore = 0\n #rejectedFragments = 0\n\n readonly stats: ReassemblerStats = {\n totalFragments: 0,\n reassembledMFUs: 0,\n rejectedFragments: 0,\n timedOutMFUs: 0,\n evictedMemoryLimit: 0,\n outOfOrderFragments: 0,\n }\n\n onDeferredAssembly?: (result: ReassembledMFU) => void\n\n constructor(options: WasmMfuReassemblerOptions) {\n const maxBuffered = options.maxBuffered ?? 100\n if (!Number.isInteger(maxBuffered) || maxBuffered <= 0) {\n throw new Error('[WasmMfuReassembler] maxBuffered must be a positive integer')\n }\n this.#bindings = options.bindings\n this.#timeoutMs = options.timeoutMs\n this.#now = options.now ?? (() => globalThis.performance?.now() ?? Date.now())\n this.#core =\n options.timeoutMs === null\n ? options.bindings.MmtReassembler.clock_free(maxBuffered)\n : new options.bindings.MmtReassembler(options.timeoutMs, maxBuffered)\n }\n\n get timeoutMs(): number | null {\n return this.#timeoutMs\n }\n\n set timeoutMs(timeoutMs: number | null) {\n if (timeoutMs !== null && (!Number.isFinite(timeoutMs) || timeoutMs <= 0)) {\n throw new Error('[WasmMfuReassembler] timeoutMs must be a finite number > 0')\n }\n this.#timeoutMs = timeoutMs\n if (timeoutMs === null) this.#core.set_clock_free()\n else this.#core.set_timeout(Math.ceil(timeoutMs))\n this.#scheduleCleanup()\n }\n\n addFragment(fragment: MFUFragment): ReassembledMFU | null {\n const wasmFragment = new this.#bindings.MfuFragmentJs(\n fragment.mpuSequenceNumber,\n fragment.fragmentationIndicator,\n fragment.fragmentCounter,\n fragment.data,\n timestampU32(fragment.timestamp),\n fragment.rapFlag,\n )\n if (\n fragment.movieFragmentSequenceNumber !== undefined &&\n fragment.sampleNumber !== undefined &&\n fragment.sampleNumber > 0 &&\n fragment.sampleOffset !== undefined\n ) {\n wasmFragment.with_sample_identity(\n fragment.movieFragmentSequenceNumber,\n fragment.sampleNumber,\n fragment.sampleOffset,\n )\n }\n if (fragment.itemId !== undefined) wasmFragment.set_item_id(fragment.itemId)\n if (fragment.packetSequenceNumber !== undefined) {\n wasmFragment.set_packet_delivery?.(\n fragment.packetSequenceNumber,\n fragment.recovered === true,\n )\n }\n if (fragment.priority !== undefined) wasmFragment.set_priority(fragment.priority)\n if (fragment.source !== undefined) {\n wasmFragment.set_attribution(fragment.source === 'multicast' ? 1 : 2)\n }\n const streamIdentity = this.#streamIdentityFor(fragment)\n if (streamIdentity !== undefined) {\n wasmFragment.set_stream_identity(streamIdentity)\n }\n\n let output: MmtWasmReassembledMfu | undefined\n const coreTotalBefore = this.#coreTotalFragments\n try {\n output = this.#core.add_fragment(this.#now(), wasmFragment)\n } catch (error) {\n const rejection = this.#asReassemblyError(error)\n if (rejection === null) throw error\n this.#refreshStats()\n if (this.#coreTotalFragments === coreTotalBefore) this.#rejectedBeforeCore++\n this.#rejectedFragments++\n this.stats.totalFragments = this.#coreTotalFragments + this.#rejectedBeforeCore\n this.stats.rejectedFragments = this.#rejectedFragments\n this.#scheduleCleanup()\n throw rejection\n }\n this.#refreshStats()\n this.#scheduleCleanup()\n if (!output) return null\n\n try {\n const catalogTrackName = output.stream_identity === undefined\n ? undefined\n : this.#streamNameByIdentity.get(output.stream_identity)\n const packetSequenceNumbers = output.packet_sequence_numbers === undefined\n ? undefined\n : Array.from(output.packet_sequence_numbers)\n const repairedPacketSequenceNumbers =\n output.repaired_packet_sequence_numbers === undefined\n ? undefined\n : Array.from(output.repaired_packet_sequence_numbers)\n return {\n mpuSequenceNumber: output.mpu_sequence_number,\n ...(packetSequenceNumbers?.[0] === undefined\n ? {}\n : { packetSequenceNumber: packetSequenceNumbers[0] }),\n ...(packetSequenceNumbers === undefined ? {} : { packetSequenceNumbers }),\n ...(repairedPacketSequenceNumbers === undefined\n ? {}\n : { repairedPacketSequenceNumbers }),\n data: output.data,\n timestamp: BigInt(output.timestamp),\n fragmentCount: output.fragment_count,\n rapFlag: output.rap_flag,\n ...(output.priority === undefined ? {} : { priority: output.priority }),\n ...(output.sample_number === undefined ? {} : { sampleNumber: output.sample_number }),\n ...(output.movie_fragment_sequence_number === undefined\n ? {}\n : {\n movieFragmentSequenceNumber: output.movie_fragment_sequence_number,\n }),\n ...(output.attribution === 1\n ? { source: 'multicast' as const }\n : output.attribution === 2\n ? { source: 'moq' as const }\n : {}),\n // The Rust core folds `attribution` over every contributing fragment\n // and reports None unless they were unanimous, so a tag here is\n // already whole-frame evidence. A missing tag means \"not unanimous or\n // never recorded\" \u2014 both unproven, never multicast.\n deliveryProvenance:\n output.attribution === 1\n ? 'multicast'\n : output.attribution === 2\n ? 'mixed'\n : 'unknown',\n ...(catalogTrackName === undefined ? {} : { catalogTrackName }),\n }\n } finally {\n output.free()\n }\n }\n\n flush(): void {\n this.#clearCleanupTimer()\n this.#core.flush()\n }\n\n resetStream(catalogTrackName: string): void {\n const streamIdentity = this.#streamIdentityByKey.get(`catalog:${catalogTrackName}`)\n if (streamIdentity === undefined) return\n this.#clearCleanupTimer()\n this.#core.reset_stream(streamIdentity)\n this.#refreshStats()\n this.#scheduleCleanup()\n }\n\n reset(): void {\n this.#clearCleanupTimer()\n this.#core.reset()\n this.#nextStreamIdentity = 1\n this.#streamIdentityByKey.clear()\n this.#streamNameByIdentity.clear()\n this.#coreTotalFragments = 0\n this.#rejectedBeforeCore = 0\n this.#rejectedFragments = 0\n this.#refreshStats()\n }\n\n get pendingCount(): number {\n return this.#core.buffered_count\n }\n\n getStatus(): {\n bufferedMFUs: number\n pendingFragments: number\n pendingSamples: PendingMfuDebug[]\n stats: ReassemblerStats\n } {\n this.#refreshStats()\n return {\n bufferedMFUs: this.#core.buffered_count,\n pendingFragments: this.#core.pending_fragment_count,\n // Detailed fragment coordinates remain internal to the Rust core. Counts\n // and health statistics stay available to MmtpRouter diagnostics.\n pendingSamples: [],\n stats: { ...this.stats },\n }\n }\n\n #refreshStats(): void {\n const stats = this.#core.stats()\n this.#coreTotalFragments = asNumber(stats.total_fragments)\n this.stats.totalFragments = this.#coreTotalFragments + this.#rejectedBeforeCore\n this.stats.reassembledMFUs = asNumber(stats.reassembled_mfus)\n this.stats.rejectedFragments = this.#rejectedFragments\n this.stats.timedOutMFUs = asNumber(stats.timed_out_mfus)\n this.stats.evictedMemoryLimit = asNumber(stats.evicted_memory_limit)\n this.stats.outOfOrderFragments = asNumber(stats.out_of_order_fragments)\n stats.free?.()\n }\n\n #streamIdentityFor(fragment: MFUFragment): number | undefined {\n const key = fragment.catalogTrackName !== undefined\n ? `catalog:${fragment.catalogTrackName}`\n : fragment.packetId !== undefined\n ? `packet:${fragment.packetId}`\n : undefined\n if (key === undefined) return undefined\n const existing = this.#streamIdentityByKey.get(key)\n if (existing !== undefined) return existing\n const identity = this.#nextStreamIdentity++\n this.#streamIdentityByKey.set(key, identity)\n if (fragment.catalogTrackName !== undefined) {\n this.#streamNameByIdentity.set(identity, fragment.catalogTrackName)\n }\n return identity\n }\n\n #asReassemblyError(error: unknown): MfuReassemblyError | null {\n if ((typeof error !== 'object' && typeof error !== 'function') || error === null) return null\n const code = Reflect.get(error, 'code')\n if (!isMfuReassemblyErrorCode(code)) return null\n const message = Reflect.get(error, 'message')\n return new MfuReassemblyError(\n code,\n typeof message === 'string' ? message : `MFU reassembly rejected: ${code}`,\n )\n }\n\n #scheduleCleanup(): void {\n this.#clearCleanupTimer()\n if (this.#timeoutMs === null) return\n const next = this.#core.next_timeout_at()\n if (next === undefined) return\n const delay = Math.max(0, Math.ceil(next - this.#now()))\n this.#cleanupTimer = setTimeout(() => {\n this.#cleanupTimer = null\n this.#core.cleanup_timeouts(this.#now())\n this.#refreshStats()\n this.#scheduleCleanup()\n }, delay)\n }\n\n #clearCleanupTimer(): void {\n if (this.#cleanupTimer === null) return\n clearTimeout(this.#cleanupTimer)\n this.#cleanupTimer = null\n }\n}\n\nexport function resolveGlobalMmtWasmBindings(): MmtWasmBindings {\n const globals = globalThis as typeof globalThis & {\n __blockcastMmtReassemblerWasmBindgen?: MmtWasmBindings\n __MmtWasmBindings?: MmtWasmBindings\n }\n const bindings = globals.__MmtWasmBindings ?? globals.__blockcastMmtReassemblerWasmBindgen\n if (!bindings?.MmtReassembler || !bindings.MfuFragmentJs) {\n throw new Error('[WasmMfuReassembler] initialized mmt-wasm bindings are unavailable')\n }\n return bindings\n}\n", "import { MmtpContainerParser, type MmtpParserConfig } from './mmtp-container-parser.js'\nimport { WasmMfuReassembler, resolveGlobalMmtWasmBindings, type MmtWasmBindings } from './wasm-mfu-reassembler.js'\nimport { mfuReassemblyTimeoutMs, type MfuReassemblyPolicy } from './mfu-reassembly-policy.js'\n\nexport type WasmMmtpParserConfig = Omit<MmtpParserConfig, 'mfuTimeoutMs' | 'reassembler' | 'reassemblyPolicy'> & {\n reassemblyPolicy: MfuReassemblyPolicy\n wasmBindings?: MmtWasmBindings\n}\n\n/** Construct the common MMTP parser over the shared Rust/WASM IFC core. */\nexport function createWasmMmtpContainerParser(config: WasmMmtpParserConfig): MmtpContainerParser {\n const timeoutMs = mfuReassemblyTimeoutMs(config.reassemblyPolicy)\n const reassembler = new WasmMfuReassembler({\n bindings: config.wasmBindings ?? resolveGlobalMmtWasmBindings(),\n timeoutMs,\n ...(config.maxBufferedMfus === undefined ? {} : { maxBuffered: config.maxBufferedMfus }),\n })\n return new MmtpContainerParser({\n ...config,\n reassemblyPolicy: config.reassemblyPolicy,\n reassembler,\n })\n}\n", "/**\n * Generic FEC codec interface. Transport-agnostic, algorithm-pluggable.\n * Implementations: NoneCodec (passthrough), RaptorQCodec (wraps WASM),\n * ReedSolomonCodec (placeholder).\n */\nexport interface FecCodec {\n readonly algorithm: 'raptorq' | 'reed-solomon' | 'none'\n addSymbol(blockId: number, symbolId: number, data: Uint8Array, isRepair: boolean): Uint8Array | null\n configure(params: Uint8Array): void\n cleanup(now: number): Uint8Array\n}\n\n/** Passthrough codec for reliable transports (QUIC streams). Returns data immediately. */\nexport class NoneCodec implements FecCodec {\n readonly algorithm = 'none' as const\n\n addSymbol(_blockId: number, _symbolId: number, data: Uint8Array, _isRepair: boolean): Uint8Array | null {\n return data\n }\n\n configure(_params: Uint8Array): void {}\n\n cleanup(_now: number): Uint8Array {\n return new Uint8Array(0)\n }\n}\n\n/**\n * Create a FecCodec from a catalog algorithm name.\n * RaptorQ is not returned here \u2014 it requires a WASM module and is injected\n * via FECManager.setFecDecoder(). This factory covers non-WASM codecs.\n */\nexport function createFecCodec(algorithm: string): FecCodec {\n switch (algorithm) {\n case 'none': return new NoneCodec()\n case 'reed-solomon': return new ReedSolomonCodec()\n case 'raptorq':\n // RaptorQ requires WASM \u2014 caller must use FECManager.setFecDecoder() instead.\n // Return NoneCodec as a safe fallback (passthrough, no recovery).\n console.warn('[FEC] RaptorQ requested via createFecCodec \u2014 use FECManager.setFecDecoder() for WASM RaptorQ. Falling back to NoneCodec.')\n return new NoneCodec()\n default:\n console.warn(`[FEC] Unknown algorithm \"${algorithm}\", falling back to NoneCodec`)\n return new NoneCodec()\n }\n}\n\n/** Placeholder for Reed-Solomon GF(2^8) per RFC 5510. Not yet implemented. */\nexport class ReedSolomonCodec implements FecCodec {\n readonly algorithm = 'reed-solomon' as const\n\n addSymbol(_blockId: number, _symbolId: number, _data: Uint8Array, _isRepair: boolean): Uint8Array | null {\n return null\n }\n\n configure(_params: Uint8Array): void {}\n\n cleanup(_now: number): Uint8Array {\n return new Uint8Array(0)\n }\n}\n", "/**\n * Thin transport wrapper for SSM multicast \u2014 join, socket, watchdog,\n * takeover/failback. Delegates raw packets to a callback (MmtpRouter\n * or any direct consumer).\n */\n\nimport type {\n WindowMulticastGateway,\n WindowMulticastResolvedTransport,\n WindowMulticastSubscription,\n} from '@blockcast/mmt-transport'\nimport { parseMMTPHeader } from '@blockcast/mmt-base'\n\nexport interface MulticastTransportConfig {\n sourceAddress: string\n groupAddress: string\n port: number\n mode?: 'auto' | 'native' | 'tunnel'\n amtRelay?: string\n amtRelayPort?: number\n packetIds?: number[]\n tracks?: { name: string; packetId: number }[]\n}\n\nexport type ResolvedMulticastTransport = WindowMulticastResolvedTransport\n\n/**\n * Receive counters plus the ingress-filter drop counters.\n *\n * `packetsReceived` / `bytesReceived` count only packets that passed the\n * allow-list and were handed to the page \u2014 a rejected packet must never\n * inflate them. The `dropped*` counters are the other half of that story:\n * without them a filtered PID is indistinguishable from a PID that was never\n * transmitted, which is exactly the ambiguity that makes an all-zero FEC\n * repair count unreadable (a repair leg dropped here and a repair leg the\n * publisher never sent produce byte-identical downstream telemetry).\n */\nexport interface MulticastTransportStats {\n packetsReceived: number\n bytesReceived: number\n /** Parsed cleanly, but the packet ID is absent from the join's allow-list. */\n packetsDroppedUnregisteredPid: number\n /** MMTP header did not parse; the packet ID is unknown, so it cannot be attributed. */\n packetsDroppedUnparseable: number\n}\n\n/**\n * Distinct unregistered PIDs retained for diagnostics. A conformant catalog\n * advertises a handful of PIDs per endpoint, so this only has to be large\n * enough to name every leg of a mis-provisioned one; the cap exists so a\n * malformed or hostile stream cycling the 16-bit PID space cannot grow the map\n * without bound. Drops keep counting in aggregate after the cap.\n */\nconst MAX_TRACKED_DROPPED_PIDS = 16\n\ntype MulticastGlobals = typeof globalThis & {\n multicast?: WindowMulticastGateway\n}\n\nfunction getMulticastGlobals(): MulticastGlobals {\n return globalThis as MulticastGlobals\n}\n\nfunction hasMulticastGateway(): boolean {\n return typeof getMulticastGlobals().multicast?.subscribe === 'function'\n}\n\nfunction normalizeResolvedTransport(value: unknown): ResolvedMulticastTransport | undefined {\n if (value === 'native') return 'native'\n if (value === 'amt') return 'amt'\n return undefined\n}\n\nexport class MulticastTransport<TPacketContext = never> {\n #onPacket: (packet: Uint8Array, context?: TPacketContext) => void\n #subscription: WindowMulticastSubscription | null = null\n #bytesListener: ((event: CustomEvent<ArrayBuffer>) => void) | null = null\n #transportListener: ((event: CustomEvent<ResolvedMulticastTransport>) => void) | null = null\n #resolvedTransport: ResolvedMulticastTransport | undefined\n #videoConfirmed = false\n #audioConfirmed = false\n #watchdog: ReturnType<typeof setTimeout> | null = null\n #failbackWindowMs: number | null = null\n #onTakeover: (() => void) | null = null\n #onFailback: (() => void) | null = null\n #disposed = false\n #stats: MulticastTransportStats = {\n packetsReceived: 0,\n bytesReceived: 0,\n packetsDroppedUnregisteredPid: 0,\n packetsDroppedUnparseable: 0,\n }\n #droppedPidCounts = new Map<number, number>()\n #packetIds: ReadonlySet<number> | null = null\n #joinGeneration = 0\n\n constructor(onPacket: (packet: Uint8Array, context?: TPacketContext) => void) {\n this.#onPacket = onPacket\n }\n\n static isAvailable(): boolean {\n return hasMulticastGateway()\n }\n\n async join(config: MulticastTransportConfig, context?: TPacketContext): Promise<boolean> {\n const generation = ++this.#joinGeneration\n this.#releaseSubscription()\n\n if (this.#disposed) return false\n if (!MulticastTransport.isAvailable()) {\n console.warn('[MulticastTransport] Multicast API not available')\n return false\n }\n\n try {\n const packetIds = config.packetIds?.length ? new Set(config.packetIds) : null\n console.log(\n `[MulticastTransport] window.multicast.subscribe(${config.sourceAddress}, ${config.groupAddress}, ${config.port})`,\n )\n const subscription = await getMulticastGlobals().multicast!.subscribe!({\n source: config.sourceAddress,\n group: config.groupAddress,\n port: config.port,\n consume: 'bytes',\n mode: config.mode,\n amtRelay: config.amtRelay,\n amtRelayPort: config.amtRelayPort,\n packetIds: config.packetIds,\n tracks: config.tracks?.map(({ name, packetId }) => ({ name, packetId })),\n })\n console.log(`[MulticastTransport] window.multicast.subscribe returned:`, !!subscription)\n\n if (this.#disposed || generation !== this.#joinGeneration) {\n void subscription.unsubscribe()\n return false\n }\n if (typeof subscription.addEventListener !== 'function') {\n if (typeof subscription.unsubscribe === 'function') {\n void subscription.unsubscribe()\n }\n throw new Error('window.multicast subscription does not support bytes events')\n }\n\n const onBytes = (event: CustomEvent<ArrayBuffer>) => {\n if (this.#disposed || generation !== this.#joinGeneration) return\n const detail = event.detail\n const packet = detail instanceof Uint8Array ? detail : new Uint8Array(detail)\n if (!this.#acceptPacket(packet)) return\n this.#stats.packetsReceived++\n this.#stats.bytesReceived += packet.byteLength\n this.#onPacket(packet, context)\n this.#resetWatchdog()\n }\n const onTransportChange = (event: CustomEvent<ResolvedMulticastTransport>) => {\n if (this.#disposed || generation !== this.#joinGeneration) return\n const transport = normalizeResolvedTransport(event.detail)\n if (transport) this.#resolvedTransport = transport\n }\n this.#subscription = subscription\n this.#bytesListener = onBytes\n this.#transportListener = onTransportChange\n this.#packetIds = packetIds\n this.#resolvedTransport = normalizeResolvedTransport(subscription.transport)\n subscription.addEventListener('bytes', onBytes)\n subscription.addEventListener('transportchange', onTransportChange)\n this.#resetWatchdog()\n console.log(\n `[MulticastTransport] Joined ${config.groupAddress}:${config.port}`,\n )\n return true\n } catch (err) {\n if (this.#disposed || generation !== this.#joinGeneration) return false\n this.#releaseSubscription()\n console.error('[MulticastTransport] Failed to join SSM group:', err)\n return false\n }\n }\n\n /** Feed a raw packet from an external source (e.g., AMT tunnel or ASM subscription). */\n feedPacket(data: ArrayBuffer | Uint8Array, context?: TPacketContext): void {\n if (this.#disposed) return\n const packet = data instanceof Uint8Array ? data : new Uint8Array(data)\n if (!this.#acceptPacket(packet)) return\n this.#stats.packetsReceived++\n this.#stats.bytesReceived += packet.byteLength\n this.#onPacket(packet, context)\n this.#resetWatchdog()\n }\n\n confirmVideo(): void {\n this.#videoConfirmed = true\n this.#checkTakeover()\n }\n\n confirmAudio(): void {\n this.#audioConfirmed = true\n this.#checkTakeover()\n }\n\n onTakeover(cb: () => void): void {\n this.#onTakeover = cb\n }\n\n onFailback(cb: () => void): void {\n this.#onFailback = cb\n }\n\n setFailbackWindowMs(windowMs: number): void {\n if (!Number.isFinite(windowMs) || windowMs <= 0) {\n throw new Error(\n `[MulticastTransport] failbackWindowMs=${windowMs}. ` +\n `Derive a positive failback window from the stream catalog.`,\n )\n }\n this.#failbackWindowMs = windowMs\n }\n\n get stats(): MulticastTransportStats {\n return this.#stats\n }\n\n /**\n * Per-PID drop breakdown, newest-capped at MAX_TRACKED_DROPPED_PIDS distinct\n * PIDs. This is what names the offender: `packetsDroppedUnregisteredPid`\n * says the ingress filter fired, this says it fired on 0x8001.\n */\n get droppedPacketIds(): ReadonlyMap<number, number> {\n return this.#droppedPidCounts\n }\n\n get resolvedTransport(): ResolvedMulticastTransport | undefined {\n return this.#resolvedTransport\n }\n\n dispose(): void {\n ++this.#joinGeneration\n this.#disposed = true\n this.#releaseSubscription()\n }\n\n #releaseSubscription(): void {\n if (this.#watchdog) clearTimeout(this.#watchdog)\n this.#watchdog = null\n if (this.#subscription) {\n if (this.#bytesListener) {\n this.#subscription.removeEventListener('bytes', this.#bytesListener)\n }\n if (this.#transportListener) {\n this.#subscription.removeEventListener('transportchange', this.#transportListener)\n }\n void this.#subscription.unsubscribe()\n }\n this.#subscription = null\n this.#bytesListener = null\n this.#transportListener = null\n this.#resolvedTransport = undefined\n this.#packetIds = null\n }\n\n #acceptPacket(packet: Uint8Array): boolean {\n if (!this.#packetIds) return true\n let packetId: number\n try {\n // This early parse is intentional: rejected PIDs must not affect page\n // delivery, receive statistics, or the failback liveness watchdog.\n packetId = parseMMTPHeader(packet).packetId\n } catch {\n this.#stats.packetsDroppedUnparseable++\n return false\n }\n if (this.#packetIds.has(packetId)) return true\n this.#recordUnregisteredPidDrop(packetId)\n return false\n }\n\n #recordUnregisteredPidDrop(packetId: number): void {\n this.#stats.packetsDroppedUnregisteredPid++\n const seen = this.#droppedPidCounts.get(packetId)\n if (seen !== undefined) {\n this.#droppedPidCounts.set(packetId, seen + 1)\n return\n }\n if (this.#droppedPidCounts.size >= MAX_TRACKED_DROPPED_PIDS) return\n this.#droppedPidCounts.set(packetId, 1)\n // Warn once per distinct PID. A PID arriving on the wire but missing from\n // the catalog's advertised set is a provisioning fault, not stream noise,\n // and it is silent in every downstream counter.\n console.warn(\n `[MulticastTransport] Dropping packetId=${packetId} (0x${packetId.toString(16)}): ` +\n `not in the advertised set [${[...this.#packetIds!].join(', ')}]. ` +\n `If this is an FEC repair leg, the catalog is not advertising its packetId.`,\n )\n }\n\n #checkTakeover(): void {\n if (this.#videoConfirmed && this.#audioConfirmed && this.#onTakeover) {\n const cb = this.#onTakeover\n this.#onTakeover = null // fire exactly once\n cb()\n }\n }\n\n #resetWatchdog(): void {\n if (this.#watchdog) clearTimeout(this.#watchdog)\n if (!this.#onFailback) return\n if (this.#failbackWindowMs === null) {\n throw new Error(\n '[MulticastTransport] Failback watchdog requires a catalog-derived window',\n )\n }\n this.#watchdog = setTimeout(() => {\n if (!this.#disposed && this.#onFailback) {\n console.warn(\n `[MulticastTransport] No packets for ${this.#failbackWindowMs}ms; failing back to MoQ`,\n )\n this.#onFailback()\n }\n }, this.#failbackWindowMs)\n }\n}\n", "/**\n * Facade composing FECManager + MmtpRouter + MulticastTransport.\n *\n * Public methods delegate to the decomposed components. Applications that\n * need individual layers can use them directly:\n * - FECManager \u2014 codec-agnostic FEC decode\n * - MmtpRouter \u2014 MMTP packet parsing + frame routing\n * - MulticastTransport \u2014 SSM join/socket/watchdog\n */\n\nimport {\n FECManager,\n MIN_INTERLEAVE_DEPTH_MS,\n type FECManagerConfig,\n type WasmFecDecoder,\n} from \"./fec-manager.js\";\nimport {\n MmtpRouter,\n type FrameCallback,\n type ManifestIdentityResolver,\n type MmtpIngressContext,\n type TrackPacketCallback,\n} from \"./mmtp-router.js\";\nimport { MulticastTransport } from \"./multicast-transport.js\";\nimport { computeFecDeliveryWindowMs } from \"./track-session.js\";\nimport { deriveFecInterleaveDepth, FecBlockMapper } from \"@blockcast/mmt-fec\";\nimport type { FecWorkerClient, FecTrackStats } from \"@blockcast/fec-worker\";\nimport type { MfuReassemblerBackend } from \"./mfu-reassembler.js\";\nimport type { MfuReassemblyPolicy } from \"./mfu-reassembly-policy.js\";\nimport type {\n ManifestObjectIdentity,\n ManifestVerifier,\n} from \"@blockcast/mmt-manifest-verify\";\nimport type {\n BlockAssemblerConfig,\n CmafBlockSample,\n} from \"./cmaf-block-assembler.js\";\nimport {\n hasSwitchingSetRole,\n parseTrackRole,\n validateSwitchingSet,\n isFecCatalogConfig,\n type FecCatalogConfig,\n type TrackRole,\n} from \"./carp-catalog.js\";\n\nexport interface MulticastEndpointConfig {\n sourceAddress: string;\n groupAddress: string;\n port: number;\n /** Explicit gateway transport policy. Omitted means gateway-selected auto mode. */\n mode?: 'auto' | 'native' | 'tunnel';\n /** AMT relay hostname/IP from catalog endpoint. */\n relayAddress?: string;\n /** AMT relay port from catalog endpoint. */\n relayPort?: number;\n /** AMT relay hostname/IP from player/UI overrides. */\n amtRelay?: string;\n amtRelayPort?: number;\n /** MMTP packet IDs to forward before data enters the page transport queue. */\n packetIds?: number[];\n /** Exact catalog tracks carried by this physical multicast endpoint. */\n tracks?: MmtpIngressContext[\"tracks\"];\n fecK?: number;\n /** Number of repair symbols per FEC block (P). Used to size recovery reorder latency. */\n fecRepairSymbols?: number;\n /** Symbol size in bytes (T). NOT repair count. */\n fecSymbolSize?: number;\n /** Catalog fec.reorderToleranceMs \u2014 signaled jitter/reorder allowance, ADDED\n * to the derived delivery window. Never a multiplier on the span, so one\n * network yields the same slack on a wide video span and a narrow audio one.\n *\n * Absence widens the window by zero rather than throwing, unlike the datagram\n * MFU deadline in hang-mmt-fec. The distinction is which way absence fails:\n * there the tolerance IS the deadline's slack term and guessing one invents\n * a reorder budget, so it must fail loud; here the window is already fully\n * derived from other catalog values and the tolerance only widens it, so\n * adding nothing is the identity and errs toward earlier failback. Do not\n * \"harmonize\" this into a throw, and do not turn the zero into a default. */\n fecReorderToleranceMs?: number;\n fecInterleaveDepthMs?: number;\n /** Aggregate endpoint bandwidth in bits per second from the multicast catalog. */\n bandwidth?: number;\n fps?: number;\n /** Catalog-derived lower bound for packet-silence failback (for example, keyframe repair cadence). */\n minimumFailbackWindowMs?: number;\n}\nexport interface MulticastManagerOptions {\n /**\n * MFU reassembly timeout (ms). Threaded to the inner MmtpRouter (and its\n * MmtpContainerParser). Caller derives from catalog `fec.interleaveDepthMs`,\n * which is already in milliseconds \u2014 `moqenc_mmt.c` converts\n * `depth_ms = fec_depth * (1000/fps)` before populating the catalog.\n * Defaults to 1000ms (with parser warning) when omitted.\n */\n mfuTimeoutMs?: number;\n reassemblyPolicy?: MfuReassemblyPolicy;\n reassembler?: MfuReassemblerBackend;\n maxBufferedMfus?: number;\n manifestVerifier?: ManifestVerifier;\n manifestVerifierOwnership?: \"caller\" | \"router\";\n resolveManifestIdentity?: (\n packetId: number,\n sbn: number,\n esi: number,\n catalogTrackName?: string,\n ) => ManifestObjectIdentity | null;\n resolveManifestIdentityForRoute?: ManifestIdentityResolver;\n}\n\nexport interface RepairBlockIdentity {\n readonly packetId: number;\n /** Non-empty catalog identity, or null when the producer has no track name. */\n readonly catalogTrackName: string | null;\n readonly sbn: number;\n}\n\nexport type RepairLifecycleEvent =\n | {\n readonly version: 1;\n readonly type: \"requested\";\n readonly block: RepairBlockIdentity;\n }\n | {\n readonly version: 1;\n readonly type: \"recovered\" | \"failed\";\n readonly block: RepairBlockIdentity;\n readonly symbols: {\n readonly multicast: number;\n readonly moq: number;\n };\n };\n\nexport type RepairLifecycleObserver = (event: RepairLifecycleEvent) => void;\n\nexport interface RepairLifecycleSource {\n addRepairLifecycleObserver(observer: RepairLifecycleObserver): () => void;\n}\n\n/** Minimal event shape accepted by application-owned event targets. */\nexport interface RepairLifecycleApplicationEvent {\n readonly type: string;\n readonly detail: RepairLifecycleEvent;\n}\n\n/** DOM EventTarget event shape, including the Node 18 Event fallback. */\nexport interface RepairLifecycleDomEvent extends Event {\n readonly detail: RepairLifecycleEvent;\n}\n\n/** Structural target contract shared by DOM and Closure-style event targets. */\nexport interface RepairLifecycleEventTarget<\n TEvent extends RepairLifecycleApplicationEvent,\n> {\n dispatchEvent(event: TEvent): unknown;\n}\n\nexport interface RepairLifecycleEventOptions<\n TEvent extends RepairLifecycleApplicationEvent = RepairLifecycleDomEvent,\n> {\n bubbles?: boolean;\n composed?: boolean;\n createEvent?: RepairLifecycleEventFactory<TEvent>;\n}\n\nexport type RepairLifecycleEventFactory<\n TEvent extends RepairLifecycleApplicationEvent = RepairLifecycleDomEvent,\n> = (\n type: typeof REPAIR_LIFECYCLE_EVENT_TYPE,\n detail: RepairLifecycleEvent,\n options: Readonly<{ bubbles: boolean; composed: boolean }>,\n) => TEvent;\n\n/** Public EventTarget event carrying an immutable RepairLifecycleEvent detail. */\nexport const REPAIR_LIFECYCLE_EVENT_TYPE = \"repairlifecycle\";\n\n/** Diagnostic JSON-lines prefix. Never consume logs as a lifecycle API. */\nexport const REPAIR_LIFECYCLE_LOG_PREFIX =\n \"[MulticastManager] repair-lifecycle \";\n\nfunction createDefaultRepairLifecycleEvent(\n detail: RepairLifecycleEvent,\n options: Readonly<{ bubbles: boolean; composed: boolean }>,\n): RepairLifecycleDomEvent {\n if (typeof globalThis.CustomEvent === \"function\") {\n return new CustomEvent<RepairLifecycleEvent>(REPAIR_LIFECYCLE_EVENT_TYPE, {\n detail,\n ...options,\n });\n }\n\n const event = new Event(REPAIR_LIFECYCLE_EVENT_TYPE, options);\n Object.defineProperty(event, \"detail\", {\n value: detail,\n enumerable: true,\n });\n return event as RepairLifecycleDomEvent;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isRepairBlockIdentity(value: unknown): value is RepairBlockIdentity {\n if (!isRecord(value)) return false;\n const packetId = value[\"packetId\"];\n const catalogTrackName = value[\"catalogTrackName\"];\n const sbn = value[\"sbn\"];\n return (\n Number.isSafeInteger(packetId) &&\n (packetId as number) >= 0 &&\n (catalogTrackName === null ||\n (typeof catalogTrackName === \"string\" && catalogTrackName.length > 0)) &&\n Number.isSafeInteger(sbn) &&\n (sbn as number) >= 0\n );\n}\n\n/** Runtime guard for observer, DOM-event, and cross-realm consumers. */\nexport function isRepairLifecycleEvent(\n value: unknown,\n): value is RepairLifecycleEvent {\n if (!isRecord(value)) return false;\n const version = value[\"version\"];\n const type = value[\"type\"];\n const block = value[\"block\"];\n const symbols = value[\"symbols\"];\n if (\n version !== 1 ||\n (type !== \"requested\" && type !== \"recovered\" && type !== \"failed\") ||\n !isRepairBlockIdentity(block)\n ) {\n return false;\n }\n if (type === \"requested\") return symbols === undefined;\n if (!isRecord(symbols)) return false;\n const multicast = symbols[\"multicast\"];\n const moq = symbols[\"moq\"];\n return (\n Number.isSafeInteger(multicast) &&\n (multicast as number) >= 0 &&\n Number.isSafeInteger(moq) &&\n (moq as number) >= 0\n );\n}\n\n/**\n * Forward typed repair lifecycle observations through an application-owned\n * EventTarget. The adapter retains no history; the returned function detaches\n * the observer.\n */\nexport function forwardRepairLifecycleEvents<\n TEvent extends RepairLifecycleApplicationEvent,\n>(\n source: RepairLifecycleSource,\n target: RepairLifecycleEventTarget<TEvent>,\n options: RepairLifecycleEventOptions<TEvent> & {\n createEvent: RepairLifecycleEventFactory<TEvent>;\n },\n): () => void;\nexport function forwardRepairLifecycleEvents(\n source: RepairLifecycleSource,\n target: EventTarget,\n options?: RepairLifecycleEventOptions,\n): () => void;\nexport function forwardRepairLifecycleEvents(\n source: RepairLifecycleSource,\n target:\n | EventTarget\n | RepairLifecycleEventTarget<RepairLifecycleApplicationEvent>,\n options: RepairLifecycleEventOptions<RepairLifecycleApplicationEvent> = {},\n): () => void {\n return source.addRepairLifecycleObserver((event) => {\n const eventOptions = {\n bubbles: options.bubbles ?? false,\n composed: options.composed ?? false,\n };\n const forwardedEvent = options.createEvent\n ? options.createEvent(REPAIR_LIFECYCLE_EVENT_TYPE, event, eventOptions)\n : createDefaultRepairLifecycleEvent(event, eventOptions);\n (\n target as RepairLifecycleEventTarget<RepairLifecycleApplicationEvent>\n ).dispatchEvent(forwardedEvent);\n });\n}\n\nfunction repairBlockIdentity(\n packetId: number,\n sbn: number,\n catalogTrackName?: string,\n): RepairBlockIdentity {\n if (!Number.isSafeInteger(packetId) || packetId < 0) {\n throw new TypeError(\"packetId must be a non-negative safe integer\");\n }\n if (!Number.isSafeInteger(sbn) || sbn < 0) {\n throw new TypeError(\"sbn must be a non-negative safe integer\");\n }\n if (\n catalogTrackName !== undefined &&\n (typeof catalogTrackName !== \"string\" || catalogTrackName.length === 0)\n ) {\n throw new TypeError(\"catalogTrackName must be a non-empty string when provided\");\n }\n return {\n packetId,\n catalogTrackName: catalogTrackName ?? null,\n sbn,\n };\n}\n\n/**\n * Reject a catalog geometry value that is present but not usable. Absent stays\n * absent \u2014 an omitted field is resolved from the previously configured track or\n * left unset \u2014 but a supplied non-finite/non-positive value is a catalog defect\n * and must fail at configuration time rather than collapse a derived deadline\n * to ~0 packets later.\n */\nfunction assertPositiveGeometry(\n label: string,\n value: number | undefined,\n): void {\n if (value === undefined) return;\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(\n `[MulticastManager] ${label}=${value}. ` +\n `Derive a positive ${label} from the stream catalog.`,\n );\n }\n}\n\n/** Repair-symbol count (P) may legitimately be 0; only reject malformed values. */\nfunction assertNonNegativeGeometry(\n label: string,\n value: number | undefined,\n): void {\n if (value === undefined) return;\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(\n `[MulticastManager] ${label}=${value}. ` +\n `Derive a non-negative ${label} from the stream catalog.`,\n );\n }\n}\n\n/**\n * One contributor to the stream-global FEC timing reduction.\n *\n * FEC geometry is per-track, but a MulticastManager owns exactly one transport\n * and one router, so the failback watchdog and the router's MFU/reorder timing\n * are stream-global. They are reduced with `max` across every contributor:\n * failback must not fire, and the router must not evict, while ANY configured\n * track's FEC block could still complete.\n */\ninterface FecTimingContribution {\n deliveryWindowMs?: number;\n interleaveDepthMs?: number;\n fps?: number;\n k?: number;\n repairSymbols?: number;\n minimumFailbackWindowMs?: number;\n}\n\nfunction fecReorderWindowFrames(\n fecInterleaveDepthMs?: number,\n fps?: number,\n _k?: number,\n _repairSymbols?: number,\n): number {\n if (!fecInterleaveDepthMs || fecInterleaveDepthMs <= 0 || !fps || fps <= 0) return 0;\n const frameMs = 1000 / fps;\n // Reorder across the FEC block span: fecInterleaveDepthMs IS the full block\n // span (\u00A75.1), so this window admits any FEC-recovered frame from the block\n // without holding frames longer than one block.\n return deriveFecInterleaveDepth(fecInterleaveDepthMs, frameMs);\n}\n\n/** Catalog track descriptor for configureMultiTrack(). */\nexport interface MultiTrackCatalogTrack {\n name: string;\n altGroup?: number;\n framerate?: number;\n fec?: unknown;\n /** PacketId for testability \u2014 in production, learned at subscribe time (Plan 03). */\n packetId?: number;\n}\n\n/** Multi-track catalog shape accepted by configureMultiTrack(). */\nexport interface MultiTrackCatalog {\n tracks: MultiTrackCatalogTrack[];\n}\n\n/** Creates one stateful synchronous FEC decoder per physical catalog stream. */\nexport type WasmFecDecoderFactory = (\n track: Readonly<MultiTrackCatalogTrack>,\n) => WasmFecDecoder;\n\nconst DEFAULT_ALT_GROUP = \"__default__\";\n\ntype FecDiagnosticsGateway = {\n reportFecDiagnostics?: (snapshot: unknown) => void;\n};\n\nfunction publishFecDiagnostics(packetId: number, stats: FecTrackStats): boolean {\n const gateway = (globalThis as typeof globalThis & {\n multicast?: FecDiagnosticsGateway;\n }).multicast;\n if (typeof gateway?.reportFecDiagnostics !== \"function\") return false;\n\n // Keep the control-plane payload bounded and JSON-safe. The Worker snapshot\n // already bounds active blocks; the bridge needs only the current block tail\n // and stable counters, not decoder internals or authentication failure text.\n const snapshot = {\n version: 1,\n packetId,\n observedAt: Date.now(),\n stats: {\n config: {\n ...stats.config,\n resolution: stats.config.resolution\n ? { ...stats.config.resolution }\n : undefined,\n },\n sourceSymbols: stats.sourceSymbols,\n repairSymbols: stats.repairSymbols,\n blocksComplete: stats.blocksComplete,\n blocksRecovered: stats.blocksRecovered,\n blocksFailed: stats.blocksFailed,\n greenFillFrames: stats.greenFillFrames,\n recoveryRate: stats.recoveryRate,\n liveSbn: stats.liveSbn,\n blocks: stats.blocks.slice(-8).map((block) => ({ ...block })),\n interleaveWindowBlocks: stats.interleaveWindowBlocks,\n avgRecoveryMs: stats.avgRecoveryMs,\n maxRecoveryMs: stats.maxRecoveryMs,\n },\n };\n\n gateway.reportFecDiagnostics(snapshot);\n return true;\n}\n\nfunction altGroupKey(track: MultiTrackCatalogTrack): string {\n return track.altGroup === undefined ? DEFAULT_ALT_GROUP : String(track.altGroup);\n}\n\nexport class MulticastManager {\n readonly #fec: FECManager;\n readonly #router: MmtpRouter;\n readonly #transport: MulticastTransport<MmtpIngressContext>;\n #reportedFecDiagnosticsFailure = false;\n\n // \u2500\u2500 Stream-global FEC timing reduction \u2500\u2500\n //\n // FEC geometry is per-track; the transport failback watchdog and the router's\n // MFU/reorder timing are stream-global (one transport, one router). Both\n // configuration entry points feed the same reduction so neither can silently\n // clip the other: configureFec() supplies the baseline, configureTrackFec()\n // supplies one entry per track, and #applyStreamGlobalFecTiming() takes the\n // max across all of them.\n #streamFecBaseline?: FecTimingContribution;\n readonly #trackFecTiming = new Map<number, FecTimingContribution>();\n\n // \u2500\u2500 Phase 14: multi-track state (D-06, D-07, D-17, D-26, D-28) \u2500\u2500\n\n /** Maps altGroup \u2192 track names sharing that altGroup. */\n #switchingSets: Map<number, string[]> = new Map();\n\n /** Repair packetIds that should not get their own FEC decoder (they ARE the repair source). */\n #repairPacketIds: Set<number> = new Set();\n\n /** Tracks physical-stream FEC blocks with an outstanding repair fetch. */\n #repairInFlight: Set<string> = new Set();\n\n /** Causal repair events; observers own any retention they require. */\n #repairLifecycleObservers = new Set<RepairLifecycleObserver>();\n\n #trackResetObservers = new Set<\n (packetId: number, catalogTrackName: string) => void\n >();\n\n /** Callback fired when a repair fetch should be issued for a block. */\n #onRepairSubscribeCb:\n | ((sbn: number, packetId: number, catalogTrackName?: string) => void)\n | null = null;\n\n constructor(options?: MulticastManagerOptions) {\n this.#fec = new FECManager();\n this.#fec.addFecStatsObserver((packetId, stats) => {\n try {\n if (publishFecDiagnostics(packetId, stats)) {\n this.#reportedFecDiagnosticsFailure = false;\n }\n } catch (error) {\n if (!this.#reportedFecDiagnosticsFailure) {\n this.#reportedFecDiagnosticsFailure = true;\n console.warn('[MulticastManager] Failed to publish FEC diagnostics:', error);\n }\n }\n });\n this.#router = new MmtpRouter({\n fecManager: this.#fec,\n mfuTimeoutMs: options?.mfuTimeoutMs,\n reassemblyPolicy: options?.reassemblyPolicy,\n reassembler: options?.reassembler,\n maxBufferedMfus: options?.maxBufferedMfus,\n manifestVerifier: options?.manifestVerifier,\n manifestVerifierOwnership: options?.manifestVerifierOwnership,\n resolveManifestIdentity: options?.resolveManifestIdentity,\n resolveManifestIdentityForRoute: options?.resolveManifestIdentityForRoute,\n onBlockFailed: (sbn, packetId, catalogTrackName) => {\n const pathCount = this.#fec.blockPathCount(\n sbn,\n packetId,\n catalogTrackName,\n );\n this.#emitRepairLifecycle({\n version: 1,\n type: \"failed\",\n block: repairBlockIdentity(packetId, sbn, catalogTrackName),\n symbols: {\n multicast: pathCount?.multicastSymbols ?? 0,\n moq: pathCount?.moqSymbols ?? 0,\n },\n });\n },\n });\n this.#transport = new MulticastTransport<MmtpIngressContext>((packet, ingress) => {\n this.#router.feedPacket(packet, \"multicast\", undefined, ingress);\n });\n\n // Worker-path FEC recovery: route recovered source-symbol bytes through\n // MmtpRouter so MFU reassembly + dedup runs. Without this wiring the\n // FecWorkerClient's recovered frames fire FECManager.onBlockRecovered\n // into a null callback and VideoDecoder never sees a real frame.\n this.#fec.onBlockRecovered = (sbn, packetId, data, meta, recoveredTrackName) => {\n const recovered = new Uint8Array(data);\n const catalogTrackName = recoveredTrackName\n ?? this.#fec.catalogTrackNameForBlock(sbn, packetId);\n if (catalogTrackName === undefined) {\n this.#router.routeRecoveredSymbol(sbn, recovered, packetId, meta.esi);\n } else {\n this.#router.routeRecoveredSymbol(\n sbn,\n recovered,\n packetId,\n meta.esi,\n catalogTrackName,\n );\n }\n };\n\n // D-28: release the in-flight repair slot when a repair-requested block\n // resolves (completes, recovers, or fails), and emit the gate log line.\n // The request-side bridge (FECManager.onRepairNeeded) is wired in\n // onRepairSubscribe() so repair deadlines only arm once a player has\n // registered a fetcher \u2014 consumers without D-28 pay no timer cost.\n this.#fec.onRepairResolved = (sbn, packetId, outcome, catalogTrackName) => {\n this.clearRepairForBlock(sbn, packetId, catalogTrackName);\n if (outcome === \"failed\") return;\n const pathCount = this.#fec.blockPathCount(\n sbn,\n packetId,\n catalogTrackName,\n );\n const multicastSymbols = pathCount?.multicastSymbols ?? 0;\n const moqSymbols = pathCount?.moqSymbols ?? 0;\n this.#emitRepairLifecycle({\n version: 1,\n type: \"recovered\",\n block: repairBlockIdentity(packetId, sbn, catalogTrackName),\n symbols: {\n multicast: multicastSymbols,\n moq: moqSymbols,\n },\n });\n };\n }\n\n // ---------------------------------------------------------------------------\n // Decomposed component accessors (for new code)\n // ---------------------------------------------------------------------------\n\n get fecManager(): FECManager {\n return this.#fec;\n }\n get router(): MmtpRouter {\n return this.#router;\n }\n get transport(): MulticastTransport {\n return this.#transport;\n }\n\n // ---------------------------------------------------------------------------\n // Stats\n // ---------------------------------------------------------------------------\n\n get reassembler() {\n return this.#router.reassembler;\n }\n get diagnostics() {\n return {\n ...this.#router.getDiagnostics(),\n _fec: this.#fec.diagnostics,\n };\n }\n get stats() {\n return this.#transport.stats;\n }\n\n get fecStats() {\n return this.#fec.fecStats;\n }\n\n /** The raw WASM decoder for stats access */\n get wasmDecoder() {\n return this.#fec.wasmDecoder;\n }\n\n // ---------------------------------------------------------------------------\n // FEC configuration (delegates to FECManager)\n // ---------------------------------------------------------------------------\n\n setFecDecoder(\n packetId: number,\n client: WasmFecDecoder,\n k: number,\n trackType: \"video\" | \"audio\" = \"video\",\n config?: FECManagerConfig,\n catalogTrackName?: string,\n ) {\n const fecConfig = { ...config, k };\n this.#fec.setFecDecoder(packetId, client, k, fecConfig, catalogTrackName);\n this.#configureTrackMapper(packetId, fecConfig);\n this.#router.setPacketIdType(packetId, trackType);\n }\n\n /**\n * Register a Worker-backed FEC decoder for a specific packetId (track).\n * Worker-backed tracks decode asynchronously \u2014 recovered frames arrive via\n * FECManager.onBlockRecovered callback instead of feedSourceSymbol return value.\n */\n setFecWorker(\n packetId: number,\n client: FecWorkerClient,\n trackType: \"video\" | \"audio\",\n config: FECManagerConfig,\n catalogTrackName?: string,\n ): void {\n this.#fec.setFecWorker(packetId, client, config, catalogTrackName);\n this.#configureTrackMapper(packetId, config);\n this.#router.setPacketIdType(packetId, trackType);\n }\n\n /** Register callback for Worker-level FecTrackStats updates. */\n setOnFecStatsUpdate(\n cb: (packetId: number, stats: FecTrackStats) => void,\n ): void {\n this.#fec.onFecStatsUpdate = cb;\n }\n\n setFramerate(fps: number) {\n this.#router.setFramerate(fps);\n }\n\n #configureTrackMapper(packetId: number, config: FECManagerConfig): void {\n const k = config.k;\n const interleaveMs = config.interleaveDepthMs;\n const fps = config.fps;\n if (k && k > 0 && interleaveMs && interleaveMs > 0 && fps && fps > 0) {\n this.#router.setFecMapper(\n new FecBlockMapper({\n k,\n interleaveDepthMs: interleaveMs,\n gopDurationMs: 1000 / fps,\n }),\n packetId,\n );\n }\n }\n\n /**\n * Reduce every registered FEC timing contributor to the stream-global\n * deadlines and apply them to the single transport and single router.\n *\n * `max`, not `min` or last-write-wins: a narrower track must never pull the\n * failback watchdog or the router's reorder window in under a wider track's\n * still-completable FEC block. Recomputed on every registration so a track\n * that arrives late widens the stream rather than being clipped by it.\n */\n #applyStreamGlobalFecTiming(): void {\n const contributions: FecTimingContribution[] = [\n ...(this.#streamFecBaseline ? [this.#streamFecBaseline] : []),\n ...this.#trackFecTiming.values(),\n ];\n if (contributions.length === 0) return;\n\n let deliveryWindowMs: number | undefined;\n let minimumFailbackWindowMs: number | undefined;\n let mfuTimeoutMs: number | undefined;\n let reorderWindowFrames: number | undefined;\n\n for (const c of contributions) {\n if (c.deliveryWindowMs !== undefined) {\n deliveryWindowMs = Math.max(deliveryWindowMs ?? 0, c.deliveryWindowMs);\n }\n if (c.minimumFailbackWindowMs !== undefined) {\n minimumFailbackWindowMs = Math.max(\n minimumFailbackWindowMs ?? 0,\n c.minimumFailbackWindowMs,\n );\n }\n // Only a valid \u00A75.1 ms interleave may drive router timing. A sub-floor\n // value is a raw frame count (no fps to convert with) and would collapse\n // the MFU timeout to ~0 and the reorder window to a single frame; skip\n // the derivation rather than clamp it, leaving previously-configured\n // router timing untouched.\n const interleaveOk =\n c.interleaveDepthMs !== undefined &&\n c.interleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS;\n if (interleaveOk && c.deliveryWindowMs) {\n mfuTimeoutMs = Math.max(mfuTimeoutMs ?? 0, c.deliveryWindowMs);\n }\n if (interleaveOk && c.fps && c.fps > 0) {\n reorderWindowFrames = Math.max(\n reorderWindowFrames ?? 0,\n fecReorderWindowFrames(\n c.interleaveDepthMs,\n c.fps,\n c.k,\n c.repairSymbols,\n ),\n );\n }\n }\n\n const failbackWindowMs =\n deliveryWindowMs === undefined\n ? minimumFailbackWindowMs\n : Math.max(deliveryWindowMs, minimumFailbackWindowMs ?? 0);\n if (failbackWindowMs !== undefined) {\n this.#transport.setFailbackWindowMs(failbackWindowMs);\n }\n if (mfuTimeoutMs !== undefined) {\n this.#router.setMfuTimeoutMs(mfuTimeoutMs);\n }\n if (reorderWindowFrames !== undefined) {\n this.#router.setReorderWindow(reorderWindowFrames);\n }\n }\n\n /**\n * Configure one track's FEC geometry, block mapper, and block-inactivity\n * deadline from that track's own catalog values, then re-reduce the\n * stream-global deadlines across every registered track.\n *\n * This is the packet-specific replacement for the global `configureFec`\n * side effects: per-track concerns stay per-track, and the genuinely\n * stream-global ones (transport failback, router MFU timeout, router reorder\n * window) become an explicit max reduction instead of a last-caller-wins race\n * in which whichever track configured last silently set the whole stream's\n * timing.\n */\n configureTrackFec(\n packetId: number,\n config: Pick<\n MulticastEndpointConfig,\n | \"fecK\"\n | \"fecRepairSymbols\"\n | \"fecSymbolSize\"\n | \"fecInterleaveDepthMs\"\n | \"fecReorderToleranceMs\"\n | \"fps\"\n | \"bandwidth\"\n | \"minimumFailbackWindowMs\"\n >,\n ): void {\n if (config.fecK === undefined || config.fecInterleaveDepthMs === undefined) {\n throw new Error(\n `[MulticastManager] configureTrackFec(${packetId}) requires fecK and ` +\n \"fecInterleaveDepthMs. Derive the per-track FEC geometry from the \" +\n \"stream catalog.\",\n );\n }\n assertPositiveGeometry(\"fecK\", config.fecK);\n assertPositiveGeometry(\"fecInterleaveDepthMs\", config.fecInterleaveDepthMs);\n assertPositiveGeometry(\"fecSymbolSize\", config.fecSymbolSize);\n assertPositiveGeometry(\"fps\", config.fps);\n assertPositiveGeometry(\"bandwidth\", config.bandwidth);\n assertPositiveGeometry(\n \"minimumFailbackWindowMs\",\n config.minimumFailbackWindowMs,\n );\n assertNonNegativeGeometry(\"fecRepairSymbols\", config.fecRepairSymbols);\n // A tolerance is the network path's allowance, added to the encoder's\n // span. These are public entry points: the catalog parser validates what it\n // parses, but a direct caller reaches this arithmetic without passing\n // through it. A negative value silently *shortens* the delivery window\n // below the signaled span, and Infinity/NaN yields a window no block can\n // ever miss -- the eviction bound stops bounding. Reuse the shared\n // non-negative guard rather than an inline check: undefined must stay\n // undefined (absent means the encoder never signaled one, and must not be\n // defaulted), while a declared 0 is a real value that has to survive.\n assertNonNegativeGeometry(\n \"fecReorderToleranceMs\",\n config.fecReorderToleranceMs,\n );\n\n // Same derivation as the stream-global path, but from THIS track's\n // geometry. FECManager already stores a per-track timeoutMs\n // (configureTrack/timeoutMsForPacket); it just never had the repair-symbol\n // and bandwidth inputs needed to compute one.\n const deliveryWindowMs =\n config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS\n ? computeFecDeliveryWindowMs({\n interleaveDepthMs: config.fecInterleaveDepthMs,\n sourceSymbols: config.fecK,\n repairSymbols: config.fecRepairSymbols,\n symbolSize: config.fecSymbolSize,\n mediaUnitsPerSecond: config.fps,\n bandwidthBps: config.bandwidth,\n }) + (config.fecReorderToleranceMs ?? 0)\n : undefined;\n\n const fecConfig: FECManagerConfig = {\n k: config.fecK,\n symbolSize: config.fecSymbolSize,\n interleaveDepthMs: config.fecInterleaveDepthMs,\n fps: config.fps,\n timeoutMs: deliveryWindowMs,\n };\n this.#fec.configureTrack(packetId, fecConfig);\n this.#configureTrackMapper(packetId, fecConfig);\n\n this.#trackFecTiming.set(packetId, {\n deliveryWindowMs,\n interleaveDepthMs: config.fecInterleaveDepthMs,\n fps: config.fps,\n k: config.fecK,\n repairSymbols: config.fecRepairSymbols,\n minimumFailbackWindowMs: config.minimumFailbackWindowMs,\n });\n this.#applyStreamGlobalFecTiming();\n }\n\n configureFec(\n config: Pick<\n MulticastEndpointConfig,\n | \"fecK\"\n | \"fecRepairSymbols\"\n | \"fecSymbolSize\"\n | \"fecInterleaveDepthMs\"\n | \"fecReorderToleranceMs\"\n | \"fps\"\n | \"bandwidth\"\n | \"minimumFailbackWindowMs\"\n >,\n ) {\n const minimumFailbackWindowMs = config.minimumFailbackWindowMs;\n if (\n minimumFailbackWindowMs !== undefined &&\n (!Number.isFinite(minimumFailbackWindowMs) || minimumFailbackWindowMs <= 0)\n ) {\n throw new Error(\n `[MulticastManager] minimumFailbackWindowMs=${minimumFailbackWindowMs}. ` +\n \"Derive a positive failback floor from the stream catalog.\",\n );\n }\n // A tolerance is the network path's allowance, added to the encoder's\n // span. These are public entry points: the catalog parser validates what it\n // parses, but a direct caller reaches this arithmetic without passing\n // through it. A negative value silently *shortens* the delivery window\n // below the signaled span, and Infinity/NaN yields a window no block can\n // ever miss -- the eviction bound stops bounding. Reuse the shared\n // non-negative guard rather than an inline check: undefined must stay\n // undefined (absent means the encoder never signaled one, and must not be\n // defaulted), while a declared 0 is a real value that has to survive.\n assertNonNegativeGeometry(\n \"fecReorderToleranceMs\",\n config.fecReorderToleranceMs,\n );\n const deliveryWindowMs =\n config.fecK &&\n config.fecK > 0 &&\n config.fecInterleaveDepthMs &&\n config.fecInterleaveDepthMs >= MIN_INTERLEAVE_DEPTH_MS\n ? computeFecDeliveryWindowMs({\n interleaveDepthMs: config.fecInterleaveDepthMs,\n sourceSymbols: config.fecK,\n repairSymbols: config.fecRepairSymbols,\n symbolSize: config.fecSymbolSize,\n mediaUnitsPerSecond: config.fps,\n bandwidthBps: config.bandwidth,\n }) + (config.fecReorderToleranceMs ?? 0)\n : undefined;\n this.#fec.configure({\n k: config.fecK,\n symbolSize: config.fecSymbolSize,\n interleaveDepthMs: config.fecInterleaveDepthMs,\n fps: config.fps,\n timeoutMs: deliveryWindowMs,\n });\n // External-feed consumers own the multicast socket and call feedPacket()\n // directly, so they never enter join(). Configure the transport watchdog\n // here where both transport-owned and external-feed paths share the same\n // catalog-derived FEC timing.\n //\n // This call supplies the stream-global BASELINE contribution. Any track\n // registered via configureTrackFec() participates in the same max\n // reduction, so a per-track window can only widen these deadlines, never\n // narrow them below what this baseline already guarantees.\n this.#streamFecBaseline = {\n deliveryWindowMs,\n interleaveDepthMs: config.fecInterleaveDepthMs,\n fps: config.fps,\n k: config.fecK,\n repairSymbols: config.fecRepairSymbols,\n minimumFailbackWindowMs,\n };\n this.#applyStreamGlobalFecTiming();\n // Wire FecBlockMapper to MmtpRouter so SBN/ESI derivation works for FEC recovery.\n //\n // gopDurationMs MUST be the per-frame interval (1000/fps), NOT the\n // interleaveDepthMs. Passing interleaveDepthMs collapses D to 1, which\n // makes the mapper math equivalent to the no-mapper fallback\n // (floor(ssId/k), ssId%k) and breaks SBN/ESI derivation for any interleaved\n // FEC. Surfaced by BLO-3323 L14 (pim-multicast-gateway run 26373805710):\n // with K=32 + interleaveDepthMs=1000ms but gopDurationMs=1000ms, blocks\n // completed with only ~8 source + ~8 repair symbols (out of 40 expected at\n // 25-30fps), so the WASM decoder failed to recover any block at 10% loss.\n const k = config.fecK;\n const interleaveMs = config.fecInterleaveDepthMs;\n const fps = config.fps;\n if (k && k > 0 && interleaveMs && interleaveMs > 0 && fps && fps > 0) {\n const mapper = new FecBlockMapper({\n k,\n interleaveDepthMs: interleaveMs,\n gopDurationMs: 1000 / fps,\n });\n this.#router.setFecMapper(mapper);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Track configuration (delegates to MmtpRouter)\n // ---------------------------------------------------------------------------\n\n // Track type discovery callback \u2014 fires when setPacketIdType is called.\n // Players wire this to MediaRouter.registerTrackTypeFromInit() for\n // init data authority (authoritative until next catalog update).\n #onTrackTypeDiscovered:\n | ((packetId: number, trackType: \"video\" | \"audio\") => void)\n | null = null;\n\n /** Register callback for track type discovery (from init data / inline signaling). */\n onTrackTypeDiscovered(\n cb: (packetId: number, trackType: \"video\" | \"audio\") => void,\n ): void {\n this.#onTrackTypeDiscovered = cb;\n }\n\n setPacketIdType(packetId: number, trackType: \"video\" | \"audio\") {\n this.#router.setPacketIdType(packetId, trackType);\n this.#onTrackTypeDiscovered?.(packetId, trackType);\n }\n\n /**\n * Set the keyframe loss timeout on the inner MmtpRouter.\n * Must be called BEFORE setPacketIdType('video').\n * Derive from explicit keyframe/group duration:\n * Math.ceil(groupDurationMs * KEYFRAME_LOST_TIMEOUT_SLACK).\n */\n setKeyframeLostTimeoutMs(ms: number): void {\n this.#router.setKeyframeLostTimeoutMs(ms);\n }\n\n setMfuTimeoutMs(ms: number): void {\n this.#router.setMfuTimeoutMs(ms);\n }\n\n setTrackCodec(packetId: number, codec: string) {\n this.#router.setTrackCodec(packetId, codec);\n }\n\n // ---------------------------------------------------------------------------\n // Callbacks (delegates to MmtpRouter)\n // ---------------------------------------------------------------------------\n\n onVideoFrame(cb: FrameCallback) {\n this.#router.onVideoFrame(cb);\n }\n onAudioFrame(cb: FrameCallback) {\n this.#router.onAudioFrame(cb);\n }\n onTrackFrame(packetId: number, cb: FrameCallback) {\n this.#router.onTrackFrame(packetId, cb);\n }\n onTrackPacket(packetId: number, cb: TrackPacketCallback) {\n this.#router.onTrackPacket(packetId, cb);\n }\n onInitSegment(cb: (data: Uint8Array) => void) {\n this.#router.onInitSegment(cb);\n }\n onTrackInit(packetId: number, cb: (data: Uint8Array) => void) {\n this.#router.onTrackInit(packetId, cb);\n }\n onFecBlockStarted(cb: (sbn: number, packetId: number) => void) {\n this.#router.onFecBlockStarted(cb);\n }\n /** Unrecoverable-FEC-block signal. Players drive decoder recovery from this\n * (a starved decoder cannot resync on its own once the block is abandoned). */\n onFecBlockFailed(\n cb: (sbn: number, packetId: number, catalogTrackName?: string) => void,\n ) {\n this.#router.onFecBlockFailed(cb);\n }\n\n // ---------------------------------------------------------------------------\n // MSE integration (delegates to MmtpRouter)\n // ---------------------------------------------------------------------------\n\n enableMseAssembler(\n packetId: number,\n config: Omit<BlockAssemblerConfig, \"k\">,\n catalogTrackName?: string,\n ) {\n return this.#router.enableMseAssembler(packetId, config, catalogTrackName);\n }\n\n onMseSegment(packetId: number, cb: (segment: Uint8Array) => void, catalogTrackName?: string) {\n this.#router.onMseSegment(packetId, cb, catalogTrackName);\n }\n\n onMseSample(packetId: number, cb: (sample: CmafBlockSample) => void, catalogTrackName?: string) {\n this.#router.onMseSample(packetId, cb, catalogTrackName);\n }\n\n onMseInitSegment(packetId: number, cb: (init: Uint8Array) => void, catalogTrackName?: string) {\n this.#router.onMseInitSegment(packetId, cb, catalogTrackName);\n }\n\n emitMseInit(packetId: number, catalogTrackName?: string) {\n this.#router.emitMseInit(packetId, catalogTrackName);\n }\n\n markExternalVideoSync(packetId?: number, catalogTrackName?: string) {\n this.#router.markExternalVideoSync(packetId, catalogTrackName);\n }\n\n // ---------------------------------------------------------------------------\n // Transport (delegates to MulticastTransport)\n // ---------------------------------------------------------------------------\n\n onTakeover(cb: () => void) {\n this.#transport.onTakeover(cb);\n }\n onFailback(cb: () => void) {\n this.#transport.onFailback(cb);\n }\n confirmVideo() {\n this.#transport.confirmVideo();\n }\n confirmAudio() {\n this.#transport.confirmAudio();\n }\n\n static isAvailable(): boolean {\n return MulticastTransport.isAvailable();\n }\n\n async join(config: MulticastEndpointConfig): Promise<boolean> {\n // Configure every FEC/MFU deadline before subscribing so direct-feed and\n // transport-owned consumers use the same catalog-derived delivery window.\n this.configureFec(config);\n\n // Every transport join starts at an arbitrary multicast symbol. Reset only\n // join-boundary classification; retain configured tracks, decoders, and\n // cumulative diagnostics across failback/rejoin cycles.\n this.#fec.beginMulticastJoin();\n const selectedPacketIds = config.packetIds?.length\n ? new Set(config.packetIds)\n : null;\n const catalogTracks = config.tracks?.filter(({ packetId }) =>\n selectedPacketIds === null || selectedPacketIds.has(packetId));\n const ingressTracks = catalogTracks\n ? catalogTracks\n : selectedPacketIds === null\n ? undefined\n : [...selectedPacketIds].map((packetId) => ({ name: \"\", packetId }));\n const ingress = ingressTracks === undefined\n ? undefined\n : {\n tracks: Object.freeze(\n ingressTracks.map(({ name, packetId }) =>\n Object.freeze({ name, packetId }),\n ),\n ),\n };\n const transportConfig = {\n sourceAddress: config.sourceAddress,\n groupAddress: config.groupAddress,\n port: config.port,\n mode: config.mode,\n amtRelay: config.amtRelay ?? config.relayAddress,\n amtRelayPort: config.amtRelayPort ?? config.relayPort,\n packetIds: config.packetIds,\n tracks: catalogTracks?.map(({ name, packetId }) => ({ name, packetId })),\n };\n return ingress === undefined\n ? this.#transport.join(transportConfig)\n : this.#transport.join(transportConfig, ingress);\n }\n\n /** Feed a raw packet from an external source (AMT tunnel, ASM subscription).\n *\n * For MoQ-unicast-fallback mode (no SSM bridge), call\n * `manager.router.feedPacket(packet, \"moq\")` directly to bypass\n * MulticastTransport's takeover/watchdog/failback gates while preserving\n * transport attribution in FEC path counters.\n * The `router` accessor above is already public.\n */\n feedPacket(data: ArrayBuffer | Uint8Array, ingress?: MmtpIngressContext) {\n this.#transport.feedPacket(data, ingress);\n }\n\n /** Process a repair packet from MoQ (reliable QUIC). */\n processRepairFromMoQ(\n packet: Uint8Array,\n ownerPacketId?: number,\n catalogTrackName?: string,\n ) {\n this.#router.feedRepairPacket(packet, ownerPacketId, catalogTrackName);\n }\n\n /** Route a Worker-recovered FEC block through the MMTP parser + dedup pipeline. */\n routeRecoveredBlock(\n blockId: number,\n data: Uint8Array,\n packetId: number,\n catalogTrackName?: string,\n ) {\n this.#router.routeRecoveredBlock(blockId, data, packetId, catalogTrackName);\n }\n\n /** Route one Worker-recovered source symbol through the MMTP parser + dedup pipeline. */\n routeRecoveredSymbol(\n blockId: number,\n data: Uint8Array,\n packetId: number,\n esi = 0,\n catalogTrackName?: string,\n ) {\n this.#router.routeRecoveredSymbol(blockId, data, packetId, esi, catalogTrackName);\n }\n\n // ---------------------------------------------------------------------------\n // Phase 14: multi-track catalog ingest (D-04, D-06, D-07, D-17, D-26, D-28)\n // ---------------------------------------------------------------------------\n\n /**\n * Configure the manager for a multi-track (base/delta/repair) CMAF switching set.\n *\n * D-04: Validates completeness via validateSwitchingSet \u2014 throws if incomplete.\n * D-07: Applies per-track FEC configs (base+delta get decoders; repair does not).\n * D-17: Builds altGroup \u2192 track-name index for getSwitchingSet().\n * D-26: Registers packetId \u2192 TrackRole on MmtpRouter for block-complete fanout.\n *\n * @param catalog - Multi-track catalog (tracks array with name, altGroup, fec, packetId).\n * @param wasmDecoderSource - One WASM FEC decoder for a single FEC stream, or\n * a factory returning a distinct decoder per stream.\n */\n configureMultiTrack(\n catalog: MultiTrackCatalog,\n wasmDecoderSource?: WasmFecDecoder | WasmFecDecoderFactory,\n ): void {\n // D-06: Build the set of repair track names from explicit fec.repairTrack pointers FIRST.\n // This allows publisher-chosen repair track names (e.g. \"video/repair-alt\") that\n // don't match the standard \"/repair\" suffix convention. The explicit pointer wins\n // over the naming convention \u2014 a track pointed to by repairTrack is always 'repair'.\n const explicitRepairNamesByGroup = new Map<string, Set<string>>();\n const trackNamesByGroup = new Map<string, string[]>();\n for (const track of catalog.tracks) {\n const groupKey = altGroupKey(track);\n const groupNames = trackNamesByGroup.get(groupKey) ?? [];\n groupNames.push(track.name);\n trackNamesByGroup.set(groupKey, groupNames);\n\n if (\n track.fec !== undefined &&\n isFecCatalogConfig(track.fec) &&\n track.fec.repairTrack\n ) {\n const explicitRepairNames =\n explicitRepairNamesByGroup.get(groupKey) ?? new Set<string>();\n explicitRepairNames.add(track.fec.repairTrack);\n explicitRepairNamesByGroup.set(groupKey, explicitRepairNames);\n }\n }\n\n const hasSwitchingSetByGroup = new Map<string, boolean>();\n for (const [groupKey, trackNames] of trackNamesByGroup) {\n hasSwitchingSetByGroup.set(groupKey, hasSwitchingSetRole(trackNames));\n }\n\n // Resolve role for each track \u2014 explicit pointer wins over naming convention.\n // Normalize non-standard repair names to 'repair' before calling validateSwitchingSet,\n // which relies on parseTrackRole and would throw on custom repair-track names.\n const resolvedRoles: TrackRole[] = catalog.tracks.map((track) => {\n const groupKey = altGroupKey(track);\n const hasSwitchingSet = hasSwitchingSetByGroup.get(groupKey) ?? false;\n const explicitRepairNames = explicitRepairNamesByGroup.get(groupKey);\n if (explicitRepairNames?.has(track.name)) return \"repair\";\n if (!hasSwitchingSet) return \"single\";\n return parseTrackRole(track.name);\n });\n\n // D-04: Validate completeness per altGroup using resolved roles.\n // We replicate validateSwitchingSet logic here to avoid re-calling parseTrackRole\n // (which would throw on explicit-pointer repair track names like \"video/repair-alt\").\n const resolvedRolesByGroup = new Map<string, TrackRole[]>();\n for (let i = 0; i < catalog.tracks.length; i++) {\n const groupKey = altGroupKey(catalog.tracks[i]!);\n const roles = resolvedRolesByGroup.get(groupKey) ?? [];\n roles.push(resolvedRoles[i]!);\n resolvedRolesByGroup.set(groupKey, roles);\n }\n for (const [groupKey, roles] of resolvedRolesByGroup) {\n if (!(hasSwitchingSetByGroup.get(groupKey) ?? false)) continue;\n\n const presentRoles = new Set(roles.filter((r) => r !== \"single\"));\n const required: Array<\"base\" | \"delta\" | \"repair\"> = [\n \"base\",\n \"delta\",\n \"repair\",\n ];\n const missing = required.filter((r) => !presentRoles.has(r));\n if (missing.length > 0) {\n throw new Error(\n `[MulticastManager] incomplete switching set: missing ${missing.join(\", \")}`,\n );\n }\n }\n\n const fecTrackIndexes: number[] = [];\n for (let i = 0; i < catalog.tracks.length; i++) {\n const track = catalog.tracks[i]!;\n if (resolvedRoles[i] === \"repair\" || track.fec === undefined) continue;\n if (!isFecCatalogConfig(track.fec)) {\n throw new Error(\n `[MulticastManager] invalid fec config for track '${track.name}'`,\n );\n }\n if (track.packetId !== undefined) fecTrackIndexes.push(i);\n }\n\n // Stateful decoders cannot span physical streams: resetting one stream\n // would otherwise clear or disable every peer sharing that instance.\n const fecDecoders = new Map<number, WasmFecDecoder>();\n if (wasmDecoderSource !== undefined && fecTrackIndexes.length > 0) {\n if (typeof wasmDecoderSource !== \"function\") {\n if (fecTrackIndexes.length > 1) {\n throw new Error(\n \"[MulticastManager] multiple FEC streams require a decoder factory\",\n );\n }\n fecDecoders.set(fecTrackIndexes[0]!, wasmDecoderSource);\n } else {\n const clients = new Set<WasmFecDecoder>();\n for (const index of fecTrackIndexes) {\n const client = wasmDecoderSource(catalog.tracks[index]!);\n if (clients.has(client)) {\n throw new Error(\n \"[MulticastManager] decoder factory must return one client per physical stream\",\n );\n }\n clients.add(client);\n fecDecoders.set(index, client);\n }\n }\n }\n\n // Reset switching-set index and repair packetId set.\n this.#switchingSets.clear();\n this.#repairPacketIds.clear();\n this.#router.clearRepairPacketIdOwners();\n const catalogTracksByName = new Map(\n catalog.tracks.map((track) => [track.name, track] as const),\n );\n\n for (let i = 0; i < catalog.tracks.length; i++) {\n const track = catalog.tracks[i]!;\n const role = resolvedRoles[i]!;\n\n // D-17: Build altGroup \u2192 track-name index.\n if (track.altGroup !== undefined) {\n const existing = this.#switchingSets.get(track.altGroup) ?? [];\n existing.push(track.name);\n this.#switchingSets.set(track.altGroup, existing);\n }\n\n // D-26: Register packetId \u2192 role on MmtpRouter (if packetId provided at ingest time).\n if (track.packetId !== undefined) {\n this.#router.setPacketIdRole(track.packetId, role);\n }\n\n if (role === \"repair\") {\n // D-06: Repair track is the repair source \u2014 no FEC decoder registered on it.\n if (track.packetId !== undefined) {\n this.#repairPacketIds.add(track.packetId);\n }\n continue;\n }\n\n // D-07: Apply per-track FEC config for non-repair tracks.\n if (track.fec !== undefined) {\n const fecCfg = track.fec as FecCatalogConfig;\n const repairTrack = catalogTracksByName.get(fecCfg.repairTrack);\n if (track.packetId !== undefined && repairTrack?.packetId !== undefined) {\n this.#router.setRepairPacketIdOwner(repairTrack.packetId, track.packetId);\n }\n const wasmDecoder = fecDecoders.get(i);\n if (track.packetId !== undefined && wasmDecoder !== undefined) {\n // trackType: base/delta/single video tracks are 'video'; audio would be 'audio'.\n // At this layer we treat all content tracks as 'video' \u2014 audio tracks typically\n // have no FEC in Phase 14 (D-26 covers video base+delta only). Callers that need\n // audio FEC can call setFecDecoder() directly after configureMultiTrack().\n this.#fec.setFecDecoder(\n track.packetId,\n wasmDecoder,\n fecCfg.sourceSymbols,\n {\n k: fecCfg.sourceSymbols,\n symbolSize: fecCfg.symbolSize,\n interleaveDepthMs: fecCfg.interleaveDepthMs,\n fps: track.framerate,\n },\n track.name,\n );\n this.#configureTrackMapper(track.packetId, {\n k: fecCfg.sourceSymbols,\n symbolSize: fecCfg.symbolSize,\n interleaveDepthMs: fecCfg.interleaveDepthMs,\n fps: track.framerate,\n });\n this.#router.setPacketIdType(track.packetId, \"video\");\n }\n }\n }\n }\n\n /**\n * Return the track names belonging to the given altGroup switching set.\n * D-17: altGroup groups base+delta+repair into one switching set.\n *\n * @param altGroup - The altGroup number to look up.\n * @returns Array of track names in that switching set (empty if none).\n */\n getSwitchingSet(altGroup: number): string[] {\n return this.#switchingSets.get(altGroup) ?? [];\n }\n\n /**\n * Register callback fired when a repair fetch should be issued for an FEC block.\n *\n * D-28: At most one outstanding repair fetch per (packetId, sbn) block.\n * clearRepairForBlock fires automatically when the block resolves (via\n * FECManager.onRepairResolved); call it manually only on fetch failure\n * paths the FEC layer cannot observe (fetch error, range not retained,\n * transport timeout) so a stuck fetch cannot wedge the block's slot.\n *\n * Registering also arms the FEC repair deadline: each block gets a one-shot\n * timer for the derived K-symbol source-production horizon; a block still\n * short of K when it fires requests repair here. Failure accounting timing\n * is unchanged.\n */\n onRepairSubscribe(\n cb: (sbn: number, packetId: number, catalogTrackName?: string) => void,\n ): void {\n this.#onRepairSubscribeCb = cb;\n this.#fec.onRepairNeeded = (sbn, packetId, catalogTrackName) => {\n this.requestRepairForBlock(sbn, packetId, catalogTrackName);\n };\n }\n\n /**\n * Observe causal repair lifecycle events without retaining snapshots in the\n * transport. Returns an unsubscribe function.\n */\n addRepairLifecycleObserver(observer: RepairLifecycleObserver): () => void {\n this.#repairLifecycleObservers.add(observer);\n return () => this.#repairLifecycleObservers.delete(observer);\n }\n\n /** Observe per-stream epoch resets (used by repair transports to abort I/O). */\n addTrackResetObserver(\n observer: (packetId: number, catalogTrackName: string) => void,\n ): () => void {\n this.#trackResetObservers.add(observer);\n return () => this.#trackResetObservers.delete(observer);\n }\n\n /** Begin a fresh retained-replay epoch for one physical media stream. */\n resetTrack(packetId: number, catalogTrackName: string): void {\n if (!catalogTrackName) {\n throw new Error('[MulticastManager] catalogTrackName is required for track reset');\n }\n for (const observer of this.#trackResetObservers) {\n try {\n observer(packetId, catalogTrackName);\n } catch (error) {\n console.warn('[MulticastManager] track reset observer failed', error);\n }\n }\n for (const key of this.#repairInFlight) {\n try {\n const [keyPacketId, keyTrackName] = JSON.parse(key);\n if (keyPacketId === packetId && keyTrackName === catalogTrackName) {\n this.#repairInFlight.delete(key);\n }\n } catch {\n // Internal keys are JSON tuples; preserve unknown legacy entries.\n }\n }\n this.#fec.resetTrack(packetId, catalogTrackName);\n this.#router.resetTrack(packetId, catalogTrackName);\n }\n\n /**\n * Request a repair-track fetch for the given FEC block.\n *\n * D-28: duplicate requests for the same (packetId, sbn) are suppressed\n * until clearRepairForBlock is called.\n *\n * @param sbn - Source Block Number identifying the FEC block needing repair.\n * @param packetId - MMTP packet_id owning the block (video/audio tracks have\n * independent SBN spaces, so the in-flight key must be composite).\n */\n requestRepairForBlock(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): void {\n // D-28: at most one outstanding fetch per FEC block (per packetId:sbn).\n // Different blocks are independent and are NOT blocked by each other.\n const block = repairBlockIdentity(packetId, sbn, catalogTrackName);\n const key = JSON.stringify([packetId, block.catalogTrackName, sbn]);\n if (this.#repairInFlight.has(key)) return;\n this.#repairInFlight.add(key);\n this.#emitRepairLifecycle({\n version: 1,\n type: \"requested\",\n block,\n });\n if (catalogTrackName === undefined) {\n this.#onRepairSubscribeCb?.(sbn, packetId);\n } else {\n this.#onRepairSubscribeCb?.(sbn, packetId, catalogTrackName);\n }\n }\n\n /**\n * Clear in-flight repair state for the given block.\n * Fired automatically when the block resolves; safe to call redundantly.\n */\n clearRepairForBlock(\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): void {\n this.#repairInFlight.delete(\n JSON.stringify([packetId, catalogTrackName ?? null, sbn]),\n );\n }\n\n #emitRepairLifecycle(event: RepairLifecycleEvent): void {\n const immutableEvent = Object.freeze({\n ...event,\n block: Object.freeze({...event.block}),\n ...(event.type === \"requested\"\n ? {}\n : {symbols: Object.freeze({...event.symbols})}),\n }) as RepairLifecycleEvent;\n // Serialize before invoking observers so a re-entrant observer cannot log\n // a resolution before the request that caused it.\n console.info(\n `${REPAIR_LIFECYCLE_LOG_PREFIX}${JSON.stringify(immutableEvent)}`,\n );\n for (const observer of this.#repairLifecycleObservers) {\n try {\n observer(immutableEvent);\n } catch (error) {\n console.warn(\"[MulticastManager] repair lifecycle observer failed\", error);\n }\n }\n }\n\n dispose() {\n this.#trackResetObservers.clear();\n this.#repairLifecycleObservers.clear();\n this.#transport.dispose();\n this.#router.dispose();\n this.#fec.dispose();\n }\n}\n", "/**\n * Shared fec-setup helper.\n *\n * Wires a FecWorkerClient into a MulticastManager for a specific track.\n * Both moq-lib (`hang-mmt-fec/js/watch/src/element.ts`) and moqtail\n * (`moqtail-private/apps/client-js/src/lib/player.ts`) call this from their\n * SessionManager `fec-setup` handler so the Worker+ALTA setup lives in one\n * place instead of being duplicated across ~80 lines per player.\n *\n * Callers are responsible for holding the returned `dispose()` and calling it\n * from their `fec-teardown` path (and/or effect cleanup).\n */\n\nimport type { FecWorkerClient, FecTrackConfig } from '@blockcast/fec-worker'\nimport { deriveFecInterleaveDepth } from '@blockcast/mmt-fec'\nimport type { MulticastManager } from './multicast-manager.js'\nimport { computeFecDeliveryWindowMs } from './track-session.js'\n\n/** Asset URLs the helper needs. Callers pass the already-resolved set. */\nexport interface FecWorkerAssetUrls {\n /** URL to the fec-worker entry bundle (new Worker() target). */\n fecWorker: string\n /** URL to the RaptorQ WASM module (compiled via compileStreaming). */\n rqWasm: string\n /** URL to the ALTA WASM module. Fetched only when an ALTA key is provided. */\n altaWasm: string\n}\n\nexport interface FecWorkerSetupOptions {\n /** MulticastManager that should receive the configured Worker. */\n multicastManager: MulticastManager\n /** MMTP packetId this track occupies (from catalog). */\n packetId: number\n /** Human-readable track name (catalog key). */\n trackName: string\n /** Track type from catalog. Never inferred from name. */\n trackType: 'video' | 'audio'\n /** FEC K from catalog (source symbols per block). */\n fecK: number\n /** Symbol size in bytes (T) \u2014 the ALTA-adjusted value if ALTA is signed. */\n fecSymbolSize: number\n /** Repair symbols per block (P). */\n fecRepairCount: number\n /** Interleave depth in ms. */\n fecInterleaveMs: number\n /** Duration of one FEC media unit in ms (video frame or audio access unit). */\n mediaUnitDurationMs: number\n /** Optional repair track name (MoQ). Forwarded to FecTrackConfig. */\n repairTrackName?: string\n /** Audio sample rate (optional \u2014 audio tracks only). */\n sampleRate?: number\n /** Codec string (optional; \"unknown\" is acceptable for first setup). */\n codec?: string\n /** Video framerate (optional metadata forwarded to the worker). */\n framerate?: number\n /** Base64 SPKI public key (optional). Accepts padded/unpadded, std or url-safe alphabet. */\n altaPublicKeySpki?: string\n /** Absolute or origin-relative asset URLs. */\n assetUrls: FecWorkerAssetUrls\n /** Optional FecWorkerClient factory \u2014 main use is test injection. */\n clientFactory?: (workerUrl: URL) => FecWorkerClient\n}\n\nexport interface FecWorkerSetupResult {\n /** The configured client (already registered with MulticastManager). */\n client: FecWorkerClient\n /** True when the ALTA public key decoded and the ALTA WASM compiled. */\n altaEnabled: boolean\n /** Releases the Worker and removes it from MulticastManager routing. */\n dispose: () => void\n}\n\n/**\n * Configure a FecWorkerClient and attach it to `multicastManager` via\n * `setFecWorker(packetId, client, trackType, config, trackName)`. Worker-path takes precedence\n * over any sync WASM decoder already registered for the same packetId \u2014\n * sync decoders remain as a fallback.\n *\n * Any loader failure is re-thrown so the caller can log and leave the sync\n * WASM decoder active as fallback (same pattern both players used before).\n */\nexport async function setupFecWorkerForTrack(\n opts: FecWorkerSetupOptions,\n): Promise<FecWorkerSetupResult> {\n const interleaveDepthBlocks = deriveFecInterleaveDepth(\n opts.fecInterleaveMs,\n opts.mediaUnitDurationMs,\n )\n const deliveryWindowMs = computeFecDeliveryWindowMs({\n interleaveDepthMs: opts.fecInterleaveMs,\n sourceSymbols: opts.fecK,\n repairSymbols: opts.fecRepairCount,\n symbolSize: opts.fecSymbolSize,\n mediaUnitsPerSecond: 1000 / opts.mediaUnitDurationMs,\n })\n // Resolve URL against location.origin when available (browser runtime).\n // Absolute URLs pass through unchanged per the URL() spec.\n const origin =\n typeof location !== 'undefined' && location.origin ? location.origin : 'http://localhost'\n const workerUrl = new URL(opts.assetUrls.fecWorker, origin)\n\n const FecWorkerCtor = await import('@blockcast/fec-worker')\n const client = opts.clientFactory\n ? opts.clientFactory(workerUrl)\n : new FecWorkerCtor.FecWorkerClient(workerUrl)\n\n // Compile RaptorQ WASM once; ALTA WASM only when we have a key to decode.\n const rqWasm = await WebAssembly.compileStreaming(fetch(opts.assetUrls.rqWasm))\n\n let altaKeyBuffer: ArrayBuffer | undefined\n let altaWasmModule: WebAssembly.Module | undefined\n if (opts.altaPublicKeySpki) {\n const b64 = opts.altaPublicKeySpki.replace(/-/g, '+').replace(/_/g, '/')\n const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, '=')\n try {\n const keyBytes = Uint8Array.from(atob(padded), c => c.charCodeAt(0))\n // SPKI size range: Ed25519=44, P-256=91; tolerate 32..=256 for forward-compat.\n if (keyBytes.length >= 32 && keyBytes.length <= 256) {\n altaKeyBuffer = keyBytes.buffer\n altaWasmModule = await WebAssembly.compileStreaming(fetch(opts.assetUrls.altaWasm))\n } else {\n console.warn(\n `[fec-worker-setup] ALTA SPKI wrong length for \"${opts.trackName}\": ` +\n `${keyBytes.length} (expected 32..256) \u2014 ALTA disabled`,\n )\n }\n } catch (e) {\n console.warn(`[fec-worker-setup] ALTA key decode / WASM compile failed for \"${opts.trackName}\":`, e)\n }\n }\n\n const altaEnabled = !!(altaKeyBuffer && altaWasmModule)\n const fecTrackConfig = {\n codec: opts.codec ?? 'unknown',\n trackName: opts.trackName,\n trackType: opts.trackType,\n framerate: opts.framerate,\n sampleRate: opts.sampleRate,\n algorithm: 'raptor',\n k: opts.fecK,\n repairCount: opts.fecRepairCount,\n symbolSize: opts.fecSymbolSize,\n interleaveDepth: interleaveDepthBlocks,\n interleaveMs: opts.fecInterleaveMs,\n deliveryWindowMs,\n fecMode: 'subframe' as const,\n repairTrack: opts.repairTrackName,\n altaEnabled,\n relayBlockSigEnabled: false,\n } as unknown as FecTrackConfig\n\n client.configure(fecTrackConfig, rqWasm, altaKeyBuffer, altaWasmModule)\n opts.multicastManager.setFecWorker(opts.packetId, client, opts.trackType, {\n k: opts.fecK,\n symbolSize: opts.fecSymbolSize,\n interleaveDepthMs: opts.fecInterleaveMs,\n // FEC timing is expressed in media units. For audio this is the access-unit\n // cadence (for example 1024/48kHz), not a video-only framerate field.\n fps: 1000 / opts.mediaUnitDurationMs,\n timeoutMs: deliveryWindowMs,\n }, opts.trackName)\n\n let disposed = false\n return {\n client,\n altaEnabled,\n dispose: () => {\n if (disposed) return\n disposed = true\n try {\n client.dispose()\n } catch {\n /* dispose errors are non-fatal */\n }\n },\n }\n}\n", "/**\n * Shared group-alignment primitive for MMT over MoQ switching sets.\n *\n * Pure math \u2014 no MMT wire parsing, no CMCD, no I/O. Callers wire the output\n * into their own drift-observation plumbing (see media-router.ts for the\n * libmmt wiring).\n *\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * Contract (integer-ticks form, authoritative):\n *\n * groupId = ticks < 0 ? 0 : floor(ticks / groupDurationTicks)\n *\n * Where `ticks` is a media timestamp expressed at the catalog's `timescale`\n * (Hz), and `groupDurationTicks = groupDurationSec * timescale`.\n *\n * Integer math means there is no epsilon and no float rounding to reason\n * about. The seconds-form wrapper `groupIdFor(Seconds, seconds)` exists for\n * legacy callers but converts to ticks internally.\n *\n * Every sender/receiver that emits or consumes MoQ groups from an MMT or\n * CMAF switching set MUST derive groupId from this formula so that base,\n * delta, and repair tracks agree on the group boundary for a given media\n * timestamp across languages and processes.\n *\n * Test vectors that exercise the contract live at\n * `packages/container/test-vectors/align.json`; the Go and Rust siblings\n * load the same fixture so cross-language agreement is executable, not\n * prose. **Formula changes require updating the fixture AND all three\n * sibling implementations in the same release** \u2014 divergence between\n * sender/relay/receiver is worse than any formula change.\n *\n * Canonical implementations:\n * Go: Blockcast/multicast `cmd/caddy/sender/group_align.go`\n * (commit 104eeff) \u2014 sender-side groupId stamping on egress.\n * Rust: moqtail/moqtail `apps/relay/src/server/abr.rs`\n * (commit 39accbf; experimental branch, may be rebased \u2014 rely\n * on the SHA, not the branch name) \u2014 relay-side check.\n * TS: THIS FILE (libmmt) \u2014 consumed by moq-lib + moqtail-ts via\n * @blockcast/mmt-container.\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n */\n\n/**\n * Branded seconds type. Construct only via `secondsFromTicks(ticks, timescale)`\n * or `asSeconds(n)` (the latter exists solely for tests and bring-up). The\n * brand prevents accidentally passing a raw tick count \u2014 or a Date.now()\n * millisecond \u2014 to an API that expects a media-time in seconds.\n */\nexport type Seconds = number & { readonly __brand: 'Seconds' }\n\n/**\n * Explicit conversion constructor: ticks at `timescale` Hz \u2192 Seconds.\n *\n * Call this at the wire/catalog boundary so the brand flows with the value\n * through the rest of the system. Throws on non-finite inputs or\n * non-positive timescale (CLAUDE.md \u00A7Architectural Principles: error on\n * bad init).\n */\nexport function secondsFromTicks(ticks: number, timescale: number): Seconds {\n if (!Number.isFinite(ticks)) {\n throw new Error(`[align] secondsFromTicks: ticks must be finite, got ${ticks}`)\n }\n if (!Number.isFinite(timescale) || timescale <= 0) {\n throw new Error(\n `[align] secondsFromTicks: timescale must be positive and finite, got ${timescale}`,\n )\n }\n return (ticks / timescale) as Seconds\n}\n\n/**\n * Escape hatch for tests / bring-up code that holds a raw seconds number\n * (e.g. a catalog field that ships as float seconds). Production code\n * should prefer `secondsFromTicks` so the conversion site is explicit.\n */\nexport function asSeconds(n: number): Seconds {\n if (!Number.isFinite(n)) {\n throw new Error(`[align] asSeconds: value must be finite, got ${n}`)\n }\n return n as Seconds\n}\n\n/**\n * Deterministic groupId from integer ticks. Authoritative form of the\n * contract (see module docstring).\n *\n * @param ticks \u2014 media presentation time in ticks (integer).\n * Negative values clamp to group 0 (matches\n * the Go sibling's base-clamp).\n * @param groupDurationTicks \u2014 group duration in ticks (positive integer).\n * @returns non-negative integer groupId.\n *\n * Throws on non-integer/non-finite inputs or non-positive duration. These\n * are init-time errors, not packet-hot-path errors \u2014 callers ingesting\n * wire data should validate once at catalog load, not once per packet.\n */\nexport function groupIdForTicks(ticks: number, groupDurationTicks: number): number {\n if (!Number.isInteger(ticks)) {\n throw new Error(\n `[align] groupIdForTicks: ticks must be an integer, got ${ticks}`,\n )\n }\n if (!Number.isInteger(groupDurationTicks) || groupDurationTicks <= 0) {\n throw new Error(\n `[align] groupIdForTicks: groupDurationTicks must be a positive integer, got ${groupDurationTicks}`,\n )\n }\n if (ticks < 0) return 0\n return Math.floor(ticks / groupDurationTicks)\n}\n\n/**\n * Seconds-form wrapper around `groupIdForTicks`. Kept for backwards\n * compatibility with existing callers that hold `Seconds` (or a raw\n * `number` that semantically represents seconds).\n *\n * Internally converts to ticks using a synthetic 1e9 Hz timescale so the\n * integer-math contract is the single source of truth. The `| number`\n * branch of the parameter type is a migration ramp \u2014 new code should\n * pass `Seconds` explicitly (via `secondsFromTicks` or `asSeconds`).\n *\n * @param mediaTimeSec \u2014 finite number of seconds; negatives clamp to 0.\n * @param groupDurationSec \u2014 positive finite seconds.\n *\n * @deprecated Use `groupIdForTicks(ticks, groupDurationTicks)` with\n * tick-space inputs from the catalog. The seconds wrapper rounds to the\n * nearest nanosecond and throws above ~104 days of media time; the\n * integer-ticks form has no rounding and a much wider safe range.\n *\n * The rounding is not benign: when `groupDurationTicks / timescale` is not\n * ns-exact (33333@90k, 1001@30k, 1024@44.1k \u2014 all real catalog cadences)\n * the quantized duration under-counts and `expected` slips a whole group.\n * See the divergence fixture in `__tests__/align.test.ts`.\n *\n * No non-test caller remains in this package: `MediaRouter` now takes\n * `GroupBoundaryTicks` (PCL-2211). Removal is tracked by BLO-21176.\n */\nexport function groupIdFor(\n mediaTimeSec: Seconds | number,\n groupDurationSec: number,\n): number {\n if (!Number.isFinite(mediaTimeSec as number)) {\n throw new Error(\n `[align] groupIdFor: mediaTimeSec must be a finite number, got ${mediaTimeSec}`,\n )\n }\n if (!Number.isFinite(groupDurationSec) || groupDurationSec <= 0) {\n throw new Error(\n `[align] groupIdFor: groupDurationSec must be a positive finite number, got ${groupDurationSec}`,\n )\n }\n if ((mediaTimeSec as number) < 0) return 0\n // 1e9 Hz keeps both operands inside Number.MAX_SAFE_INTEGER (2^53-1)\n // for ~104 days of media time. Beyond that, Math.round returns a\n // non-safe-integer float that Number.isInteger still accepts \u2014 throw\n // explicitly so the caller picks a narrower timescale rather than\n // silently quantizing.\n const TS = 1_000_000_000\n const ticks = Math.round((mediaTimeSec as number) * TS)\n const durTicks = Math.round(groupDurationSec * TS)\n if (durTicks <= 0) {\n throw new Error(\n `[align] groupIdFor: groupDurationSec=${groupDurationSec} rounds to <1 tick at 1e9 Hz; pass groupIdForTicks directly for sub-nanosecond durations`,\n )\n }\n if (!Number.isSafeInteger(ticks)) {\n throw new Error(\n `[align] groupIdFor: mediaTimeSec=${mediaTimeSec} overflows safe-integer ticks at 1e9 Hz (>~104 days); use groupIdForTicks with a narrower timescale`,\n )\n }\n return groupIdForTicks(ticks, durTicks)\n}\n\n/**\n * Result of a drift check between expected and actual groupIds.\n *\n * Discriminated on `kind` so callers must handle both outcomes explicitly\n * (the previous `{ drifted: boolean, deltaGroups: number }` shape let a\n * drifted result slip through with a silent `if (result.drifted)` gate).\n */\nexport type DriftCheckResult =\n | { readonly kind: 'aligned'; readonly deltaGroups: 0 }\n | { readonly kind: 'drifted'; readonly deltaGroups: number }\n\n/**\n * Compare a packet-stamped groupId against the formula-derived expectation.\n *\n * Pure observer \u2014 does not throw on drift, does not mutate anything.\n * Callers decide how to react (e.g. bump a CMCD counter, log, ignore).\n *\n * @param expected \u2014 groupId as derived via `groupIdForTicks` or `groupIdFor`.\n * @param actual \u2014 groupId as stamped on the incoming packet/group.\n */\nexport function driftCheck(expected: number, actual: number): DriftCheckResult {\n if (!Number.isInteger(expected) || !Number.isInteger(actual)) {\n throw new Error(\n `[align] driftCheck: both arguments must be integer groupIds, got expected=${expected} actual=${actual}`,\n )\n }\n const deltaGroups = actual - expected\n if (deltaGroups === 0) return { kind: 'aligned', deltaGroups: 0 }\n return { kind: 'drifted', deltaGroups }\n}\n\nexport type MmtpSubgroupKeyInput =\n | { readonly kind: 'mpu-metadata' }\n | {\n readonly kind: 'timed'\n readonly movieFragmentSequenceNumber: number\n readonly sampleNumber: number\n }\n | { readonly kind: 'non-timed'; readonly itemId: number }\n\n/**\n * Canonical, domain-separated key for MMTP payloads sharing one MoQ subgroup.\n * This is a map/storage key, not the scalar MoQ subgroup ID. MPU metadata is\n * carried in subgroup 0; media identity remains explicit and collision-free.\n */\nexport function subgroupKeyFor(input: MmtpSubgroupKeyInput): string {\n if (input.kind === 'mpu-metadata') return 'metadata:0'\n\n const values =\n input.kind === 'timed'\n ? [input.movieFragmentSequenceNumber, input.sampleNumber]\n : [input.itemId]\n for (const value of values) {\n if (!Number.isInteger(value) || value < 0 || value > 0xffff_ffff) {\n throw new Error(`[align] subgroup key coordinates must be uint32, got ${value}`)\n }\n }\n\n return input.kind === 'timed'\n ? `timed:${input.movieFragmentSequenceNumber}:${input.sampleNumber}`\n : `non-timed:${input.itemId}`\n}\n", "/**\n * MediaRouter \u2014 source arbitration for MoQ \u2194 multicast switching.\n *\n * Both moq-lib and moqtail wire their MoQ and multicast frame outputs\n * through this router. The router decides which source reaches the decoder:\n *\n * - MoQ frames pass through while multicast is inactive.\n * - Multicast video frames are suppressed until the first keyframe.\n * - Once multicast has both a video keyframe and audio, takeover is requested.\n * - MoQ is suppressed only after the source-switch callback completes.\n * - failbackToMoq() reverses the switch immediately.\n *\n * Per-track setTrackSource() allows individual tracks to be wired to a\n * specific transport source (video=multicast, audio=moq, overlay=amt) without\n * changing the binary MoQ\u2194multicast switching for other tracks.\n *\n * notifyGroupBoundary() is an observer hook: callers report the\n * packet-stamped groupId + media time for each group, and the router\n * cross-checks against the shared `align.ts` formula. Drift is emitted via\n * onGroupDrift so the player can bump the CMCD `x-bc-switch-drift-us` /\n * `x-bc-switch-miscount` counters. Observation only \u2014 does NOT alter routing.\n */\n\nimport { groupIdForTicks, driftCheck } from './align.js'\nimport { computeFecDeliveryWindowMs } from './track-session.js'\n\nexport type FrameMeta = {\n timestamp: number\n keyframe: boolean\n packetId?: number\n packetSequenceNumber?: number\n source?: 'moq' | 'multicast' | 'repair'\n recovered?: boolean\n sourceDiscontinuity?: boolean\n}\nexport type FrameKind = 'video' | 'audio'\nexport type SourceKind = 'moq' | 'multicast'\n\n/**\n * Tick-space timing for one group boundary \u2014 the exact, authoritative form.\n *\n * These are the integers the catalog already carries (`groupDurationTicks`,\n * `timescale`) plus the packet's raw timestamp in the same units. Passing\n * them straight through keeps the whole drift check in integer math.\n *\n * Prefer this over the seconds overload: `groupDurationTicks / timescale`\n * is not exactly representable for the common non-integer-second cadences\n * (33333@90k, 1001@30k, 1024@44.1k), and the seconds path has to quantize\n * to whole nanoseconds \u2014 which shifts `expected` by a whole group from the\n * second boundary onward and reports drift on a perfectly aligned stream.\n */\nexport interface GroupBoundaryTicks {\n /** Packet media timestamp in `timescale` units. Integer; negatives clamp to 0. */\n readonly mediaTimeTicks: number\n /** Group duration in `timescale` units. Positive integer. */\n readonly groupDurationTicks: number\n /**\n * Catalog timescale in Hz. Positive integer \u2014 **validated, and a hard gate**:\n * a non-integer or non-positive value skips the drift check entirely rather\n * than reporting against it. It does not feed the groupId computation (that\n * is `mediaTimeTicks / groupDurationTicks` alone), but it does derive the\n * `mediaTimeSec` / `groupDurationSec` on every GroupDriftEvent \u2014 so letting a\n * bad value through would push NaN/Infinity into CMCD `x-bc-switch-drift-us`,\n * which is worse than skipping.\n */\n readonly timescale: number\n}\n\n/** Payload emitted by MediaRouter.onGroupDrift when a packet-stamped groupId\n * disagrees with the shared `align.ts` formula. */\nexport interface GroupDriftEvent {\n trackName: string\n expectedGroupId: number\n actualGroupId: number\n /** `actual - expected` (positive = ahead, negative = behind). */\n deltaGroups: number\n /** Seconds view, kept for CMCD consumers. Derived when the caller used ticks. */\n mediaTimeSec: number\n groupDurationSec: number\n /** Exact tick-space inputs. Present only when the caller used the ticks form. */\n timing?: GroupBoundaryTicks\n}\n\n/** Normalized output of the two notifyGroupBoundary input forms. */\ntype ResolvedBoundary = {\n readonly expected: number\n readonly mediaTimeSec: number\n readonly groupDurationSec: number\n readonly timing?: GroupBoundaryTicks\n}\n\n/**\n * Distinct failure modes on the notifyGroupBoundary drift path. Each gets its\n * own bounded warn budget (see `#skipWithWarning`), so a stream that trips one\n * cannot permanently silence the diagnostic for a genuinely different one.\n *\n * Deliberately a closed union rather than `string`: the tag is the dedup key,\n * and a typo would silently mint a second budget for an existing reason.\n */\ntype GroupAlignSkipReason =\n | 'invalid-tick-inputs'\n // Currently UNREACHABLE, kept as defence in depth \u2014 see the catch in\n // #resolveTicks. Do not write a test chasing it.\n | 'ticks-formula-threw'\n | 'invalid-seconds-inputs'\n | 'sub-nanosecond-duration'\n | 'media-time-overflow'\n | 'seconds-formula-threw'\n | 'callback-threw'\n\n/**\n * Warns allowed per `GroupAlignSkipReason` for the lifetime of a router.\n *\n * Not a single flat number (PCL-2211, #329 review). The six resolver reasons\n * are closed, deterministic conditions: once you have seen\n * `sub-nanosecond-duration` on this router, the second occurrence carries no\n * new information, so one slot is the right budget and repeated malformed\n * catalog input stays silent.\n *\n * `callback-threw` is the opposite. It wraps arbitrary downstream consumer\n * code and interpolates `err.message` *because the error varies*. Under one\n * slot, a consumer that throws at startup permanently hides an unrelated\n * exception later \u2014 including one from a *replacement* consumer registered\n * through `onGroupDrift()`, which is a public setter with no budget reset.\n * That is the same argument `#skipWithWarning` makes against a single shared\n * boolean, one level down. A small cap bounds the flood, at the cost of\n * silencing distinct errors past the cap: three throws at startup exhaust\n * `callback-threw`, and a replacement consumer's unrelated exception is then\n * silenced permanently \u2014 the same failure as the one-slot budget, moved from\n * the 1st throw to the 4th. That residual is the inherent price of a bounded\n * budget and is accepted deliberately; the alternative is an unbounded warn.\n *\n * Deliberately NOT a reset on `onGroupDrift()` re-registration: that setter is\n * public and unthrottled, so a caller re-registering per boundary would\n * refresh the budget every time and restore the exact flood this prevents.\n *\n * A total `Record` rather than a lookup with a default: the union is closed, so\n * a new reason that forgets to declare its budget is a compile error \u2014 the\n * same typo-safety rationale that makes the union closed in the first place.\n */\nconst GROUP_ALIGN_WARN_BUDGET: Record<GroupAlignSkipReason, number> = {\n 'invalid-tick-inputs': 1,\n 'ticks-formula-threw': 1,\n 'invalid-seconds-inputs': 1,\n 'sub-nanosecond-duration': 1,\n 'media-time-overflow': 1,\n 'seconds-formula-threw': 1,\n 'callback-threw': 3,\n}\n\ntype FrameCb = (data: Uint8Array, meta: FrameMeta) => void\ntype SwitchCb = (source: SourceKind) => void | Promise<void>\n\nexport interface MediaRouterOptions {\n /** Set false for video-only streams. Default: true. */\n requireAudio?: boolean\n /**\n * Audio fallback timeout (ms): force multicast activation if video keyframe\n * arrived but audio hasn't within this window. Catalog-derived via\n * computeFecDeliveryWindowMs (the single FEC recovery-window source of truth).\n * Omit to disable the independent audio-fallback timer (no magic default).\n */\n audioFallbackMs?: number\n /**\n * Number of binary-path frames to allow through during a crossfade transition.\n * Derive from FEC block size (e.g., ceil(K / groupsPerBlock)). Default: 10.\n */\n crossfadeFrames?: number\n /**\n * Max pre-boundary overlay frames to buffer before dropping.\n * Derive from FEC config (e.g., ceil(K * 1.5)). Default: 128.\n */\n maxOverlayBuffer?: number\n /**\n * Max concurrent in-flight repairs. Prevents relay SUBSCRIBE flood (T-13-01 mitigation).\n * Default: 2.\n */\n maxRepairInFlight?: number\n /**\n * Allow multicast takeover from a delta frame after the player has confirmed\n * that MoQ already seeded the same decoder timeline with a video keyframe.\n * Default false: decoder-reset paths still need multicast to provide a RAP.\n */\n allowDeltaMulticastTakeoverAfterMoqKeyframe?: boolean\n}\n\n/**\n * A per-track source that emits frames independently of the binary MoQ/multicast path.\n * Used by setTrackSource() to wire specific tracks to specific transport sources.\n */\nexport interface TrackSource {\n name: string\n /**\n * Register a frame callback. Returns an unsubscribe function.\n * MediaRouter calls unsubscribe() when clearTrackSource() is called.\n */\n onFrame(cb: (data: Uint8Array, meta: FrameMeta) => void): () => void\n /**\n * Optional error callback for when the source fails.\n * MediaRouter uses this to trigger failback to binary switching for this track.\n */\n onError?(cb: () => void): () => void\n /** Optional dispose hook called when clearTrackSource() is called. */\n dispose?(): void\n}\n\n/**\n * Thrown when setTrackSource() is called for a track that already has a source\n * without an intervening clearTrackSource(). Prevents silent concurrent-assignment\n * races (T-02-16 mitigation).\n */\nexport class TrackSourceConflictError extends Error {\n constructor(public readonly trackName: string) {\n super(`Track source already assigned for '${trackName}'; call clearTrackSource first`)\n this.name = 'TrackSourceConflictError'\n }\n}\n\n/**\n * Migration policy for setTrackSource() transport switching.\n *\n * - 'crossfade': Overlap old (binary) and new source briefly (CROSSFADE_WINDOW_FRAMES),\n * then suppress the binary path. Default for audio.\n * - 'drop': Cut to new source immediately. Binary path suppressed at once.\n * Suitable for latency-critical tracks.\n * - 'wait': Defer migration until first FEC block boundary (same as overlay=true).\n * Default for video \u2014 avoids decoder reset mid-block.\n */\nexport type MigrationPolicy = 'crossfade' | 'drop' | 'wait'\n\n/** Default crossfade window \u2014 override via MediaRouterOptions.crossfadeFrames. */\nconst DEFAULT_CROSSFADE_FRAMES = 10\n\n/** Options passed to setTrackSource(). */\nexport interface SetTrackSourceOptions {\n /**\n * If true, this source is an overlay track that must wait for the first FEC\n * block boundary signal (via notifyFecBlockBoundary()) before emitting frames.\n * Frames arriving before the first boundary are buffered up to MAX_OVERLAY_BUFFER.\n *\n * Backwards compat: equivalent to policy: 'wait'.\n */\n overlay?: boolean\n /**\n * Migration policy for the transport switch. Overrides `overlay` when provided.\n * See MigrationPolicy for semantics.\n */\n policy?: MigrationPolicy\n}\n\n/** Default overlay buffer cap \u2014 override via MediaRouterOptions.maxOverlayBuffer. */\nconst DEFAULT_MAX_OVERLAY_BUFFER = 128\n\n/** Internal per-track source record. */\ninterface TrackSourceRecord {\n source: TrackSource\n unsubscribeFrame: () => void\n unsubscribeError?: () => void\n emitCount: number\n}\n\nexport class MediaRouter {\n #active: SourceKind = 'moq'\n #multicastVideoKeyed = false\n #multicastAudioReceived = false\n #moqVideoKeyframeEmitted = false\n #requireAudio: boolean\n #suppressed = false // true after multicast confirmed \u2014 suppresses MoQ\n #audioFallbackTimer: ReturnType<typeof setTimeout> | null = null\n\n // Configurable constants (from catalog/FEC config, not magic numbers)\n readonly #audioFallbackMs: number\n readonly #crossfadeFrames: number\n readonly #maxOverlayBuffer: number\n readonly #allowDeltaMulticastTakeoverAfterMoqKeyframe: boolean\n\n // \u2500\u2500 Keyframe repair state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** Groups currently being repaired \u2014 idempotent guard per Pitfall 2 (T-13-01 mitigation) */\n #repairInFlight = new Set<number>()\n /** Max concurrent repairs \u2014 prevents relay SUBSCRIBE flood (T-13-01 mitigation) */\n readonly #maxRepairInFlight: number\n /** Callback to request MoQ subscribe for a specific group (wired by player) */\n #onRepairRequest: ((expectedGroupId: number, trackName: string) => void) | null = null\n\n #onVideoFrame: FrameCb | null = null\n #onAudioFrame: FrameCb | null = null\n #onOverlayFrame: FrameCb | null = null\n #onSourceSwitch: SwitchCb | null = null\n #sourceSwitchReady: Promise<void> | null = null\n #sourceSwitchToken = 0\n #pendingMulticastToken: number | null = null\n #pendingMulticastVideo: { data: Uint8Array; meta: FrameMeta } | null = null\n #pendingMulticastAudio: { data: Uint8Array; meta: FrameMeta } | null = null\n\n // Per-track source state\n #trackSources = new Map<string, TrackSourceRecord>()\n\n // Track type registry: trackName \u2192 { type, source }.\n // Init data / inline signaling is authoritative until catalog updates.\n // Priority: 'init' > 'catalog' > 'name' (backward compat fallback).\n #trackTypes = new Map<string, { type: FrameKind; source: 'catalog' | 'init' }>()\n\n // Crossfade state: maps trackName \u2192 remaining binary-path frames allowed during window\n #crossfadeCounters = new Map<string, number>()\n\n // Overlay alignment state\n #overlayBoundaryReached = false\n #overlayBuffer: Array<{ data: Uint8Array; meta: FrameMeta }> = []\n\n // \u2500\u2500 Phase 14: deltaActive state machine (D-11..D-14) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // No timers \u2014 state transitions driven entirely by notifyDeltaBlock() callbacks.\n // All thresholds derived from catalog fields or throw at configureDelta() time.\n #deltaConfigured = false\n #deltaActive: boolean = true\n #deltaWindow: boolean[] = [] // ring buffer: true = block failed\n #deltaWindowSize: number = 0\n #deltaFailThresholdRatio: number = 0 // P/K\n #deltaRecoverThresholdRatio: number = 0 // (P/K)/2 \u2014 hysteresis (D-13)\n #deltaProbeIntervalMs: number = 0\n #lastProbeAt: number = 0\n #onDeltaFallbackCb: (() => void) | null = null\n #onDeltaRecoverCb: (() => void) | null = null\n\n constructor(options?: MediaRouterOptions) {\n this.#requireAudio = options?.requireAudio ?? true\n // Catalog-derived \u2014 the caller passes an audioFallbackMs computed from the\n // FEC recovery window (computeFecDeliveryWindowMs). No magic default: when\n // absent, the independent audio-fallback timer is disabled (0) and\n // activation waits for real audio arrival.\n this.#audioFallbackMs = options?.audioFallbackMs ?? 0\n this.#crossfadeFrames = options?.crossfadeFrames ?? DEFAULT_CROSSFADE_FRAMES\n this.#maxOverlayBuffer = options?.maxOverlayBuffer ?? DEFAULT_MAX_OVERLAY_BUFFER\n this.#maxRepairInFlight = options?.maxRepairInFlight ?? 2\n this.#allowDeltaMulticastTakeoverAfterMoqKeyframe =\n options?.allowDeltaMulticastTakeoverAfterMoqKeyframe ?? false\n }\n\n onVideoFrame(cb: FrameCb) { this.#onVideoFrame = cb }\n onAudioFrame(cb: FrameCb) { this.#onAudioFrame = cb }\n onOverlayFrame(cb: FrameCb) { this.#onOverlayFrame = cb }\n onSourceSwitch(cb: SwitchCb) { this.#onSourceSwitch = cb }\n\n // \u2500\u2500 Group-alignment observer (align.ts formula) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Observation only \u2014 does NOT alter routing. Producers are wired in\n // follow-up PRs; when they land they will call\n // CmcdProducer.incrementSwitchMiscount() + emitSwitchDrift() from the\n // onGroupDrift callback so divergence between packet-stamped groupIds\n // and the shared formula surfaces on `x-bc-switch-drift-us` /\n // `x-bc-switch-miscount`. Canonical Go impl: Blockcast/multicast\n // cmd/caddy/sender/group_align.go (commit 104eeff). Canonical Rust impl:\n // moqtail/moqtail apps/relay/src/server/abr.rs (branch\n // experimental/server-side-abr, commit 39accbf).\n #onGroupDrift: ((evt: GroupDriftEvent) => void) | null = null\n #groupAlignWarnCounts = new Map<GroupAlignSkipReason, number>()\n onGroupDrift(cb: (evt: GroupDriftEvent) => void): void {\n this.#onGroupDrift = cb\n }\n\n /**\n * Report a group boundary observed on an incoming track. Cross-checks the\n * packet-stamped groupId against the shared `align.ts` formula and fires\n * the onGroupDrift callback when they disagree.\n *\n * Two input forms:\n * - **Ticks (preferred).** `notifyGroupBoundary(track, id, { mediaTimeTicks,\n * groupDurationTicks, timescale })` \u2014 pure integer math via\n * `align.groupIdForTicks`, no rounding anywhere.\n * - **Seconds (deprecated).** The 4-argument form. Quantizes both operands\n * to whole nanoseconds, which is exact only when `groupDurationSec` is a\n * whole number of nanoseconds. It is not for 33333@90k, 1001@30k or\n * 1024@44.1k, and there `expected` diverges by a whole group from the\n * second boundary onward \u2014 a false drift event on every subsequent\n * group, and a systematically wrong CMCD `x-bc-switch-drift-us`.\n *\n * No-op when no onGroupDrift callback is registered (avoid unnecessary work\n * on hot paths).\n *\n * Hot-path robustness (does NOT throw on bad inputs \u2014 the ingest stream\n * must not be torn down by a malformed catalog or a disposed producer):\n * - Invalid duration (NaN / <=0) or non-finite media time \u2192 warn + early\n * return (same convention as audioFallbackMs).\n * - Any throw out of the align formula (e.g. the safe-integer overflow\n * guard at ~104 days of media time on the seconds path) is caught +\n * logged so an unusually long-running stream cannot tear down ingest.\n * - Callback throws are caught + logged; ingest continues.\n * - Skip warnings are coalesced **per failure mode** for the lifetime\n * of the router (never reset, including across `dispose()`), so a hot\n * path cannot flood the log while a genuinely new failure mode still\n * gets heard. Deterministic modes get one line; a throwing consumer\n * callback gets a small budget because its error varies. See\n * `#skipWithWarning` and `GROUP_ALIGN_WARN_BUDGET`.\n *\n * Init-track filter: media time === 0 with a non-zero stamped groupId is\n * treated as an init/ambient track emitting pre-PTS-zero packets and is\n * skipped (matches Go's base-clamp intent without producing spurious\n * drift).\n *\n * NOTE: do NOT call from the repair path (feedRepair). Repair frames carry\n * the groupId they are recovering for, not a freshly-stamped one.\n */\n notifyGroupBoundary(\n trackName: string,\n packetGroupId: number,\n timing: GroupBoundaryTicks,\n ): void\n /**\n * @deprecated Pass a {@link GroupBoundaryTicks} instead. The seconds form\n * rounds to whole nanoseconds and reports phantom drift on catalogs whose\n * `groupDurationTicks / timescale` is not ns-exact.\n */\n notifyGroupBoundary(\n trackName: string,\n packetGroupId: number,\n mediaTimeSec: number,\n groupDurationSec: number,\n ): void\n notifyGroupBoundary(\n trackName: string,\n packetGroupId: number,\n timingOrMediaTimeSec: GroupBoundaryTicks | number,\n groupDurationSec?: number,\n ): void {\n if (!this.#onGroupDrift) return\n\n const resolved =\n typeof timingOrMediaTimeSec === 'object' && timingOrMediaTimeSec !== null\n ? this.#resolveTicks(trackName, timingOrMediaTimeSec)\n : this.#resolveSeconds(\n trackName,\n timingOrMediaTimeSec as number,\n groupDurationSec as number,\n )\n if (!resolved) return\n\n // Init/ambient track heuristic: mediaTime=0 with stamped groupId !== 0\n // is a signaling packet pre-PTS-zero, not a real drift. Skipping here\n // avoids every stream opening with a false drift event.\n if (resolved.mediaTimeSec === 0 && packetGroupId !== 0) return\n\n const expected = resolved.expected\n const result = driftCheck(expected, packetGroupId)\n if (result.kind === 'aligned') return\n\n try {\n this.#onGroupDrift({\n trackName,\n expectedGroupId: expected,\n actualGroupId: packetGroupId,\n deltaGroups: result.deltaGroups,\n mediaTimeSec: resolved.mediaTimeSec,\n groupDurationSec: resolved.groupDurationSec,\n ...(resolved.timing ? { timing: resolved.timing } : {}),\n })\n } catch (err) {\n // A disposed CmcdProducer (or any downstream consumer) throwing here\n // must NOT tear down the MoQ ingest stream. Log and continue.\n //\n // Routed through the same per-reason budget as the resolver skips: this\n // fires once per DRIFT EVENT, so a consumer that throws on every\n // boundary would otherwise flood at boundary rate \u2014 exactly the failure\n // mode #skipWithWarning exists to prevent, and exactly the disposed-\n // producer scenario this comment already cites (#329 review).\n this.#skipWithWarning(\n 'callback-threw',\n `[MediaRouter] onGroupDrift callback threw (track=${trackName} expected=${expected} actual=${packetGroupId}); continuing: ${(err as Error)?.message ?? err}`,\n )\n }\n }\n\n /**\n * Warn at most `GROUP_ALIGN_WARN_BUDGET[reason]` times **per reason**, across\n * every skip and post-emit failure on the drift path.\n *\n * Note the two call shapes: the resolvers call this *instead of* computing a\n * groupId, and return its `null` to skip the drift check; the `onGroupDrift`\n * catch calls it after the check already ran and the event was already\n * emitted, so there only the warn is coalesced and the `null` is discarded.\n *\n * Keyed on a short stable `reason` tag, NOT on the formatted message: the\n * messages interpolate track name and operand values, so message-keying\n * would hand a malformed stream a fresh warn slot on every boundary \u2014 the\n * log flood this coalescing exists to prevent.\n *\n * Per-reason rather than one shared boolean (PCL-2211, #329 review): the\n * budget is never reset \u2014 not by `dispose()`, not on source switch \u2014 so a\n * single flag means the FIRST malformed boundary of a long-running stream\n * permanently silences every later, genuinely different failure mode. The\n * throw/overflow diagnostics carry the operand values (`ticks=`, `durTicks=`,\n * `err.message`) an incident responder actually needs, and under one shared\n * flag those are precisely the ones most likely to be already spent by an\n * unrelated NaN at catalog load.\n *\n * A closed, bounded set of reasons times a small per-reason budget is still\n * bounded, so this cannot flood. See `GROUP_ALIGN_WARN_BUDGET` for why the\n * budget is not uniform.\n */\n #skipWithWarning(reason: GroupAlignSkipReason, message: string): null {\n const seen = this.#groupAlignWarnCounts.get(reason) ?? 0\n if (seen < GROUP_ALIGN_WARN_BUDGET[reason]) {\n this.#groupAlignWarnCounts.set(reason, seen + 1)\n console.warn(message)\n }\n return null\n }\n\n /**\n * Exact path: integers straight from the catalog, no rounding. `mediaTimeSec`\n * / `groupDurationSec` are derived for reporting only and never feed the\n * groupId computation.\n */\n #resolveTicks(\n trackName: string,\n timing: GroupBoundaryTicks,\n ): ResolvedBoundary | null {\n const { mediaTimeTicks, groupDurationTicks, timescale } = timing\n if (\n !Number.isInteger(mediaTimeTicks) ||\n !Number.isInteger(groupDurationTicks) ||\n groupDurationTicks <= 0 ||\n !Number.isInteger(timescale) ||\n timescale <= 0\n ) {\n return this.#skipWithWarning(\n 'invalid-tick-inputs',\n `[MediaRouter] notifyGroupBoundary invalid tick inputs (track=${trackName} mediaTimeTicks=${mediaTimeTicks} groupDurationTicks=${groupDurationTicks} timescale=${timescale}); skipping drift check. Pre-validate at catalog load time.`,\n )\n }\n try {\n return {\n expected: groupIdForTicks(mediaTimeTicks, groupDurationTicks),\n mediaTimeSec: mediaTimeTicks / timescale,\n groupDurationSec: groupDurationTicks / timescale,\n // Snapshot, not the caller's reference: a producer that reuses one\n // scratch object per boundary would otherwise retroactively mutate\n // every GroupDriftEvent it has already emitted. Three integers.\n timing: { mediaTimeTicks, groupDurationTicks, timescale },\n }\n } catch (err) {\n // UNREACHABLE as of today, and deliberately kept. groupIdForTicks throws\n // on exactly two conditions \u2014 !Number.isInteger(ticks) and\n // !Number.isInteger(groupDurationTicks) || groupDurationTicks <= 0 \u2014 and\n // the guard above pre-rejects both; Math.floor(a / b) does not throw.\n //\n // Kept because align.ts is a separate module that can grow a new\n // precondition (a safe-integer check, say) without this file changing,\n // and the no-throw contract must hold on that day too. Nobody should\n // write a test chasing this branch \u2014 see BLO-21176 note.\n //\n // NOTE the asymmetry this implies: the seconds path is the only one that\n // can actually reach its catch, because durTicks <= 0 cannot catch\n // Infinity and Number.isSafeInteger guards only `ticks`. That is the real\n // shape of the AC#5a bug, not an oversight in this resolver.\n return this.#skipWithWarning(\n 'ticks-formula-threw',\n `[MediaRouter] notifyGroupBoundary: groupIdForTicks threw (track=${trackName} mediaTimeTicks=${mediaTimeTicks} groupDurationTicks=${groupDurationTicks}); skipping drift check: ${(err as Error)?.message ?? err}`,\n )\n }\n }\n\n /**\n * Legacy seconds path. Quantizes to a synthetic 1e9 Hz timescale \u2014 the exact\n * behaviour the deprecated `align.groupIdFor` wrapper had, reproduced here so\n * that wrapper has no non-test callers left and can be deleted (BLO-21176).\n *\n * Inherits that wrapper's ns-rounding error; see the `@deprecated` overload.\n * Delete this branch once every caller passes ticks.\n */\n #resolveSeconds(\n trackName: string,\n mediaTimeSec: number,\n groupDurationSec: number,\n ): ResolvedBoundary | null {\n if (\n !Number.isFinite(groupDurationSec) ||\n groupDurationSec <= 0 ||\n !Number.isFinite(mediaTimeSec)\n ) {\n return this.#skipWithWarning(\n 'invalid-seconds-inputs',\n `[MediaRouter] notifyGroupBoundary invalid inputs (track=${trackName} mediaTimeSec=${mediaTimeSec} groupDurationSec=${groupDurationSec}); skipping drift check. Pre-validate at catalog load time.`,\n )\n }\n // 1e9 Hz keeps both operands inside MAX_SAFE_INTEGER for ~104 days of\n // media time. Past that Math.round yields a non-safe integer that\n // Number.isInteger still accepts, so bail explicitly rather than\n // silently quantizing \u2014 ingest must survive either way.\n const TS = 1_000_000_000\n const ticks = mediaTimeSec < 0 ? 0 : Math.round(mediaTimeSec * TS)\n const durTicks = Math.round(groupDurationSec * TS)\n if (durTicks <= 0) {\n return this.#skipWithWarning(\n 'sub-nanosecond-duration',\n `[MediaRouter] notifyGroupBoundary: groupDurationSec=${groupDurationSec} rounds to <1 tick at 1e9 Hz (track=${trackName}); skipping drift check. Pass GroupBoundaryTicks for sub-nanosecond durations.`,\n )\n }\n if (!Number.isSafeInteger(ticks)) {\n return this.#skipWithWarning(\n 'media-time-overflow',\n `[MediaRouter] notifyGroupBoundary: mediaTimeSec=${mediaTimeSec} overflows safe-integer ticks at 1e9 Hz (>~104 days, track=${trackName}); skipping drift check. Pass GroupBoundaryTicks with the catalog timescale.`,\n )\n }\n // Same try as #resolveTicks: a finite groupDurationSec can still overflow\n // the * 1e9 multiply to Infinity, which clears the durTicks <= 0 bail above\n // but fails Number.isInteger inside groupIdForTicks. Without this catch the\n // throw escapes notifyGroupBoundary and breaks its documented no-throw\n // contract \u2014 the one path #resolveTicks already guards and this did not.\n try {\n return {\n expected: groupIdForTicks(ticks, durTicks),\n mediaTimeSec,\n groupDurationSec,\n }\n } catch (err) {\n return this.#skipWithWarning(\n 'seconds-formula-threw',\n `[MediaRouter] notifyGroupBoundary: groupIdForTicks threw (track=${trackName} mediaTimeSec=${mediaTimeSec} groupDurationSec=${groupDurationSec} ticks=${ticks} durTicks=${durTicks}); skipping drift check: ${(err as Error)?.message ?? err}`,\n )\n }\n }\n\n get activeSource(): SourceKind { return this.#active }\n\n // \u2500\u2500 Keyframe repair API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Register callback for keyframe repair requests. Player wires this to issue MoQ SUBSCRIBE. */\n onRepairRequest(cb: (expectedGroupId: number, trackName: string) => void): void {\n this.#onRepairRequest = cb\n }\n\n /**\n * Request repair for a lost keyframe group. Only fires when activeSource is 'multicast'.\n * Idempotent per groupId \u2014 duplicate calls for the same group are suppressed.\n * @param expectedGroupId - The group ID expected but not received\n * @param trackName - Track name for the SUBSCRIBE (e.g. 'video'). Default: 'video'.\n */\n repairLostGroup(expectedGroupId: number, trackName = 'video'): void {\n if (this.#active !== 'multicast') return // only repair on multicast path\n if (this.#repairInFlight.has(expectedGroupId)) return // idempotent\n if (this.#repairInFlight.size >= this.#maxRepairInFlight) {\n console.warn(\n `[MediaRouter] max repair in flight (${this.#maxRepairInFlight}), dropping repair for group ${expectedGroupId}`,\n )\n return\n }\n this.#repairInFlight.add(expectedGroupId)\n this.#onRepairRequest?.(expectedGroupId, trackName)\n }\n\n /**\n * Feed a recovered keyframe frame from the repair path.\n * Bypasses source arbitration entirely \u2014 goes directly to decoder callbacks.\n * Does NOT change activeSource. Does NOT trigger failbackToMoq.\n */\n feedRepair(data: Uint8Array, meta: FrameMeta, kind: FrameKind): void {\n // Repair only makes sense when multicast is active.\n // Silently drop if MoQ became active (race with failbackToMoq) to avoid\n // injecting stale-timestamp frames into the MoQ decoder path (WR-02).\n if (this.#active !== 'multicast') return\n const routedMeta = { ...meta, source: 'repair' as const, recovered: true }\n if (kind === 'video') this.#onVideoFrame?.(data, routedMeta)\n else this.#onAudioFrame?.(data, routedMeta)\n }\n\n /**\n * Clear repair-in-flight state for a group (called after repair completes or times out).\n */\n clearRepairInFlight(groupId: number): void {\n this.#repairInFlight.delete(groupId)\n }\n\n // \u2500\u2500 Phase 14: deltaActive state machine API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Configure the delta-track fallback state machine from catalog parameters.\n *\n * D-11: Thresholds derive from fec.sourceSymbols, fec.repairSymbols, framerate.\n * D-13: Probe interval derives from deltaProbeIntervalMs or interleaveDepthMs*2.\n * CLAUDE.md: error on bad init \u2014 throws if required inputs are absent.\n *\n * Calling again resets deltaActive=true and clears the window buffer (stream restart).\n */\n configureDelta(cfg: {\n framerate: number\n sourceSymbols: number\n repairSymbols: number\n deltaFailWindowBlocks?: number\n deltaProbeIntervalMs?: number\n interleaveDepthMs?: number\n }): void {\n if (typeof cfg.framerate !== 'number' || !Number.isFinite(cfg.framerate) || cfg.framerate <= 0) {\n throw new Error('[MediaRouter] configureDelta: framerate required and must be > 0')\n }\n if (typeof cfg.sourceSymbols !== 'number' || cfg.sourceSymbols <= 0) {\n throw new Error('[MediaRouter] configureDelta: sourceSymbols (K_delta) required and must be > 0')\n }\n if (typeof cfg.repairSymbols !== 'number' || cfg.repairSymbols <= 0) {\n throw new Error('[MediaRouter] configureDelta: repairSymbols (P_delta) required and must be > 0')\n }\n if (cfg.deltaProbeIntervalMs === undefined && cfg.interleaveDepthMs === undefined) {\n throw new Error(\n '[MediaRouter] configureDelta: either deltaProbeIntervalMs or interleaveDepthMs required for probe interval derivation'\n )\n }\n\n // Derivations \u2014 no magic floors, no hardcoded defaults (CLAUDE.md).\n this.#deltaWindowSize = cfg.deltaFailWindowBlocks ?? Math.ceil(cfg.framerate)\n this.#deltaFailThresholdRatio = cfg.repairSymbols / cfg.sourceSymbols\n this.#deltaRecoverThresholdRatio = this.#deltaFailThresholdRatio / 2\n // Probe interval routes through the ONE FEC recovery-window derivation\n // (computeFecDeliveryWindowMs). Interleave-span + jitter margin only \u2014 the\n // K-cycle production horizon (mediaUnitsPerSecond) is deliberately omitted:\n // this is a re-subscribe cadence, not a block-recovery deadline. With no\n // catalog jitterBufferMs the margin defaults to one interleave window, so\n // the value equals the retired interleaveDepthMs*2 but is now catalog-tunable.\n this.#deltaProbeIntervalMs = cfg.deltaProbeIntervalMs ?? computeFecDeliveryWindowMs({\n interleaveDepthMs: cfg.interleaveDepthMs!,\n sourceSymbols: cfg.sourceSymbols,\n })\n\n // Reset state \u2014 useful for stream restart.\n this.#deltaActive = true\n this.#deltaWindow = []\n this.#lastProbeAt = 0\n this.#deltaConfigured = true\n }\n\n /**\n * Notify the state machine of a completed FEC block for the delta track.\n *\n * @param failed - true if the block was unrecoverable (FEC failure)\n * @param nowMs - caller-injected monotonic ms (no internal timer \u2014 D-13, CLAUDE.md)\n *\n * If configureDelta() has not been called, this is a silent no-op (pre-catalog state).\n */\n notifyDeltaBlock(failed: boolean, nowMs: number): void {\n if (!this.#deltaConfigured) return\n\n // Maintain sliding window \u2014 ring buffer semantics.\n this.#deltaWindow.push(failed)\n if (this.#deltaWindow.length > this.#deltaWindowSize) {\n this.#deltaWindow.shift()\n }\n\n // Window not yet full \u2014 insufficient data for decision.\n if (this.#deltaWindow.length < this.#deltaWindowSize) return\n\n const failedCount = this.#deltaWindow.filter(x => x).length\n const ratio = failedCount / this.#deltaWindowSize\n\n if (this.#deltaActive) {\n // Fallback transition: active \u2192 inactive (T-14-03: fires exactly once)\n if (ratio > this.#deltaFailThresholdRatio) {\n this.#deltaActive = false\n this.#lastProbeAt = nowMs\n this.#onDeltaFallbackCb?.()\n }\n } else {\n // Recovery transition: inactive \u2192 active (after probe interval + low ratio)\n if (\n (nowMs - this.#lastProbeAt) >= this.#deltaProbeIntervalMs &&\n ratio < this.#deltaRecoverThresholdRatio\n ) {\n this.#deltaActive = true\n this.#lastProbeAt = nowMs\n this.#onDeltaRecoverCb?.()\n }\n }\n }\n\n /** Register callback fired when delta track degrades past threshold (D-12). */\n onDeltaFallback(cb: () => void): void {\n this.#onDeltaFallbackCb = cb\n }\n\n /** Register callback fired when delta track recovers below recovery threshold (D-13). */\n onDeltaRecover(cb: () => void): void {\n this.#onDeltaRecoverCb = cb\n }\n\n /** Whether the delta track is currently considered healthy (true = active). */\n get deltaActive(): boolean {\n return this.#deltaActive\n }\n\n // \u2500\u2500 Test-visible accessors for threshold verification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // These expose internal derived values for test assertions (Tests 3,4,5,6,7).\n // Prefixed with 'delta' to namespace clearly within the public API surface.\n\n /** Derived window size \u2014 exposed for test assertions (Test 3, 4). */\n get deltaWindowSize(): number { return this.#deltaWindowSize }\n /** Derived fail threshold ratio (P/K) \u2014 exposed for test assertions (Test 6). */\n get deltaFailThresholdRatio(): number { return this.#deltaFailThresholdRatio }\n /** Derived recovery threshold ratio ((P/K)/2) \u2014 exposed for test assertions (Test 7). */\n get deltaRecoverThresholdRatio(): number { return this.#deltaRecoverThresholdRatio }\n /** Derived probe interval ms \u2014 exposed for test assertions (Test 5a). */\n get deltaProbeIntervalMs(): number { return this.#deltaProbeIntervalMs }\n\n /**\n * Register a track name \u2192 type mapping from catalog.\n * Catalog is the default authority. Init data / inline signaling\n * takes precedence until the next catalog update.\n */\n registerTrackType(trackName: string, type: FrameKind): void {\n // Catalog overwrites any previous registration (including init).\n this.#trackTypes.set(trackName, { type, source: 'catalog' })\n }\n\n /**\n * Register a track name \u2192 type from init data / inline signaling (MMTP FT=0).\n * Authoritative until the next catalog update per \u00A75.3.\n * Does NOT overwrite an existing catalog registration.\n */\n registerTrackTypeFromInit(trackName: string, type: FrameKind): void {\n const existing = this.#trackTypes.get(trackName)\n if (existing?.source === 'catalog') return // catalog already set \u2014 don't downgrade\n this.#trackTypes.set(trackName, { type, source: 'init' })\n }\n\n /**\n * Assign a per-track source. Frames from this source bypass the binary\n * MoQ\u2194multicast switching for this track only.\n *\n * Throws TrackSourceConflictError if a source is already assigned for this\n * track without a clearTrackSource() in between.\n */\n setTrackSource(trackName: string, source: TrackSource, opts?: SetTrackSourceOptions): void {\n if (this.#trackSources.has(trackName)) {\n throw new TrackSourceConflictError(trackName)\n }\n\n // Resolve effective policy. opts.policy takes precedence; overlay=true is legacy 'wait'.\n const effectivePolicy: MigrationPolicy | undefined =\n opts?.policy ?? (opts?.overlay ? 'wait' : undefined)\n\n // For crossfade: open the binary-path window immediately so the first per-track frame\n // triggers the window (counter starts before the frame callback is wired).\n if (effectivePolicy === 'crossfade') {\n this.#crossfadeCounters.set(trackName, this.#crossfadeFrames)\n }\n\n const record: TrackSourceRecord = {\n source,\n unsubscribeFrame: () => {},\n emitCount: 0,\n }\n\n // Wire the frame callback\n record.unsubscribeFrame = source.onFrame((data, meta) => {\n record.emitCount++\n if (effectivePolicy === 'wait') {\n this.#handleOverlayFrame(data, meta)\n } else {\n // 'drop', 'crossfade', or undefined: emit immediately\n this.#emitForTrack(trackName, data, meta)\n }\n })\n\n // Wire the optional error callback \u2014 failback to binary switching\n if (source.onError) {\n record.unsubscribeError = source.onError(() => {\n this.clearTrackSource(trackName)\n })\n }\n\n this.#trackSources.set(trackName, record)\n }\n\n /**\n * Remove the per-track source for the given track.\n * Binary switching resumes for this track after this call.\n */\n clearTrackSource(trackName: string): void {\n const record = this.#trackSources.get(trackName)\n if (!record) return\n\n record.unsubscribeFrame()\n record.unsubscribeError?.()\n record.source.dispose?.()\n this.#trackSources.delete(trackName)\n\n // Clean up crossfade window if one was active for this track\n this.#crossfadeCounters.delete(trackName)\n\n // Reset overlay buffer if removing a non-media track (overlay/unknown)\n const reg = this.#trackTypes.get(trackName)\n const isMedia = reg?.type === 'video' || reg?.type === 'audio' ||\n (reg === undefined && (trackName === 'video' || trackName === 'audio'))\n if (!isMedia) {\n this.#overlayBoundaryReached = false\n this.#overlayBuffer = []\n }\n }\n\n /**\n * Signal that a FEC block boundary has been reached on the base track.\n * Overlay tracks waiting for alignment flush their buffered frames now.\n */\n notifyFecBlockBoundary(): void {\n if (this.#overlayBoundaryReached) return\n this.#overlayBoundaryReached = true\n\n // Flush buffered overlay frames\n const buffered = this.#overlayBuffer.splice(0)\n for (const { data, meta } of buffered) {\n this.#onOverlayFrame?.(data, meta)\n }\n }\n\n /**\n * Return the number of frames emitted from the per-track source for a given\n * track. Used in test-mode to verify per-track routing histogram.\n * Returns 0 if no per-track source is active for the track.\n */\n trackSourceEmitCount(trackName: string): number {\n return this.#trackSources.get(trackName)?.emitCount ?? 0\n }\n\n /** Feed a frame from the MoQ path. Suppressed when multicast is active OR a per-track source owns this track. */\n feedMoq(data: Uint8Array, meta: FrameMeta, kind: FrameKind) {\n // Per-track source takes precedence; allow through during crossfade window\n if (this.#trackSources.has(kind)) {\n const remaining = this.#crossfadeCounters.get(kind)\n if (remaining && remaining > 0) {\n // Decrement crossfade counter and allow this frame through\n const next = remaining - 1\n if (next === 0) {\n this.#crossfadeCounters.delete(kind)\n } else {\n this.#crossfadeCounters.set(kind, next)\n }\n // Fall through to emit below (no return)\n } else {\n return // suppressed \u2014 no crossfade window active\n }\n }\n if (this.#suppressed) return\n // Audio suppression is handled by SessionManager onSuppress callback \u2014\n // do NOT independently suppress based on multicastAudioReceived.\n // The state machine decides when to switch; the router just routes.\n const routedMeta = { ...meta, source: 'moq' as const }\n if (kind === 'video') this.#onVideoFrame?.(data, routedMeta)\n else this.#onAudioFrame?.(data, routedMeta)\n }\n\n /** Feed a frame from the multicast path. Video waits for keyframe before activating. */\n feedMulticast(data: Uint8Array, meta: FrameMeta, kind: FrameKind) {\n // Per-track source takes precedence; allow through during crossfade window\n if (this.#trackSources.has(kind)) {\n const remaining = this.#crossfadeCounters.get(kind)\n if (remaining && remaining > 0) {\n const next = remaining - 1\n if (next === 0) {\n this.#crossfadeCounters.delete(kind)\n } else {\n this.#crossfadeCounters.set(kind, next)\n }\n // Fall through to emit below (no return)\n } else {\n return // suppressed\n }\n }\n\n if (kind === 'video') {\n if (!this.#multicastVideoKeyed) {\n const moqSeededDelta =\n !meta.keyframe &&\n this.#allowDeltaMulticastTakeoverAfterMoqKeyframe &&\n this.#moqVideoKeyframeEmitted\n if (!meta.keyframe && !moqSeededDelta) return // drop until first safe decode point\n this.#multicastVideoKeyed = true\n const pendingMeta: FrameMeta = { ...meta, source: 'multicast' }\n if (!moqSeededDelta) pendingMeta.sourceDiscontinuity = true\n this.#pendingMulticastVideo = {\n data: data.slice(),\n meta: pendingMeta,\n }\n // If audio is required but hasn't arrived yet, start a fallback timer.\n // Audio FEC blocks may recover after video \u2014 don't block activation forever.\n // Only arms when a catalog-derived audioFallbackMs was supplied (>0).\n if (this.#requireAudio && this.#audioFallbackMs > 0 && !this.#multicastAudioReceived && !this.#audioFallbackTimer) {\n this.#audioFallbackTimer = setTimeout(() => {\n if (!this.#multicastAudioReceived && this.#active !== 'multicast') {\n this.#multicastAudioReceived = true // force\n // Also release the MoQ-audio-warmup gate so the fallback actually\n // activates \u2014 otherwise #tryActivateMulticast would still bail.\n this.#moqAudioWarmed = true\n this.#tryActivateMulticast()\n }\n }, this.#audioFallbackMs)\n }\n } else if (this.#active !== 'multicast') {\n // Do not feed video into the multicast decoder while the source switch\n // is still pending. Keep the newest keyframe as the post-activation\n // decoder opener and drop deltas until takeover commits.\n if (meta.keyframe) {\n this.#pendingMulticastVideo = {\n data: data.slice(),\n meta: { ...meta, source: 'multicast', sourceDiscontinuity: true },\n }\n }\n this.#tryActivateMulticast()\n return\n }\n if (this.#active === 'multicast') this.#onVideoFrame?.(data, { ...meta, source: 'multicast' })\n this.#tryActivateMulticast()\n } else {\n this.#multicastAudioReceived = true\n if (this.#audioFallbackTimer) {\n clearTimeout(this.#audioFallbackTimer)\n this.#audioFallbackTimer = null\n }\n // BLO-3035 layer 5: in a multicast-first scenario (no MoQ unicast audio\n // source) `notifyMoqAudioEmit()` is never called and the audio-fallback\n // timer's force-set is bypassed (the timer's inner `!multicastAudioReceived`\n // check returns early because audio already arrived). Without this gate\n // `#moqAudioWarmed` stays false forever and `#tryActivateMulticast` never\n // fires `onSourceSwitch('multicast')` \u2014 the player can't know to emit\n // `__MULTICAST_TAKEOVER_TIME`. Treat the arrival of multicast audio as\n // sufficient evidence to release the warmup gate. For the MoQ\u2192multicast\n // handoff case `notifyMoqAudioEmit()` will already have set this flag, so\n // the assignment is idempotent.\n if (!this.#moqAudioWarmed) this.#moqAudioWarmed = true\n // Activate BEFORE delivering the frame \u2014 the source switch callback\n // (e.g. ring buffer reset) must fire before the first multicast audio\n // frame reaches the backend. Otherwise the NTP timestamp hits the\n // MoQ-era ring buffer base \u2192 overflow \u2192 silence gap.\n this.#tryActivateMulticast()\n // Only emit multicast audio AFTER takeover succeeds. Before takeover,\n // MoQ audio is still flowing through feedMoq \u2192 #onAudioFrame; emitting\n // multicast audio too would double-feed the backend (2x audio frames,\n // pitch wobble and distortion). Post-takeover, feedMoq is suppressed\n // (#suppressed=true) so only multicast audio reaches the backend.\n if (this.#active === 'multicast') {\n this.#onAudioFrame?.(data, { ...meta, source: 'multicast' })\n } else if (this.#pendingMulticastToken !== null) {\n this.#pendingMulticastAudio = { data: data.slice(), meta: { ...meta, source: 'multicast' } }\n }\n }\n }\n\n /**\n * MoQ unicast audio warmup gate. When audio is required, multicast takeover\n * waits until the MoQ unicast audio decoder has emitted at least one frame\n * (so downstream samples-before-takeover counters observe nonzero) or until\n * the existing audio-fallback timer forces the flag true.\n */\n #moqAudioWarmed = false\n\n /** Called by the player when the MoQ unicast audio decoder emits first frame. */\n public notifyMoqAudioEmit(): void {\n if (this.#moqAudioWarmed) return\n this.#moqAudioWarmed = true\n this.#tryActivateMulticast()\n }\n\n /**\n * Called by the player after a MoQ video keyframe has reached the active\n * decoder path. Used only by MSE/MMTP continuity switching; decoder-reset\n * paths leave allowDeltaMulticastTakeoverAfterMoqKeyframe disabled.\n */\n public notifyMoqVideoKeyframeEmit(): void {\n this.#moqVideoKeyframeEmitted = true\n this.#tryActivateMulticast()\n }\n\n #tryActivateMulticast() {\n if (this.#active === 'multicast') return\n if (this.#pendingMulticastToken !== null) return\n if (!this.#multicastVideoKeyed) return\n if (this.#requireAudio && !this.#multicastAudioReceived) return\n if (this.#requireAudio && !this.#moqAudioWarmed) return\n const token = ++this.#sourceSwitchToken\n const maybeReady = this.#onSourceSwitch?.('multicast')\n if (!maybeReady || typeof (maybeReady as Promise<void>).then !== 'function') {\n this.#commitMulticastActivation(token)\n return\n }\n const ready = Promise.resolve(maybeReady)\n this.#pendingMulticastToken = token\n this.#sourceSwitchReady = ready\n void ready.then(\n () => this.#commitMulticastActivation(token),\n err => this.#rejectMulticastActivation(token, err),\n ).finally(() => {\n if (this.#sourceSwitchReady === ready) this.#sourceSwitchReady = null\n })\n }\n\n #commitMulticastActivation(token: number) {\n if (token !== this.#sourceSwitchToken) return\n this.#active = 'multicast'\n this.#suppressed = true\n this.#pendingMulticastToken = null\n const video = this.#pendingMulticastVideo\n this.#pendingMulticastVideo = null\n const audio = this.#pendingMulticastAudio\n this.#pendingMulticastAudio = null\n if (video) this.#onVideoFrame?.(video.data, video.meta)\n if (audio) this.#onAudioFrame?.(audio.data, audio.meta)\n }\n\n #rejectMulticastActivation(token: number, err: unknown) {\n if (token !== this.#sourceSwitchToken) return\n if (this.#pendingMulticastToken === token) this.#pendingMulticastToken = null\n this.#pendingMulticastVideo = null\n this.#pendingMulticastAudio = null\n console.warn('[MediaRouter] onSourceSwitch(multicast) failed', err)\n }\n\n /** Switch back to MoQ immediately (called on multicast failback). */\n failbackToMoq() {\n const hadPendingMulticast = this.#pendingMulticastToken !== null\n if (this.#active === 'moq' && !hadPendingMulticast) return // already on MoQ, no-op\n ++this.#sourceSwitchToken\n this.#active = 'moq'\n this.#suppressed = false\n this.#pendingMulticastToken = null\n this.#pendingMulticastVideo = null\n this.#pendingMulticastAudio = null\n this.#sourceSwitchReady = null\n this.#multicastVideoKeyed = false\n this.#multicastAudioReceived = false\n this.#moqVideoKeyframeEmitted = false\n // A pending audio-fallback timer belongs to the abandoned takeover attempt.\n // Left running it would force-set the audio flags against the next attempt's\n // fresh state (BLO-9413 failback coherence \u2014 every takeover-era flag/timer\n // unwinds on this edge).\n if (this.#audioFallbackTimer) {\n clearTimeout(this.#audioFallbackTimer)\n this.#audioFallbackTimer = null\n }\n this.#onSourceSwitch?.('moq')\n }\n\n /** Dispose timers and per-track sources. Call on teardown to prevent\n * stale timer callbacks firing into garbage-collected closures (WR-03). */\n dispose(): void {\n if (this.#audioFallbackTimer) {\n clearTimeout(this.#audioFallbackTimer)\n this.#audioFallbackTimer = null\n }\n for (const [name] of this.#trackSources) {\n this.clearTrackSource(name)\n }\n }\n\n // \u2500\u2500 Private helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Emit a frame on the correct per-track callback using the type registry.\n * Falls back to name match if no type registered (backward compat). */\n #emitForTrack(trackName: string, data: Uint8Array, meta: FrameMeta): void {\n // Prefer explicit type registry (catalog or init data)\n const reg = this.#trackTypes.get(trackName)\n if (reg?.type === 'video') {\n this.#onVideoFrame?.(data, meta)\n } else if (reg?.type === 'audio') {\n this.#onAudioFrame?.(data, meta)\n } else if (reg !== undefined) {\n // Registered but unknown type \u2014 overlay\n this.#onOverlayFrame?.(data, meta)\n } else {\n // Not registered \u2014 fall back to name match (backward compat for tests/legacy)\n if (trackName === 'video') this.#onVideoFrame?.(data, meta)\n else if (trackName === 'audio') this.#onAudioFrame?.(data, meta)\n else this.#onOverlayFrame?.(data, meta)\n }\n }\n\n /** Handle an overlay frame: buffer until first FEC block boundary, then pass through. */\n #handleOverlayFrame(data: Uint8Array, meta: FrameMeta): void {\n if (this.#overlayBoundaryReached) {\n this.#onOverlayFrame?.(data, meta)\n return\n }\n\n // Buffer until boundary \u2014 enforcing cap for T-02-17 DoS mitigation\n if (this.#overlayBuffer.length >= this.#maxOverlayBuffer) {\n console.warn(\n `[MediaRouter] overlay buffer full (${this.#maxOverlayBuffer} frames); dropping frame ts=${meta.timestamp}`\n )\n return\n }\n this.#overlayBuffer.push({ data, meta })\n }\n}\n", "/**\n * AudioRingBuffer \u2014 plain FIFO audio buffer (no clock recovery).\n *\n * Source of truth for the audio buffer algorithm used in both moq-lib and\n * moqtail AudioWorklet processors. AudioWorklet isolation requires the\n * worklet copy to be self-contained (no imports), so changes here must be\n * manually synced to:\n * - moqtail-private/apps/client-js/src/lib/audio-worklet-processor.js\n * - hang-mmt-fec/js/watch/src/audio/ring-buffer.ts (uses @moq/lite Time types)\n *\n * Rationale: FEC guarantees gap-free, ordered audio on multicast; MoQ\n * delivers ordered audio without drops. The decoder output rate matches\n * the AudioContext destination rate closely enough that skip/dup clock\n * recovery is unnecessary and harmful \u2014 a single sample skip at 48 kHz is\n * a ~21 \u00B5s phase glitch, and repeated skip/dup produces audible pitch\n * wobble on pure tones (measured: 16 Hz offset + \u00B111 Hz spread on a 1 kHz\n * test tone before this refactor).\n */\n\nexport interface AudioRingBufferConfig {\n /** Number of audio channels */\n channels: number;\n /** Sample rate in Hz */\n rate: number;\n /** Buffer latency in milliseconds */\n latencyMs: number;\n /** Start in unstalled state (for source switching) */\n startUnstalled?: boolean;\n}\n\nexport class AudioRingBuffer {\n readonly channels: number;\n readonly rate: number;\n\n #buffer: Float32Array[];\n #writeIndex = 0;\n #readIndex = 0;\n #stalled: boolean;\n\n constructor(config: AudioRingBufferConfig) {\n this.channels = config.channels;\n this.rate = config.rate;\n this.#stalled = !(config.startUnstalled ?? false);\n\n const samples = Math.ceil(config.rate * (config.latencyMs / 1000));\n this.#buffer = [];\n for (let i = 0; i < config.channels; i++) {\n this.#buffer[i] = new Float32Array(samples);\n }\n }\n\n get stalled(): boolean { return this.#stalled; }\n get capacity(): number { return this.#buffer[0].length; }\n get length(): number { return this.#writeIndex - this.#readIndex; }\n\n /**\n * Append decoded audio samples to the FIFO.\n * Timestamp parameter retained for API compatibility but is ignored \u2014\n * FIFO is sequence-based, not time-based.\n */\n write(_timestampUs: number, data: Float32Array[]): void {\n const samples = data[0].length;\n\n const start = this.#writeIndex;\n const end = start + samples;\n\n // Overflow: discard oldest samples (protects against encoder/AudioContext\n // clock drift when one runs faster than the other).\n const overflow = end - this.#readIndex - this.#buffer[0].length;\n if (overflow >= 0) {\n this.#stalled = false;\n this.#readIndex += overflow;\n }\n\n // Unstall at half capacity \u2014 enough prebuffer for bounded jitter.\n if (this.#stalled && (end - this.#readIndex) >= Math.floor(this.#buffer[0].length / 2)) {\n this.#stalled = false;\n }\n\n // Append samples sequentially\n for (let ch = 0; ch < this.channels; ch++) {\n const src = data[ch];\n const dst = this.#buffer[ch];\n for (let i = 0; i < samples; i++) {\n dst[(start + i) % dst.length] = src[i];\n }\n }\n this.#writeIndex = end;\n }\n\n /**\n * Read samples into output buffers \u2014 plain FIFO, no clock recovery.\n * FEC + MoQ delivery guarantees no gaps, so skip/dup is unnecessary.\n * Underrun \u2192 zero-fill.\n */\n read(output: Float32Array[]): number {\n if (this.#stalled) return 0;\n\n const available = this.#writeIndex - this.#readIndex;\n if (available === 0) return 0;\n\n const requested = output[0].length;\n const readSamples = Math.min(available, requested);\n\n for (let ch = 0; ch < this.channels; ch++) {\n const dst = output[ch];\n const src = this.#buffer[ch];\n for (let i = 0; i < readSamples; i++) {\n dst[i] = src[(this.#readIndex + i) % src.length];\n }\n // Zero-fill remainder on underrun\n for (let i = readSamples; i < requested; i++) {\n dst[i] = 0;\n }\n }\n\n this.#readIndex += readSamples;\n return readSamples;\n }\n}\n", "/**\n * MmtpDecodePipeline \u2014 unified MMTP decode pipeline for MoQ and multicast paths.\n *\n * Wraps MmtpContainerParser with optional FEC client integration and a keyframe gate.\n * Designed for the MoQ decode path (hang-mmt-fec audio/video decoders).\n *\n * Two entry points:\n * - feedMoqObject(): raw MMTP packets from MoQ QUIC streams\n * - feedAssembledFrame(): pre-assembled codec frames from multicast (post-reassembly)\n */\n\nimport {\n MmtpContainerParser,\n type FrameMeta,\n type MmtpReorderStats,\n type TrackInfo,\n} from './mmtp-container-parser.js'\nimport type {\n MfuReassemblerBackend,\n MmtpDeliveryProvenance,\n} from './mfu-reassembler.js'\nimport type { MfuReassemblyPolicy } from './mfu-reassembly-policy.js'\n\n/** Minimal FEC client interface for source symbol feeding. */\nexport interface PipelineFecClient {\n addSourceSymbol?(ssId: number, data: Uint8Array): Uint8Array | null\n}\n\nexport interface PipelineConfig {\n codec: string\n /** Catalog video cadence exposed to downstream media stages. */\n framerate?: number\n /** Catalog MMTP group duration exposed to downstream media stages. */\n groupDurationMs?: number\n /** MMTP packet_id for this track \u2014 from catalog. When omitted, parser accepts all packetIds. */\n packetId?: number\n description?: Uint8Array\n reorderWindow?: number\n /**\n * MFU reassembly timeout (ms). Caller derives from catalog `fec.interleaveDepthMs`,\n * already in milliseconds (FFmpeg `moqenc_mmt.c` converts depth-in-frames \u2192\n * `depth_ms = fec_depth * (1000/fps)` before set_fec_config). Defaults to\n * 1000ms when omitted. Pass null for reliable, clock-free MoQ streams;\n * incomplete MFUs then remain bounded by the reassembler's memory limit.\n */\n mfuTimeoutMs?: number | null\n /** Explicit transport policy; preferred over mfuTimeoutMs. */\n reassemblyPolicy?: MfuReassemblyPolicy\n /** Shared Rust/WASM backend for browser playback. */\n reassembler?: MfuReassemblerBackend\n maxBufferedMfus?: number\n}\n\ntype FrameCallback = (data: Uint8Array, meta: FrameMeta) => void\ntype InitCallback = (data: Uint8Array, packetId: number) => void\n\nexport class MmtpDecodePipeline {\n #parser: MmtpContainerParser | null = null\n #fecClient: PipelineFecClient | null = null\n #frameCb: FrameCallback | null = null\n #initCb: InitCallback | null = null\n #needsKeyframe = false\n #codec = ''\n /** Diagnostic counters. The keyframe gate drops silently, so a parser that\n * never marks a frame as a keyframe stalls the whole pipeline with no\n * output anywhere: frames are fed, frames are parsed, and nothing is\n * emitted. Bounded logging only \u2014 these gate nothing. */\n #gateDropped = 0\n #gateEmitted = 0\n #fedObjects = 0\n /** Warned-once latch for feeds arriving with no parser. Kept separate from\n * #fedObjects so that counter stays a pure monotonic count. */\n #warnedNoParser = false\n /** Histogram of feed() outcomes. feed() already reports which of its ~7 exit\n * paths a packet took, via its return value \u2014 but the call below discards it,\n * so a packet that fails header parse, fails MFU extraction, or accumulates\n * forever as an unreassembled fragment is indistinguishable from one that\n * produced a frame. Tally the kinds instead of guessing. */\n #feedOutcomes = new Map<string, number>()\n\n /** Configure the inner parser. Call before feedMoqObject(). */\n configure(config: PipelineConfig): void {\n // Reconfiguration is an ordered boundary: emit frames accepted under the\n // old configuration before replacing its parser and track metadata.\n this.#parser?.flush()\n this.#parser?.dispose()\n\n // The diagnostic tallies are per-configuration, matching the boundary above.\n // Carrying them across would blend two codecs into one histogram and let\n // \"keyframe gate OPENED after N drops\" attribute drops accrued under the\n // previous configuration.\n this.#fedObjects = 0\n this.#gateDropped = 0\n this.#gateEmitted = 0\n this.#feedOutcomes.clear()\n this.#warnedNoParser = false\n\n this.#codec = config.codec\n\n const trackType: 'video' | 'audio' = isVideoCodec(config.codec) ? 'video' : 'audio'\n const trackInfo: TrackInfo = {\n type: trackType,\n codec: config.codec,\n ...(config.framerate !== undefined ? { framerate: config.framerate } : {}),\n ...(config.groupDurationMs !== undefined\n ? { groupDurationMs: config.groupDurationMs }\n : {}),\n }\n const tracks = new Map<number, TrackInfo>()\n if (config.packetId != null) {\n tracks.set(config.packetId, trackInfo)\n }\n // When packetId omitted: empty tracks map \u2192 parser accepts all packets,\n // codec/type derived from content. Caller should provide packetId from catalog.\n\n this.#parser = new MmtpContainerParser({\n tracks,\n reorderWindow: config.reorderWindow ?? 0,\n mfuTimeoutMs: config.mfuTimeoutMs,\n reassemblyPolicy: config.reassemblyPolicy,\n reassembler: config.reassembler,\n maxBufferedMfus: config.maxBufferedMfus,\n defaultTrack: trackInfo,\n })\n\n this.#parser.onFrame((data, meta) => {\n this.#gatedEmit(data, meta)\n })\n this.#parser.onInit((data, packetId) => {\n this.#initCb?.(data, packetId)\n })\n }\n\n /** Inject a FEC client for source symbol feeding on the MoQ path. */\n setFecClient(client: PipelineFecClient): void {\n this.#fecClient = client\n }\n\n /** Register frame output callback. */\n onFrame(cb: FrameCallback): void {\n this.#frameCb = cb\n }\n\n /** Register init segment callback. */\n onInit(cb: InitCallback): void {\n this.#initCb = cb\n }\n\n /** Current shared-parser ordering/drop telemetry. */\n get reorderStats(): Readonly<MmtpReorderStats> | null {\n return this.#parser?.reorderStats ?? null\n }\n\n /**\n * Feed a raw MoQ object payload (one MMTP packet per MoQ object).\n * Optionally feeds source symbols to FEC client before parsing.\n */\n feedMoqObject(packet: Uint8Array): void {\n if (!this.#parser) {\n // Distinguishes \"never configured\" from \"configured but emitting nothing\".\n // Silent today: the caller has no way to see that its feed went nowhere.\n //\n // The warned-once flag is deliberately separate from #fedObjects. Folding\n // it in (a -1 sentinel) makes this guard test \"no parser AND nothing ever\n // fed\", so a feed arriving after dispose() \u2014 which nulls #parser but\n // leaves the count \u2014 would return here silently. That is the exact\n // silent-drop class this file exists to expose, and teardown racing an\n // in-flight feed is the likelier ordering in a live pipeline than\n // feed-before-configure.\n if (!this.#warnedNoParser) {\n this.#warnedNoParser = true\n console.warn('[mmtp-pipeline] feedMoqObject called with NO PARSER configured \u2014 packet dropped')\n }\n return\n }\n this.#fedObjects += 1\n if (this.#fedObjects === 1) {\n console.log(`[mmtp-pipeline] first object fed codec=${this.#codec} bytes=${packet.length}`)\n }\n\n if (this.#fecClient?.addSourceSymbol) {\n const info = this.#parser.parseHeader(packet)\n if (info?.fecPayloadId) {\n this.#fecClient.addSourceSymbol(info.fecPayloadId.ssId, packet)\n }\n }\n\n const record = this.#parser.feed(packet)\n // `null` means feed() bailed at one of its early returns \u2014 header parse,\n // MFU extraction, timedFlag, or a fragmentation-indicator guard.\n const kind = record?.kind ?? 'null(parse-or-extract-failed)'\n const seen = (this.#feedOutcomes.get(kind) ?? 0) + 1\n this.#feedOutcomes.set(kind, seen)\n if (seen === 1) {\n console.log(\n `[mmtp-pipeline] feed outcome FIRST '${kind}' at object #${this.#fedObjects} bytes=${packet.length}`,\n )\n }\n if (this.#fedObjects % 200 === 0) {\n const hist = [...this.#feedOutcomes].map(([k, v]) => `${k}=${v}`).join(' ')\n console.log(\n `[mmtp-pipeline] feed histogram @${this.#fedObjects}: ${hist} | emitted=${this.#gateEmitted} gateDropped=${this.#gateDropped}`,\n )\n }\n }\n\n /**\n * Feed a pre-assembled codec frame (from multicast, post-reassembly).\n * Bypasses MMTP parsing \u2014 emits directly through the keyframe gate.\n *\n * Callers MUST supply:\n * - packetSequenceNumber: unique per-AU identifier (typically the MMTP\n * packet_sequence_number of the first fragment). Required for\n * deterministic dedup against duplicates arriving via other paths.\n * - packetId: MMTP packet_id of the track (1=video, 2=audio by convention).\n */\n feedAssembledFrame(\n data: Uint8Array,\n meta: {\n timestamp: number\n keyframe: boolean\n packetSequenceNumber: number\n packetId: number\n sourceDiscontinuity?: boolean\n recovered?: boolean\n /**\n * Whole-frame delivery provenance, when the caller knows it. Absent means\n * the caller never told us \u2014 which is NOT the same as 'unknown', a\n * verdict the caller reached and is asserting. Keep them distinguishable.\n */\n deliveryProvenance?: MmtpDeliveryProvenance\n },\n ): void {\n const frameMeta: FrameMeta = {\n timestamp: meta.timestamp,\n keyframe: meta.keyframe,\n packetId: meta.packetId,\n trackType: isVideoCodec(this.#codec) ? 'video' : 'audio',\n packetSequenceNumber: meta.packetSequenceNumber,\n }\n if (meta.sourceDiscontinuity) frameMeta.sourceDiscontinuity = true\n if (meta.recovered) frameMeta.recovered = true\n // Unlike the boolean flags above, forward this whenever it is present \u2014\n // 'unknown' is a real verdict, not a falsy value to drop.\n if (meta.deliveryProvenance !== undefined)\n frameMeta.deliveryProvenance = meta.deliveryProvenance\n this.#parser?.routeFrame(data, frameMeta)\n }\n\n /** Request keyframe \u2014 gates output until a keyframe arrives. */\n requestKeyframe(): void {\n this.#needsKeyframe = true\n }\n\n /** Emit buffered ordered frames. Finite consumers call this before dispose(). */\n flush(): void {\n this.#parser?.flush()\n }\n\n /** Clean up the pipeline. Finite inputs pass flush=true at EOS; live\n * cancellation keeps the default drop-only teardown behavior. */\n dispose(options: { flush?: boolean } = {}): void {\n const droppedBefore = this.#parser?.reorderStats.droppedOnLiveDispose ?? 0\n if (options.flush) this.#parser?.flush()\n this.#parser?.dispose()\n const droppedFrames = (this.#parser?.reorderStats.droppedOnLiveDispose ?? droppedBefore) -\n droppedBefore\n if (droppedFrames > 0) {\n console.warn(\n `[MmtpDecodePipeline] live dispose dropped ${droppedFrames} buffered frame(s); ` +\n 'finite inputs must use dispose({ flush: true })',\n )\n }\n this.#parser = null\n this.#frameCb = null\n this.#initCb = null\n this.#fecClient = null\n }\n\n #gatedEmit(data: Uint8Array, meta: FrameMeta): void {\n let sourceDiscontinuity = false\n if (this.#needsKeyframe) {\n if (!meta.keyframe) {\n // Silent drop: the caller keeps feeding, the parser keeps parsing, and\n // no layer reports anything. If the parser never sets meta.keyframe the\n // gate never opens and the decoder is never called.\n this.#gateDropped += 1\n if (this.#gateDropped <= 3 || this.#gateDropped % 200 === 0) {\n console.warn(\n `[mmtp-pipeline] keyframe gate DROP #${this.#gateDropped} codec=${this.#codec} bytes=${data.length} fed=${this.#fedObjects} emitted=${this.#gateEmitted}`,\n )\n }\n return\n }\n this.#needsKeyframe = false\n sourceDiscontinuity = true\n console.log(\n `[mmtp-pipeline] keyframe gate OPENED codec=${this.#codec} after ${this.#gateDropped} drops, fed=${this.#fedObjects}`,\n )\n }\n this.#gateEmitted += 1\n if (this.#gateEmitted === 1) {\n console.log(`[mmtp-pipeline] first frame emitted codec=${this.#codec} bytes=${data.length}`)\n }\n const outMeta = sourceDiscontinuity ? { ...meta, sourceDiscontinuity: true } : meta\n this.#frameCb?.(data, outMeta)\n }\n}\n\nfunction isVideoCodec(codec: string): boolean {\n return codec.startsWith('avc') ||\n codec.startsWith('hev1') || codec.startsWith('hvc1') ||\n codec.startsWith('av01') ||\n codec.startsWith('vp09') || codec.startsWith('vp8')\n}\n", "/**\n * TrackPipeline \u2014 per-track transport promotion + ABR integration.\n *\n * Coordinates transport promotion (MoQ \u2192 multicast/AMT) on a per-track basis.\n * Shared by both web players (moq-lib, moqtail) and the IWA.\n *\n * Responsibilities:\n * 1. Takes per-track transport preferences and current capabilities\n * 2. Runs the pure resolveTransports() function to get assignments\n * 3. Calls MediaRouter.setTrackSource() per-track with the resolved policy\n * 4. Re-resolves when capabilities change (network switch, multicast detected)\n * 5. Integrates ABR quality changes with track-level subscription management\n *\n * NOT responsible for:\n * - Socket management (that's MulticastTransport / NativeMulticastManager)\n * - FEC decode (that's FECManager)\n * - MMTP parsing (that's MmtpRouter)\n * - Binary MoQ\u2194multicast switching (that's MediaRouter.feedMoq/feedMulticast)\n *\n * Usage:\n * const pipeline = new TrackPipeline(mediaRouter);\n * pipeline.setPreferences(prefs);\n * pipeline.updateCapabilities(caps);\n * // pipeline calls mediaRouter.setTrackSource() automatically\n * // listen for transport changes:\n * pipeline.onTransportChange((trackName, from, to, policy) => { ... });\n */\n\nimport type { MediaRouter, TrackSource } from './media-router.js'\nimport type { MigrationPolicy } from './media-router.js'\n\n// Inline transport types to avoid dep on @blockcast/shared at runtime.\n// These MUST stay compatible with @blockcast/shared/transport-settings.\nexport type TrackTransport = 'multicast' | 'amt' | 'moq' | 'auto'\n\nexport interface TrackTransportPreference {\n trackName: string\n /** Actual media type from catalog (never inferred from name). */\n trackType: 'video' | 'audio'\n preferred: TrackTransport[]\n maxLatencyMs?: number\n migrationPolicy?: MigrationPolicy\n}\n\nexport interface TransportCapabilityReport {\n nativeMulticast: { confirmed: boolean; lastPacketAt?: number }\n amt: { available: boolean; relay?: string; rttMs?: number }\n moqRelay: { reachable: boolean; rttMs?: number }\n networkHint?: 'wifi' | 'cellular' | 'ethernet' | 'unknown'\n observedAt: number\n}\n\nexport interface TrackTransportResolution {\n trackName: string\n assignedTransport: TrackTransport\n reason: 'preferred' | 'fallback' | 'capability-limited' | 'latency-gated' | 'abr-override'\n migrationPolicy: MigrationPolicy\n}\n\n/** Callback for transport change events */\ntype TransportChangeCb = (\n trackName: string,\n from: TrackTransport | null,\n to: TrackTransport,\n policy: MigrationPolicy,\n) => void\n\n/** Callback for ABR quality change requests */\ntype QualityChangeCb = (trackName: string, quality: { name: string; bitrate: number }) => void\n\n/** Factory that creates a TrackSource for a given transport + track */\nexport type TrackSourceFactory = (\n trackName: string,\n transport: TrackTransport,\n) => TrackSource | null\n\n// Default auto expansion: multicast \u2192 amt \u2192 moq\nconst AUTO_CHAIN: Exclude<TrackTransport, 'auto'>[] = ['multicast', 'amt', 'moq']\n\n// Default migration policies\nconst DEFAULT_POLICY: Record<string, MigrationPolicy> = {\n audio: 'crossfade',\n video: 'wait',\n}\n\n/** Per-track state tracked by the pipeline */\ninterface TrackState {\n currentTransport: TrackTransport | null\n preference: TrackTransportPreference\n resolution: TrackTransportResolution | null\n}\n\nexport class TrackPipeline {\n #router: MediaRouter\n #sourceFactory: TrackSourceFactory | null = null\n #tracks = new Map<string, TrackState>()\n #capabilities: TransportCapabilityReport | null = null\n #onTransportChange: TransportChangeCb | null = null\n #onQualityChange: QualityChangeCb | null = null\n\n constructor(router: MediaRouter) {\n this.#router = router\n }\n\n // ---------------------------------------------------------------------------\n // Configuration\n // ---------------------------------------------------------------------------\n\n /**\n * Register a factory that creates TrackSource instances for a given transport.\n * The factory is called when the pipeline resolves a track to a new transport\n * and needs to wire it into the MediaRouter.\n *\n * The factory returns null if the transport is not available for that track\n * (e.g., no multicast socket for this group).\n */\n setSourceFactory(factory: TrackSourceFactory): void {\n this.#sourceFactory = factory\n }\n\n /** Listen for transport change events (after setTrackSource is called). */\n onTransportChange(cb: TransportChangeCb): void {\n this.#onTransportChange = cb\n }\n\n /** Listen for ABR-driven quality change requests. */\n onQualityChange(cb: QualityChangeCb): void {\n this.#onQualityChange = cb\n }\n\n // ---------------------------------------------------------------------------\n // Preferences + Capabilities\n // ---------------------------------------------------------------------------\n\n /**\n * Set per-track transport preferences. Triggers resolution if capabilities\n * are already known. Safe to call multiple times (replaces previous prefs).\n */\n setPreferences(prefs: TrackTransportPreference[]): void {\n for (const pref of prefs) {\n const existing = this.#tracks.get(pref.trackName)\n this.#tracks.set(pref.trackName, {\n currentTransport: existing?.currentTransport ?? null,\n preference: pref,\n resolution: existing?.resolution ?? null,\n })\n }\n // Remove tracks no longer in prefs\n for (const name of this.#tracks.keys()) {\n if (!prefs.find(p => p.trackName === name)) {\n this.#router.clearTrackSource(name)\n this.#tracks.delete(name)\n }\n }\n if (this.#capabilities) this.#resolve()\n }\n\n /**\n * Update capability snapshot. Triggers re-resolution against current prefs.\n * Called by the IWA's CapabilityObserver, or by the player's extension bridge\n * on capability change events.\n */\n updateCapabilities(caps: TransportCapabilityReport): void {\n this.#capabilities = caps\n if (this.#tracks.size > 0) this.#resolve()\n }\n\n /**\n * Force a specific transport for a track (e.g., user override, debug).\n * Skips the resolver; applies immediately.\n */\n forceTransport(trackName: string, transport: TrackTransport, policy?: MigrationPolicy): void {\n const state = this.#tracks.get(trackName)\n if (!state) return\n\n const effectivePolicy = policy ?? this.#defaultPolicy(state.preference.trackType)\n this.#applyTransport(trackName, transport, effectivePolicy)\n }\n\n // ---------------------------------------------------------------------------\n // ABR Integration\n // ---------------------------------------------------------------------------\n\n /**\n * Notify the pipeline of an ABR quality change. The pipeline adjusts the\n * transport ceiling and may trigger a transport switch if the new quality\n * exceeds the current transport's capacity.\n */\n notifyQualityChange(trackName: string, quality: { name: string; bitrate: number }): void {\n this.#onQualityChange?.(trackName, quality)\n // Re-resolve: ABR quality may push a track to a different transport\n if (this.#capabilities) this.#resolve()\n }\n\n // ---------------------------------------------------------------------------\n // Query\n // ---------------------------------------------------------------------------\n\n /** Current transport for a track (null if not yet resolved). */\n getTransport(trackName: string): TrackTransport | null {\n return this.#tracks.get(trackName)?.currentTransport ?? null\n }\n\n /** Current resolution for a track (null if not yet resolved). */\n getResolution(trackName: string): TrackTransportResolution | null {\n return this.#tracks.get(trackName)?.resolution ?? null\n }\n\n /** All track names managed by this pipeline. */\n get trackNames(): string[] {\n return [...this.#tracks.keys()]\n }\n\n // ---------------------------------------------------------------------------\n // Teardown\n // ---------------------------------------------------------------------------\n\n dispose(): void {\n for (const name of this.#tracks.keys()) {\n this.#router.clearTrackSource(name)\n }\n this.#tracks.clear()\n this.#capabilities = null\n this.#onTransportChange = null\n this.#onQualityChange = null\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n /** Run the resolver and apply transport changes. */\n #resolve(): void {\n const caps = this.#capabilities!\n for (const [trackName, state] of this.#tracks) {\n const resolution = this.#resolveTrack(state.preference, caps)\n state.resolution = resolution\n\n if (resolution.assignedTransport !== state.currentTransport) {\n this.#applyTransport(trackName, resolution.assignedTransport, resolution.migrationPolicy)\n }\n }\n }\n\n /** Pure per-track resolution (inline, no dependency on @blockcast/shared). */\n #resolveTrack(\n pref: TrackTransportPreference,\n caps: TransportCapabilityReport,\n ): TrackTransportResolution {\n const chain = this.#expandPreferred(pref.preferred)\n\n for (let i = 0; i < chain.length; i++) {\n const transport = chain[i]!\n if (!this.#isAvailable(transport, caps)) continue\n\n return {\n trackName: pref.trackName,\n assignedTransport: transport,\n reason: i === 0 ? 'preferred' : 'fallback',\n migrationPolicy: pref.migrationPolicy ?? this.#defaultPolicy(pref.trackType),\n }\n }\n\n // Nothing available \u2014 stay on current or fall back to moq\n return {\n trackName: pref.trackName,\n assignedTransport: 'moq',\n reason: 'capability-limited',\n migrationPolicy: pref.migrationPolicy ?? this.#defaultPolicy(pref.trackType),\n }\n }\n\n /** Apply a resolved transport to a track via MediaRouter. */\n #applyTransport(trackName: string, transport: TrackTransport, policy: MigrationPolicy): void {\n const state = this.#tracks.get(trackName)\n if (!state) return\n\n const prev = state.currentTransport\n\n // MoQ is the default binary path \u2014 clear per-track source to fall back\n if (transport === 'moq') {\n this.#router.clearTrackSource(trackName)\n state.currentTransport = 'moq'\n if (prev !== 'moq') {\n this.#onTransportChange?.(trackName, prev, 'moq', policy)\n }\n return\n }\n\n // For multicast/amt: create a TrackSource via the factory\n if (!this.#sourceFactory) {\n console.warn(`[TrackPipeline] No source factory registered; cannot create source for ${trackName}/${transport}`)\n return\n }\n\n const source = this.#sourceFactory(trackName, transport)\n if (!source) {\n console.warn(`[TrackPipeline] Factory returned null for ${trackName}/${transport}; keeping current transport`)\n return\n }\n\n // Clear previous per-track source (if any) before setting new one\n if (prev && prev !== 'moq') {\n this.#router.clearTrackSource(trackName)\n }\n\n this.#router.setTrackSource(trackName, source, { policy })\n state.currentTransport = transport\n this.#onTransportChange?.(trackName, prev, transport, policy)\n }\n\n #expandPreferred(preferred: TrackTransport[]): Exclude<TrackTransport, 'auto'>[] {\n const out: Exclude<TrackTransport, 'auto'>[] = []\n for (const t of preferred) {\n if (t === 'auto') out.push(...AUTO_CHAIN)\n else out.push(t)\n }\n return out\n }\n\n #isAvailable(transport: Exclude<TrackTransport, 'auto'>, caps: TransportCapabilityReport): boolean {\n switch (transport) {\n case 'multicast': return caps.nativeMulticast.confirmed\n case 'amt': return caps.amt.available\n case 'moq': return caps.moqRelay.reachable\n }\n }\n\n #defaultPolicy(trackType: 'video' | 'audio'): MigrationPolicy {\n return DEFAULT_POLICY[trackType]!\n }\n}\n", "/**\n * Track Pipeline Setup \u2014 shared wiring helper for both players.\n *\n * Provides a one-call setup that connects TrackPipeline to MediaRouter +\n * MulticastManager, including the TrackSourceFactory that adapts multicast\n * frames into the TrackSource interface expected by MediaRouter.setTrackSource().\n *\n * Usage in both moq-lib (element.ts) and moqtail (player.ts):\n *\n * import { setupTrackPipeline, type TrackDescriptor } from '@blockcast/mmt-container';\n *\n * const tracks: TrackDescriptor[] = catalog.getTracks().map(t => ({\n * name: t.name,\n * type: t.role as 'video' | 'audio',\n * packetId: t.packetId,\n * }));\n *\n * const { pipeline, dispose } = setupTrackPipeline({\n * router,\n * multicastManager: mc,\n * tracks,\n * preferences: transportPrefs,\n * capabilities: caps,\n * onTransportChange: (track, from, to) => { ... },\n * });\n */\n\nimport type { MediaRouter, TrackSource, MigrationPolicy, FrameKind } from './media-router.js'\nimport type { MulticastManager } from './multicast-manager.js'\nimport {\n TrackPipeline,\n type TrackTransport,\n type TrackTransportPreference,\n type TransportCapabilityReport,\n} from './track-pipeline.js'\n\nexport { TrackPipeline }\n\n/**\n * Catalog-derived track descriptor. Carries the actual track type\n * so the pipeline never guesses from track names.\n */\nexport interface TrackDescriptor {\n /** Track name as it appears in the catalog / MoQ namespace. */\n name: string\n /** Actual media type from catalog role. */\n type: FrameKind\n /** MMTP packet ID (used for MulticastManager per-track callbacks). */\n packetId?: number\n}\n\nexport interface SetupTrackPipelineOptions {\n /** MediaRouter instance (already wired to decoder backends). */\n router: MediaRouter\n /** MulticastManager instance (already joined or about to join). */\n multicastManager: MulticastManager\n /** Catalog-derived track descriptors. */\n tracks: TrackDescriptor[]\n /** Per-track transport preferences. */\n preferences?: TrackTransportPreference[]\n /** Initial capability snapshot (optional; call pipeline.updateCapabilities later). */\n capabilities?: TransportCapabilityReport\n /** Called after each per-track transport switch. */\n onTransportChange?: (\n trackName: string,\n from: TrackTransport | null,\n to: TrackTransport,\n policy: MigrationPolicy,\n ) => void\n}\n\nexport interface SetupTrackPipelineResult {\n pipeline: TrackPipeline\n /** Call to tear down the pipeline and all per-track sources. */\n dispose: () => void\n}\n\n/**\n * Create and wire a TrackPipeline into existing MediaRouter + MulticastManager.\n *\n * The factory creates TrackSource adapters that:\n * - For 'multicast': subscribe to MulticastManager's per-track frame callbacks\n * and forward through the TrackSource.onFrame interface.\n * - For 'amt': same as multicast (AMT packets flow through MulticastManager.feedPacket).\n * - For 'moq': no per-track source needed (MoQ is the default binary path).\n */\nexport function setupTrackPipeline(opts: SetupTrackPipelineOptions): SetupTrackPipelineResult {\n const { router, multicastManager, tracks, preferences, capabilities, onTransportChange } = opts\n\n // Build lookup: trackName \u2192 TrackDescriptor\n const trackMap = new Map<string, TrackDescriptor>()\n for (const t of tracks) {\n trackMap.set(t.name, t)\n }\n\n const pipeline = new TrackPipeline(router)\n\n // Register the TrackSource factory that bridges MulticastManager \u2192 TrackSource.\n // Uses TrackDescriptor.type and .packetId \u2014 never guesses from track names.\n pipeline.setSourceFactory((trackName: string, transport: TrackTransport): TrackSource | null => {\n if (transport === 'moq') return null // MoQ is default binary path\n\n const desc = trackMap.get(trackName)\n if (!desc) {\n console.warn(`[TrackPipeline] Unknown track \"${trackName}\" \u2014 no descriptor registered`)\n return null\n }\n\n // Both 'multicast' and 'amt' route through MulticastManager\n // (AMT packets arrive via feedPacket \u2192 same MmtpRouter pipeline)\n return createMulticastTrackSource(multicastManager, desc)\n })\n\n if (onTransportChange) {\n pipeline.onTransportChange(onTransportChange)\n }\n\n if (preferences) {\n pipeline.setPreferences(preferences)\n }\n\n if (capabilities) {\n pipeline.updateCapabilities(capabilities)\n }\n\n return {\n pipeline,\n dispose: () => pipeline.dispose(),\n }\n}\n\n/**\n * Create a TrackSource adapter that bridges MulticastManager frame callbacks\n * into the TrackSource interface expected by MediaRouter.setTrackSource().\n *\n * Routes using the track descriptor's type and packetId \u2014 never name-based.\n * If packetId is available, uses the specific onTrackFrame(packetId) callback\n * for precise per-track routing. Falls back to onVideoFrame/onAudioFrame\n * based on descriptor type.\n */\nfunction createMulticastTrackSource(\n manager: MulticastManager,\n desc: TrackDescriptor,\n): TrackSource {\n let frameCb: ((data: Uint8Array, meta: { timestamp: number; keyframe: boolean }) => void) | null = null\n let errorCb: (() => void) | null = null\n let wired = false\n\n return {\n name: desc.name,\n\n onFrame(cb) {\n frameCb = cb\n\n if (!wired) {\n wired = true\n\n if (desc.packetId != null) {\n // Precise per-packetId routing (preferred)\n manager.onTrackFrame(desc.packetId, (data, meta) => {\n frameCb?.(data, meta)\n })\n } else if (desc.type === 'video') {\n manager.onVideoFrame((data, meta) => {\n frameCb?.(data, meta)\n })\n } else if (desc.type === 'audio') {\n manager.onAudioFrame((data, meta) => {\n frameCb?.(data, meta)\n })\n }\n }\n\n return () => { frameCb = null }\n },\n\n onError(cb) {\n errorCb = cb\n manager.onFailback(() => { errorCb?.() })\n return () => { errorCb = null }\n },\n\n dispose() {\n frameCb = null\n errorCb = null\n wired = false\n },\n }\n}\n", "/**\n * Audio Init Data Parser \u2014 extract frame samples from a codec descriptor.\n *\n * Consumes the RAW codec configuration bytes (AudioSpecificConfig for AAC,\n * OpusHead for Opus) \u2014 i.e. the catalog `description` field (hang\n * `AudioConfig.description`), NOT the catalog `initData` field.\n *\n * Since BLO-9884 these are two distinct catalog fields: `initData` is a\n * base64 ISOBMFF init segment (ftyp + moov) per draft-ietf-moq-cmsf-00 \u00A73.1,\n * while `description` carries the bare codec descriptor. Hang's `root.ts`\n * parses a CMSF `initData` segment with `decodeInitSegment()` and routes the\n * descriptor it recovers (`init.description`) into `description`; the bytes\n * that reach this parser are therefore always the bare descriptor. Do NOT\n * pass a ftyp+moov `initData` blob here \u2014 the ASC bit-reader would interpret\n * the ftyp box size word as an audioObjectType and return a garbage frame\n * count (the audio analogue of the \"Failed to parse avcC\" CMAF regression\n * documented in root.ts).\n *\n * Supported codecs:\n * - AAC (mp4a.40.*): AudioSpecificConfig per ISO 14496-3 \u00A71.6.2.1\n * - Opus: fixed 960 samples per RFC 6716 \u00A72.1.4\n *\n * Reference: hang-mmt-fec/rs/moq-mux/src/import/aac.rs:AacConfig::parse()\n */\n\n// ISO 14496-3 Table 1.14: samplingFrequencyIndex \u2192 Hz\nconst AAC_SAMPLE_RATES = [\n 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,\n 16000, 12000, 11025, 8000, 7350,\n] as const\n\n/**\n * AAC Audio Object Types that determine frame size.\n * ISO 14496-3 Table 1.3:\n * 1 = AAC-Main \u2192 1024 samples\n * 2 = AAC-LC \u2192 1024 samples\n * 3 = AAC-SSR \u2192 1024 samples\n * 4 = AAC-LTP \u2192 1024 samples\n * 5 = SBR \u2192 2048 samples (HE-AAC v1)\n * 6 = AAC-Scalable \u2192 1024 samples\n * 23 = ER AAC-LD \u2192 512 samples\n * 39 = ER AAC-ELD \u2192 480 samples\n * 29 = PS \u2192 2048 samples (HE-AAC v2, implies SBR)\n */\nfunction aacFrameSamplesForObjectType(objectType: number): number {\n switch (objectType) {\n case 5: return 2048 // SBR (HE-AAC v1)\n case 29: return 2048 // PS (HE-AAC v2, implies SBR)\n case 23: return 512 // ER AAC-LD\n case 39: return 480 // ER AAC-ELD\n default: return 1024 // LC, Main, SSR, LTP, Scalable, etc.\n }\n}\n\n/**\n * Parse AAC AudioSpecificConfig (ASC) to extract frame samples.\n *\n * ASC is a bit-packed structure (ISO 14496-3 \u00A71.6.2.1):\n * audioObjectType (5 bits, or 5+6 if ==31)\n * samplingFrequencyIndex (4 bits, explicit rate if ==0xf)\n * channelConfiguration (4 bits)\n * ... (SBR/PS extensions follow)\n *\n * The audioObjectType determines frame samples:\n * LC-AAC \u2192 1024, HE-AAC(SBR) \u2192 2048, AAC-LD \u2192 512, AAC-ELD \u2192 480\n *\n * @param asc - Raw AudioSpecificConfig bytes (NOT ADTS, NOT fMP4 esds box)\n * @returns frame samples, or null if ASC is invalid/too short\n */\nexport function parseAacFrameSamples(asc: Uint8Array): number | null {\n if (asc.length < 2) return null\n\n // Bit reader\n let bitPos = 0\n const readBits = (n: number): number => {\n let val = 0\n for (let i = 0; i < n; i++) {\n const byteIdx = (bitPos + i) >> 3\n const bitIdx = 7 - ((bitPos + i) & 7)\n if (byteIdx >= asc.length) return -1\n val = (val << 1) | ((asc[byteIdx]! >> bitIdx) & 1)\n }\n bitPos += n\n return val\n }\n\n // audioObjectType: 5 bits, extended to 5+6=11 bits if ==31\n let objectType = readBits(5)\n if (objectType === 31) {\n objectType = 32 + readBits(6)\n }\n if (objectType < 0) return null\n\n // samplingFrequencyIndex: 4 bits\n const freqIndex = readBits(4)\n if (freqIndex < 0) return null\n if (freqIndex === 0xf) {\n // Explicit 24-bit sample rate follows\n readBits(24)\n }\n\n // channelConfiguration: 4 bits\n readBits(4)\n\n // Check for SBR/PS signaling (implicit in HE-AAC)\n // If base objectType is AAC-LC and SBR extension is present, frame size doubles\n if (objectType === 2 /* AAC-LC */ && asc.length >= 4) {\n // Check for SBR extension: 11-bit sync word 0x2b7\n const remaining = (asc.length * 8) - bitPos\n if (remaining >= 11) {\n const syncExt = readBits(11)\n if (syncExt === 0x2b7) {\n const extObjectType = readBits(5)\n if (extObjectType === 5 /* SBR */ || extObjectType === 22 /* ER SBR */) {\n return 2048 // SBR present \u2192 2048 samples\n }\n if (extObjectType === 29 /* PS */) {\n return 2048 // PS implies SBR \u2192 2048 samples\n }\n }\n }\n }\n\n return aacFrameSamplesForObjectType(objectType)\n}\n\n/**\n * Parse AAC ASC to extract sample rate.\n * Useful when catalog samplerate is missing but initData is available.\n */\nexport function parseAacSampleRate(asc: Uint8Array): number | null {\n if (asc.length < 2) return null\n\n let bitPos = 0\n const readBits = (n: number): number => {\n let val = 0\n for (let i = 0; i < n; i++) {\n const byteIdx = (bitPos + i) >> 3\n const bitIdx = 7 - ((bitPos + i) & 7)\n if (byteIdx >= asc.length) return -1\n val = (val << 1) | ((asc[byteIdx]! >> bitIdx) & 1)\n }\n bitPos += n\n return val\n }\n\n // Skip audioObjectType (5 or 11 bits)\n let objectType = readBits(5)\n if (objectType === 31) readBits(6)\n\n const freqIndex = readBits(4)\n if (freqIndex < 0) return null\n if (freqIndex === 0xf) {\n return readBits(24)\n }\n return AAC_SAMPLE_RATES[freqIndex] ?? null\n}\n\n/**\n * Opus frame samples: 960 at 48kHz (20ms) per RFC 6716 \u00A72.1.4.\n *\n * Opus supports variable frame sizes (2.5, 5, 10, 20, 40, 60, 80, 100, 120ms)\n * but the frame size is per-packet (in the TOC byte), not in the init data.\n * OpusHead (RFC 7845) carries sample_rate and channel_count only.\n *\n * 960 samples at 48kHz = 20ms is the normative default per RFC 6716.\n */\nexport function parseOpusFrameSamples(_opusHead?: Uint8Array): number {\n return 960\n}\n\n/**\n * Extract frame samples from a bare codec descriptor based on codec string.\n *\n * @param codec - Codec string from catalog (e.g., \"mp4a.40.2\", \"opus\")\n * @param codecConfig - Raw codec descriptor bytes: AudioSpecificConfig for\n * AAC, OpusHead for Opus. This is the catalog `description` field, NOT the\n * catalog `initData` (ftyp+moov) field \u2014 see the file header.\n * @returns frame samples, or null if codec unsupported or the descriptor is invalid\n */\nexport function frameSamplesFromInit(codec: string, codecConfig: Uint8Array): number | null {\n if (codec.startsWith('mp4a')) {\n return parseAacFrameSamples(codecConfig)\n }\n if (codec.startsWith('opus') || codec === 'Opus') {\n return parseOpusFrameSamples(codecConfig)\n }\n return null\n}\n", "/**\n * FecStarvationDetector \u2014 post-cutover source-starvation detection (BLO-9413/D4).\n *\n * The packet-presence watchdog (MulticastTransport, 5s) only catches TOTAL\n * silence: any packet \u2014 including FEC repair \u2014 resets it. The observed\n * BLO-9413 failure delivered repair symbols continuously while ZERO source\n * symbols arrived (`sourceSymbols:0, repairSymbols:551, blocksComplete:0`):\n * the watchdog stayed fed forever while nothing reconstructable reached the\n * decoder. This detector closes that hole by watching delivery progress and\n * driving the designed-but-previously-uncalled TrackSession loss arcs:\n *\n * starvation sustained \u2265 windowMs \u2192 onStarvation() \u2192 session.lossDetected()\n * block progress resumes \u2192 onRecovery() \u2192 session.lossRecovered()\n *\n * TrackSession's own FEC block-span hysteresis then decides whether a recovery\n * cancels the failback \u2014 this detector deliberately does NOT add a second\n * hysteresis layer.\n *\n * Push-fed: call update() with each FecTrackStats-shaped snapshot (e.g. from\n * MulticastManager.setOnFecStatsUpdate or a host sampler). Hosts that can also\n * observe decoded/appended media should include a monotonic mediaFrames count:\n * media progress is delivery progress even if sparse FEC block counters do not\n * advance in the same sample window. Evaluation happens only on update \u2014 a\n * fully dead transport stops producing updates and stays the transport\n * watchdog's responsibility (total-silence backstop).\n */\n\n/** Monotonic counters sampled from the FEC layer (FecTrackStats subset). */\nexport interface FecStarvationSnapshot {\n sourceSymbols: number\n repairSymbols: number\n blocksComplete: number\n blocksRecovered: number\n /** Optional host-observed decoded/appended media frames for this packetId. */\n mediaFrames?: number\n}\n\nexport interface FecStarvationDetectorConfig {\n /**\n * How long starvation must persist before onStarvation fires. Derive from\n * the stream's FEC block-span config \u2014 no magic defaults, matching the\n * TrackSession hysteresis convention.\n */\n windowMs: number\n /** Starvation sustained \u2265 windowMs. Fires once per starvation episode. */\n onStarvation(): void\n /** Healthy delivery resumed after onStarvation fired. Fires once per episode. */\n onRecovery(): void\n /** Injectable clock for tests. Defaults to Date.now. */\n now?: () => number\n}\n\nexport class FecStarvationDetector {\n #config: FecStarvationDetectorConfig\n #now: () => number\n #last: FecStarvationSnapshot | null = null\n #lastSampleAt = 0\n #starvedSince: number | null = null\n #latched = false\n\n constructor(config: FecStarvationDetectorConfig) {\n if (config.windowMs <= 0) {\n throw new Error(\n `[FecStarvationDetector] windowMs=${config.windowMs}. Derive a positive window ` +\n `from the stream's FEC block-span config \u2014 a non-positive ` +\n `window would fire on the first repair packet of a healthy stream.`\n )\n }\n this.#config = config\n this.#now = config.now ?? Date.now\n }\n\n /** True while a starvation episode is active (onStarvation fired, no recovery yet). */\n get isStarved(): boolean { return this.#latched }\n\n update(snapshot: FecStarvationSnapshot): void {\n const last = this.#last\n const prevSampleAt = this.#lastSampleAt\n const now = this.#now()\n this.#last = { ...snapshot }\n this.#lastSampleAt = now\n if (last === null) return // first sample = baseline only\n\n const srcDelta = snapshot.sourceSymbols - last.sourceSymbols\n const repDelta = snapshot.repairSymbols - last.repairSymbols\n const doneDelta =\n (snapshot.blocksComplete - last.blocksComplete) +\n (snapshot.blocksRecovered - last.blocksRecovered)\n const mediaDelta = (snapshot.mediaFrames ?? 0) - (last.mediaFrames ?? 0)\n\n const packetsFlowing = srcDelta > 0 || repDelta > 0\n if (!packetsFlowing && mediaDelta <= 0) return // total silence \u2014 transport watchdog's domain\n\n // Starving: traffic arrives but nothing reconstructable comes out. Source\n // and repair symbols arrive at symbol cadence while complete/recovered\n // blocks arrive at block cadence; requiring source and block deltas in the\n // same snapshot false-fails healthy live streams. A repaired block is still\n // decodable media, and host-observed frame progress is even stronger\n // evidence, so either resets the starvation clock.\n const starving = doneDelta === 0 && mediaDelta <= 0\n\n if (starving) {\n // The episode began when delivery was last known healthy \u2014 the previous\n // sample's timestamp, not this sample's arrival (the deltas in THIS\n // sample describe the interval since then).\n if (this.#starvedSince === null) this.#starvedSince = prevSampleAt\n if (!this.#latched && now - this.#starvedSince >= this.#config.windowMs) {\n this.#latched = true\n this.#config.onStarvation()\n }\n return\n }\n\n // Healthy sample: completed or recovered blocks are reaching the decoder.\n this.#starvedSince = null\n if (this.#latched) {\n this.#latched = false\n this.#config.onRecovery()\n }\n }\n\n /** Reset episode state (e.g. on transport switch / re-join). Keeps no baseline. */\n reset(): void {\n this.#last = null\n this.#starvedSince = null\n this.#latched = false\n }\n}\n", "/**\n * CmcdProducer \u2014 transport-agnostic CMCD telemetry producer.\n *\n * Maps FecTrackStats snapshots and TransportSnapshot state to x-bc-*\n * CMCD vendor keys (proto tags 70-99). Fires callbacks for two delivery\n * channels: MoQ SUBSCRIBE params and multicast HTTP POST.\n *\n * Does NOT call fetch() or write to QUIC directly \u2014 the player wiring\n * layer is responsible for attaching params to SUBSCRIBE or POSTing\n * the payload to the CDN CMCD endpoint.\n *\n * @see docs/superpowers/specs/2026-04-15-iwa-multicast-stack-design.md \u00A73.3\n * @see CDN+ Track D BLO-1044\n */\n\nimport type { FecTrackStats } from '@blockcast/fec-worker'\nimport type { ManifestVerificationStats } from '@blockcast/mmt-manifest-verify'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Transport state snapshot, built by the player wiring layer from SocketPool. */\nexport interface TransportSnapshot {\n transport: 'native' | 'amt' | 'moq'\n /** IPv4/IPv6 multicast group address */\n multicastGroup?: string\n /** SSM source address */\n multicastSource?: string\n /** AMT relay address (omit when undefined) */\n amtRelay?: string\n}\n\n/** Transport transition event identifiers for x-bc-ct. */\nexport type TransportTransitionEvent = 'mc-start' | 'mc-to-uc' | 'amt-start'\n\n/**\n * Phase 14 play-mode state (D-12, x-bc-play-mode):\n * - `full` \u2014 base + delta both active\n * - `base-only` \u2014 delta fell back; base carries playback\n * - `single-track` \u2014 stream is not a multi-track switching set (legacy)\n */\nexport type PlayMode = 'full' | 'base-only' | 'single-track'\n\nconst PLAY_MODES: readonly PlayMode[] = ['full', 'base-only', 'single-track']\n\n/** CMCD vendor key map. Values are string, number, or boolean. */\nexport type CmcdParams = Record<string, string | number | boolean>\n\n/** Options for CmcdProducer construction. */\nexport interface CmcdProducerOptions {\n /** Fired on each snapshot and on transport transitions. Params for MoQ SUBSCRIBE. */\n onCmcdParams?: (params: CmcdParams) => void\n /** Fired on each snapshot and on transport transitions. RFC 9559 query-string for HTTP POST body. */\n onCmcdPost?: (payload: string) => void\n}\n\n/** Snapshot data passed to onSnapshot(). */\nexport interface SnapshotData {\n tracks: FecTrackStats[]\n timestampMs: number\n}\n\n/** Transport-only telemetry for receivers that do not own FEC track stats. */\nexport interface TransportOnlySnapshotData {\n transport: TransportSnapshot\n timestampMs: number\n playMode?: PlayMode\n switchMiscount?: number\n}\n\n// ---------------------------------------------------------------------------\n// Value sanitization (T-06-01: header injection mitigation)\n// ---------------------------------------------------------------------------\n\n/** Strip commas, newlines, and carriage returns from string values. */\nfunction sanitize(value: string): string {\n return value.replace(/[,\\r\\n]/g, '')\n}\n\n// ---------------------------------------------------------------------------\n// Key building (pure functions, no side effects)\n// ---------------------------------------------------------------------------\n\n/** Build FEC vendor keys from a FecTrackStats snapshot. */\nfunction buildFecParams(stats: FecTrackStats): CmcdParams {\n const params: CmcdParams = {}\n const cfg = stats.config\n\n params['x-bc-fec-algo'] = cfg.algorithm\n params['x-bc-fec-k'] = cfg.k\n params['x-bc-fec-p'] = cfg.repairCount\n params['x-bc-fec-recovery'] = parseFloat((stats.recoveryRate / 100).toFixed(4))\n\n // Green-fill (symbol underrun) \u2014 omit when 0\n if (stats.greenFillFrames > 0) {\n params['x-bc-scu'] = stats.greenFillFrames\n }\n\n // ALTA keys: omit entirely when altaEnabled is false (D-09)\n if (cfg.altaEnabled) {\n params['x-bc-alta-verified'] = stats.altaVerified > 0\n params['x-bc-alta-failures'] = stats.altaFailed\n }\n\n return params\n}\n\n/** Build transport vendor keys from a TransportSnapshot. */\nfunction buildTransportParams(snap: TransportSnapshot | undefined): CmcdParams {\n if (!snap) return {}\n\n const params: CmcdParams = {}\n params['x-bc-transport'] = snap.transport\n\n if (snap.multicastGroup != null) {\n params['x-bc-multicast-group'] = snap.multicastGroup\n }\n if (snap.multicastSource != null) {\n params['x-bc-multicast-source'] = snap.multicastSource\n }\n if (snap.amtRelay != null) {\n params['x-bc-amt-relay'] = snap.amtRelay\n }\n\n return params\n}\n\n// ---------------------------------------------------------------------------\n// Query-string serialization (RFC 9559 \u00A72)\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize CmcdParams to RFC 9559 query-string format.\n * Keys sorted for deterministic output. String values sanitized.\n */\nfunction formatQueryString(params: CmcdParams): string {\n return Object.keys(params)\n .sort()\n .map(key => {\n const v = params[key]\n if (typeof v === 'string') return `${key}=${sanitize(v)}`\n return `${key}=${v}`\n })\n .join(',')\n}\n\n// ---------------------------------------------------------------------------\n// CmcdProducer\n// ---------------------------------------------------------------------------\n\nexport class CmcdProducer {\n #onCmcdParams: ((params: CmcdParams) => void) | undefined\n #onCmcdPost: ((payload: string) => void) | undefined\n #transport: TransportSnapshot | undefined\n #lastStats: FecTrackStats | undefined\n // Phase 14 state \u2014 Plan 03 Task 2 (D-12, D-15).\n #playMode: PlayMode | null = null\n #deltaFailRate: number | null = null\n #switchMiscount = 0\n #manifestStats: ManifestVerificationStats | null = null\n\n constructor(options: CmcdProducerOptions) {\n this.#onCmcdParams = options.onCmcdParams\n this.#onCmcdPost = options.onCmcdPost\n }\n\n /** Update the stored transport state. Called by player wiring layer. */\n updateTransport(snapshot: TransportSnapshot): void {\n this.#transport = snapshot\n }\n\n /**\n * Set the current play-mode. Emitted as `x-bc-play-mode` on subsequent\n * snapshots. Throws on any value outside {'full','base-only','single-track'}\n * (CLAUDE.md: error on bad init).\n */\n updatePlayMode(mode: PlayMode): void {\n if (typeof mode !== 'string' || !PLAY_MODES.includes(mode as PlayMode)) {\n throw new Error(\n `[CmcdProducer] invalid play-mode \"${String(mode)}\"; expected one of: ${PLAY_MODES.join(', ')}`,\n )\n }\n this.#playMode = mode\n }\n\n /**\n * Set the delta-track FEC block-failure rate (0..1 inclusive). Emitted as\n * `x-bc-delta-fail-rate` on snapshots when play-mode is 'full' or 'base-only'.\n * Omitted on single-track streams.\n */\n updateDeltaFailRate(rate: number): void {\n if (typeof rate !== 'number' || !Number.isFinite(rate) || rate < 0 || rate > 1) {\n throw new Error(\n `[CmcdProducer] invalid delta-fail-rate \"${String(rate)}\"; expected a finite number in [0, 1]`,\n )\n }\n this.#deltaFailRate = rate\n }\n\n /**\n * Bump the cumulative switching-set misalignment counter. Emitted as\n * `x-bc-switch-miscount` on snapshots when the counter is > 0.\n */\n incrementSwitchMiscount(): void {\n this.#switchMiscount += 1\n }\n\n updateManifestVerification(stats: ManifestVerificationStats): void {\n this.#manifestStats = stats\n }\n\n /**\n * One-shot emit of `x-bc-switch-drift-us` for a single misalignment event.\n * Does NOT mutate snapshot state \u2014 subsequent onSnapshot() calls will not\n * include the drift key until the next emitSwitchDrift(). Mirrors the\n * existing onTransition() pattern.\n */\n emitSwitchDrift(driftUs: number): void {\n if (\n typeof driftUs !== 'number' ||\n !Number.isFinite(driftUs) ||\n driftUs < 0\n ) {\n throw new Error(\n `[CmcdProducer] invalid drift-us \"${String(driftUs)}\"; expected a non-negative finite number`,\n )\n }\n const params: CmcdParams = {}\n if (this.#lastStats) Object.assign(params, buildFecParams(this.#lastStats))\n Object.assign(params, buildTransportParams(this.#transport))\n if (this.#switchMiscount > 0) params['x-bc-switch-miscount'] = this.#switchMiscount\n params['x-bc-switch-drift-us'] = driftUs\n this.#emit(params)\n }\n\n /**\n * Process a FecTrackStats snapshot. Finds the video track, builds\n * x-bc-* vendor keys, and fires both delivery callbacks.\n */\n onSnapshot(data: SnapshotData): void {\n // Find first video track; skip if none\n const videoTrack = data.tracks.find(t => t.config.trackType === 'video')\n if (!videoTrack) return\n\n this.#lastStats = videoTrack\n\n const params: CmcdParams = {\n ...buildFecParams(videoTrack),\n ...buildTransportParams(this.#transport),\n }\n\n if (this.#playMode !== null) {\n params['x-bc-play-mode'] = this.#playMode\n }\n\n // Delta-fail-rate is only meaningful when a delta track exists.\n if (\n this.#playMode !== null &&\n this.#playMode !== 'single-track' &&\n this.#deltaFailRate !== null\n ) {\n params['x-bc-delta-fail-rate'] = parseFloat(this.#deltaFailRate.toFixed(4))\n }\n\n if (this.#switchMiscount > 0) {\n params['x-bc-switch-miscount'] = this.#switchMiscount\n }\n if (this.#manifestStats) {\n params['x-bc-manifest-verified'] = this.#manifestStats.verified > 0\n params['x-bc-manifest-failures'] = Object.values(this.#manifestStats.failures)\n .reduce((total, count) => total + count, 0)\n }\n\n this.#emit(params)\n }\n\n /**\n * Emit transport state without inventing FEC metrics. Cadence is owned by\n * the caller because only the transport integration knows when state changed.\n */\n onTransportSnapshot(data: TransportOnlySnapshotData): void {\n if (!Number.isFinite(data.timestampMs) || data.timestampMs < 0) {\n throw new Error('[CmcdProducer] transport snapshot timestamp must be a non-negative finite number')\n }\n if (data.playMode !== undefined) this.updatePlayMode(data.playMode)\n if (\n data.switchMiscount !== undefined &&\n (!Number.isSafeInteger(data.switchMiscount) || data.switchMiscount < 0)\n ) {\n throw new Error('[CmcdProducer] transport snapshot switch-miscount must be a non-negative safe integer')\n }\n\n this.#transport = data.transport\n const params = buildTransportParams(data.transport)\n if (this.#playMode !== null) params['x-bc-play-mode'] = this.#playMode\n const switchMiscount = data.switchMiscount ?? this.#switchMiscount\n if (switchMiscount > 0) params['x-bc-switch-miscount'] = switchMiscount\n this.#emit(params)\n }\n\n /**\n * Signal a transport transition event. Fires an immediate out-of-band\n * emit with x-bc-ct set. The subsequent onSnapshot() will NOT include\n * x-bc-ct (one-shot).\n */\n onTransition(event: TransportTransitionEvent): void {\n const params: CmcdParams = {}\n\n // Include last known FEC stats if available\n if (this.#lastStats) {\n Object.assign(params, buildFecParams(this.#lastStats))\n }\n\n // Include transport state if available\n Object.assign(params, buildTransportParams(this.#transport))\n\n // Add the transition event key (one-shot)\n params['x-bc-ct'] = event\n\n this.#emit(params)\n }\n\n /** Fire both delivery callbacks with the given params. */\n #emit(params: CmcdParams): void {\n this.#onCmcdParams?.(params)\n this.#onCmcdPost?.(formatQueryString(params))\n }\n}\n", "// Catalog \u2192 per-track FEC geometry resolution.\n//\n// Moved from hang-mmt-fec's js/watch/src/track-fec-params.ts so every player\n// (moq-lib/hang, moqtail, shaka MSF) resolves catalog FEC semantics through\n// one shared implementation \u2014 players keep only wiring, not MMT/catalog\n// interpretation. Structural track shape ({ name, type }) keeps this module\n// player-agnostic.\n\nimport { catalogFecInterleaveMs, type FecInterleaveCatalog } from \"@blockcast/mmt-fec\";\n\nexport { catalogFecInterleaveMs, type FecInterleaveCatalog };\n\n/**\n * Minimal structural shape of a resolved endpoint track. Callers may carry\n * extra fields (e.g. hang's ResolvedEndpointTrack adds `pid`) \u2014 the resolver\n * only reads `name` and `type`.\n */\nexport type CatalogTrackRef = {\n\tname: string;\n\ttype: \"video\" | \"audio\" | (string & {});\n};\n\ntype FecCatalog = FecInterleaveCatalog & {\n\tsourceSymbols?: number;\n\trepairSymbols?: number;\n\tsymbolSize?: number;\n\t/** Signaled jitter/reorder allowance in ms (added to the block span). */\n\treorderToleranceMs?: number;\n\trepairTrack?: string;\n};\n\ntype RenditionCatalog = KeyframeCadenceCatalog & {\n\tfec?: FecCatalog;\n\tframerate?: unknown;\n\tsampleRate?: unknown;\n\tsamplerate?: unknown;\n};\n\ntype CatalogWithRenditions = {\n\tvideo?: { renditions?: Record<string, RenditionCatalog> };\n\taudio?: { renditions?: Record<string, RenditionCatalog> };\n};\n\nexport type GroupDurationCatalog = {\n\tgroupDurationTicks?: number;\n\tgroupDurationMs?: number;\n\ttimescale?: number;\n};\n\nexport type KeyframeCadenceCatalog = GroupDurationCatalog & {\n\tkeyframeDurationTicks?: number;\n\tkeyframeDurationMs?: number;\n\tkeyframeIntervalTicks?: number;\n\tkeyframeIntervalMs?: number;\n\tgopDurationTicks?: number;\n\tgopDurationMs?: number;\n};\n\nexport type TrackFecParams = {\n\tfecK: number;\n\tfecRepairCount: number;\n\tfecSymbolSize: number;\n\tfecInterleaveMs: number;\n\t/** Catalog fec.reorderToleranceMs \u2014 signaled jitter/reorder allowance, ADDED\n\t * to the block span to form a deadline. Deliberately `number | undefined`\n\t * and NOT floored to 0: a declared 0 (\"this path needs no slack\") and an\n\t * absent value (\"the encoder never said\") must stay distinguishable, or a\n\t * consumer that wants to fail loud on absence silently reads a default\n\t * instead \u2014 which is exactly how the magic 3.0 survived. */\n\tfecReorderToleranceMs?: number;\n\trepairTrackName?: string;\n\tfps?: number;\n\tsampleRate?: number;\n};\n\nconst NO_FEC: TrackFecParams = {\n\tfecK: 0,\n\tfecRepairCount: 0,\n\tfecSymbolSize: 0,\n\tfecInterleaveMs: 0,\n};\n\nfunction numericField(value: unknown): number | undefined {\n\tconst n = typeof value === \"number\" ? value : typeof value === \"string\" ? Number(value) : Number.NaN;\n\treturn Number.isFinite(n) && n > 0 ? n : undefined;\n}\n\n/** Like `numericField` but admits an explicit 0, and still rejects absent,\n * negative and non-finite input. Absence returns undefined so callers can\n * tell \"declared none\" from \"never declared\". */\nfunction nonNegativeField(value: unknown): number | undefined {\n\tconst n = typeof value === \"number\" ? value : typeof value === \"string\" ? Number(value) : Number.NaN;\n\treturn Number.isFinite(n) && n >= 0 ? n : undefined;\n}\n\nfunction fecParams(\n\trendition: RenditionCatalog | undefined,\n\tfps?: number,\n\tsampleRate?: number,\n): TrackFecParams {\n\tconst mediaRate = sampleRate === undefined ? { fps } : { fps, sampleRate };\n\tconst fec = rendition?.fec;\n\tif (!fec) return { ...NO_FEC, ...mediaRate };\n\treturn {\n\t\tfecK: fec.sourceSymbols ?? 0,\n\t\tfecRepairCount: fec.repairSymbols ?? 0,\n\t\tfecSymbolSize: fec.symbolSize ?? 0,\n\t\t// interleaveDepthMs is the FEC block span in MILLISECONDS (\u00A75.1) \u2014 the\n\t\t// only interleave key the catalog carries post-BLO-14526. Receivers\n\t\t// derive D by positive round-half-up of span / GOP duration where needed.\n\t\tfecInterleaveMs: catalogFecInterleaveMs(fec) ?? 0,\n\t\t// Additive, never a multiplier on the span: jitter belongs to the network\n\t\t// path and the span belongs to the encoder, so scaling one by the other\n\t\t// hands one network different slack per track. `numericField` rejects 0,\n\t\t// so it cannot be reused here \u2014 a declared 0 is a legitimate value.\n\t\tfecReorderToleranceMs: nonNegativeField(fec.reorderToleranceMs),\n\t\trepairTrackName: fec.repairTrack,\n\t\t...mediaRate,\n\t};\n}\n\n/**\n * Resolves per-track FEC geometry from the catalog for the active endpoint tracks.\n *\n * Invariant: expects at most one video track and one audio track. `forTrack`\n * dispatches purely on `track.type`, so a second video track (e.g. a simulcast\n * variant) would silently receive the first video rendition's geometry. Revisit\n * the `track.type` dispatch before relaxing that one-per-type assumption.\n */\nexport function resolveCatalogTrackFec(\n\tcatalog: CatalogWithRenditions | undefined,\n\ttracks: readonly CatalogTrackRef[],\n): {\n\tvideoTrackName?: string;\n\taudioTrackName?: string;\n\tmissingVideoRendition?: string;\n\tmissingAudioRendition?: string;\n\tvideoForKeyframeRepair?: KeyframeCadenceCatalog;\n\tcatalogFps?: number;\n\tvideo: TrackFecParams;\n\taudio: TrackFecParams;\n\tforTrack(track: CatalogTrackRef): TrackFecParams;\n} {\n\tconst videoTrackName = tracks.find((t) => t.type === \"video\")?.name;\n\tconst audioTrackName = tracks.find((t) => t.type === \"audio\")?.name;\n\tconst videoRenditions = catalog?.video?.renditions;\n\tconst audioRenditions = catalog?.audio?.renditions;\n\tconst video = videoTrackName ? videoRenditions?.[videoTrackName] : undefined;\n\tconst audio = audioTrackName ? audioRenditions?.[audioTrackName] : undefined;\n\tconst catalogFps = numericField(video?.framerate);\n\tconst sampleRate = numericField(audio?.sampleRate ?? audio?.samplerate);\n\tconst audioFps = sampleRate ? sampleRate / 1024 : undefined;\n\tconst videoParams = fecParams(video, catalogFps);\n\tconst audioParams = fecParams(audio, audioFps, sampleRate);\n\n\treturn {\n\t\tvideoTrackName,\n\t\taudioTrackName,\n\t\tmissingVideoRendition: videoTrackName && videoRenditions && !video ? videoTrackName : undefined,\n\t\tmissingAudioRendition: audioTrackName && audioRenditions && !audio ? audioTrackName : undefined,\n\t\tvideoForKeyframeRepair: video,\n\t\tcatalogFps,\n\t\tvideo: videoParams,\n\t\taudio: audioParams,\n\t\tforTrack: (track) => (track.type === \"audio\" ? audioParams : videoParams),\n\t};\n}\n", "// Catalog \u2192 AMT relay discovery (multicast.networkSource).\n//\n// Shared by every player (moq-lib/hang, moqtail, shaka MSF) so networkSource\n// interpretation lives in one place \u2014 players keep only wiring, not\n// MMT/catalog semantics.\n//\n// draft-ramadan-moq-multicast \u00A74.1 carries relay discovery as\n// `multicast.networkSource`. Publishers emit the canonical ARRAY form\n// (hang-rs `Option<Vec<NetworkSource>>`; cast's bridge + ladder catalogs), but\n// hang's zod RootSchema also accepts a bare object and older hand-authored\n// catalogs used it \u2014 consume both.\n\n/** One `networkSource[]` entry as published (`{type, relay, port?}`). */\nexport type CatalogNetworkSource = {\n\ttype?: string;\n\trelay?: string;\n\tport?: number;\n};\n\n/** Resolved AMT relay a player can hand to its multicast join/subscribe. */\nexport type AmtNetworkSource = {\n\trelay: string;\n\tport?: number;\n};\n\nfunction normalizePort(value: unknown): number | undefined {\n\tconst n = typeof value === \"number\" ? value : typeof value === \"string\" ? Number(value) : Number.NaN;\n\treturn Number.isInteger(n) && n > 0 && n <= 65535 ? n : undefined;\n}\n\n/**\n * Resolve the AMT relay from a catalog `multicast` block's `networkSource`\n * (array or bare-object form). Returns the first entry with `type: \"amt\"`\n * and a non-blank relay, or `undefined` when the catalog advertises none \u2014\n * callers fall back to native multicast (or their own explicit relay config).\n */\nexport function catalogAmtNetworkSource(networkSource: unknown): AmtNetworkSource | undefined {\n\tconst entries = Array.isArray(networkSource) ? networkSource : networkSource ? [networkSource] : [];\n\tfor (const raw of entries) {\n\t\tif (!raw || typeof raw !== \"object\") continue;\n\t\tconst ns = raw as CatalogNetworkSource;\n\t\tif (ns.type !== \"amt\") continue;\n\t\tconst relay = typeof ns.relay === \"string\" ? ns.relay.trim() : \"\";\n\t\tif (!relay) continue;\n\t\tconst port = normalizePort(ns.port);\n\t\treturn port !== undefined ? { relay, port } : { relay };\n\t}\n\treturn undefined;\n}\n", "/**\n * D-28 shared repair-fetch shell.\n *\n * Player-agnostic orchestration for per-SBN on-demand MoQ FEC repair\n * backfill. Each player (moqtail client, hang watch, Shaka MSF bridge)\n * supplies only its protocol verb via {@link RepairFetchTransport}:\n *\n * - moqtail: standalone FETCH (FetchType.Standalone) for group [sbn, sbn+1)\n * - moq-lite/hang: Subscribe with startGroup=sbn, endGroup=sbn (the relay\n * propagates startGroup upstream to pull the past group from the origin's\n * producer cache)\n *\n * Everything else \u2014 registration, fire-time repair-track resolution, per-fetch\n * abort/timeout, feeding symbols, and in-flight slot release on failure \u2014 is\n * identical across players and lives here.\n */\n\nimport type { MulticastManager } from './multicast-manager.js'\n\n/** Resolve a packetId to its repair track name at FIRE TIME.\n *\n * Called on every repair request, never cached by the shell: ABR ladder\n * rungs can share a packetId (e.g. video/720p and video/360p both p:1), so\n * the mapping must reflect the track active when the request fires, not\n * when the shell was wired. Return null when the packetId has no\n * repair-bearing track \u2014 the request is released without a fetch. */\nexport type ResolveRepairTarget = (\n packetId: number,\n catalogTrackName?: string,\n) => { repairTrack: string } | null\n\n/** Protocol-specific fetch of one FEC block's repair group.\n *\n * Yield each repair object payload (MMTP packet bytes, ISO 23008-1 \u00A7C.5.3\n * Repair FEC Payload ID framing). Return when the group ends. MUST abort\n * promptly when `signal` fires \u2014 the shell bounds every fetch by the\n * block's remaining repair budget. */\nexport type RepairFetchTransport = (\n repairTrack: string,\n sbn: number,\n packetId: number,\n signal: AbortSignal,\n) => AsyncIterable<Uint8Array>\n\nexport interface WireRepairFetchOptions {\n resolveTarget: ResolveRepairTarget\n transport: RepairFetchTransport\n /** Override the per-fetch timeout. Default: the budget between the resolved\n * K-symbol source horizon and the block fail bound. */\n timeoutMsForPacket?: (packetId: number) => number\n /** Classify a transport error as a transient publisher/relay retention race.\n * Matching failures are retried only inside the block's remaining budget. */\n shouldRetry?: (error: unknown) => boolean\n /** Delay between classified transient attempts. Default: 10ms. */\n retryDelayMs?: number\n}\n\n/**\n * Wire the D-28 repair-fetch shell onto a MulticastManager.\n *\n * Call AFTER the manager has a repair-bearing FEC configuration (players gate\n * this on the first fec-setup that carries a repairTrack) \u2014 registration arms\n * the per-block repair deadlines in the FEC layer.\n *\n * Returns a dispose function that aborts outstanding fetches and stops new\n * ones from being issued.\n */\nexport function wireRepairFetch(mc: MulticastManager, opts: WireRepairFetchOptions): () => void {\n type InFlightRepair = {\n controller: AbortController\n sbn: number\n packetId: number\n catalogTrackName?: string\n }\n const inFlight = new Map<string, InFlightRepair>()\n let disposed = false\n const requestKey = (\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): string => JSON.stringify([packetId, catalogTrackName ?? null, sbn])\n\n const removeResetObserver = mc.addTrackResetObserver(\n (packetId, catalogTrackName) => {\n for (const [key, request] of inFlight) {\n if (\n request.packetId === packetId &&\n request.catalogTrackName === catalogTrackName\n ) {\n request.controller.abort()\n inFlight.delete(key)\n }\n }\n },\n )\n\n const timeoutFor = (\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): number =>\n opts.timeoutMsForPacket?.(packetId) ??\n mc.fecManager.repairFetchTimeoutMsForBlock(\n sbn,\n packetId,\n catalogTrackName,\n )\n const clearRepair = (\n sbn: number,\n packetId: number,\n catalogTrackName?: string,\n ): void => {\n if (catalogTrackName === undefined) mc.clearRepairForBlock(sbn, packetId)\n else mc.clearRepairForBlock(sbn, packetId, catalogTrackName)\n }\n const clearRepairIfOwner = (key: string, request: InFlightRepair): void => {\n if (inFlight.get(key) !== request) return\n clearRepair(request.sbn, request.packetId, request.catalogTrackName)\n }\n const waitForRetry = (signal: AbortSignal): Promise<void> =>\n new Promise((resolve) => {\n if (signal.aborted) {\n resolve()\n return\n }\n let timer: ReturnType<typeof setTimeout>\n const onAbort = () => {\n clearTimeout(timer)\n resolve()\n }\n timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, Math.max(1, opts.retryDelayMs ?? 10))\n signal.addEventListener('abort', onAbort, { once: true })\n })\n\n mc.onRepairSubscribe((sbn, packetId, catalogTrackName) => {\n if (disposed) {\n clearRepair(sbn, packetId, catalogTrackName)\n return\n }\n\n // Fire-time resolution \u2014 see ResolveRepairTarget.\n const target = opts.resolveTarget(packetId, catalogTrackName)\n if (!target) {\n clearRepair(sbn, packetId, catalogTrackName)\n return\n }\n\n const ctrl = new AbortController()\n const key = requestKey(sbn, packetId, catalogTrackName)\n const previous = inFlight.get(key)\n previous?.controller.abort()\n const request: InFlightRepair = { controller: ctrl, sbn, packetId, catalogTrackName }\n inFlight.set(key, request)\n const timeoutMs = timeoutFor(sbn, packetId, catalogTrackName)\n const timer = setTimeout(() => ctrl.abort(), Math.max(1, timeoutMs))\n\n void (async () => {\n let fed = 0\n try {\n while (!ctrl.signal.aborted) {\n try {\n for await (const packet of opts.transport(\n target.repairTrack,\n sbn,\n packetId,\n ctrl.signal,\n )) {\n if (ctrl.signal.aborted) break\n if (inFlight.get(key) !== request) break\n fed++\n if (catalogTrackName === undefined) {\n mc.processRepairFromMoQ(packet, packetId)\n } else {\n mc.processRepairFromMoQ(packet, packetId, catalogTrackName)\n }\n }\n break\n } catch (err) {\n if (fed > 0 || ctrl.signal.aborted || !opts.shouldRetry?.(err)) throw err\n await waitForRetry(ctrl.signal)\n }\n }\n // A fetch that ended without feeding anything cannot resolve the\n // block; release the slot so failure accounting is not wedged. A\n // fetch that fed symbols leaves resolution to the FEC layer\n // (onRepairResolved clears the slot on completion OR failure).\n if (fed === 0) clearRepairIfOwner(key, request)\n } catch (err) {\n // Range-not-retained, transport error, or abort: the block can only\n // resolve via the fail bound now \u2014 release the slot immediately.\n clearRepairIfOwner(key, request)\n if (!ctrl.signal.aborted) {\n console.warn(\n `[RepairFetch] fetch failed pid=${packetId} sbn=${sbn} track=${target.repairTrack}:`,\n err,\n )\n }\n } finally {\n clearTimeout(timer)\n if (inFlight.get(key) === request) inFlight.delete(key)\n }\n })()\n })\n\n return () => {\n disposed = true\n removeResetObserver()\n for (const request of inFlight.values()) {\n clearRepair(request.sbn, request.packetId, request.catalogTrackName)\n request.controller.abort()\n }\n inFlight.clear()\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["location", "_disposed", "formatWorkerError", "_worker", "_onError", "_disposed", "handleEvent_fn", "MMTP_HEADER_SIZE", "MPU_HEADER_SIZE", "MPU_LENGTH_FIELD_SIZE", "MPU_HEADER_REMAINDER_SIZE", "PACKET_TYPE_MPU", "MMTP_HEADER_SIZE", "view", "MPU_HEADER_SIZE", "MPU_HEADER_REMAINDER_SIZE", "MPU_LENGTH_FIELD_SIZE", "PACKET_TYPE_MPU", "MPU_HEADER_SIZE", "view", "MMTP_HEADER_SIZE", "PACKET_TYPE_MPU", "MPU_HEADER_REMAINDER_SIZE", "MPU_LENGTH_FIELD_SIZE", "MMTP_EXT_TYPE_ALTA_AUTH", "MMTP_EXT_TRAILER_HEADER_SIZE", "extractAltaTrailer", "active", "view", "_maxBuffered", "view", "box", "LOCHeaderExtensionId", "view", "buf", "MMTP_HEADER_SIZE", "view", "children", "INT32_MIN", "INT32_MAX", "_frameDuration", "_sequence", "_baseTs", "_stats", "MMTP_EXT_TRAILER_HEADER_SIZE", "view", "MMTP_EXT_TYPE_ALTA_AUTH", "MMTP_HEADER_SIZE", "MPU_HEADER_SIZE", "view", "box", "trex", "isU32", "isMfuKeyframeMetadata", "_disposed", "streamKey_fn", "emitFrame_fn", "PACKET_TYPE_MPU", "extractAltaTrailer", "resolver", "decodeTimestamp", "_disposed", "dedupedAgainstSource", "droppedBeforeSync", "isMfuKeyframeMetadata", "_timeoutMs", "_now", "_subscription", "_disposed", "_stats", "_repairInFlight", "_onVideoFrame", "_onAudioFrame", "_buffer", "_parser", "_frameCb", "_initCb", "_router", "_tracks", "_config", "_now", "_transport"]
7
+ }