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