@blockcast/mmt-container 0.1.0-main.0399efef1f07

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 (694) 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 +152 -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 +20087 -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 +873 -0
  26. package/dist/carp-catalog.js.map +7 -0
  27. package/dist/catalog-fec-params.d.ts +202 -0
  28. package/dist/catalog-fec-params.d.ts.map +1 -0
  29. package/dist/catalog-fec-params.js +568 -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 +217 -0
  40. package/dist/cmaf-block-assembler.d.ts.map +1 -0
  41. package/dist/cmaf-block-assembler.js +1769 -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 +248 -0
  52. package/dist/codec-utils.d.ts.map +1 -0
  53. package/dist/codec-utils.js +1268 -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 +307 -0
  60. package/dist/fec-manager.d.ts.map +1 -0
  61. package/dist/fec-manager.js +1530 -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 +88 -0
  68. package/dist/fec-worker-setup.d.ts.map +1 -0
  69. package/dist/fec-worker-setup.js +614 -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 +380 -0
  82. package/dist/index.js.map +7 -0
  83. package/dist/iso-bmff-timing.d.ts +78 -0
  84. package/dist/iso-bmff-timing.d.ts.map +1 -0
  85. package/dist/iso-bmff-timing.js +618 -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 +1285 -0
  94. package/dist/manifest-auth-plan.js.map +7 -0
  95. package/dist/media-router.d.ts +251 -0
  96. package/dist/media-router.d.ts.map +1 -0
  97. package/dist/media-router.js +624 -0
  98. package/dist/media-router.js.map +7 -0
  99. package/dist/mfu-reassembler.d.ts +396 -0
  100. package/dist/mfu-reassembler.d.ts.map +1 -0
  101. package/dist/mfu-reassembler.js +3085 -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 +434 -0
  108. package/dist/mmtp-container-parser.d.ts.map +1 -0
  109. package/dist/mmtp-container-parser.js +6164 -0
  110. package/dist/mmtp-container-parser.js.map +7 -0
  111. package/dist/mmtp-container-parser.no-modules.js +8999 -0
  112. package/dist/mmtp-container-parser.no-modules.js.map +7 -0
  113. package/dist/mmtp-decode-pipeline.d.ts +132 -0
  114. package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
  115. package/dist/mmtp-decode-pipeline.js +6413 -0
  116. package/dist/mmtp-decode-pipeline.js.map +7 -0
  117. package/dist/mmtp-router.d.ts +390 -0
  118. package/dist/mmtp-router.d.ts.map +1 -0
  119. package/dist/mmtp-router.js +9242 -0
  120. package/dist/mmtp-router.js.map +7 -0
  121. package/dist/mmtp-to-cmaf.d.ts +181 -0
  122. package/dist/mmtp-to-cmaf.d.ts.map +1 -0
  123. package/dist/mmtp-to-cmaf.js +802 -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 +369 -0
  134. package/dist/multicast-manager.d.ts.map +1 -0
  135. package/dist/multicast-manager.js +13400 -0
  136. package/dist/multicast-manager.js.map +7 -0
  137. package/dist/multicast-transport.d.ts +61 -0
  138. package/dist/multicast-transport.d.ts.map +1 -0
  139. package/dist/multicast-transport.js +1665 -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 +8965 -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 +127 -0
  156. package/dist/repair-fetch.js.map +7 -0
  157. package/dist/repair-layers.d.ts +141 -0
  158. package/dist/repair-layers.d.ts.map +1 -0
  159. package/dist/repair-layers.js +303 -0
  160. package/dist/repair-layers.js.map +7 -0
  161. package/dist/sample-combiner.d.ts +212 -0
  162. package/dist/sample-combiner.d.ts.map +1 -0
  163. package/dist/sample-combiner.js +616 -0
  164. package/dist/sample-combiner.js.map +7 -0
  165. package/dist/signaling.d.ts +29 -0
  166. package/dist/signaling.d.ts.map +1 -0
  167. package/dist/signaling.js +232 -0
  168. package/dist/signaling.js.map +7 -0
  169. package/dist/source-auth.d.ts +2 -0
  170. package/dist/source-auth.d.ts.map +1 -0
  171. package/dist/source-auth.js +1238 -0
  172. package/dist/source-auth.js.map +7 -0
  173. package/dist/switching-set-alignment.d.ts +61 -0
  174. package/dist/switching-set-alignment.d.ts.map +1 -0
  175. package/dist/switching-set-alignment.js +85 -0
  176. package/dist/switching-set-alignment.js.map +7 -0
  177. package/dist/timestamp-codec.d.ts +34 -0
  178. package/dist/timestamp-codec.d.ts.map +1 -0
  179. package/dist/timestamp-codec.js +456 -0
  180. package/dist/timestamp-codec.js.map +7 -0
  181. package/dist/track-pipeline-setup.d.ts +72 -0
  182. package/dist/track-pipeline-setup.d.ts.map +1 -0
  183. package/dist/track-pipeline-setup.js +306 -0
  184. package/dist/track-pipeline-setup.js.map +7 -0
  185. package/dist/track-pipeline.d.ts +121 -0
  186. package/dist/track-pipeline.d.ts.map +1 -0
  187. package/dist/track-pipeline.js +229 -0
  188. package/dist/track-pipeline.js.map +7 -0
  189. package/dist/track-session.d.ts +398 -0
  190. package/dist/track-session.d.ts.map +1 -0
  191. package/dist/track-session.js +767 -0
  192. package/dist/track-session.js.map +7 -0
  193. package/dist/transport-pool.d.ts +69 -0
  194. package/dist/transport-pool.d.ts.map +1 -0
  195. package/dist/transport-pool.js +88 -0
  196. package/dist/transport-pool.js.map +7 -0
  197. package/dist/wasm-mfu-reassembler.d.ts +92 -0
  198. package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
  199. package/dist/wasm-mfu-reassembler.js +2139 -0
  200. package/dist/wasm-mfu-reassembler.js.map +7 -0
  201. package/dist/wasm-mmtp-container-parser.d.ts +10 -0
  202. package/dist/wasm-mmtp-container-parser.d.ts.map +1 -0
  203. package/dist/wasm-mmtp-container-parser.js +6418 -0
  204. package/dist/wasm-mmtp-container-parser.js.map +7 -0
  205. package/node_modules/@blockcast/fec-worker/README.md +213 -0
  206. package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
  207. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +87 -0
  208. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -0
  209. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +279 -0
  210. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -0
  211. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +252 -0
  212. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -0
  213. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +11 -0
  214. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +1 -0
  215. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +14 -0
  216. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +1 -0
  217. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +565 -0
  218. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +7 -0
  219. package/node_modules/@blockcast/fec-worker/dist/index.d.ts +10 -0
  220. package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +1 -0
  221. package/node_modules/@blockcast/fec-worker/dist/index.js +8 -0
  222. package/node_modules/@blockcast/fec-worker/dist/index.js.map +1 -0
  223. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +20 -0
  224. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +1 -0
  225. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +349 -0
  226. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +7 -0
  227. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +104 -0
  228. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +1 -0
  229. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +181 -0
  230. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +1 -0
  231. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +179 -0
  232. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +1 -0
  233. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +47 -0
  234. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +1 -0
  235. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +9 -0
  236. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +1 -0
  237. package/node_modules/@blockcast/fec-worker/dist/transfer.js +13 -0
  238. package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +1 -0
  239. package/node_modules/@blockcast/fec-worker/package.json +59 -0
  240. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +319 -0
  241. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +268 -0
  242. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1048 -0
  243. package/node_modules/@blockcast/fec-worker/src/index.ts +32 -0
  244. package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +558 -0
  245. package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +222 -0
  246. package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +194 -0
  247. package/node_modules/@blockcast/fec-worker/src/transfer.ts +12 -0
  248. package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  249. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  250. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  251. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  252. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  253. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  254. package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  255. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  256. package/node_modules/@blockcast/mmt-base/README.md +120 -0
  257. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  258. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
  259. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  260. package/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
  261. package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  262. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +276 -0
  263. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  264. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +645 -0
  265. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  266. package/node_modules/@blockcast/mmt-base/package.json +49 -0
  267. package/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
  268. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +899 -0
  269. package/node_modules/@blockcast/mmt-fec/README.md +270 -0
  270. package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
  271. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +82 -0
  272. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +1 -0
  273. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +296 -0
  274. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +1 -0
  275. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +72 -0
  276. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +1 -0
  277. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +192 -0
  278. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +1 -0
  279. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +46 -0
  280. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +1 -0
  281. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +96 -0
  282. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +1 -0
  283. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +33 -0
  284. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +1 -0
  285. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +57 -0
  286. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +1 -0
  287. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +185 -0
  288. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +1 -0
  289. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +273 -0
  290. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +1 -0
  291. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +22 -0
  292. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
  293. package/node_modules/@blockcast/mmt-fec/dist/index.js +22 -0
  294. package/node_modules/@blockcast/mmt-fec/dist/index.js.map +1 -0
  295. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +20 -0
  296. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +1 -0
  297. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +28 -0
  298. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +1 -0
  299. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +172 -0
  300. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
  301. package/node_modules/@blockcast/mmt-fec/dist/oti.js +254 -0
  302. package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +1 -0
  303. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +41 -0
  304. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +1 -0
  305. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +81 -0
  306. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +1 -0
  307. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  308. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  309. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  310. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  311. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  312. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  313. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  314. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  315. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +120 -0
  316. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  317. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +2 -0
  318. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  319. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +2 -0
  320. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  321. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +276 -0
  322. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  323. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +645 -0
  324. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  325. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +49 -0
  326. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +1 -0
  327. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +899 -0
  328. package/node_modules/@blockcast/mmt-fec/package.json +79 -0
  329. package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +371 -0
  330. package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +277 -0
  331. package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +127 -0
  332. package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +68 -0
  333. package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +332 -0
  334. package/node_modules/@blockcast/mmt-fec/src/index.ts +77 -0
  335. package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
  336. package/node_modules/@blockcast/mmt-fec/src/oti.ts +317 -0
  337. package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -0
  338. package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
  339. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +28 -0
  340. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +1 -0
  341. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +128 -0
  342. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +1 -0
  343. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +4 -0
  344. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +1 -0
  345. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +4 -0
  346. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +1 -0
  347. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +37 -0
  348. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +1 -0
  349. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +179 -0
  350. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +1 -0
  351. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +99 -0
  352. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +1 -0
  353. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +492 -0
  354. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +1 -0
  355. package/node_modules/@blockcast/mmt-manifest-verify/package.json +45 -0
  356. package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +171 -0
  357. package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +3 -0
  358. package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +230 -0
  359. package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +669 -0
  360. package/node_modules/@noble/hashes/LICENSE +21 -0
  361. package/node_modules/@noble/hashes/README.md +521 -0
  362. package/node_modules/@noble/hashes/_assert.d.ts +17 -0
  363. package/node_modules/@noble/hashes/_assert.d.ts.map +1 -0
  364. package/node_modules/@noble/hashes/_assert.js +18 -0
  365. package/node_modules/@noble/hashes/_assert.js.map +1 -0
  366. package/node_modules/@noble/hashes/_blake.d.ts +14 -0
  367. package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
  368. package/node_modules/@noble/hashes/_blake.js +50 -0
  369. package/node_modules/@noble/hashes/_blake.js.map +1 -0
  370. package/node_modules/@noble/hashes/_md.d.ts +51 -0
  371. package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
  372. package/node_modules/@noble/hashes/_md.js +162 -0
  373. package/node_modules/@noble/hashes/_md.js.map +1 -0
  374. package/node_modules/@noble/hashes/_u64.d.ts +55 -0
  375. package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
  376. package/node_modules/@noble/hashes/_u64.js +90 -0
  377. package/node_modules/@noble/hashes/_u64.js.map +1 -0
  378. package/node_modules/@noble/hashes/argon2.d.ts +32 -0
  379. package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
  380. package/node_modules/@noble/hashes/argon2.js +401 -0
  381. package/node_modules/@noble/hashes/argon2.js.map +1 -0
  382. package/node_modules/@noble/hashes/blake1.d.ts +106 -0
  383. package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
  384. package/node_modules/@noble/hashes/blake1.js +459 -0
  385. package/node_modules/@noble/hashes/blake1.js.map +1 -0
  386. package/node_modules/@noble/hashes/blake2.d.ts +116 -0
  387. package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
  388. package/node_modules/@noble/hashes/blake2.js +420 -0
  389. package/node_modules/@noble/hashes/blake2.js.map +1 -0
  390. package/node_modules/@noble/hashes/blake2b.d.ts +11 -0
  391. package/node_modules/@noble/hashes/blake2b.d.ts.map +1 -0
  392. package/node_modules/@noble/hashes/blake2b.js +14 -0
  393. package/node_modules/@noble/hashes/blake2b.js.map +1 -0
  394. package/node_modules/@noble/hashes/blake2s.d.ts +20 -0
  395. package/node_modules/@noble/hashes/blake2s.d.ts.map +1 -0
  396. package/node_modules/@noble/hashes/blake2s.js +24 -0
  397. package/node_modules/@noble/hashes/blake2s.js.map +1 -0
  398. package/node_modules/@noble/hashes/blake3.d.ts +54 -0
  399. package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
  400. package/node_modules/@noble/hashes/blake3.js +255 -0
  401. package/node_modules/@noble/hashes/blake3.js.map +1 -0
  402. package/node_modules/@noble/hashes/crypto.d.ts +2 -0
  403. package/node_modules/@noble/hashes/crypto.d.ts.map +1 -0
  404. package/node_modules/@noble/hashes/crypto.js +5 -0
  405. package/node_modules/@noble/hashes/crypto.js.map +1 -0
  406. package/node_modules/@noble/hashes/cryptoNode.d.ts +2 -0
  407. package/node_modules/@noble/hashes/cryptoNode.d.ts.map +1 -0
  408. package/node_modules/@noble/hashes/cryptoNode.js +18 -0
  409. package/node_modules/@noble/hashes/cryptoNode.js.map +1 -0
  410. package/node_modules/@noble/hashes/eskdf.d.ts +47 -0
  411. package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
  412. package/node_modules/@noble/hashes/eskdf.js +166 -0
  413. package/node_modules/@noble/hashes/eskdf.js.map +1 -0
  414. package/node_modules/@noble/hashes/esm/_assert.d.ts +17 -0
  415. package/node_modules/@noble/hashes/esm/_assert.d.ts.map +1 -0
  416. package/node_modules/@noble/hashes/esm/_assert.js +15 -0
  417. package/node_modules/@noble/hashes/esm/_assert.js.map +1 -0
  418. package/node_modules/@noble/hashes/esm/_blake.d.ts +14 -0
  419. package/node_modules/@noble/hashes/esm/_blake.d.ts.map +1 -0
  420. package/node_modules/@noble/hashes/esm/_blake.js +45 -0
  421. package/node_modules/@noble/hashes/esm/_blake.js.map +1 -0
  422. package/node_modules/@noble/hashes/esm/_md.d.ts +51 -0
  423. package/node_modules/@noble/hashes/esm/_md.d.ts.map +1 -0
  424. package/node_modules/@noble/hashes/esm/_md.js +155 -0
  425. package/node_modules/@noble/hashes/esm/_md.js.map +1 -0
  426. package/node_modules/@noble/hashes/esm/_u64.d.ts +55 -0
  427. package/node_modules/@noble/hashes/esm/_u64.d.ts.map +1 -0
  428. package/node_modules/@noble/hashes/esm/_u64.js +67 -0
  429. package/node_modules/@noble/hashes/esm/_u64.js.map +1 -0
  430. package/node_modules/@noble/hashes/esm/argon2.d.ts +32 -0
  431. package/node_modules/@noble/hashes/esm/argon2.d.ts.map +1 -0
  432. package/node_modules/@noble/hashes/esm/argon2.js +392 -0
  433. package/node_modules/@noble/hashes/esm/argon2.js.map +1 -0
  434. package/node_modules/@noble/hashes/esm/blake1.d.ts +106 -0
  435. package/node_modules/@noble/hashes/esm/blake1.d.ts.map +1 -0
  436. package/node_modules/@noble/hashes/esm/blake1.js +452 -0
  437. package/node_modules/@noble/hashes/esm/blake1.js.map +1 -0
  438. package/node_modules/@noble/hashes/esm/blake2.d.ts +116 -0
  439. package/node_modules/@noble/hashes/esm/blake2.d.ts.map +1 -0
  440. package/node_modules/@noble/hashes/esm/blake2.js +413 -0
  441. package/node_modules/@noble/hashes/esm/blake2.js.map +1 -0
  442. package/node_modules/@noble/hashes/esm/blake2b.d.ts +11 -0
  443. package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +1 -0
  444. package/node_modules/@noble/hashes/esm/blake2b.js +11 -0
  445. package/node_modules/@noble/hashes/esm/blake2b.js.map +1 -0
  446. package/node_modules/@noble/hashes/esm/blake2s.d.ts +20 -0
  447. package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +1 -0
  448. package/node_modules/@noble/hashes/esm/blake2s.js +21 -0
  449. package/node_modules/@noble/hashes/esm/blake2s.js.map +1 -0
  450. package/node_modules/@noble/hashes/esm/blake3.d.ts +54 -0
  451. package/node_modules/@noble/hashes/esm/blake3.d.ts.map +1 -0
  452. package/node_modules/@noble/hashes/esm/blake3.js +251 -0
  453. package/node_modules/@noble/hashes/esm/blake3.js.map +1 -0
  454. package/node_modules/@noble/hashes/esm/crypto.d.ts +2 -0
  455. package/node_modules/@noble/hashes/esm/crypto.d.ts.map +1 -0
  456. package/node_modules/@noble/hashes/esm/crypto.js +2 -0
  457. package/node_modules/@noble/hashes/esm/crypto.js.map +1 -0
  458. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +2 -0
  459. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +1 -0
  460. package/node_modules/@noble/hashes/esm/cryptoNode.js +15 -0
  461. package/node_modules/@noble/hashes/esm/cryptoNode.js.map +1 -0
  462. package/node_modules/@noble/hashes/esm/eskdf.d.ts +47 -0
  463. package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +1 -0
  464. package/node_modules/@noble/hashes/esm/eskdf.js +160 -0
  465. package/node_modules/@noble/hashes/esm/eskdf.js.map +1 -0
  466. package/node_modules/@noble/hashes/esm/hkdf.d.ts +36 -0
  467. package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +1 -0
  468. package/node_modules/@noble/hashes/esm/hkdf.js +82 -0
  469. package/node_modules/@noble/hashes/esm/hkdf.js.map +1 -0
  470. package/node_modules/@noble/hashes/esm/hmac.d.ts +35 -0
  471. package/node_modules/@noble/hashes/esm/hmac.d.ts.map +1 -0
  472. package/node_modules/@noble/hashes/esm/hmac.js +86 -0
  473. package/node_modules/@noble/hashes/esm/hmac.js.map +1 -0
  474. package/node_modules/@noble/hashes/esm/index.d.ts +2 -0
  475. package/node_modules/@noble/hashes/esm/index.d.ts.map +1 -0
  476. package/node_modules/@noble/hashes/esm/index.js +33 -0
  477. package/node_modules/@noble/hashes/esm/index.js.map +1 -0
  478. package/node_modules/@noble/hashes/esm/legacy.d.ts +71 -0
  479. package/node_modules/@noble/hashes/esm/legacy.d.ts.map +1 -0
  480. package/node_modules/@noble/hashes/esm/legacy.js +281 -0
  481. package/node_modules/@noble/hashes/esm/legacy.js.map +1 -0
  482. package/node_modules/@noble/hashes/esm/package.json +10 -0
  483. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +23 -0
  484. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +1 -0
  485. package/node_modules/@noble/hashes/esm/pbkdf2.js +97 -0
  486. package/node_modules/@noble/hashes/esm/pbkdf2.js.map +1 -0
  487. package/node_modules/@noble/hashes/esm/ripemd160.d.ts +13 -0
  488. package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +1 -0
  489. package/node_modules/@noble/hashes/esm/ripemd160.js +13 -0
  490. package/node_modules/@noble/hashes/esm/ripemd160.js.map +1 -0
  491. package/node_modules/@noble/hashes/esm/scrypt.d.ts +34 -0
  492. package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +1 -0
  493. package/node_modules/@noble/hashes/esm/scrypt.js +228 -0
  494. package/node_modules/@noble/hashes/esm/scrypt.js.map +1 -0
  495. package/node_modules/@noble/hashes/esm/sha1.d.ts +11 -0
  496. package/node_modules/@noble/hashes/esm/sha1.d.ts.map +1 -0
  497. package/node_modules/@noble/hashes/esm/sha1.js +11 -0
  498. package/node_modules/@noble/hashes/esm/sha1.js.map +1 -0
  499. package/node_modules/@noble/hashes/esm/sha2.d.ts +159 -0
  500. package/node_modules/@noble/hashes/esm/sha2.d.ts.map +1 -0
  501. package/node_modules/@noble/hashes/esm/sha2.js +375 -0
  502. package/node_modules/@noble/hashes/esm/sha2.js.map +1 -0
  503. package/node_modules/@noble/hashes/esm/sha256.d.ts +20 -0
  504. package/node_modules/@noble/hashes/esm/sha256.d.ts.map +1 -0
  505. package/node_modules/@noble/hashes/esm/sha256.js +20 -0
  506. package/node_modules/@noble/hashes/esm/sha256.js.map +1 -0
  507. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +142 -0
  508. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +1 -0
  509. package/node_modules/@noble/hashes/esm/sha3-addons.js +393 -0
  510. package/node_modules/@noble/hashes/esm/sha3-addons.js.map +1 -0
  511. package/node_modules/@noble/hashes/esm/sha3.d.ts +53 -0
  512. package/node_modules/@noble/hashes/esm/sha3.d.ts.map +1 -0
  513. package/node_modules/@noble/hashes/esm/sha3.js +234 -0
  514. package/node_modules/@noble/hashes/esm/sha3.js.map +1 -0
  515. package/node_modules/@noble/hashes/esm/sha512.d.ts +26 -0
  516. package/node_modules/@noble/hashes/esm/sha512.d.ts.map +1 -0
  517. package/node_modules/@noble/hashes/esm/sha512.js +26 -0
  518. package/node_modules/@noble/hashes/esm/sha512.js.map +1 -0
  519. package/node_modules/@noble/hashes/esm/utils.d.ts +161 -0
  520. package/node_modules/@noble/hashes/esm/utils.d.ts.map +1 -0
  521. package/node_modules/@noble/hashes/esm/utils.js +281 -0
  522. package/node_modules/@noble/hashes/esm/utils.js.map +1 -0
  523. package/node_modules/@noble/hashes/hkdf.d.ts +36 -0
  524. package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
  525. package/node_modules/@noble/hashes/hkdf.js +88 -0
  526. package/node_modules/@noble/hashes/hkdf.js.map +1 -0
  527. package/node_modules/@noble/hashes/hmac.d.ts +35 -0
  528. package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
  529. package/node_modules/@noble/hashes/hmac.js +91 -0
  530. package/node_modules/@noble/hashes/hmac.js.map +1 -0
  531. package/node_modules/@noble/hashes/index.d.ts +1 -0
  532. package/node_modules/@noble/hashes/index.d.ts.map +1 -0
  533. package/node_modules/@noble/hashes/index.js +33 -0
  534. package/node_modules/@noble/hashes/index.js.map +1 -0
  535. package/node_modules/@noble/hashes/legacy.d.ts +71 -0
  536. package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
  537. package/node_modules/@noble/hashes/legacy.js +287 -0
  538. package/node_modules/@noble/hashes/legacy.js.map +1 -0
  539. package/node_modules/@noble/hashes/package.json +266 -0
  540. package/node_modules/@noble/hashes/pbkdf2.d.ts +23 -0
  541. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
  542. package/node_modules/@noble/hashes/pbkdf2.js +101 -0
  543. package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
  544. package/node_modules/@noble/hashes/ripemd160.d.ts +13 -0
  545. package/node_modules/@noble/hashes/ripemd160.d.ts.map +1 -0
  546. package/node_modules/@noble/hashes/ripemd160.js +16 -0
  547. package/node_modules/@noble/hashes/ripemd160.js.map +1 -0
  548. package/node_modules/@noble/hashes/scrypt.d.ts +34 -0
  549. package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
  550. package/node_modules/@noble/hashes/scrypt.js +232 -0
  551. package/node_modules/@noble/hashes/scrypt.js.map +1 -0
  552. package/node_modules/@noble/hashes/sha1.d.ts +11 -0
  553. package/node_modules/@noble/hashes/sha1.d.ts.map +1 -0
  554. package/node_modules/@noble/hashes/sha1.js +14 -0
  555. package/node_modules/@noble/hashes/sha1.js.map +1 -0
  556. package/node_modules/@noble/hashes/sha2.d.ts +159 -0
  557. package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
  558. package/node_modules/@noble/hashes/sha2.js +384 -0
  559. package/node_modules/@noble/hashes/sha2.js.map +1 -0
  560. package/node_modules/@noble/hashes/sha256.d.ts +20 -0
  561. package/node_modules/@noble/hashes/sha256.d.ts.map +1 -0
  562. package/node_modules/@noble/hashes/sha256.js +23 -0
  563. package/node_modules/@noble/hashes/sha256.js.map +1 -0
  564. package/node_modules/@noble/hashes/sha3-addons.d.ts +142 -0
  565. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
  566. package/node_modules/@noble/hashes/sha3-addons.js +402 -0
  567. package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
  568. package/node_modules/@noble/hashes/sha3.d.ts +53 -0
  569. package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
  570. package/node_modules/@noble/hashes/sha3.js +239 -0
  571. package/node_modules/@noble/hashes/sha3.js.map +1 -0
  572. package/node_modules/@noble/hashes/sha512.d.ts +26 -0
  573. package/node_modules/@noble/hashes/sha512.d.ts.map +1 -0
  574. package/node_modules/@noble/hashes/sha512.js +29 -0
  575. package/node_modules/@noble/hashes/sha512.js.map +1 -0
  576. package/node_modules/@noble/hashes/src/_assert.ts +22 -0
  577. package/node_modules/@noble/hashes/src/_blake.ts +50 -0
  578. package/node_modules/@noble/hashes/src/_md.ts +176 -0
  579. package/node_modules/@noble/hashes/src/_u64.ts +91 -0
  580. package/node_modules/@noble/hashes/src/argon2.ts +497 -0
  581. package/node_modules/@noble/hashes/src/blake1.ts +534 -0
  582. package/node_modules/@noble/hashes/src/blake2.ts +486 -0
  583. package/node_modules/@noble/hashes/src/blake2b.ts +10 -0
  584. package/node_modules/@noble/hashes/src/blake2s.ts +20 -0
  585. package/node_modules/@noble/hashes/src/blake3.ts +272 -0
  586. package/node_modules/@noble/hashes/src/crypto.ts +9 -0
  587. package/node_modules/@noble/hashes/src/cryptoNode.ts +15 -0
  588. package/node_modules/@noble/hashes/src/eskdf.ts +187 -0
  589. package/node_modules/@noble/hashes/src/hkdf.ts +88 -0
  590. package/node_modules/@noble/hashes/src/hmac.ts +94 -0
  591. package/node_modules/@noble/hashes/src/index.ts +31 -0
  592. package/node_modules/@noble/hashes/src/legacy.ts +293 -0
  593. package/node_modules/@noble/hashes/src/pbkdf2.ts +122 -0
  594. package/node_modules/@noble/hashes/src/ripemd160.ts +12 -0
  595. package/node_modules/@noble/hashes/src/scrypt.ts +257 -0
  596. package/node_modules/@noble/hashes/src/sha1.ts +10 -0
  597. package/node_modules/@noble/hashes/src/sha2.ts +402 -0
  598. package/node_modules/@noble/hashes/src/sha256.ts +24 -0
  599. package/node_modules/@noble/hashes/src/sha3-addons.ts +499 -0
  600. package/node_modules/@noble/hashes/src/sha3.ts +258 -0
  601. package/node_modules/@noble/hashes/src/sha512.ts +34 -0
  602. package/node_modules/@noble/hashes/src/utils.ts +395 -0
  603. package/node_modules/@noble/hashes/utils.d.ts +161 -0
  604. package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
  605. package/node_modules/@noble/hashes/utils.js +313 -0
  606. package/node_modules/@noble/hashes/utils.js.map +1 -0
  607. package/package.json +203 -0
  608. package/schemas/catalog.schema.json +360 -0
  609. package/src/align.ts +239 -0
  610. package/src/audio-init-parser.ts +189 -0
  611. package/src/audio-ring-buffer.ts +120 -0
  612. package/src/authored-timing.ts +63 -0
  613. package/src/carp-catalog.ts +845 -0
  614. package/src/catalog-fec-params.ts +535 -0
  615. package/src/catalog-network-source.ts +49 -0
  616. package/src/catalog-validator.ts +34 -0
  617. package/src/cmaf-block-assembler.ts +1167 -0
  618. package/src/cmaf-group-batcher.ts +104 -0
  619. package/src/cmcd-producer.ts +334 -0
  620. package/src/codec-utils.ts +1611 -0
  621. package/src/fec-codec.ts +61 -0
  622. package/src/fec-manager.ts +2081 -0
  623. package/src/fec-starvation-detector.ts +128 -0
  624. package/src/fec-worker-setup.ts +213 -0
  625. package/src/fmp4-completion.ts +47 -0
  626. package/src/fmp4-reader.ts +112 -0
  627. package/src/index.ts +486 -0
  628. package/src/iso-bmff-timing.ts +813 -0
  629. package/src/loc.ts +181 -0
  630. package/src/manifest-auth-plan.ts +107 -0
  631. package/src/media-router.ts +795 -0
  632. package/src/mfu-reassembler.ts +3741 -0
  633. package/src/mfu-reassembly-policy.ts +134 -0
  634. package/src/mmtp-container-parser.ts +1836 -0
  635. package/src/mmtp-decode-pipeline.ts +389 -0
  636. package/src/mmtp-router.ts +3136 -0
  637. package/src/mmtp-to-cmaf.ts +1024 -0
  638. package/src/multicast-auth-catalog.ts +340 -0
  639. package/src/multicast-input.ts +439 -0
  640. package/src/multicast-manager.ts +1720 -0
  641. package/src/multicast-transport.ts +321 -0
  642. package/src/multicast-unwrap.ts +132 -0
  643. package/src/no-modules.ts +20 -0
  644. package/src/phase14-catalog-override.ts +336 -0
  645. package/src/repair-fetch.ts +238 -0
  646. package/src/repair-layers.ts +490 -0
  647. package/src/sample-combiner.ts +938 -0
  648. package/src/signaling.ts +78 -0
  649. package/src/source-auth.ts +1 -0
  650. package/src/switching-set-alignment.ts +109 -0
  651. package/src/timestamp-codec.ts +265 -0
  652. package/src/track-pipeline-setup.ts +189 -0
  653. package/src/track-pipeline.ts +332 -0
  654. package/src/track-session.ts +994 -0
  655. package/src/transport-pool.ts +128 -0
  656. package/src/wasm-mfu-reassembler.ts +389 -0
  657. package/src/wasm-mmtp-container-parser.ts +23 -0
  658. package/test-vectors/align.json +199 -0
  659. package/test-vectors/catalog/README.md +40 -0
  660. package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +27 -0
  661. package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +32 -0
  662. package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +30 -0
  663. package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +31 -0
  664. package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +33 -0
  665. package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +23 -0
  666. package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +32 -0
  667. package/test-vectors/catalog/negative/legacy-selection-params.json +15 -0
  668. package/test-vectors/catalog/negative/media-track-repair-layer.json +16 -0
  669. package/test-vectors/catalog/negative/missing-mmtp-fields.json +12 -0
  670. package/test-vectors/catalog/negative/multicast-auth-padded-key.json +37 -0
  671. package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +53 -0
  672. package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +37 -0
  673. package/test-vectors/catalog/negative/multicast-endpoint-missing-protocol-and-source.json +24 -0
  674. package/test-vectors/catalog/negative/network-source-object.json +25 -0
  675. package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +23 -0
  676. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +26 -0
  677. package/test-vectors/catalog/negative/repair-track-legacy-shape.json +19 -0
  678. package/test-vectors/catalog/negative/track-role-empty.json +16 -0
  679. package/test-vectors/catalog/negative/track-role-non-string.json +16 -0
  680. package/test-vectors/catalog/positive/fec-layered-repair.json +104 -0
  681. package/test-vectors/catalog/positive/fec-multicast.json +51 -0
  682. package/test-vectors/catalog/positive/flat-av.json +30 -0
  683. package/test-vectors/catalog/positive/multicast-auth-rotation.json +55 -0
  684. package/test-vectors/catalog/positive/ticks-and-mixed-timescales.json +39 -0
  685. package/test-vectors/catalog/positive/track-role.json +49 -0
  686. package/test-vectors/catalog/releases/moqcast-native-v16-v1/catalog.json +65 -0
  687. package/test-vectors/catalog/releases/moqcast-native-v16-v1.archive.sha256 +1 -0
  688. package/test-vectors/catalog-multicast-auth-v1.json +26 -0
  689. package/test-vectors/catalog-validation.json +148 -0
  690. package/test-vectors/catalog-validator-public-api.mjs +25 -0
  691. package/test-vectors/generate-source-auth-fixture.mjs +114 -0
  692. package/test-vectors/source-auth-v1.json +61 -0
  693. package/test-vectors/source-contiguity-v1.json +131 -0
  694. package/test-vectors/subsample-placement-v1.json +230 -0
@@ -0,0 +1,994 @@
1
+ /**
2
+ * TrackSession — per-track transport state machine.
3
+ * SessionManager — orchestrates all sessions + TransportPool.
4
+ *
5
+ * Implements Patent Claim 9 (draft-ramadan-moq-fec APPLICATION-1):
6
+ * (a) subscribe unicast
7
+ * (b) simultaneously join multicast
8
+ * (c) render from unicast during join
9
+ * (d) activate multicast on RAP, suppress unicast
10
+ * (e) cross-transport FEC repair during transition
11
+ * (f) hysteresis before switching away from multicast
12
+ * (g) re-subscribe unicast on sustained failure
13
+ *
14
+ * PIM: manages transport state without inspecting packet contents.
15
+ */
16
+
17
+ import {
18
+ TransportPool,
19
+ type Subscription,
20
+ type SubscriptionMode,
21
+ type AcquireOptions,
22
+ type DirectSocketHealth,
23
+ type TransportPoolOptions,
24
+ } from './transport-pool.js'
25
+ import { fecBlockBudgetMs } from '@blockcast/mmt-fec'
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Types
29
+ // ---------------------------------------------------------------------------
30
+
31
+ export type TrackState =
32
+ | 'idle'
33
+ | 'unicast'
34
+ | 'unicast-fallback'
35
+ | 'joining'
36
+ | 'multicast'
37
+ | 'amt'
38
+ | 'failing'
39
+
40
+ export interface TrackSessionConfig {
41
+ trackName: string
42
+ trackType: 'video' | 'audio'
43
+ packetId: number
44
+ /** FEC interleave depth from catalog (ms). Used for hysteresis timer. */
45
+ fecInterleaveMs: number
46
+ /** Explicit catalog jitter/reorder allowance in ms. */
47
+ fecReorderToleranceMs?: number
48
+ /** FEC K from catalog. Used for audio worklet latency calculation. */
49
+ fecK: number
50
+ /** FEC repair symbols per block (P). NOT symbol size. */
51
+ fecRepairCount: number
52
+ /**
53
+ * Repair symbols per block this receiver actually joined: the sum of
54
+ * `repairSymbols` over the joined BASE layers, not a count of layers
55
+ * (draft-ramadan-moq-fec 6.3). A keyframe overlay is a separate FEC instance
56
+ * and is not included.
57
+ *
58
+ * This is runtime join state, not catalog geometry: a receiver on a clean
59
+ * path joins layer 0 only while a lossy one adds layers, so the catalog's
60
+ * total P is not what this receiver is receiving. Diagnostics that report P
61
+ * (the worker snapshot, CMCD `x-bc-fec-p`) must read this.
62
+ *
63
+ * Absent when the caller joins no layers explicitly, in which case P_0 is
64
+ * what was joined. That is an identity on a single-layer track, not a
65
+ * catalog default: with one layer, P_0 is the total.
66
+ */
67
+ joinedRepairCount?: number
68
+ /** FEC symbol size in bytes (T). Required for WASM decoder OTI. */
69
+ fecSymbolSize: number
70
+ /** MoQ repair track name from catalog FEC config. */
71
+ repairTrackName?: string
72
+ /** Frame samples from initData parsing (audio-init-parser.ts). */
73
+ frameSamples?: number
74
+ /** Audio sample rate from catalog. */
75
+ sampleRate?: number
76
+ /** Ordered transport preference: best first. */
77
+ preferredTransports: SubscriptionMode[]
78
+ /** Subscription key for multicast/AMT: "(S,G,port)". */
79
+ multicastKey?: string
80
+ /** MoQ track name for unicast subscription. */
81
+ moqTrackName?: string
82
+ /** AMT relay address (if AMT is in preference chain). */
83
+ amtRelay?: string
84
+ /** MoQ relay URL (for WebTransport subscription). */
85
+ moqRelayUrl?: string
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // PIM callbacks (protocol-independent — transport state only)
90
+ // ---------------------------------------------------------------------------
91
+
92
+ export interface SessionManagerCallbacks {
93
+ /** Fired on every transport state transition. */
94
+ onStateChange?(trackName: string, from: TrackState, to: TrackState): void
95
+ /** Fired to suppress/unsuppress unicast for this track (transport arbitration). */
96
+ onSuppress?(trackName: string, suppressed: boolean): void
97
+ /**
98
+ * Fired when a track sat in `joining` for SessionManager.takeoverDeadlineMs
99
+ * without a RAP (BLO-9413/D9). The session has already returned to `unicast`
100
+ * (nothing was suppressed, so nothing to restore) and it keeps its FEC
101
+ * decoder armed, because the multicast socket is still joined and the router
102
+ * still admits the stream's symbols. The host should abandon the router-side
103
+ * attempt (e.g. MediaRouter.failbackToMoq()) so the router stops waiting for
104
+ * a keyframe that is never coming; it must not dispose the decoder here. The
105
+ * decoder is released on the next transport edge (unsubscribe, failbackAll,
106
+ * or transportFailed falling back to unicast) through fec-teardown.
107
+ */
108
+ onTakeoverTimeout?(trackName: string): void
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // MMTP application-layer extension (FEC lifecycle, media resets)
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /**
116
+ * MMTP track events (discriminated union).
117
+ * Each protocol layer defines its own event union — MMTP events cover
118
+ * media resets, latency, and FEC lifecycle.
119
+ */
120
+ export type MmtpTrackEvent =
121
+ | { type: 'reset' }
122
+ | { type: 'latency'; latencyMs: number }
123
+ | { type: 'fec-setup' }
124
+ | { type: 'fec-teardown' }
125
+
126
+ /**
127
+ * MMTP application-layer extension of SessionManagerCallbacks.
128
+ * Single onTrackEvent dispatches all MMTP-specific events.
129
+ * PIM consumers use the base SessionManagerCallbacks (no onTrackEvent).
130
+ *
131
+ * IWA can define its own subinterface with its own TrackEvent union
132
+ * (e.g., IwaTrackEvent for socket health, AMT state) using the same pattern.
133
+ */
134
+ export interface MmtpSessionCallbacks extends SessionManagerCallbacks {
135
+ onTrackEvent?(trackName: string, event: MmtpTrackEvent, config: TrackSessionConfig): void
136
+ }
137
+
138
+ /**
139
+ * Loss-hysteresis window covering the signaled FEC block span plus jitter.
140
+ *
141
+ * `fecK` no longer feeds the math; it is still validated as a catalog
142
+ * well-formedness guard and kept in the signature for positional API compat
143
+ * (removal is a follow-up with the next cross-repo pin bump).
144
+ */
145
+ export function computeFecLossWindowMs(fecInterleaveMs: number, fecK: number, jitterBufferMs?: number): number {
146
+ return computeFecDeliveryWindowMs({
147
+ interleaveDepthMs: fecInterleaveMs,
148
+ sourceSymbols: fecK,
149
+ jitterBufferMs,
150
+ })
151
+ }
152
+
153
+ export interface FecDeliveryWindowConfig {
154
+ interleaveDepthMs: number
155
+ sourceSymbols: number
156
+ repairSymbols?: number
157
+ symbolSize?: number
158
+ mediaUnitsPerSecond?: number
159
+ bandwidthBps?: number
160
+ /**
161
+ * Explicit jitter/reorder allowance in ms, added once to the complete span.
162
+ * Callers pass their signaled catalog allowance, never a local default.
163
+ */
164
+ jitterBufferMs?: number
165
+ }
166
+
167
+ /**
168
+ * The catalog interleaveDepthMs is the complete FEC block span, including the
169
+ * repair tail. K, P, cadence, and bandwidth must not expand it again. Geometry
170
+ * fields remain accepted for existing callers, but do not feed deadline math.
171
+ */
172
+ export function computeFecDeliveryWindowMs(
173
+ config: FecDeliveryWindowConfig,
174
+ ): number {
175
+ const fecInterleaveMs = config.interleaveDepthMs
176
+ const fecK = config.sourceSymbols
177
+ if (!Number.isFinite(fecInterleaveMs) || fecInterleaveMs <= 0) {
178
+ throw new Error(
179
+ `[TrackSession] fecInterleaveMs=${fecInterleaveMs}. ` +
180
+ `Derive a positive FEC interleave window from the stream catalog.`
181
+ )
182
+ }
183
+ if (!Number.isFinite(fecK) || fecK <= 0) {
184
+ throw new Error(
185
+ `[TrackSession] fecK=${fecK}. ` +
186
+ `Derive a positive FEC source-symbol count from the stream catalog.`
187
+ )
188
+ }
189
+
190
+ // Absence stays absent; it does not authorize a synthesized reorder budget.
191
+ if (config.jitterBufferMs === undefined) return Math.ceil(fecInterleaveMs)
192
+ return fecBlockBudgetMs(fecInterleaveMs, config.jitterBufferMs)
193
+ }
194
+
195
+ /** Convert the shared FEC delivery deadline into an integer media timescale. */
196
+ export function computeFecDeliveryWindowMediaUnits(
197
+ config: FecDeliveryWindowConfig,
198
+ timestampTimescale: number,
199
+ ): number {
200
+ if (!Number.isFinite(timestampTimescale) || timestampTimescale <= 0) {
201
+ throw new Error(
202
+ `[TrackSession] timestampTimescale=${timestampTimescale}. ` +
203
+ 'Derive a positive media timescale from the container.',
204
+ )
205
+ }
206
+ const deliveryWindow = Math.ceil(
207
+ computeFecDeliveryWindowMs(config) * timestampTimescale / 1000,
208
+ )
209
+ if (!Number.isSafeInteger(deliveryWindow)) {
210
+ throw new Error('[TrackSession] FEC delivery window exceeds the media timestamp range.')
211
+ }
212
+ return deliveryWindow
213
+ }
214
+
215
+ // ---------------------------------------------------------------------------
216
+ // TrackSession
217
+ // ---------------------------------------------------------------------------
218
+
219
+ export class TrackSession {
220
+ readonly config: TrackSessionConfig
221
+ #state: TrackState = 'idle'
222
+ #subscription: Subscription | null = null
223
+ #hysteresisTimer: ReturnType<typeof setTimeout> | null = null
224
+ #joinDeadlineTimer: ReturnType<typeof setTimeout> | null = null
225
+ #previousPrimary: SubscriptionMode | null = null
226
+ #manager: SessionManager
227
+ // Single suppression authority flag (BLO-9413 failback coherence): every
228
+ // suppress this session fires is recorded here, and every path that leaves
229
+ // a suppressed state unwinds through #unsuppress(). Before this flag,
230
+ // unsubscribe() only un-suppressed from multicast/amt — a session torn down
231
+ // from `failing` (exactly where a starving track sits) was re-subscribed to
232
+ // unicast with the host's media still muted: MoQ playing into a dead sink.
233
+ #suppressFired = false
234
+ // Single FEC authority flag: set by the fec-setup edges (joinStarted,
235
+ // enterUnicastFallback), cleared by the fec-teardown edge in #transition.
236
+ // Setup and teardown are paired by construction, so a takeover timeout that
237
+ // parks the session in `unicast` keeps the decoder the still-joined
238
+ // transport is feeding, and the later unsubscribe still tears it down.
239
+ #fecArmed = false
240
+
241
+ constructor(config: TrackSessionConfig, manager: SessionManager) {
242
+ this.config = config
243
+ this.#manager = manager
244
+ }
245
+
246
+ // -----------------------------------------------------------------------
247
+ // State
248
+ // -----------------------------------------------------------------------
249
+
250
+ get state(): TrackState { return this.#state }
251
+ get subscription(): Subscription | null { return this.#subscription }
252
+
253
+ /** True while this session's onSuppress(true) is outstanding (no matching
254
+ * onSuppress(false) yet). Test/assertion surface for failback coherence. */
255
+ get isSuppressed(): boolean { return this.#suppressFired }
256
+
257
+ /** Current active transport mode, or null if idle. */
258
+ get transport(): SubscriptionMode | null {
259
+ switch (this.#state) {
260
+ case 'multicast': return 'multicast'
261
+ case 'amt': return 'amt'
262
+ case 'unicast': return 'moq'
263
+ case 'unicast-fallback': return 'moq' // SSM bridge absent — MoQ unicast is the only source
264
+ case 'joining': return 'moq' // MoQ still primary during join
265
+ case 'failing': return this.#previousPrimary
266
+ default: return null
267
+ }
268
+ }
269
+
270
+ // -----------------------------------------------------------------------
271
+ // Computed (no magic numbers)
272
+ // -----------------------------------------------------------------------
273
+
274
+ /** Audio worklet latency: max(fecInterleaveMs, frameDurationMs * fecK).
275
+ * Returns undefined if frame duration unknown (no initData). */
276
+ get audioLatencyMs(): number | undefined {
277
+ const fd = this.frameDurationMs
278
+ if (fd === undefined) {
279
+ return this.config.fecInterleaveMs > 0 ? this.config.fecInterleaveMs : undefined
280
+ }
281
+ return Math.max(this.config.fecInterleaveMs, fd * this.config.fecK)
282
+ }
283
+
284
+ /** Frame duration in ms, derived from frameSamples + sampleRate. */
285
+ get frameDurationMs(): number | undefined {
286
+ const { frameSamples, sampleRate } = this.config
287
+ if (!frameSamples || !sampleRate || sampleRate <= 0) return undefined
288
+ return (frameSamples / sampleRate) * 1000
289
+ }
290
+
291
+ // -----------------------------------------------------------------------
292
+ // Transitions (Patent Claim 9)
293
+ // -----------------------------------------------------------------------
294
+
295
+ /** idle → unicast: player subscribes to this track. */
296
+ subscribe(): void {
297
+ if (this.#state !== 'idle') return
298
+ this.#transition('unicast')
299
+
300
+ // Emit audio latency on first subscribe
301
+ if (this.config.trackType === 'audio') {
302
+ const latency = this.audioLatencyMs
303
+ if (latency !== undefined) {
304
+ this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'latency', latencyMs: latency }, this.config)
305
+ }
306
+ }
307
+ }
308
+
309
+ /** unicast → joining: multicast/AMT capability detected, join in progress. */
310
+ joinStarted(): void {
311
+ if (this.#state !== 'unicast') return
312
+ this.#transition('joining')
313
+ // FEC decoders must be ready before multicast data arrives (Claim 9e:
314
+ // cross-transport repair). A session re-joining after a takeover timeout
315
+ // still owns its decoder, so fec-setup fires once per FEC epoch.
316
+ this.#armFec()
317
+ }
318
+
319
+ /**
320
+ * any non-fallback state → unicast-fallback. Used when the SSM bridge is
321
+ * absent (vanilla Chromium / no extension) and the host falls back to a
322
+ * MoQ unicast subscription per track. Terminal-ish: activateMulticast,
323
+ * failbackAll, and transportFailed are no-ops while in this state.
324
+ *
325
+ * Fires fec-setup so the per-track repair worker spins up — repair packets
326
+ * still flow over the MoQ repair-track subscription, so FEC source+repair
327
+ * can converge as in the multicast path.
328
+ *
329
+ * Audio latency is NOT re-emitted here: subscribe() already fires latency
330
+ * once on the idle → unicast edge, and enterUnicastFallback is only ever
331
+ * reached from a non-idle state (SessionManager skips idle sessions).
332
+ */
333
+ enterUnicastFallback(): void {
334
+ if (this.#state === 'unicast-fallback') return
335
+ this.#clearHysteresis()
336
+ this.#transition('unicast-fallback')
337
+ // Suppress the redundant unicast media decoder. On the no-bridge path the
338
+ // host feeds MoQ-unicast source packets into MulticastManager (the render
339
+ // consumer); a second base-media subscribe (the @moq/watch decoder) would
340
+ // split the single connection's QUIC flow-control budget and stall delivery
341
+ // (BLO-8969). Fire suppress here, on the fallback edge, so the decoder is
342
+ // gated off before it subscribes rather than after the relay forward task
343
+ // has already wedged.
344
+ this.#suppress()
345
+ this.#armFec()
346
+ }
347
+
348
+ /**
349
+ * unicast-fallback → unicast. Called when the SSM bridge appears mid-session
350
+ * (extension installed after fallback engaged). Host first aborts the fallback
351
+ * drainers, then transitions sessions back to unicast so the regular
352
+ * multicast/AMT preference chain can take over via joinStarted().
353
+ */
354
+ exitUnicastFallback(): void {
355
+ if (this.#state !== 'unicast-fallback') return
356
+ this.#transition('unicast')
357
+ // Un-suppress the unicast decoder: the bridge appeared mid-session, so the
358
+ // host tears down the fallback drainers and the regular preference chain
359
+ // (joinStarted -> joining -> rapReceived) will re-suppress on multicast RAP.
360
+ this.#unsuppress()
361
+ }
362
+
363
+ /**
364
+ * joining → multicast or amt: RAP received on the joined transport (Claim 9d).
365
+ * Resets audio worklet, then suppresses MoQ.
366
+ */
367
+ rapReceived(mode: 'multicast' | 'amt'): void {
368
+ if (this.#state !== 'joining') return
369
+ this.#previousPrimary = mode
370
+ this.#transition(mode)
371
+ if (this.config.trackType === 'audio') {
372
+ this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'reset' }, this.config)
373
+ }
374
+ this.#suppress()
375
+ }
376
+
377
+ /** multicast/amt → failing: loss detected, start hysteresis timer (Claim 9f). */
378
+ lossDetected(): void {
379
+ if (this.#state !== 'multicast' && this.#state !== 'amt') return
380
+ this.#previousPrimary = this.#state as SubscriptionMode
381
+ this.#transition('failing')
382
+
383
+ // Hysteresis timer: sustained loss before failback (Claim 9f). Use the
384
+ // same complete span plus explicit allowance as FECManager and workers.
385
+ const hysteresisMs = computeFecLossWindowMs(
386
+ this.config.fecInterleaveMs,
387
+ this.config.fecK,
388
+ this.config.fecReorderToleranceMs,
389
+ )
390
+ this.#hysteresisTimer = setTimeout(() => {
391
+ this.hysteresisExpired()
392
+ }, hysteresisMs)
393
+ }
394
+
395
+ /** failing → previous primary: loss recovered within hysteresis window. */
396
+ lossRecovered(): void {
397
+ if (this.#state !== 'failing') return
398
+ this.#clearHysteresis()
399
+ if (this.#previousPrimary) {
400
+ this.#transition(this.#previousPrimary as TrackState)
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Transport failed (AMT relay down, multicast interface gone).
406
+ * Try next transport in preference chain.
407
+ */
408
+ transportFailed(): void {
409
+ this.#clearHysteresis()
410
+ // Unicast-fallback is terminal: the host owns the subscription and
411
+ // SessionManager transport machinery does not arbitrate it.
412
+ if (this.#state === 'unicast-fallback') return
413
+ const current = this.#previousPrimary ?? this.transport
414
+ const prefs = this.config.preferredTransports
415
+ const currentIdx = current ? prefs.indexOf(current) : -1
416
+ const nextMode = prefs[currentIdx + 1]
417
+
418
+ if (nextMode !== undefined && nextMode !== 'moq') {
419
+ // Next is multicast or AMT: start joining. `joining` means MoQ is
420
+ // primary again (see transport getter), so if the failed transport had
421
+ // suppressed unicast, restore it for the join window or the viewer
422
+ // watches black video until the next RAP (which may never come).
423
+ // The chain position moves with the attempt: a join that times out
424
+ // parks the session in `unicast` with this transport recorded, so the
425
+ // next failure continues past it rather than re-trying the transport
426
+ // that already failed and re-firing onTakeoverTimeout on every lap.
427
+ this.#previousPrimary = nextMode
428
+ this.#transition('joining')
429
+ this.#unsuppress()
430
+ return
431
+ }
432
+ // MoQ is next, or the chain is exhausted: unicast either way (Claim 9g).
433
+ // For a session parked in `unicast` with its decoder retained after a
434
+ // takeover timeout, this transition is a self-edge and fires no teardown,
435
+ // so the release is explicit. It is the "transportFailed falling back to
436
+ // unicast" release path the onTakeoverTimeout doc names, and #disarmFec is
437
+ // idempotent, so it is a no-op when the transition already fired the
438
+ // teardown. The audio reset is not flag-guarded, so it is tied to the edge
439
+ // itself: a session already resting in unicast changed no transport and
440
+ // owes the host no reset, however many pool subscriptions fail for it.
441
+ const from = this.#state
442
+ this.#previousPrimary = null
443
+ this.#transition('unicast')
444
+ this.#disarmFec()
445
+ this.#unsuppress()
446
+ if (from !== 'unicast' && this.config.trackType === 'audio') {
447
+ this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'reset' }, this.config)
448
+ }
449
+ }
450
+
451
+ /** Hysteresis expired → failback to next transport (Claim 9g). */
452
+ hysteresisExpired(): void {
453
+ if (this.#state !== 'failing') return
454
+ this.#clearHysteresis()
455
+ this.transportFailed()
456
+ }
457
+
458
+ /** → idle: player unsubscribes / tab close / cleanup. */
459
+ unsubscribe(): void {
460
+ this.#clearHysteresis()
461
+ if (this.#state !== 'idle') {
462
+ this.#transition('idle')
463
+ // Unwind any outstanding suppression regardless of which state we left.
464
+ // The old `wasActive` check (multicast/amt only) leaked suppression when
465
+ // tearing down from `failing` or `unicast-fallback` — failbackAll()'s
466
+ // unsubscribe→subscribe cycle then re-entered unicast with the host's
467
+ // media still muted (BLO-9413 freeze: "failing back" logged, video dead).
468
+ this.#unsuppress()
469
+ }
470
+ }
471
+
472
+ /** True when SessionManager.enterUnicastFallback put this track into the
473
+ * host-driven unicast subscription mode. Host can iterate sessions to
474
+ * decide whether to keep its fallback drainer alive across MediaRouter
475
+ * arbitration events. */
476
+ get isUnicastFallback(): boolean {
477
+ return this.#state === 'unicast-fallback'
478
+ }
479
+
480
+ /**
481
+ * True while this track owns a FEC decoder (fec-setup fired, fec-teardown
482
+ * not yet). Stays true across a takeover timeout that parked the session in
483
+ * `unicast`; SessionManager.failbackAll uses it to release such sessions.
484
+ */
485
+ get isFecArmed(): boolean {
486
+ return this.#fecArmed
487
+ }
488
+
489
+ /**
490
+ * Start the deadline a `joining` entry deferred while the join was inactive
491
+ * (SessionManager.joinActive). The track gets the FULL deadline from here,
492
+ * because that is when media can first arrive. No-op outside `joining` and
493
+ * when a deadline is already running, so it never shortens or restarts one.
494
+ *
495
+ * The `joinActive` guard is what makes this safe to call from anywhere: a
496
+ * track parked in `joining` with a deferred (null) timer satisfies the other
497
+ * two, so without it a host calling this while the join is still inactive
498
+ * would arm the exact deadline the gate exists to defer -- silently
499
+ * reinstating BLO-34289. It is a no-op for the internal caller, which writes
500
+ * `#joinActive = true` before iterating the sessions.
501
+ */
502
+ armDeferredJoinDeadline(): void {
503
+ if (this.#state !== 'joining' || this.#joinDeadlineTimer !== null) return
504
+ if (!this.#manager.joinActive) return
505
+ this.#armJoinDeadline(this.#manager.takeoverDeadlineMs)
506
+ }
507
+
508
+ dispose(): void {
509
+ this.#clearHysteresis()
510
+ this.#clearJoinDeadline()
511
+ this.#state = 'idle'
512
+ this.#subscription = null
513
+ // Release through the teardown edge, not a bare assignment: the
514
+ // retained-FEC park makes `unicast` + isFecArmed a resting state, and
515
+ // SessionManager.dispose() calls this directly (removeTrack() reaches it
516
+ // via unsubscribe(), which has already disarmed). A bare assignment there
517
+ // drops the host's decoder without a fec-teardown -- a leaked Worker.
518
+ this.#disarmFec()
519
+ }
520
+
521
+ // -----------------------------------------------------------------------
522
+ // Internal
523
+ // -----------------------------------------------------------------------
524
+
525
+ #transition(to: TrackState, opts?: { retainFec?: boolean }): void {
526
+ const from = this.#state
527
+ if (from === to) return
528
+ // Takeover deadline (BLO-9413/D9): every entry into `joining` arms it
529
+ // (joinStarted AND transportFailed-failover both route through here);
530
+ // every exit cancels it. Without this, a transport that accepts the
531
+ // subscribe but never delivers a RAP parks the session in `joining`
532
+ // forever (observed live in the Phase-0 headless repro). The value is
533
+ // resolved before any state write, so the one residual failure -- a
534
+ // manager whose deadline was never set at all, which addTrack() refuses --
535
+ // leaves the session in its previous state rather than half-transitioned.
536
+ // Invalid values never reach here: SessionManager's setter refuses them.
537
+ // The read is unconditional even when `joinActive` defers the arm, so a
538
+ // manager without a deadline still fails closed on every joining edge.
539
+ const joinDeadlineMs = to === 'joining' ? this.#manager.takeoverDeadlineMs : undefined
540
+ this.#state = to
541
+ if (from === 'joining') this.#clearJoinDeadline()
542
+ if (joinDeadlineMs !== undefined && this.#manager.joinActive) {
543
+ this.#armJoinDeadline(joinDeadlineMs)
544
+ }
545
+ // FEC teardown: entering a state without a FEC decoder releases the one
546
+ // this track armed, unless the caller keeps it (takeover timeout: the
547
+ // transport is still joined and still feeding the decoder).
548
+ const fecActive = (s: TrackState) =>
549
+ s === 'joining' ||
550
+ s === 'multicast' ||
551
+ s === 'amt' ||
552
+ s === 'failing' ||
553
+ s === 'unicast-fallback'
554
+ if (!fecActive(to) && !opts?.retainFec) this.#disarmFec()
555
+ this.#manager.callbacks?.onStateChange?.(this.config.trackName, from, to)
556
+ }
557
+
558
+ #clearHysteresis(): void {
559
+ if (this.#hysteresisTimer !== null) {
560
+ clearTimeout(this.#hysteresisTimer)
561
+ this.#hysteresisTimer = null
562
+ }
563
+ }
564
+
565
+ #armJoinDeadline(deadlineMs: number): void {
566
+ this.#clearJoinDeadline()
567
+ if (deadlineMs === 0) return // a declared 0 disables the deadline
568
+ this.#joinDeadlineTimer = setTimeout(() => {
569
+ this.#joinDeadlineTimer = null
570
+ if (this.#state !== 'joining') return
571
+ // No RAP within the deadline: return to unicast for arbitration only.
572
+ // MoQ was never suppressed in `joining`, so there is nothing to restore
573
+ // and this edge stays silent on onSuppress (asserted in tests). The
574
+ // multicast socket is still joined and the router still admits this
575
+ // stream's symbols, so the FEC decoder stays armed; the next transport
576
+ // edge releases it.
577
+ this.#transition('unicast', { retainFec: true })
578
+ this.#manager.callbacks?.onTakeoverTimeout?.(this.config.trackName)
579
+ }, deadlineMs)
580
+ }
581
+
582
+ #clearJoinDeadline(): void {
583
+ if (this.#joinDeadlineTimer !== null) {
584
+ clearTimeout(this.#joinDeadlineTimer)
585
+ this.#joinDeadlineTimer = null
586
+ }
587
+ }
588
+
589
+ /** Fire fec-setup exactly once per FEC epoch. */
590
+ #armFec(): void {
591
+ if (this.#fecArmed) return
592
+ this.#fecArmed = true
593
+ this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'fec-setup' }, this.config)
594
+ }
595
+
596
+ /** Fire fec-teardown for an armed decoder; no-op when none is armed. */
597
+ #disarmFec(): void {
598
+ if (!this.#fecArmed) return
599
+ this.#fecArmed = false
600
+ this.#manager.mmtp?.onTrackEvent?.(this.config.trackName, { type: 'fec-teardown' }, this.config)
601
+ }
602
+
603
+ /** Fire onSuppress(true) exactly once per suppression epoch. */
604
+ #suppress(): void {
605
+ if (this.#suppressFired) return
606
+ this.#suppressFired = true
607
+ this.#manager.callbacks?.onSuppress?.(this.config.trackName, true)
608
+ }
609
+
610
+ /** Unwind an outstanding suppression; no-op when none is outstanding. */
611
+ #unsuppress(): void {
612
+ if (!this.#suppressFired) return
613
+ this.#suppressFired = false
614
+ this.#manager.callbacks?.onSuppress?.(this.config.trackName, false)
615
+ }
616
+ }
617
+
618
+ // ---------------------------------------------------------------------------
619
+ // SessionManager
620
+ // ---------------------------------------------------------------------------
621
+
622
+ /**
623
+ * Largest takeover deadline setTimeout represents faithfully. setTimeout
624
+ * coerces its delay to a signed 32-bit int, so a larger value fires on the
625
+ * next tick (Node also warns TimeoutOverflowWarning) instead of after the
626
+ * window the host asked for -- silently inverting the feature. Rejecting it
627
+ * keeps the classic units bug (seconds-as-ms, a wrong timescale) loud.
628
+ */
629
+ export const MAX_TAKEOVER_DEADLINE_MS = 2_147_483_647
630
+
631
+ /**
632
+ * How long `joinActive` may stay false before the library says so. The deferral
633
+ * is unbounded by construction -- that is the feature -- so a host that never
634
+ * flips it back parks every track in `joining` with no timer at all: exactly the
635
+ * forever-join the takeover deadline exists to prevent, only silent. MoQ stays
636
+ * unsuppressed so the viewer sees video, but onTakeoverTimeout never fires, the
637
+ * FEC decoder stays armed, and the preference chain never advances past the
638
+ * transport that failed. It looks like a slow join. A host is one un-awaited
639
+ * rejection between `false` and `true` away from it, so the library complains
640
+ * rather than trusting every future host to get all of its exit paths right.
641
+ *
642
+ * Generous against a genuinely slow subscribe -- a bridge reconnect or AMT relay
643
+ * handshake is seconds -- so anything still unsettled here is broken, not slow.
644
+ */
645
+ export const JOIN_DEFER_WARN_MS = 30_000
646
+
647
+ export class SessionManager {
648
+ readonly pool: TransportPool
649
+ readonly sessions = new Map<string, TrackSession>()
650
+ callbacks: SessionManagerCallbacks | null = null
651
+ #takeoverDeadlineMs: number | undefined = undefined
652
+ #joinActive = true
653
+ #joinDeferWarnTimer: ReturnType<typeof setTimeout> | null = null
654
+
655
+ /**
656
+ * Takeover deadline (BLO-9413/D9): the time a session may sit in `joining`
657
+ * without a RAP before returning to `unicast` and firing onTakeoverTimeout.
658
+ * MoQ is never suppressed during `joining`, so expiry abandons the takeover
659
+ * without a playback glitch. The host derives it from the catalog (RAP
660
+ * cadence from groupDurationTicks/timescale plus the signaled FEC delivery
661
+ * window or reorder tolerance) and sets it before addTrack(). There is no
662
+ * default: an unset value fails addTrack() closed, and reading the getter
663
+ * before assignment throws the same way, so no host ever holds a
664
+ * `number | undefined` it could coalesce back into a default.
665
+ * `hasTakeoverDeadline` answers whether one was admitted. A declared 0
666
+ * disables the deadline. The value is an integer number of milliseconds:
667
+ * setTimeout truncates fractions, so a host that divides ticks by a
668
+ * timescale must round before assigning.
669
+ *
670
+ * Assignment validates, so the diagnostic lands at the host's own call site.
671
+ * That is what bounds the blast radius: `joining` is entered from
672
+ * joinStarted(), from the transportFailed() failover branch -- itself
673
+ * reachable from a setTimeout via hysteresisExpired(), where no caller could
674
+ * catch a throw -- and from the activateMulticast()/activateAmt()/pool-failure
675
+ * loops, where a throw on one session would abandon its siblings mid-loop
676
+ * (the split-brain those loops exist to prevent). Refusing a bad value at
677
+ * assignment means no host mutation can make an already-admitted session
678
+ * throw out of a transport edge.
679
+ */
680
+ get takeoverDeadlineMs(): number {
681
+ return this.#requireTakeoverDeadlineMs()
682
+ }
683
+
684
+ set takeoverDeadlineMs(value: number) {
685
+ if (!Number.isInteger(value) || value < 0 || value > MAX_TAKEOVER_DEADLINE_MS) {
686
+ throw new Error(
687
+ `[SessionManager] takeoverDeadlineMs must be an integer from 0 to ${MAX_TAKEOVER_DEADLINE_MS} (0 disables it, larger values overflow setTimeout); got ${String(value)}`,
688
+ )
689
+ }
690
+ this.#takeoverDeadlineMs = value
691
+ }
692
+
693
+ /** True once the host has admitted a deadline; a declared 0 counts. */
694
+ get hasTakeoverDeadline(): boolean {
695
+ return this.#takeoverDeadlineMs !== undefined
696
+ }
697
+
698
+ /**
699
+ * Whether the transport the joining tracks are waiting on is actually
700
+ * subscribed. The takeover deadline is a media-arrival budget and media
701
+ * cannot arrive before the join exists, so while this is false, entering
702
+ * `joining` arms no deadline; flipping it true starts the FULL deadline for
703
+ * every track already parked there.
704
+ *
705
+ * Default true, which is the arm-on-`joining` behaviour every host had
706
+ * before: a host that only calls joinStarted() after its own join resolves
707
+ * (moqtail-private's player, post-BLO-33339) is already at the right edge and
708
+ * needs no gate. A host that arms joinStarted() up front — to have FEC
709
+ * decoders ready before the first datagram — sets this false before addTrack()
710
+ * and true when its subscribe settles. Charging a slow subscribe (bridge
711
+ * reconnect, AMT relay handshake) against the media budget expired the
712
+ * deadline before the subscription existed and abandoned the join on the
713
+ * happy path. BLO-34289.
714
+ *
715
+ * The flag tracks the host's primary join, not per-track transport state. A
716
+ * track that fails over to the next transport in its own chain while this is
717
+ * false (transportFailed's failover branch also enters `joining`) has that
718
+ * deadline gated on the primary join activating, not on its own transport.
719
+ * Under the documented usage -- false once at setup, true once the subscribe
720
+ * settles -- the two coincide.
721
+ *
722
+ * The gate is on the ENTRY into `joining`, and false cannot take back a
723
+ * deadline already running: the `true -> false` edge arms the warn below and
724
+ * clears nothing. So set it false BEFORE the `joining` entry it is meant to
725
+ * gate. A host that instead flipped it false after a `transportFailed()`
726
+ * failover had already entered `joining` would leave the full deadline
727
+ * counting against a subscribe that had not begun -- BLO-34289 again, on the
728
+ * failover edge. Clearing running deadlines on that edge is deliberately NOT
729
+ * done: this flag is manager-wide, so one transport's brief false would
730
+ * cancel an unrelated track's live deadline and then re-arm it at FULL length
731
+ * on the next activation (armDeferredJoinDeadline sees `joining` + a null
732
+ * timer and cannot tell a cleared deadline from a deferred one). Every
733
+ * unrelated transport blip would restart the whole budget, so under repeated
734
+ * failover a track that should have timed out is extended indefinitely and
735
+ * never advances. Only a false that never returns true loses the timer
736
+ * outright, and JOIN_DEFER_WARN_MS already covers that.
737
+ *
738
+ * Leaving this false is not free: see JOIN_DEFER_WARN_MS.
739
+ */
740
+ get joinActive(): boolean {
741
+ return this.#joinActive
742
+ }
743
+
744
+ set joinActive(active: boolean) {
745
+ const wasActive = this.#joinActive
746
+ this.#joinActive = active
747
+ if (!active) {
748
+ // Edge-triggered: a host re-declaring false has not restarted its
749
+ // subscribe, and total deferral is what the warning is about.
750
+ if (wasActive) this.#armJoinDeferWarn()
751
+ return
752
+ }
753
+ this.#clearJoinDeferWarn()
754
+ if (wasActive) return
755
+ for (const [, session] of this.sessions) session.armDeferredJoinDeadline()
756
+ }
757
+
758
+ #armJoinDeferWarn(): void {
759
+ this.#joinDeferWarnTimer = setTimeout(() => {
760
+ this.#joinDeferWarnTimer = null
761
+ const deferred = [...this.sessions.values()].filter(s => s.state === 'joining').length
762
+ console.warn(
763
+ `[SessionManager] joinActive has been false for ${JOIN_DEFER_WARN_MS} ms and ${deferred} track(s) sit in 'joining' with no takeover deadline. Set joinActive = true when the subscribe settles, on every exit path including failures, or these tracks never time out, never release their FEC decoders, and never advance to the next transport.`,
764
+ )
765
+ }, JOIN_DEFER_WARN_MS)
766
+ }
767
+
768
+ #clearJoinDeferWarn(): void {
769
+ if (this.#joinDeferWarnTimer === null) return
770
+ clearTimeout(this.#joinDeferWarnTimer)
771
+ this.#joinDeferWarnTimer = null
772
+ }
773
+
774
+ /** Narrow to MMTP callbacks (onTrackEvent). Safe — optional chaining handles PIM-only consumers. */
775
+ get mmtp(): MmtpSessionCallbacks | null {
776
+ return this.callbacks as MmtpSessionCallbacks | null
777
+ }
778
+
779
+ constructor(poolOptions?: TransportPoolOptions) {
780
+ this.pool = new TransportPool(poolOptions)
781
+
782
+ // Wire pool failure → per-track failover
783
+ this.pool.onSubscriptionFailed(sub => {
784
+ for (const trackName of sub.consumers) {
785
+ const session = this.sessions.get(trackName)
786
+ if (session) session.transportFailed()
787
+ }
788
+ })
789
+ }
790
+
791
+ // -----------------------------------------------------------------------
792
+ // Track management
793
+ // -----------------------------------------------------------------------
794
+
795
+ /**
796
+ * The admitted takeover deadline. Throws only when the host never set one,
797
+ * every other invalid value being refused by the setter, and returns a
798
+ * declared 0 as 0. The getter and addTrack() both route through it, so a
799
+ * session cannot exist without an admitted deadline, which is what makes
800
+ * the #transition() read unable to throw on an already-admitted session.
801
+ */
802
+ #requireTakeoverDeadlineMs(): number {
803
+ const value = this.#takeoverDeadlineMs
804
+ if (value === undefined) {
805
+ throw new Error(
806
+ '[SessionManager] takeoverDeadlineMs is unset; set a catalog-derived takeover deadline (0 disables it) before adding tracks',
807
+ )
808
+ }
809
+ return value
810
+ }
811
+
812
+ /** Add a track session from catalog config. */
813
+ addTrack(config: TrackSessionConfig): TrackSession {
814
+ if (this.sessions.has(config.trackName)) {
815
+ throw new Error(`[SessionManager] Track "${config.trackName}" already exists`)
816
+ }
817
+ this.#requireTakeoverDeadlineMs()
818
+ const session = new TrackSession(config, this)
819
+ this.sessions.set(config.trackName, session)
820
+ return session
821
+ }
822
+
823
+ /** Remove a track session. Releases subscription with grace period. */
824
+ removeTrack(trackName: string): void {
825
+ const session = this.sessions.get(trackName)
826
+ if (!session) return
827
+ session.unsubscribe()
828
+
829
+ // Release pool subscription
830
+ const subKey = session.config.multicastKey ?? session.config.moqTrackName
831
+ if (subKey) this.pool.releaseSubscription(subKey, trackName)
832
+
833
+ session.dispose()
834
+ this.sessions.delete(trackName)
835
+ }
836
+
837
+ // -----------------------------------------------------------------------
838
+ // Atomic multi-track operations
839
+ // -----------------------------------------------------------------------
840
+
841
+ /**
842
+ * All tracks with multicast RAP → multicast state (atomic).
843
+ * Called when MediaRouter fires onSourceSwitch('multicast').
844
+ * Prevents split-brain between video and audio.
845
+ *
846
+ * A takeover deadline can return a track from `joining` to `unicast` while
847
+ * the multicast socket keeps delivering packets and the track keeps its FEC
848
+ * decoder armed. If a valid RAP arrives later, that late-RAP edge suppresses
849
+ * unicast and promotes multicast without a second fec-setup.
850
+ */
851
+ activateMulticast(): void {
852
+ for (const [, session] of this.sessions) {
853
+ if (session.state === 'unicast') {
854
+ session.joinStarted()
855
+ }
856
+ if (session.state === 'joining') {
857
+ session.rapReceived('multicast')
858
+ }
859
+ }
860
+ }
861
+
862
+ /**
863
+ * All tracks with AMT RAP → amt state (atomic).
864
+ */
865
+ activateAmt(): void {
866
+ for (const [, session] of this.sessions) {
867
+ if (session.state === 'unicast') {
868
+ session.joinStarted()
869
+ }
870
+ if (session.state === 'joining') {
871
+ session.rapReceived('amt')
872
+ }
873
+ }
874
+ }
875
+
876
+ /**
877
+ * All tracks → unicast (failback, atomic).
878
+ * Called when MulticastManager fires onFailback().
879
+ * unicast-fallback sessions are skipped — the host owns those subscriptions
880
+ * and they must drain uninterrupted across transport-state events.
881
+ *
882
+ * Coherence invariant (BLO-9413): after this returns, every non-fallback
883
+ * session is `unicast` (or `idle`), unsuppressed, and without a FEC decoder.
884
+ * unsubscribe() unwinds any outstanding suppression regardless of the state
885
+ * it tears down from (`failing` included; see TrackSession.#suppressFired),
886
+ * and a session that a takeover timeout parked in `unicast` with its decoder
887
+ * still armed is cycled too, because this transport edge is what releases
888
+ * that decoder (unsubscribe fires fec-teardown).
889
+ */
890
+ failbackAll(): void {
891
+ for (const [, session] of this.sessions) {
892
+ const parkedWithFec = session.state === 'unicast' && session.isFecArmed
893
+ if (
894
+ parkedWithFec ||
895
+ (session.state !== 'idle' &&
896
+ session.state !== 'unicast' &&
897
+ session.state !== 'unicast-fallback')
898
+ ) {
899
+ session.unsubscribe()
900
+ session.subscribe() // re-enter unicast
901
+ }
902
+ }
903
+ }
904
+
905
+ /**
906
+ * All tracks → unicast-fallback (atomic).
907
+ * Called by the host when the multicast gateway is absent and
908
+ * the player will subscribe to source tracks over MoQ unicast and feed
909
+ * packets via MulticastManager.feedUnicastSource() (or manager.router
910
+ * directly). Sessions in this state stay terminal until the host calls
911
+ * unsubscribe() / disposes the SessionManager.
912
+ */
913
+ enterUnicastFallback(): void {
914
+ for (const [, session] of this.sessions) {
915
+ if (session.state === 'idle') continue
916
+ session.enterUnicastFallback()
917
+ }
918
+ }
919
+
920
+ /**
921
+ * True if any session is currently in unicast-fallback. Hosts use this to
922
+ * gate `multicastTrack.close()` on failback events — closing the shared
923
+ * broadcast would kill the host's fallback drainers.
924
+ */
925
+ hasActiveFallback(): boolean {
926
+ for (const [, session] of this.sessions) {
927
+ if (session.isUnicastFallback) return true
928
+ }
929
+ return false
930
+ }
931
+
932
+ /**
933
+ * All fallback tracks → unicast (atomic).
934
+ * Called when the SSM bridge appears mid-session and the host has aborted
935
+ * the fallback drainers. The session re-enters the regular preference chain
936
+ * via joinStarted() (issued by the host immediately after).
937
+ */
938
+ exitUnicastFallback(): void {
939
+ for (const [, session] of this.sessions) {
940
+ if (session.isUnicastFallback) session.exitUnicastFallback()
941
+ }
942
+ }
943
+
944
+ // -----------------------------------------------------------------------
945
+ // Cleanup lifecycle
946
+ // -----------------------------------------------------------------------
947
+
948
+ /** Tab closed — release all subscriptions for this tab (grace period). */
949
+ tabClosed(tabId: string): void {
950
+ // All tracks owned by this tab → idle
951
+ for (const [trackName, session] of this.sessions) {
952
+ session.unsubscribe()
953
+ this.pool.releaseAllForConsumer(trackName)
954
+ }
955
+ }
956
+
957
+ /** Bridge WebTransport connection dropped — same as tab close. */
958
+ bridgeDisconnected(): void {
959
+ this.tabClosed('bridge')
960
+ }
961
+
962
+ /**
963
+ * Track selection changed (user or ABR).
964
+ * Old track released (grace period), new track subscribed.
965
+ */
966
+ trackSelectionChanged(oldTrackName: string, newConfig: TrackSessionConfig): void {
967
+ this.removeTrack(oldTrackName)
968
+ const session = this.addTrack(newConfig)
969
+ session.subscribe()
970
+ }
971
+
972
+ // -----------------------------------------------------------------------
973
+ // Query
974
+ // -----------------------------------------------------------------------
975
+
976
+ /** Get a track session by name. */
977
+ getTrack(trackName: string): TrackSession | undefined {
978
+ return this.sessions.get(trackName)
979
+ }
980
+
981
+ // -----------------------------------------------------------------------
982
+ // Cleanup
983
+ // -----------------------------------------------------------------------
984
+
985
+ dispose(): void {
986
+ this.#clearJoinDeferWarn()
987
+ for (const [, session] of this.sessions) {
988
+ session.dispose()
989
+ }
990
+ this.sessions.clear()
991
+ this.pool.dispose()
992
+ this.callbacks = null
993
+ }
994
+ }