@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,1058 @@
1
+ /**
2
+ * FEC-block-aware CMAF segment assembler for MSE multicast.
3
+ *
4
+ * Buffers reassembled frames per FEC source block (SBN). On block completion
5
+ * (K source symbols received clean, or FEC recovery), sorts frames by ESI
6
+ * and builds a single multi-sample moof+mdat CMAF segment. On block timeout,
7
+ * generates fill frames (green video / silence audio) for missing ESI slots.
8
+ *
9
+ * One assembler instance per track (video or audio). MulticastManager owns
10
+ * these instances and calls them directly with SBN/ESI from the FEC pipeline.
11
+ */
12
+
13
+ import {
14
+ encodeDataSegment,
15
+ buildVideoInitSegment,
16
+ buildAudioInitSegment,
17
+ generateGreenFillNAL,
18
+ normalizeAacSampleForCmaf,
19
+ } from './mmtp-to-cmaf.js'
20
+ import {
21
+ classifyH264Keyframe,
22
+ detectH264Keyframe,
23
+ splitH264SampleNalUnits,
24
+ } from './codec-utils.js'
25
+ import {
26
+ scaleAuthoredTicks,
27
+ scaleAuthoredTiming,
28
+ type AuthoredTiming,
29
+ } from './authored-timing.js'
30
+ import type { MmtpDeliveryProvenance } from './mfu-reassembler.js'
31
+
32
+ const INT32_MIN = -0x80000000
33
+ const INT32_MAX = 0x7fffffff
34
+
35
+ interface BlockFrame {
36
+ data: Uint8Array
37
+ timestamp: number
38
+ keyframe: boolean
39
+ /** Exact ISO-BMFF timing retained from the parser for FEC/source ordering. */
40
+ authoredTiming?: AuthoredTiming
41
+ /** MMTP packet_sequence_number identifying the delivered access unit. */
42
+ packetSequenceNumber?: number
43
+ /** Every authoritative MMTP source-packet sequence that formed this sample. */
44
+ packetSequenceNumbers?: number[]
45
+ /** Subset of packetSequenceNumbers reconstructed by FEC. */
46
+ repairedPacketSequenceNumbers?: number[]
47
+ /** Native MMTP MPU group coordinate retained across source/FEC ordering. */
48
+ mpuSequenceNumber?: number
49
+ catalogTrackName?: string
50
+ /**
51
+ * Whole-frame delivery verdict folded across every contributing fragment.
52
+ * Absent means no caller declared one; it is never an implied 'multicast'.
53
+ */
54
+ deliveryProvenance?: MmtpDeliveryProvenance
55
+ }
56
+
57
+ export interface CmafBlockSample {
58
+ data: Uint8Array
59
+ timestamp: number
60
+ duration: number
61
+ keyframe: boolean
62
+ /** Signed PTS-DTS offset in the configured CMAF timescale. */
63
+ compositionTimeOffset?: number
64
+ /** Exact ISO-BMFF timing in the media track's native timescale. */
65
+ authoredTiming?: AuthoredTiming
66
+ /** MMTP packet_sequence_number identifying the delivered access unit. */
67
+ packetSequenceNumber?: number
68
+ /** Every authoritative MMTP source-packet sequence that formed this sample. */
69
+ packetSequenceNumbers?: number[]
70
+ /** Subset of packetSequenceNumbers reconstructed by FEC. */
71
+ repairedPacketSequenceNumbers?: number[]
72
+ /** Native MMTP MPU group coordinate retained for exact replay selection. */
73
+ mpuSequenceNumber?: number
74
+ /** Immutable catalog identity supplied by the owning ingress subscription. */
75
+ catalogTrackName?: string
76
+ /**
77
+ * Whole-frame delivery verdict, carried so MSE `onSample` consumers can see
78
+ * the same evidence the non-MSE frame path gets. Without it the MSE branch
79
+ * silently loses a computed 'mixed' or 'multicast' finding.
80
+ */
81
+ deliveryProvenance?: MmtpDeliveryProvenance
82
+ }
83
+
84
+ export type CmafSampleOrderPolicy =
85
+ | { mode: 'arrival' }
86
+ | {
87
+ /** Release samples according to media progression, without a wall clock. */
88
+ mode: 'media-window'
89
+ /** Delivery window in the same units as CmafBlockSample.timestamp. */
90
+ deliveryWindow: number
91
+ /** Optional modulo period for wrapped input timestamps, in timestamp units. */
92
+ timestampWrapPeriod?: number
93
+ }
94
+
95
+ export interface CmafSampleOrderStats {
96
+ staleSamples: number
97
+ duplicateSamples: number
98
+ preRapDeadlineDrops: number
99
+ noRapFlushDrops: number
100
+ forcedReleases: number
101
+ }
102
+
103
+ export interface BlockAssemblerConfig {
104
+ /** Number of source symbols per FEC block */
105
+ k: number
106
+ /** Timescale for CMAF segments (default 1_000_000 = microseconds) */
107
+ timescale?: number
108
+ /** ISO track ID (default 1) */
109
+ trackId?: number
110
+ /** Track type — determines fill frame generation and init segment format */
111
+ trackType: 'video' | 'audio'
112
+ /** Video/audio codec string (e.g. 'avc1.64002a', 'mp4a.40.2'). */
113
+ codec?: string
114
+ // Video config (required when trackType='video')
115
+ /** Raw avcC payload from catalog description — for init + green fill */
116
+ avcC?: Uint8Array
117
+ /** Video width in pixels */
118
+ width?: number
119
+ /** Video height in pixels */
120
+ height?: number
121
+ // Audio config (required when trackType='audio')
122
+ /** Audio codec string (e.g. 'mp4a.40.2', 'opus') — for silence fill generation */
123
+ audioCodec?: string
124
+ /** Audio sample rate (e.g. 48000) */
125
+ sampleRate?: number
126
+ /** Audio channel count (e.g. 1 or 2) */
127
+ channels?: number
128
+ /** Raw AudioSpecificConfig from catalog */
129
+ asc?: Uint8Array
130
+ // Shared
131
+ /** Frames per second — fallback for duration estimation */
132
+ fps?: number
133
+ /** Explicit sample ordering policy. Datagram/FEC callers use media-window. */
134
+ sampleOrder?: CmafSampleOrderPolicy
135
+ /**
136
+ * Bounded sample reorder depth for callback mode.
137
+ *
138
+ * Default 0 preserves immediate emission. When set on video tracks with
139
+ * onSample enabled, samples are sorted by timestamp and the oldest sample is
140
+ * emitted only after this many newer samples are available.
141
+ * @deprecated Use sampleOrder with an explicit policy.
142
+ */
143
+ sampleReorderWindow?: number
144
+ }
145
+
146
+ interface OrderedSample extends CmafBlockSample {
147
+ advancesHorizon: boolean
148
+ }
149
+
150
+ export class CmafBlockAssembler {
151
+ #config: Required<Pick<BlockAssemblerConfig, 'k' | 'timescale' | 'trackId' | 'trackType'>>
152
+ #blocks = new Map<number, Map<number, BlockFrame>>()
153
+ #pendingFlush = new Set<number>()
154
+ #pendingRecovered = new Map<number, BlockFrame>()
155
+ #sequence = 1
156
+ #baseTs = -1
157
+ #fillFrame: Uint8Array
158
+ #lastDecodableAudioFrame: Uint8Array | null = null
159
+ #frameDuration: number
160
+ #lastEmittedTs = -1
161
+ #lastSourceTs = -1
162
+ #sampleReorderWindow = 0
163
+ #sampleOrder: CmafSampleOrderPolicy = { mode: 'arrival' }
164
+ #sampleOrderCapacity = 0
165
+ #sampleReorderQueue: OrderedSample[] = []
166
+ #recoveredHorizonTs = -1
167
+ #sampleOrderStats: CmafSampleOrderStats = {
168
+ staleSamples: 0,
169
+ duplicateSamples: 0,
170
+ preRapDeadlineDrops: 0,
171
+ noRapFlushDrops: 0,
172
+ forcedReleases: 0,
173
+ }
174
+ #validateH264Keyframes = false
175
+ #keyframeRejectDumpCount = 0
176
+
177
+ /** Callback: emits a complete moof+mdat CMAF segment */
178
+ onSegment: ((segment: Uint8Array) => void) | null = null
179
+ /** Callback: emits a gated/deduped raw sample for callers that already own the MSE timeline. */
180
+ onSample: ((sample: CmafBlockSample) => void) | null = null
181
+ /** Callback: emits an init segment (ftyp+moov) */
182
+ onInitSegment: ((init: Uint8Array) => void) | null = null
183
+
184
+ // Video keyframe gate (BLO-3035 layer 3): Chrome's CHUNK_DEMUXER rejects a
185
+ // moof+mdat whose first sample is non-sync with no prior reference frames
186
+ // ("CHUNK_DEMUXER_ERROR_APPEND_FAILED: Failed to prepare video sample for
187
+ // decode") and detaches the SourceBuffer for the rest of the session.
188
+ // Mid-stream join almost guarantees the first FEC blocks are mid-GOP — all
189
+ // delta frames. Gate emission until the first block containing an IDR;
190
+ // within that block, splice samples to start at the IDR index. Audio passes
191
+ // unconditionally — valid for the codecs supported here (AAC, Opus) where
192
+ // every frame is independently decodable. SBR-only AAC, USAC, or HE-AAC v2
193
+ // with PCE re-entry would need a similar gate.
194
+ #armedVideo = false
195
+
196
+ // Legacy SBN emission watermark for the old block-buffered path. Direct
197
+ // source emission is now timestamp-ordered instead; recovered frames are
198
+ // queued by media timestamp so they can fill gaps without racing ahead of
199
+ // source frames.
200
+ #lastEmittedSbn = -1
201
+
202
+ /** Attempt to claim emission for `sbn`. Returns true if `sbn` is past
203
+ * the watermark (and advances it); false if this sbn has already been
204
+ * claimed or is older (caller should drop). */
205
+ #claimSbnEmission(sbn: number): boolean {
206
+ if (sbn <= this.#lastEmittedSbn) return false
207
+ this.#lastEmittedSbn = sbn
208
+ return true
209
+ }
210
+
211
+ /** Truncate `frames` to start at the first keyframe for video tracks before
212
+ * the first emission. Returns the (possibly spliced) frames, or null to
213
+ * drop the block entirely. Audio + already-armed video: identity. */
214
+ #applyVideoKeyframeGate<T extends { data: Uint8Array; keyframe: boolean }>(frames: T[]): T[] | null {
215
+ const keyedFrames = this.#normalizeVideoKeyframes(frames)
216
+ if (this.#config.trackType !== 'video' || this.#armedVideo) return keyedFrames
217
+ const firstKfIdx = keyedFrames.findIndex(f => f.keyframe)
218
+ if (firstKfIdx < 0) return null
219
+ return firstKfIdx === 0 ? keyedFrames : keyedFrames.slice(firstKfIdx)
220
+ }
221
+
222
+ #normalizeVideoKeyframes<T extends { data: Uint8Array; keyframe: boolean }>(frames: T[]): T[] {
223
+ if (this.#config.trackType !== 'video' || !this.#validateH264Keyframes) return frames
224
+ return frames.map((frame) => this.#withEffectiveVideoKeyframe(frame))
225
+ }
226
+
227
+ #withEffectiveVideoKeyframe<T extends { data: Uint8Array; keyframe: boolean }>(frame: T): T {
228
+ if (this.#config.trackType !== 'video' || !this.#validateH264Keyframes) return frame
229
+ const keyframe = detectH264Keyframe(frame.data)
230
+ if (!keyframe) this.#dumpRejectedVideoKeyframe(frame.data, frame.keyframe)
231
+ return keyframe === frame.keyframe ? frame : { ...frame, keyframe }
232
+ }
233
+
234
+ #dumpRejectedVideoKeyframe(data: Uint8Array, metadataKeyframe: boolean): void {
235
+ if (this.#keyframeRejectDumpCount >= 12) return
236
+ this.#keyframeRejectDumpCount++
237
+ const sample = splitH264SampleNalUnits(data)
238
+ const nalTypes = describeH264NalTypes(sample.nals)
239
+ const classify = classifyH264Keyframe(data)
240
+ const hex = Array.from(data.subarray(0, Math.min(48, data.length)))
241
+ .map(b => (b as number).toString(16).padStart(2, '0'))
242
+ .join(' ')
243
+ console.log(
244
+ `[L4-KF-GATE] reject#${this.#keyframeRejectDumpCount} len=${data.length} ` +
245
+ `metadataKf=${metadataKeyframe} classify=${classify} format=${sample.format} ` +
246
+ `nalTypes=[${nalTypes.join(',')}] first48=[${hex}]`,
247
+ )
248
+ }
249
+
250
+ /** Mark video as already synchronized by an external decoder path.
251
+ * Used by MoQ→multicast MSE continuity when MoQ has already appended a
252
+ * keyframe to the same SourceBuffer timeline before multicast takeover. */
253
+ markVideoSynced(): void {
254
+ if (this.#config.trackType === 'video') this.#armedVideo = true
255
+ }
256
+
257
+ /** Emit each frame as its own single-sample moof+mdat segment. Matches the
258
+ * case-20 path (`MmtpToCmaf.wrapVideoSample` → `encodeDataSegment`) byte-
259
+ * for-byte per frame. Working hypothesis (pending Chromium-source
260
+ * validation against `media/filters/chunk_demuxer.cc`):
261
+ * `encodeMultiSampleSegment` blobs are accepted by ChunkDemuxer in this
262
+ * pipeline but only the first sample renders for live, possibly because
263
+ * the pipeline expects per-frame moof boundaries. Track in BLO-3035.
264
+ *
265
+ * Stale-tfdt defense-in-depth: drops any frame whose timestamp is older
266
+ * than `#lastEmittedTs`. The SBN watermark already prevents out-of-order
267
+ * FEC-block emissions, but `addBypassFrame` (oversized MFU / no-FEC path)
268
+ * doesn't touch the watermark — a bypass frame at t=T followed by a FEC
269
+ * block whose frames precede T would otherwise inject earlier tfdt and
270
+ * detach the SourceBuffer. */
271
+ #ordersSamples(): boolean {
272
+ return this.#sampleOrder.mode === 'media-window' || this.#sampleReorderWindow > 0
273
+ }
274
+
275
+ #unwrapTimestamp(timestamp: number): number {
276
+ const wrapPeriod = this.#sampleOrder.mode === 'media-window'
277
+ ? this.#sampleOrder.timestampWrapPeriod
278
+ : undefined
279
+ if (!wrapPeriod) return timestamp
280
+ const reference = this.#lastSourceTs >= 0
281
+ ? this.#lastSourceTs
282
+ : this.#recoveredHorizonTs >= 0
283
+ ? this.#recoveredHorizonTs
284
+ : this.#lastEmittedTs
285
+ if (reference < 0) return timestamp
286
+ return timestamp + Math.round((reference - timestamp) / wrapPeriod) * wrapPeriod
287
+ }
288
+
289
+ #emitSample(
290
+ data: Uint8Array,
291
+ timestamp: number,
292
+ duration: number,
293
+ keyframe: boolean,
294
+ advancesHorizon: boolean,
295
+ catalogTrackName: string | undefined,
296
+ mpuSequenceNumber: number | undefined,
297
+ packetSequenceNumber: number | undefined,
298
+ packetSequenceNumbers: number[] | undefined,
299
+ repairedPacketSequenceNumbers: number[] | undefined,
300
+ authoredTiming: AuthoredTiming | undefined,
301
+ // Required, not optional: an omitted optional argument is silently
302
+ // undefined, which is exactly how this field went missing from the
303
+ // recovered path in the first place. Callers with nothing to declare pass
304
+ // `undefined` on purpose.
305
+ deliveryProvenance: MmtpDeliveryProvenance | undefined,
306
+ ): void {
307
+ const scaledTiming = authoredTiming === undefined
308
+ ? null
309
+ : scaleAuthoredTiming(authoredTiming, this.#config.timescale)
310
+ if (authoredTiming !== undefined && scaledTiming === null) return
311
+ const authoredTimestamp = scaledTiming?.timestamp ?? timestamp
312
+ const authoredDuration = scaledTiming?.duration ?? duration
313
+ let compositionTimeOffset: number | undefined
314
+ if (authoredTiming !== undefined) {
315
+ const scaledOffset = scaleAuthoredTicks(
316
+ authoredTiming.presentationTime - authoredTiming.decodeTime,
317
+ authoredTiming.mediaTimescale,
318
+ this.#config.timescale,
319
+ )
320
+ if (scaledOffset === null) return
321
+ // trun stores composition offsets as integer target-timescale ticks.
322
+ // Preserve the nearest representable tick, then enforce the signed
323
+ // int32 serialization range on the rounded value.
324
+ const roundedOffset = Math.round(scaledOffset)
325
+ if (
326
+ !Number.isSafeInteger(roundedOffset) ||
327
+ roundedOffset < INT32_MIN ||
328
+ roundedOffset > INT32_MAX
329
+ ) return
330
+ compositionTimeOffset = roundedOffset
331
+ }
332
+ const unwrappedTimestamp = this.#unwrapTimestamp(authoredTimestamp)
333
+ if (unwrappedTimestamp <= this.#lastEmittedTs) {
334
+ this.#sampleOrderStats.staleSamples++
335
+ return
336
+ }
337
+
338
+ const sample = this.#withEffectiveVideoKeyframe({
339
+ data,
340
+ timestamp: unwrappedTimestamp,
341
+ duration: authoredDuration,
342
+ keyframe,
343
+ ...(compositionTimeOffset === undefined ? {} : { compositionTimeOffset }),
344
+ advancesHorizon,
345
+ ...(authoredTiming === undefined ? {} : { authoredTiming }),
346
+ ...(packetSequenceNumber === undefined ? {} : { packetSequenceNumber }),
347
+ ...(packetSequenceNumbers === undefined ? {} : { packetSequenceNumbers }),
348
+ ...(repairedPacketSequenceNumbers === undefined
349
+ ? {}
350
+ : { repairedPacketSequenceNumbers }),
351
+ ...(mpuSequenceNumber === undefined ? {} : { mpuSequenceNumber }),
352
+ ...(catalogTrackName === undefined ? {} : { catalogTrackName }),
353
+ ...(deliveryProvenance === undefined ? {} : { deliveryProvenance }),
354
+ })
355
+ if (advancesHorizon && unwrappedTimestamp > this.#lastSourceTs) {
356
+ this.#lastSourceTs = unwrappedTimestamp
357
+ } else if (!advancesHorizon && unwrappedTimestamp > this.#recoveredHorizonTs) {
358
+ this.#recoveredHorizonTs = unwrappedTimestamp
359
+ }
360
+
361
+ if (this.#ordersSamples()) {
362
+ this.#queueSample(sample)
363
+ return
364
+ }
365
+ this.#emitArrivalOrdered(sample)
366
+ }
367
+
368
+ #emitArrivalOrdered(sample: OrderedSample): void {
369
+ if (this.#config.trackType === 'video' && !this.#armedVideo && !sample.keyframe) {
370
+ this.#sampleOrderStats.preRapDeadlineDrops++
371
+ return
372
+ }
373
+ this.#emitSampleNow(sample)
374
+ }
375
+
376
+ #queueSample(sample: OrderedSample): void {
377
+ if (this.#sampleReorderQueue.some(s => s.timestamp === sample.timestamp)) {
378
+ this.#sampleOrderStats.duplicateSamples++
379
+ return
380
+ }
381
+ this.#sampleReorderQueue.push(sample)
382
+ this.#sampleReorderQueue.sort((a, b) => a.timestamp - b.timestamp)
383
+ this.#drainSampleReorder(false)
384
+ this.#enforceSampleOrderCapacity()
385
+ }
386
+
387
+ #releaseHorizon(): number {
388
+ return this.#lastSourceTs >= 0 ? this.#lastSourceTs : this.#recoveredHorizonTs
389
+ }
390
+
391
+ #sampleIsReleasable(sample: OrderedSample, flushAll: boolean): boolean {
392
+ if (flushAll) return true
393
+ if (this.#sampleOrder.mode === 'media-window') {
394
+ const horizon = this.#releaseHorizon()
395
+ return horizon >= 0 && sample.timestamp < horizon - this.#sampleOrder.deliveryWindow
396
+ }
397
+ return this.#sampleReorderQueue.length > this.#sampleReorderWindow
398
+ }
399
+
400
+ #dropPreRapSamples(flushAll: boolean): boolean {
401
+ if (this.#config.trackType !== 'video' || this.#armedVideo) return false
402
+
403
+ const firstRap = this.#sampleReorderQueue.findIndex(
404
+ sample => sample.keyframe && this.#sampleIsReleasable(sample, flushAll),
405
+ )
406
+ if (firstRap >= 0) {
407
+ if (firstRap > 0) {
408
+ this.#sampleOrderStats.preRapDeadlineDrops += firstRap
409
+ this.#sampleReorderQueue.splice(0, firstRap)
410
+ }
411
+ return false
412
+ }
413
+
414
+ let dropped = 0
415
+ while (
416
+ this.#sampleReorderQueue[0] &&
417
+ this.#sampleIsReleasable(this.#sampleReorderQueue[0]!, flushAll)
418
+ ) {
419
+ this.#sampleReorderQueue.shift()
420
+ dropped++
421
+ }
422
+ if (flushAll) this.#sampleOrderStats.noRapFlushDrops += dropped
423
+ else this.#sampleOrderStats.preRapDeadlineDrops += dropped
424
+ return dropped > 0
425
+ }
426
+
427
+ #drainSampleReorder(flushAll: boolean): void {
428
+ while (this.#sampleReorderQueue.length > 0) {
429
+ if (this.#dropPreRapSamples(flushAll) && this.#sampleReorderQueue.length === 0) return
430
+ const sample = this.#sampleReorderQueue[0]
431
+ if (!sample || !this.#sampleIsReleasable(sample, flushAll)) return
432
+ this.#sampleReorderQueue.shift()
433
+ this.#emitQueuedSample(sample)
434
+ }
435
+ }
436
+
437
+ #emitQueuedSample(sample: OrderedSample): void {
438
+ const next = this.#sampleReorderQueue.find(s => s.timestamp > sample.timestamp)
439
+ const duration = sample.authoredTiming
440
+ ? sample.duration
441
+ : next ? next.timestamp - sample.timestamp : sample.duration
442
+ this.#emitSampleNow({
443
+ ...sample,
444
+ duration: duration > 0
445
+ ? duration
446
+ : sample.authoredTiming ? sample.duration : this.#frameDuration,
447
+ })
448
+ }
449
+
450
+ #enforceSampleOrderCapacity(): void {
451
+ if (this.#sampleOrder.mode !== 'media-window') return
452
+ while (this.#sampleReorderQueue.length > this.#sampleOrderCapacity) {
453
+ const sample = this.#sampleReorderQueue.shift()
454
+ if (!sample) return
455
+ this.#sampleOrderStats.forcedReleases++
456
+ console.warn(
457
+ `[CmafBlockAssembler] ${this.#config.trackType} media-window queue exceeded ` +
458
+ `${this.#sampleOrderCapacity} samples; force-releasing timestamp=${sample.timestamp}`,
459
+ )
460
+ if (this.#config.trackType === 'video' && !this.#armedVideo && !sample.keyframe) {
461
+ this.#sampleOrderStats.preRapDeadlineDrops++
462
+ continue
463
+ }
464
+ this.#emitQueuedSample(sample)
465
+ }
466
+ }
467
+
468
+ #emitSampleNow(sample: CmafBlockSample): void {
469
+ if (sample.timestamp <= this.#lastEmittedTs) {
470
+ this.#sampleOrderStats.staleSamples++
471
+ return
472
+ }
473
+ this.#emitAudioGapFillNowBefore(sample.timestamp)
474
+ this.#writeSampleNow(sample, true)
475
+ }
476
+
477
+ #writeSampleNow(sample: CmafBlockSample, rememberAudio: boolean): void {
478
+ const { timestamp, duration, keyframe } = sample
479
+ this.#lastEmittedTs = timestamp
480
+ if (this.#config.trackType === 'video' && keyframe) this.#armedVideo = true
481
+ const data = this.#normalizeSampleData(sample.data)
482
+ if (this.#config.trackType === 'audio' && rememberAudio && data.length > 0) {
483
+ // Keep decoder-proven codec data for concealment only after the FEC
484
+ // delivery deadline has passed without the missing audio sample.
485
+ this.#lastDecodableAudioFrame = data.slice()
486
+ }
487
+ if (this.onSample) {
488
+ this.onSample(data === sample.data ? sample : { ...sample, data })
489
+ } else {
490
+ const baseTime = this.#rebase(timestamp)
491
+ const segment = encodeDataSegment(
492
+ data,
493
+ baseTime,
494
+ duration,
495
+ keyframe,
496
+ this.#sequence++,
497
+ this.#config.trackId,
498
+ sample.compositionTimeOffset,
499
+ )
500
+ this.onSegment?.(segment)
501
+ }
502
+ }
503
+
504
+ #emitAudioGapFillNowBefore(timestamp: number): void {
505
+ if (
506
+ this.#config.trackType !== 'audio' ||
507
+ this.#lastEmittedTs < 0 ||
508
+ this.#lastDecodableAudioFrame === null
509
+ ) return
510
+
511
+ const gap = timestamp - this.#lastEmittedTs
512
+ if (gap <= this.#frameDuration * 1.5) return
513
+
514
+ const missingFrames = Math.max(0, Math.round(gap / this.#frameDuration) - 1)
515
+ for (let i = 0; i < missingFrames; i++) {
516
+ const fillTs = this.#lastEmittedTs + this.#frameDuration
517
+ this.#writeSampleNow({
518
+ data: this.#lastDecodableAudioFrame.slice(),
519
+ timestamp: fillTs,
520
+ duration: this.#frameDuration,
521
+ keyframe: true,
522
+ }, false)
523
+ }
524
+ }
525
+
526
+ #emitPerSample(
527
+ frames: BlockFrame[],
528
+ audioDurationOverride?: number,
529
+ advancesHorizon = false,
530
+ ): void {
531
+ for (let i = 0; i < frames.length; i++) {
532
+ const f = frames[i]!
533
+ // Per-frame duration from delta to next frame, or fallback.
534
+ let duration: number
535
+ if (this.#config.trackType === 'audio') {
536
+ duration = audioDurationOverride ?? this.#frameDuration
537
+ } else if (i < frames.length - 1) {
538
+ duration = frames[i + 1]!.timestamp - f.timestamp
539
+ } else if (i > 0) {
540
+ duration = f.timestamp - frames[i - 1]!.timestamp
541
+ } else {
542
+ duration = audioDurationOverride ?? this.#frameDuration
543
+ }
544
+ if (duration <= 0) duration = audioDurationOverride ?? this.#frameDuration
545
+ this.#emitSample(
546
+ f.data,
547
+ f.timestamp,
548
+ duration,
549
+ f.keyframe,
550
+ advancesHorizon,
551
+ f.catalogTrackName,
552
+ f.mpuSequenceNumber,
553
+ f.packetSequenceNumber,
554
+ f.packetSequenceNumbers,
555
+ f.repairedPacketSequenceNumbers,
556
+ f.authoredTiming,
557
+ f.deliveryProvenance,
558
+ )
559
+ }
560
+ }
561
+
562
+ // [BLO-3035-LAYER4-DIAG] Byte-dump for the first 6 video emits + 1Hz
563
+ // aggregate counters that survive the whole run. The counters tell us
564
+ // whether "emits=low" (assembler emits too few segments) or
565
+ // "addFrame=low" (parser delivers too few frames) — which routes the
566
+ // root-cause hunt to assembler vs router vs publisher.
567
+ #dumpCount = 0
568
+ #emitCount = 0
569
+ #sampleCount = 0
570
+ #keyframeCount = 0
571
+ #addFrameCount = 0
572
+ #recoveredBlockCount = 0
573
+ #completeBlockCount = 0
574
+ #failBlockCount = 0
575
+ #aggregateTimer: ReturnType<typeof setInterval> | null = null
576
+ #startAggregateTimer(): void {
577
+ if (this.#aggregateTimer) return
578
+ this.#aggregateTimer = setInterval(() => {
579
+ console.log(
580
+ `[L4-AGG-${this.#config.trackType}] emits=${this.#emitCount} samples=${this.#sampleCount} ` +
581
+ `kf=${this.#keyframeCount} addFrame=${this.#addFrameCount} ` +
582
+ `recovered=${this.#recoveredBlockCount} complete=${this.#completeBlockCount} ` +
583
+ `fail=${this.#failBlockCount} pendingBlocks=${this.#blocks.size} ` +
584
+ `pendingFlushes=${this.#pendingFlush.size} armedVideo=${this.#armedVideo}`,
585
+ )
586
+ this.#emitCount = 0
587
+ this.#sampleCount = 0
588
+ this.#keyframeCount = 0
589
+ this.#addFrameCount = 0
590
+ this.#recoveredBlockCount = 0
591
+ this.#completeBlockCount = 0
592
+ this.#failBlockCount = 0
593
+ }, 1000)
594
+ }
595
+ #dumpEmit(path: string, samples: Array<{ data: Uint8Array; keyframe: boolean; timestamp?: number }>, sbn?: number): void {
596
+ this.#emitCount++
597
+ this.#sampleCount += samples.length
598
+ this.#keyframeCount += samples.filter(s => s.keyframe).length
599
+ // Always log video keyframes (no cap) — needed to see bytes of every IDR
600
+ // including the t=5s one that Chrome's decoder rejected (BLO-3035 L4).
601
+ const anyKf = this.#config.trackType === 'video' && samples.some(s => s.keyframe)
602
+ if (this.#config.trackType !== 'video' || (this.#dumpCount >= 6 && !anyKf)) return
603
+ this.#dumpCount++
604
+ const s0 = samples[0]
605
+ if (!s0) return
606
+ const d = s0.data
607
+ const hex = Array.from(d.subarray(0, Math.min(48, d.length)))
608
+ .map(b => (b as number).toString(16).padStart(2, '0'))
609
+ .join(' ')
610
+ // djb2 hash of full s0 buffer — used to compare clean vs FEC-recovered emit bytes.
611
+ let h = 5381
612
+ for (let i = 0; i < d.length; i++) h = (h * 33 + (d[i] as number)) | 0
613
+ const hash = (h >>> 0).toString(16).padStart(8, '0')
614
+ const ts0 = s0.timestamp ?? -1
615
+ const tsN = samples[samples.length - 1]?.timestamp ?? -1
616
+ console.log(
617
+ `[L4-DIAG] EMIT#${this.#dumpCount} path=${path}${sbn !== undefined ? ` sbn=${sbn}` : ''} ` +
618
+ `samples=${samples.length} s0.kf=${s0.keyframe} s0.len=${d.length} ` +
619
+ `ts0=${ts0} tsN=${tsN} dur=${tsN - ts0} hash=${hash} first48=[${hex}]`,
620
+ )
621
+ }
622
+
623
+ constructor(config: BlockAssemblerConfig) {
624
+ this.#config = {
625
+ k: config.k,
626
+ timescale: config.timescale ?? 1_000_000,
627
+ trackId: config.trackId ?? 1,
628
+ trackType: config.trackType,
629
+ }
630
+
631
+ // Compute default frame duration from fps (updated from timestamp deltas at runtime)
632
+ if (config.fps && config.fps > 0) {
633
+ this.#frameDuration = Math.round(this.#config.timescale / config.fps)
634
+ } else if (config.trackType === 'audio' && config.sampleRate) {
635
+ this.#frameDuration = Math.round(this.#config.timescale * 1024 / config.sampleRate)
636
+ } else {
637
+ // No fps info — will be refined from actual timestamp deltas
638
+ this.#frameDuration = 33333
639
+ }
640
+
641
+ // Build fill frame based on track type (requires codec config from catalog)
642
+ if (config.trackType === 'video' && config.avcC && config.width && config.height) {
643
+ this.#fillFrame = generateGreenFillNAL(config.avcC, config.width, config.height)
644
+ } else {
645
+ this.#fillFrame = new Uint8Array(0)
646
+ }
647
+
648
+ const sampleReorderWindow = config.sampleReorderWindow ?? 0
649
+ if (!Number.isFinite(sampleReorderWindow) || sampleReorderWindow < 0) {
650
+ throw new Error('[CmafBlockAssembler] sampleReorderWindow must be finite and non-negative')
651
+ }
652
+ if (config.sampleOrder && sampleReorderWindow > 0) {
653
+ throw new Error('[CmafBlockAssembler] configure sampleOrder or sampleReorderWindow, not both')
654
+ }
655
+ this.#sampleReorderWindow = Math.floor(sampleReorderWindow)
656
+ this.#sampleOrder = config.sampleOrder ?? { mode: 'arrival' }
657
+ if (this.#sampleOrder.mode === 'media-window') {
658
+ const { deliveryWindow, timestampWrapPeriod } = this.#sampleOrder
659
+ if (!Number.isSafeInteger(deliveryWindow) || deliveryWindow <= 0) {
660
+ throw new Error(
661
+ '[CmafBlockAssembler] media-window deliveryWindow must be a positive safe integer',
662
+ )
663
+ }
664
+ if (
665
+ timestampWrapPeriod !== undefined &&
666
+ (!Number.isSafeInteger(timestampWrapPeriod) || timestampWrapPeriod <= deliveryWindow * 2)
667
+ ) {
668
+ throw new Error(
669
+ '[CmafBlockAssembler] timestampWrapPeriod must be a safe integer larger than two delivery windows',
670
+ )
671
+ }
672
+ const samplesPerSecond = config.trackType === 'audio' && config.sampleRate
673
+ ? config.sampleRate / 1024
674
+ : config.fps
675
+ if (!samplesPerSecond || !Number.isFinite(samplesPerSecond) || samplesPerSecond <= 0) {
676
+ throw new Error(
677
+ '[CmafBlockAssembler] media-window ordering requires catalog-derived media cadence',
678
+ )
679
+ }
680
+ this.#sampleOrderCapacity =
681
+ Math.ceil(deliveryWindow * samplesPerSecond / this.#config.timescale) + 1
682
+ }
683
+ this.#validateH264Keyframes =
684
+ config.trackType === 'video' &&
685
+ (!!config.avcC || !config.codec || isH264Codec(config.codec))
686
+ if (config.trackType === 'video') {
687
+ console.log(
688
+ `[L4-KF-CONFIG] validate=${this.#validateH264Keyframes} ` +
689
+ `codec=${config.codec ?? ''} avcC=${config.avcC?.length ?? 0}`,
690
+ )
691
+ }
692
+
693
+ // Store config for init segment generation
694
+ this._videoConfig = config.trackType === 'video' && config.avcC
695
+ ? { avcC: config.avcC, width: config.width ?? 0, height: config.height ?? 0 }
696
+ : null
697
+ this._audioConfig = config.trackType === 'audio' && config.sampleRate && config.channels
698
+ ? { sampleRate: config.sampleRate, channels: config.channels, asc: config.asc }
699
+ : null
700
+ }
701
+
702
+ private _videoConfig: { avcC?: Uint8Array; width: number; height: number } | null
703
+ private _audioConfig: { sampleRate: number; channels: number; asc?: Uint8Array } | null
704
+
705
+ get sampleOrderStats(): Readonly<CmafSampleOrderStats> {
706
+ return { ...this.#sampleOrderStats }
707
+ }
708
+
709
+ /** Emit a single frame immediately as a one-sample moof+mdat segment.
710
+ *
711
+ * BLO-3035 layer 4 (4b): the prior SBN-grouped buffering scheme stranded
712
+ * frames whenever a multi-packet frame spanned FEC block boundaries —
713
+ * intermediate SBNs had packets-but-no-frames and got `completeBlock`
714
+ * events that landed on empty `#blocks` (→ `pendingFlush` leak), while
715
+ * the SBN where the multi-packet frame finally completed received the
716
+ * `addFrame` but its `trackBlock` count never reached K (→ `pendingBlocks`
717
+ * leak). Result: ~50% of received frames never reached MSE.
718
+ *
719
+ * This path now mirrors `addBypassFrame` — emit on arrival, no buffering.
720
+ * FEC recovery (`addRecoveredBlock`) keeps its SBN-watermark dedup against
721
+ * the same SBN being re-emitted from two paths. Out-of-order regression
722
+ * is guarded by the per-frame `lastEmittedTs` check (also enforced in
723
+ * `#emitPerSample`). The `sbn`/`esi` parameters are kept for the call-site
724
+ * signature but no longer affect routing. */
725
+ addFrame(
726
+ sbn: number,
727
+ esi: number,
728
+ data: Uint8Array,
729
+ timestamp: number,
730
+ keyframe: boolean,
731
+ catalogTrackName?: string,
732
+ mpuSequenceNumber?: number,
733
+ packetSequenceNumber?: number,
734
+ packetSequenceNumbers?: number[],
735
+ repairedPacketSequenceNumbers?: number[],
736
+ authoredTiming?: AuthoredTiming,
737
+ deliveryProvenance?: MmtpDeliveryProvenance,
738
+ ): void {
739
+ void sbn; void esi
740
+ this.#addFrameCount++
741
+ this.#startAggregateTimer()
742
+
743
+ if (!this.#ordersSamples()) this.#drainRecoveredUpTo(timestamp)
744
+
745
+ // Per-frame duration: default to catalog-derived `#frameDuration`. For
746
+ // variable-rate content Chrome refines duration from the next sample's
747
+ // DTS at append time, so a fallback here is safe.
748
+ this.#emitSample(
749
+ data,
750
+ timestamp,
751
+ this.#frameDuration,
752
+ keyframe,
753
+ true,
754
+ catalogTrackName,
755
+ mpuSequenceNumber,
756
+ packetSequenceNumber,
757
+ packetSequenceNumbers,
758
+ repairedPacketSequenceNumbers,
759
+ authoredTiming,
760
+ deliveryProvenance,
761
+ )
762
+
763
+ // Stats — #dumpEmit increments emit/sample/keyframe counters internally.
764
+ this.#dumpEmit('addFrame-direct', [{ data, timestamp, keyframe }])
765
+ }
766
+
767
+ /** Mark a block as complete (all K source symbols received or FEC recovery succeeded).
768
+ * If all frames are already buffered, flushes immediately. Otherwise sets pending-flush. */
769
+ completeBlock(sbn: number): void {
770
+ void sbn
771
+ this.#completeBlockCount++
772
+ this.#startAggregateTimer()
773
+ // Source samples are emitted directly from addFrame(). A clean FEC block
774
+ // completion is still useful for stats, but it must not enqueue stale
775
+ // pending flushes from the retired block-buffered path.
776
+ }
777
+
778
+ /** Handle block timeout — emit whatever frames we have + fill for missing slots. */
779
+ failBlock(sbn: number): void {
780
+ this.#failBlockCount++
781
+ this.#startAggregateTimer()
782
+ this.#pendingFlush.delete(sbn)
783
+ const block = this.#blocks.get(sbn)
784
+ if (!block || block.size === 0) {
785
+ this.#blocks.delete(sbn)
786
+ return
787
+ }
788
+ this.#flushBlockWithFill(sbn)
789
+ }
790
+
791
+ /** Add all frames from a FEC-recovered block at once and emit immediately. */
792
+ addRecoveredBlock(sbn: number, frames: BlockFrame[]): void {
793
+ void sbn
794
+ this.#recoveredBlockCount++
795
+ this.#startAggregateTimer()
796
+ // Build samples directly — recovered frames are already sorted
797
+ this.#pendingFlush.delete(sbn)
798
+ this.#blocks.delete(sbn)
799
+
800
+ if (frames.length === 0) return
801
+ const normalized = this.#normalizeVideoKeyframes(frames)
802
+ this.#dumpEmit('addRecoveredBlock', normalized, sbn)
803
+ if (this.#ordersSamples()) {
804
+ this.#emitPerSample(normalized, undefined, false)
805
+ return
806
+ }
807
+
808
+ // Arrival mode preserves the legacy protection against future recovered
809
+ // samples advancing past source media. Explicit media-window mode routes
810
+ // both paths through one queue instead.
811
+ if (this.#lastSourceTs >= 0) {
812
+ this.#enqueueRecovered(normalized)
813
+ this.#drainRecoveredUpTo(this.#lastSourceTs)
814
+ } else {
815
+ const gated = this.#applyVideoKeyframeGate(normalized)
816
+ if (gated) this.#emitPerSample(gated, undefined, false)
817
+ }
818
+ }
819
+
820
+ /** Emit a single frame immediately as a one-sample segment (for oversized packets bypassing FEC). */
821
+ addBypassFrame(
822
+ data: Uint8Array,
823
+ timestamp: number,
824
+ keyframe: boolean,
825
+ catalogTrackName?: string,
826
+ mpuSequenceNumber?: number,
827
+ packetSequenceNumber?: number,
828
+ packetSequenceNumbers?: number[],
829
+ repairedPacketSequenceNumbers?: number[],
830
+ authoredTiming?: AuthoredTiming,
831
+ deliveryProvenance?: MmtpDeliveryProvenance,
832
+ ): void {
833
+ if (!this.#ordersSamples()) this.#drainRecoveredUpTo(timestamp)
834
+ this.#emitSample(
835
+ data,
836
+ timestamp,
837
+ this.#frameDuration,
838
+ keyframe,
839
+ true,
840
+ catalogTrackName,
841
+ mpuSequenceNumber,
842
+ packetSequenceNumber,
843
+ packetSequenceNumbers,
844
+ repairedPacketSequenceNumbers,
845
+ authoredTiming,
846
+ deliveryProvenance,
847
+ )
848
+ }
849
+
850
+ /** Build and emit init segment from catalog config. */
851
+ emitInit(): void {
852
+ let init: Uint8Array | null = null
853
+ if (this._videoConfig?.avcC) {
854
+ init = buildVideoInitSegment(
855
+ this._videoConfig.avcC,
856
+ this._videoConfig.width,
857
+ this._videoConfig.height,
858
+ this.#config.timescale,
859
+ this.#config.trackId,
860
+ )
861
+ } else if (this._audioConfig) {
862
+ init = buildAudioInitSegment(
863
+ this._audioConfig.sampleRate,
864
+ this._audioConfig.channels,
865
+ this._audioConfig.asc,
866
+ this.#config.timescale,
867
+ this.#config.trackId,
868
+ )
869
+ }
870
+ if (init) this.onInitSegment?.(init)
871
+ }
872
+
873
+ /** Drain all ordered samples at end-of-stream or before reconfiguration. */
874
+ flush(): void {
875
+ if (this.#ordersSamples()) {
876
+ this.#drainSampleReorder(true)
877
+ return
878
+ }
879
+ if (this.#pendingRecovered.size === 0) return
880
+ const frames = [...this.#pendingRecovered.values()].sort((a, b) => a.timestamp - b.timestamp)
881
+ this.#pendingRecovered.clear()
882
+ const gated = this.#applyVideoKeyframeGate(frames)
883
+ if (gated) this.#emitPerSample(gated, undefined, false)
884
+ else if (this.#config.trackType === 'video') {
885
+ this.#sampleOrderStats.noRapFlushDrops += frames.length
886
+ }
887
+ }
888
+
889
+ /** Drop all queued state and begin a fresh timestamp epoch. */
890
+ reset(): void {
891
+ this.#blocks.clear()
892
+ this.#pendingFlush.clear()
893
+ this.#pendingRecovered.clear()
894
+ this.#sequence = 1
895
+ this.#baseTs = -1
896
+ this.#lastEmittedTs = -1
897
+ this.#lastSourceTs = -1
898
+ this.#recoveredHorizonTs = -1
899
+ this.#lastDecodableAudioFrame = null
900
+ this.#armedVideo = false
901
+ this.#lastEmittedSbn = -1
902
+ this.#sampleReorderQueue = []
903
+ }
904
+
905
+ dispose(): void {
906
+ this.reset()
907
+ this.onSegment = null
908
+ this.onSample = null
909
+ this.onInitSegment = null
910
+ if (this.#aggregateTimer) {
911
+ clearInterval(this.#aggregateTimer)
912
+ this.#aggregateTimer = null
913
+ }
914
+ }
915
+
916
+ // ── Private ──
917
+
918
+ /** Rebase an absolute timestamp to relative media time (starting near 0). */
919
+ #rebase(timestamp: number): number {
920
+ if (this.#baseTs < 0) this.#baseTs = timestamp
921
+ return timestamp - this.#baseTs
922
+ }
923
+
924
+ #normalizeSampleData(data: Uint8Array): Uint8Array {
925
+ return this.#config.trackType === 'audio' ? normalizeAacSampleForCmaf(data) : data
926
+ }
927
+
928
+ #enqueueRecovered(frames: BlockFrame[]): void {
929
+ for (const frame of frames) {
930
+ if (this.#lastEmittedTs >= 0 && frame.timestamp <= this.#lastEmittedTs) {
931
+ this.#sampleOrderStats.staleSamples++
932
+ continue
933
+ }
934
+ if (this.#pendingRecovered.has(frame.timestamp)) {
935
+ this.#sampleOrderStats.duplicateSamples++
936
+ continue
937
+ }
938
+ this.#pendingRecovered.set(frame.timestamp, frame)
939
+ }
940
+ }
941
+
942
+ #drainRecoveredUpTo(horizonTs: number): void {
943
+ if (this.#pendingRecovered.size === 0) return
944
+ const ready = [...this.#pendingRecovered.values()]
945
+ .filter(frame => frame.timestamp <= horizonTs)
946
+ .sort((a, b) => a.timestamp - b.timestamp)
947
+
948
+ for (const frame of ready) {
949
+ this.#pendingRecovered.delete(frame.timestamp)
950
+ this.#emitSample(
951
+ frame.data,
952
+ frame.timestamp,
953
+ this.#frameDuration,
954
+ frame.keyframe,
955
+ false,
956
+ frame.catalogTrackName,
957
+ frame.mpuSequenceNumber,
958
+ frame.packetSequenceNumber,
959
+ frame.packetSequenceNumbers,
960
+ frame.repairedPacketSequenceNumbers,
961
+ frame.authoredTiming,
962
+ frame.deliveryProvenance,
963
+ )
964
+ this.#dumpEmit('recovered-drain', [frame])
965
+ }
966
+ }
967
+
968
+ /** Flush a block's buffered frames as a multi-sample CMAF segment. */
969
+ #flushBlock(sbn: number): void {
970
+ const block = this.#blocks.get(sbn)
971
+ if (!block || block.size === 0) {
972
+ this.#blocks.delete(sbn)
973
+ return
974
+ }
975
+
976
+ // Sort by ESI to restore original frame order
977
+ const entries = [...block.entries()].sort((a, b) => a[0] - b[0])
978
+ const sortedFrames = entries.map(([, f]) => f)
979
+ this.#blocks.delete(sbn)
980
+
981
+ if (!this.#claimSbnEmission(sbn)) return
982
+ const frames = this.#applyVideoKeyframeGate(sortedFrames)
983
+ if (!frames) return
984
+
985
+ this.#dumpEmit('flushBlock', frames, sbn)
986
+ this.#emitPerSample(frames)
987
+ }
988
+
989
+ /** Flush a block with fill frames for missing ESI slots (FEC timeout path). */
990
+ #flushBlockWithFill(sbn: number): void {
991
+ const block = this.#blocks.get(sbn)
992
+ if (!block) return
993
+
994
+ const k = this.#config.k
995
+ const isVideo = this.#config.trackType === 'video'
996
+ const firstAudioFrame = !isVideo
997
+ ? [...block.values()].sort((a, b) => a.timestamp - b.timestamp)[0]?.data
998
+ : undefined
999
+ const audioConcealmentFrame = this.#lastDecodableAudioFrame ?? firstAudioFrame
1000
+
1001
+ // Build full K-slot array: real frames + fill for missing
1002
+ const allFrames: BlockFrame[] = []
1003
+ let lastRealTs = -1
1004
+ let firstRealTs = -1
1005
+
1006
+ // Find min/max timestamps from received frames to interpolate missing ones
1007
+ for (const [, f] of block) {
1008
+ if (firstRealTs < 0 || f.timestamp < firstRealTs) firstRealTs = f.timestamp
1009
+ if (f.timestamp > lastRealTs) lastRealTs = f.timestamp
1010
+ }
1011
+ const estimatedBlockDuration = block.size > 1
1012
+ ? (lastRealTs - firstRealTs) / (block.size - 1) * k
1013
+ : this.#frameDuration * k
1014
+
1015
+ for (let esi = 0; esi < k; esi++) {
1016
+ const real = block.get(esi)
1017
+ if (real) {
1018
+ allFrames.push(real)
1019
+ } else {
1020
+ // Interpolate timestamp for missing slot
1021
+ const interpolatedTs = firstRealTs >= 0
1022
+ ? firstRealTs + Math.round(esi * estimatedBlockDuration / k)
1023
+ : (this.#lastEmittedTs >= 0
1024
+ ? this.#lastEmittedTs + this.#frameDuration * (esi + 1)
1025
+ : 0)
1026
+ allFrames.push({
1027
+ // A synthesized AAC access unit is not portable across every
1028
+ // AudioSpecificConfig. Repeat known-good encoded audio instead;
1029
+ // if this is the first block, use its earliest received sample.
1030
+ data: isVideo
1031
+ ? this.#fillFrame
1032
+ : (audioConcealmentFrame?.slice() ?? new Uint8Array(0)),
1033
+ timestamp: interpolatedTs,
1034
+ // Video: first fill is keyframe (IDR). Audio: all fill marked keyframe.
1035
+ keyframe: isVideo ? !block.has(0) && esi === 0 : true,
1036
+ })
1037
+ }
1038
+ }
1039
+
1040
+ this.#blocks.delete(sbn)
1041
+ if (!this.#claimSbnEmission(sbn)) return
1042
+ const gatedFrames = this.#applyVideoKeyframeGate(allFrames)
1043
+ if (!gatedFrames) return
1044
+
1045
+ this.#emitPerSample(gatedFrames)
1046
+ }
1047
+ }
1048
+
1049
+ function isH264Codec(codec?: string): boolean {
1050
+ const c = codec?.toLowerCase()
1051
+ return c?.startsWith('avc1') === true || c?.startsWith('avc3') === true
1052
+ }
1053
+
1054
+ function describeH264NalTypes(nals: Uint8Array[]): number[] {
1055
+ return nals.slice(0, 16).map((nal) => (
1056
+ nal[0] !== undefined ? nal[0] & 0x1f : -1
1057
+ ))
1058
+ }