@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,1539 @@
1
+ /**
2
+ * H.264/AVC codec utilities for MMTP container format conversion.
3
+ *
4
+ * Shared between moq-lib (hang-mmt-fec) and moqtail players.
5
+ * Handles AVCC↔Annex-B conversion, SPS/PPS extraction, fallback avcC generation,
6
+ * and MMTP timestamp conversion.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ // ─── Public Types ────────────────────────────────────────────────
12
+
13
+ /** Result of extracting a video codec configuration record from an fMP4 init segment. */
14
+ export interface AvcCInfo {
15
+ codec: string
16
+ description: Uint8Array
17
+ }
18
+
19
+ /** Result of extracting AAC AudioSpecificConfig from an fMP4 init segment. */
20
+ export interface AacAscInfo {
21
+ /** Codec string like "mp4a.40.2" (AAC-LC). Derived from AOT in ASC[0] >> 3. */
22
+ codec: string
23
+ /** AudioSpecificConfig bytes — what AudioDecoder.configure({description}) needs for raw AAC. */
24
+ description: Uint8Array
25
+ }
26
+
27
+ /** SPS and PPS NAL units extracted from an avcC record. */
28
+ export interface SpsPps {
29
+ sps: Uint8Array
30
+ pps: Uint8Array
31
+ }
32
+
33
+ /** Options for fallback avcC generation. */
34
+ export interface FallbackAvcCOptions {
35
+ /** Codec string like "avc1.640033". Profile/constraint/level parsed from hex triplet. */
36
+ codec?: string
37
+ /** Coded width in pixels. Used in SPS pic_width_in_mbs. */
38
+ width?: number
39
+ /** Coded height in pixels. Used in SPS pic_height_in_map_units. */
40
+ height?: number
41
+ }
42
+
43
+ // ─── Public Functions ────────────────────────────────────────────
44
+
45
+ /**
46
+ * Extract the codec configuration record (avcC/hvcC/av1C/vpcC) from
47
+ * an fMP4 init segment (moov box).
48
+ * MMTP FT=0 packets contain a complete fMP4: moov > trak > mdia > minf > stbl > stsd > sample entry > codec config.
49
+ *
50
+ * Walks every trak and returns the first supported video sample entry. This
51
+ * handles both per-track CMSF init segments and multiplexed ftyp+moov blobs
52
+ * where the audio trak may appear before video.
53
+ */
54
+ export function extractAvcCFromInitSegment(data: Uint8Array): AvcCInfo | undefined {
55
+ if (!hasIsoBmffMagic(data)) return undefined
56
+
57
+ for (const stsd of findTrackStsdBoxes(data)) {
58
+ const info = extractVideoConfigFromStsd(data, stsd)
59
+ if (info) return info
60
+ }
61
+
62
+ return undefined
63
+ }
64
+
65
+ function extractVideoConfigFromStsd(data: Uint8Array, stsd: { o: number; sz: number }): AvcCInfo | undefined {
66
+ let s = stsd.o + 16
67
+ const e = stsd.o + stsd.sz
68
+
69
+ // H.264: avc1 VisualSampleEntry → avcC.
70
+ const avc1 = findBox(data, s, e, 'avc1')
71
+ if (avc1 && avc1.sz >= 86) {
72
+ const avcC = findBox(data, avc1.o + 86, avc1.o + avc1.sz, 'avcC')
73
+ if (avcC && avcC.sz >= 12) {
74
+ const d = data.subarray(avcC.o + 8, avcC.o + avcC.sz)
75
+ const codec = `avc1.${hex2(d[1])}${hex2(d[2])}${hex2(d[3])}`
76
+ return { codec, description: d }
77
+ }
78
+ }
79
+ // HEVC: hvc1/hev1 VisualSampleEntry → hvcC. Same +86 child-box offset as
80
+ // avc1 (identical VisualSampleEntry layout). Without this branch, HEVC
81
+ // ftyp+moov inits yield no descriptor, so the WebCodecs path configures the
82
+ // VideoDecoder with no `description` and the first length-prefixed hvc1
83
+ // keyframe decode() throws DataError ("must fill out the description field"),
84
+ // which the player's MoQ pump catch swallows → video track silently 0-fps
85
+ // (BLO-8054).
86
+ const hvc1 = findBox(data, s, e, 'hvc1') ?? findBox(data, s, e, 'hev1')
87
+ if (hvc1 && hvc1.sz >= 86) {
88
+ const hvcC = findBox(data, hvc1.o + 86, hvc1.o + hvc1.sz, 'hvcC')
89
+ // HEVCDecoderConfigurationRecord is ≥23 bytes (+8 box header).
90
+ if (hvcC && hvcC.sz >= 8 + 23) {
91
+ const d = data.subarray(hvcC.o + 8, hvcC.o + hvcC.sz)
92
+ return { codec: hevcCodecStringFromHvcC(d), description: d }
93
+ }
94
+ }
95
+ // AV1: av01 VisualSampleEntry -> av1C. Real FFmpeg MMTP inits can carry an
96
+ // empty av1C box (size=8) while still providing the authoritative sample
97
+ // entry/codec signal. Return an empty description for that shape so callers
98
+ // can open their FT=0 gate without passing a zero-byte WebCodecs description.
99
+ const av01 = findBox(data, s, e, 'av01')
100
+ if (av01 && av01.sz >= 86) {
101
+ const av1C = findBox(data, av01.o + 86, av01.o + av01.sz, 'av1C')
102
+ if (av1C && av1C.sz >= 8) {
103
+ const d = data.subarray(av1C.o + 8, av1C.o + av1C.sz)
104
+ return { codec: codecStringFromAv1C(d), description: d }
105
+ }
106
+ }
107
+ // VP9: vp09 VisualSampleEntry -> vpcC. Some encoders omit the VP codec record
108
+ // payload; use the sample entry as readiness evidence and fall back to a
109
+ // conservative vp09 codec string in that case.
110
+ const vp09 = findBox(data, s, e, 'vp09')
111
+ if (vp09 && vp09.sz >= 86) {
112
+ const vpcC = findBox(data, vp09.o + 86, vp09.o + vp09.sz, 'vpcC')
113
+ if (vpcC && vpcC.sz >= 8) {
114
+ const d = data.subarray(vpcC.o + 8, vpcC.o + vpcC.sz)
115
+ return { codec: codecStringFromVpcC(d), description: d }
116
+ }
117
+ }
118
+ return undefined
119
+ }
120
+
121
+ /**
122
+ * Generate a minimal avcC (AVCDecoderConfigurationRecord) from codec parameters.
123
+ * Parses "avc1.XXYYZZ" → profile_idc=0xXX, constraint_flags=0xYY, level_idc=0xZZ.
124
+ * Width/height are encoded in the SPS; if not provided, they're inferred from the
125
+ * H.264 level's max frame size (e.g. level 4.2 → 2048x1088).
126
+ * Used when late MoQ subscribers miss the FT=0 init segment (moov/avcC).
127
+ */
128
+ export function generateFallbackAvcC(options?: FallbackAvcCOptions | string): Uint8Array {
129
+ const opts: FallbackAvcCOptions = typeof options === 'string' ? { codec: options } : (options ?? {})
130
+
131
+ let profile = 0x64
132
+ let constraint = 0x00
133
+ let level = 0x2a
134
+
135
+ if (opts.codec) {
136
+ const match = opts.codec.match(/^avc[13]\.([0-9a-fA-F]{6})$/)
137
+ if (match) {
138
+ profile = Number.parseInt(match[1].substring(0, 2), 16)
139
+ constraint = Number.parseInt(match[1].substring(2, 4), 16)
140
+ level = Number.parseInt(match[1].substring(4, 6), 16)
141
+ }
142
+ }
143
+
144
+ const { width, height } = resolveResolution(opts.width, opts.height, level)
145
+ const sps = buildMinimalSps(profile, constraint, level, width, height)
146
+ const pps = buildMinimalPps(profile)
147
+
148
+ const avcC = new Uint8Array(5 + 1 + 2 + sps.length + 1 + 2 + pps.length)
149
+ let w = 0
150
+ avcC[w++] = 0x01
151
+ avcC[w++] = profile
152
+ avcC[w++] = constraint
153
+ avcC[w++] = level
154
+ avcC[w++] = 0xff
155
+ avcC[w++] = 0xe1
156
+ avcC[w++] = (sps.length >> 8) & 0xff
157
+ avcC[w++] = sps.length & 0xff
158
+ avcC.set(sps, w)
159
+ w += sps.length
160
+ avcC[w++] = 0x01
161
+ avcC[w++] = (pps.length >> 8) & 0xff
162
+ avcC[w++] = pps.length & 0xff
163
+ avcC.set(pps, w)
164
+ return avcC
165
+ }
166
+
167
+ /**
168
+ * Extract raw SPS and PPS NAL unit bytes from an avcC (AVCDecoderConfigurationRecord).
169
+ * Returns the first SPS and first PPS found.
170
+ */
171
+ export function extractSpsPpsFromAvcC(avcC: Uint8Array): SpsPps | undefined {
172
+ if (avcC.length < 8) return undefined
173
+ const numSPS = avcC[5] & 0x1f
174
+ let offset = 6
175
+ let sps: Uint8Array | undefined
176
+ for (let i = 0; i < numSPS && offset + 2 <= avcC.length; i++) {
177
+ const spsLen = (avcC[offset] << 8) | avcC[offset + 1]
178
+ offset += 2
179
+ if (offset + spsLen <= avcC.length) {
180
+ if (!sps) sps = avcC.subarray(offset, offset + spsLen)
181
+ offset += spsLen
182
+ }
183
+ }
184
+ let pps: Uint8Array | undefined
185
+ if (offset < avcC.length) {
186
+ const numPPS = avcC[offset] & 0x1f
187
+ offset++
188
+ for (let i = 0; i < numPPS && offset + 2 <= avcC.length; i++) {
189
+ const ppsLen = (avcC[offset] << 8) | avcC[offset + 1]
190
+ offset += 2
191
+ if (offset + ppsLen <= avcC.length) {
192
+ if (!pps) pps = avcC.subarray(offset, offset + ppsLen)
193
+ offset += ppsLen
194
+ }
195
+ }
196
+ }
197
+ return sps && pps ? { sps, pps } : undefined
198
+ }
199
+
200
+ /**
201
+ * Convert AVCC (4-byte NAL length prefixed) to Annex-B (start code prefixed).
202
+ * Prepends SPS/PPS NALs before keyframes.
203
+ */
204
+ export function avccToAnnexB(avccData: Uint8Array, isKeyframe: boolean, spsPps: SpsPps): Uint8Array {
205
+ let offset = 0
206
+ let totalNalSize = 0
207
+ let nalCount = 0
208
+ while (offset + 4 <= avccData.length) {
209
+ const nalLen =
210
+ (avccData[offset] << 24) |
211
+ (avccData[offset + 1] << 16) |
212
+ (avccData[offset + 2] << 8) |
213
+ avccData[offset + 3]
214
+ offset += 4
215
+ if (nalLen <= 0 || offset + nalLen > avccData.length) break
216
+ totalNalSize += nalLen
217
+ nalCount++
218
+ offset += nalLen
219
+ }
220
+ if (nalCount === 0) return avccData
221
+
222
+ let outSize = nalCount * 4 + totalNalSize
223
+ if (isKeyframe) {
224
+ outSize += 4 + spsPps.sps.length + 4 + spsPps.pps.length
225
+ }
226
+ const out = new Uint8Array(outSize)
227
+ let w = 0
228
+
229
+ if (isKeyframe) {
230
+ out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1
231
+ out.set(spsPps.sps, w); w += spsPps.sps.length
232
+ out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1
233
+ out.set(spsPps.pps, w); w += spsPps.pps.length
234
+ }
235
+
236
+ offset = 0
237
+ while (offset + 4 <= avccData.length) {
238
+ const nalLen =
239
+ (avccData[offset] << 24) |
240
+ (avccData[offset + 1] << 16) |
241
+ (avccData[offset + 2] << 8) |
242
+ avccData[offset + 3]
243
+ offset += 4
244
+ if (nalLen <= 0 || offset + nalLen > avccData.length) break
245
+ out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1
246
+ out.set(avccData.subarray(offset, offset + nalLen), w)
247
+ w += nalLen
248
+ offset += nalLen
249
+ }
250
+ return out
251
+ }
252
+
253
+ /**
254
+ * Build an avcC record by parsing Annex-B data to extract SPS (NAL type 7) and PPS (NAL type 8).
255
+ * Returns the avcC record, or null if SPS/PPS not found.
256
+ */
257
+ export function extractCodecDescriptionFromAnnexB(data: Uint8Array): Uint8Array | null {
258
+ const nalUnits: Array<{ type: number; data: Uint8Array }> = []
259
+ let i = 0
260
+ while (i < data.length - 4) {
261
+ if (data[i] === 0 && data[i + 1] === 0) {
262
+ let nalStart = 0
263
+ if (data[i + 2] === 1) {
264
+ nalStart = i + 3
265
+ } else if (data[i + 2] === 0 && data[i + 3] === 1) {
266
+ nalStart = i + 4
267
+ }
268
+ if (nalStart > 0 && nalStart < data.length) {
269
+ let nalEnd = data.length
270
+ for (let j = nalStart; j < data.length - 3; j++) {
271
+ if (data[j] === 0 && data[j + 1] === 0 &&
272
+ (data[j + 2] === 1 || (data[j + 2] === 0 && data[j + 3] === 1))) {
273
+ nalEnd = j
274
+ break
275
+ }
276
+ }
277
+ const nalType = data[nalStart] & 0x1f
278
+ nalUnits.push({ type: nalType, data: data.slice(nalStart, nalEnd) })
279
+ i = nalEnd
280
+ continue
281
+ }
282
+ }
283
+ i++
284
+ }
285
+
286
+ const sps = nalUnits.find(n => n.type === 7)
287
+ const pps = nalUnits.find(n => n.type === 8)
288
+ if (!sps || !pps) return null
289
+
290
+ const avcCLength = 6 + 2 + sps.data.length + 1 + 2 + pps.data.length
291
+ const avcC = new Uint8Array(avcCLength)
292
+ let offset = 0
293
+ avcC[offset++] = 1
294
+ avcC[offset++] = sps.data[1]
295
+ avcC[offset++] = sps.data[2]
296
+ avcC[offset++] = sps.data[3]
297
+ avcC[offset++] = 0xff
298
+ avcC[offset++] = 0xe1
299
+ avcC[offset++] = (sps.data.length >> 8) & 0xff
300
+ avcC[offset++] = sps.data.length & 0xff
301
+ avcC.set(sps.data, offset); offset += sps.data.length
302
+ avcC[offset++] = 1
303
+ avcC[offset++] = (pps.data.length >> 8) & 0xff
304
+ avcC[offset++] = pps.data.length & 0xff
305
+ avcC.set(pps.data, offset)
306
+ return avcC
307
+ }
308
+
309
+ /**
310
+ * Derive codec string from avcC record (e.g. "avc1.640033").
311
+ */
312
+ export function codecStringFromAvcC(avcC: Uint8Array): string {
313
+ if (avcC.length < 4) return 'avc1.640033'
314
+ return `avc1.${hex2(avcC[1])}${hex2(avcC[2])}${hex2(avcC[3])}`
315
+ }
316
+
317
+ /** VPS/SPS/PPS NAL units extracted from an HEVC keyframe (raw NAL bytes, no start code/length prefix). */
318
+ export interface HevcParameterSets {
319
+ vps?: Uint8Array
320
+ sps?: Uint8Array
321
+ pps?: Uint8Array
322
+ }
323
+
324
+ /** True for HEVC/H.265 codec strings (hev1.* / hvc1.*). */
325
+ export function isHevcCodec(codec: string): boolean {
326
+ return codec.startsWith('hev1') || codec.startsWith('hvc1')
327
+ }
328
+
329
+ /** HEVC NAL unit type from the first NAL header byte: (byte >> 1) & 0x3f. */
330
+ function hevcNalType(nal: Uint8Array): number {
331
+ return (nal[0] >> 1) & 0x3f
332
+ }
333
+
334
+ /**
335
+ * Remove H.26x emulation-prevention bytes (0x03 after 0x00 0x00) from an RBSP so
336
+ * fixed-position fields (e.g. HEVC profile_tier_level) can be read by byte offset.
337
+ */
338
+ export function removeEmulationPrevention(rbsp: Uint8Array): Uint8Array {
339
+ const out: number[] = []
340
+ let zeros = 0
341
+ for (let i = 0; i < rbsp.length; i++) {
342
+ const b = rbsp[i]
343
+ if (zeros >= 2 && b === 0x03 && i + 1 < rbsp.length && rbsp[i + 1] <= 0x03) {
344
+ zeros = 0
345
+ continue
346
+ }
347
+ out.push(b)
348
+ zeros = b === 0 ? zeros + 1 : 0
349
+ }
350
+ return Uint8Array.from(out)
351
+ }
352
+
353
+ /**
354
+ * Split an AVCC/hvcC bitstream (length-prefixed NAL units) into raw NAL byte slices.
355
+ * `lengthSize` is the NAL length prefix width (4 for FFmpeg LOC / fMP4 sample data).
356
+ */
357
+ export function splitAvccNalUnits(data: Uint8Array, lengthSize = 4): Uint8Array[] {
358
+ const nals: Uint8Array[] = []
359
+ let i = 0
360
+ while (i + lengthSize <= data.length) {
361
+ let len = 0
362
+ for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]
363
+ i += lengthSize
364
+ if (len <= 0 || i + len > data.length) break
365
+ nals.push(data.subarray(i, i + len))
366
+ i += len
367
+ }
368
+ return nals
369
+ }
370
+
371
+ /**
372
+ * True when `data` is exactly one or more length-prefixed NAL units.
373
+ *
374
+ * This is intentionally stricter than "starts with a plausible length": a
375
+ * H.264 AVCC access unit may begin with bytes `00 00 00 01` when the first
376
+ * NAL is a one-byte AUD. That byte pattern is also an Annex-B start code, so
377
+ * callers must prove the whole buffer is complete AVCC before falling back to
378
+ * Annex-B detection/conversion.
379
+ */
380
+ export function isCompleteAvccSample(data: Uint8Array, lengthSize = 4): boolean {
381
+ let i = 0
382
+ let count = 0
383
+ while (i + lengthSize <= data.length) {
384
+ let len = 0
385
+ for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]
386
+ i += lengthSize
387
+ if (len <= 0 || i + len > data.length) return false
388
+ i += len
389
+ count++
390
+ }
391
+ return count > 0 && i === data.length
392
+ }
393
+
394
+ export type H264SampleFormat = 'avcc' | 'annexb' | 'raw-nal' | 'unknown'
395
+ export type H264NalUnitFormat = H264SampleFormat | 'partial-avcc'
396
+
397
+ export interface H264AvccSample {
398
+ data: Uint8Array
399
+ format: H264SampleFormat
400
+ }
401
+
402
+ export interface H264NalUnitSample {
403
+ nals: Uint8Array[]
404
+ format: H264NalUnitFormat
405
+ }
406
+
407
+ /**
408
+ * Normalize a H.264 access unit/sample to length-prefixed AVCC for fMP4 mdat
409
+ * payloads. Already-AVCC samples are returned as-is. Annex-B and single raw
410
+ * NAL samples are converted one-way to AVCC at the MSE/fMP4 boundary.
411
+ */
412
+ export function normalizeH264SampleToAvcc(data: Uint8Array): H264AvccSample {
413
+ if (isCompleteAvccSample(data)) return { data, format: 'avcc' }
414
+ if (isAnnexB(data)) return { data: annexBToAvcc(data), format: 'annexb' }
415
+ if (isLikelyRawH264NalUnit(data)) return { data: rawH264NalToAvcc(data), format: 'raw-nal' }
416
+ return { data, format: 'unknown' }
417
+ }
418
+
419
+ /**
420
+ * Split a H.264 access unit/sample into NAL units without changing its framing.
421
+ * Use this for routing, keyframe gates, and diagnostics. Conversion to AVCC is
422
+ * reserved for `normalizeH264SampleToAvcc` at the fMP4/MSE boundary.
423
+ */
424
+ export function splitH264SampleNalUnits(
425
+ data: Uint8Array,
426
+ options: H264KeyframeDetectionOptions = {},
427
+ ): H264NalUnitSample {
428
+ if (isCompleteAvccSample(data)) return { nals: splitAvccNalUnits(data), format: 'avcc' }
429
+ if (isAnnexB(data)) return { nals: splitAnnexBNalUnits(data), format: 'annexb' }
430
+ if (options.allowPartialAvcc === true) {
431
+ const partial = splitPartialAvccNalUnits(data)
432
+ if (partial.length > 0) return { nals: partial, format: 'partial-avcc' }
433
+ }
434
+ if (isLikelyRawH264NalUnit(data)) return { nals: [data], format: 'raw-nal' }
435
+ const nals = splitAvccNalUnits(data)
436
+ return { nals, format: nals.length > 0 ? 'avcc' : 'unknown' }
437
+ }
438
+
439
+ /**
440
+ * Extract HEVC VPS (type 32) / SPS (33) / PPS (34) NAL units from a keyframe access
441
+ * unit. Accepts either Annex-B (start-code delimited) or AVCC/hvcC (length-prefixed)
442
+ * framing — FFmpeg LOC delivers the latter.
443
+ */
444
+ export function extractHevcParameterSets(keyframe: Uint8Array): HevcParameterSets {
445
+ const nals = isAnnexB(keyframe) ? splitAnnexBNalUnits(keyframe) : splitAvccNalUnits(keyframe)
446
+ const out: HevcParameterSets = {}
447
+ for (const nal of nals) {
448
+ if (nal.length === 0) continue
449
+ const t = hevcNalType(nal)
450
+ if (t === 32 && !out.vps) out.vps = nal
451
+ else if (t === 33 && !out.sps) out.sps = nal
452
+ else if (t === 34 && !out.pps) out.pps = nal
453
+ }
454
+ return out
455
+ }
456
+
457
+ /**
458
+ * Build a minimal hvcC (HEVCDecoderConfigurationRecord, ISO 14496-15 §8.3.3.1) from
459
+ * VPS/SPS/PPS NAL units. The general profile_tier_level (12 bytes) is copied from the
460
+ * SPS (de-emulated); remaining fields use the common 4:2:0 / 8-bit / 4-byte-NAL-length
461
+ * defaults. Suitable as the `description` for a WebCodecs hvc1 VideoDecoderConfig.
462
+ *
463
+ * WebCodecs REQUIRES a description for length-prefixed HEVC — unlike H.264, Chrome will
464
+ * not self-configure HEVC from the bitstream (BLO-7764).
465
+ */
466
+ export function generateHvcCFromParameterSets(sets: HevcParameterSets): Uint8Array | null {
467
+ const { vps, sps, pps } = sets
468
+ if (!sps || !pps) return null
469
+ const spsRbsp = removeEmulationPrevention(sps)
470
+ // general profile_tier_level is 12 bytes at RBSP offset 3:
471
+ // 2-byte NAL header + 1 byte (sps_video_parameter_set_id u(4), sps_max_sub_layers_minus1
472
+ // u(3), sps_temporal_id_nesting_flag u(1)), then general_profile_space..general_level_idc.
473
+ const ptl = spsRbsp.subarray(3, 15)
474
+ const arrays = [
475
+ { type: 32, nal: vps },
476
+ { type: 33, nal: sps },
477
+ { type: 34, nal: pps },
478
+ ].filter((a): a is { type: number; nal: Uint8Array } => a.nal !== undefined)
479
+
480
+ let size = 23
481
+ for (const a of arrays) size += 5 + a.nal.length
482
+ const h = new Uint8Array(size)
483
+ let o = 0
484
+ h[o++] = 1 // configurationVersion
485
+ for (let i = 0; i < 12; i++) h[o++] = ptl[i] ?? 0 // general profile_tier_level
486
+ h[o++] = 0xf0; h[o++] = 0x00 // reserved(1111) + min_spatial_segmentation_idc(0)
487
+ h[o++] = 0xfc // reserved(111111) + parallelismType(0)
488
+ h[o++] = 0xfd // reserved(111111) + chromaFormat(1 = 4:2:0)
489
+ h[o++] = 0xf8 // reserved(11111) + bitDepthLumaMinus8(0)
490
+ h[o++] = 0xf8 // reserved(11111) + bitDepthChromaMinus8(0)
491
+ h[o++] = 0x00; h[o++] = 0x00 // avgFrameRate(0 = unspecified)
492
+ h[o++] = 0x0b // CFR(0) numTempLayers(1) nested(0) lengthSizeMinusOne(3)
493
+ h[o++] = arrays.length // numOfArrays
494
+ for (const a of arrays) {
495
+ h[o++] = a.type & 0x3f // array_completeness(0) + NAL_unit_type
496
+ h[o++] = 0; h[o++] = 1 // numNalus = 1
497
+ h[o++] = (a.nal.length >> 8) & 0xff
498
+ h[o++] = a.nal.length & 0xff
499
+ h.set(a.nal, o); o += a.nal.length
500
+ }
501
+ return h.subarray(0, o)
502
+ }
503
+
504
+ /**
505
+ * Build an hvcC `description` for a WebCodecs HEVC VideoDecoderConfig directly from a
506
+ * keyframe access unit (Annex-B or length-prefixed). Returns null if VPS/SPS/PPS aren't
507
+ * present in-band. HEVC analogue of {@link extractCodecDescriptionFromAnnexB}.
508
+ */
509
+ export function extractHevcCodecDescription(keyframe: Uint8Array): Uint8Array | null {
510
+ return generateHvcCFromParameterSets(extractHevcParameterSets(keyframe))
511
+ }
512
+
513
+ /**
514
+ * Strip non-VCL NAL units (VPS/SPS/PPS/SEI/AUD, type >= 32) from an HEVC access unit,
515
+ * returning only the VCL NAL units (type <= 31) as a 4-byte length-prefixed buffer.
516
+ *
517
+ * Required when decoding `hvc1` with an explicit hvcC `description`: WebCodecs rejects a
518
+ * sample whose data still carries in-band parameter sets ("Unable to determine size of
519
+ * bitstream buffer"). Accepts Annex-B or length-prefixed input. Returns the input
520
+ * unchanged if it already contains only VCL NALs (cheap no-op for delta frames).
521
+ */
522
+ export function hevcVclOnly(data: Uint8Array): Uint8Array {
523
+ const nals = isAnnexB(data) ? splitAnnexBNalUnits(data) : splitAvccNalUnits(data)
524
+ const vcl = nals.filter((n) => n.length > 0 && hevcNalType(n) <= 31)
525
+ if (vcl.length === nals.length && !isAnnexB(data)) return data
526
+ let size = 0
527
+ for (const n of vcl) size += 4 + n.length
528
+ const out = new Uint8Array(size)
529
+ let o = 0
530
+ for (const n of vcl) {
531
+ out[o++] = (n.length >> 24) & 0xff
532
+ out[o++] = (n.length >> 16) & 0xff
533
+ out[o++] = (n.length >> 8) & 0xff
534
+ out[o++] = n.length & 0xff
535
+ out.set(n, o); o += n.length
536
+ }
537
+ return out
538
+ }
539
+
540
+ /**
541
+ * Check if data starts with an Annex-B start code (00 00 01 or 00 00 00 01).
542
+ */
543
+ export function isAnnexB(data: Uint8Array): boolean {
544
+ if (data.length >= 4 && data[0] === 0 && data[1] === 0 &&
545
+ (data[2] === 1 || (data[2] === 0 && data[3] === 1))) {
546
+ return true
547
+ }
548
+ return false
549
+ }
550
+
551
+ /**
552
+ * Find the byte offset of the first Annex-B start code in data.
553
+ * Returns -1 if no start code found.
554
+ */
555
+ export function findAnnexBStartCode(data: Uint8Array): number {
556
+ for (let i = 0; i < data.length - 3; i++) {
557
+ if (data[i] === 0 && data[i + 1] === 0) {
558
+ if (data[i + 2] === 1) return i
559
+ if (data[i + 2] === 0 && data[i + 3] === 1) return i
560
+ }
561
+ }
562
+ return -1
563
+ }
564
+
565
+ /**
566
+ * Split Annex-B bitstream into NAL units.
567
+ * Returns array of NAL unit byte slices (without start codes).
568
+ */
569
+ export function splitAnnexBNalUnits(data: Uint8Array): Uint8Array[] {
570
+ const nalUnits: Uint8Array[] = []
571
+ let i = 0
572
+ while (i < data.length - 3) {
573
+ if (data[i] === 0 && data[i + 1] === 0) {
574
+ let scLen = 0
575
+ if (data[i + 2] === 1) scLen = 3
576
+ else if (data[i + 2] === 0 && i + 3 < data.length && data[i + 3] === 1) scLen = 4
577
+ if (scLen > 0) {
578
+ const nalStart = i + scLen
579
+ let nalEnd = data.length
580
+ for (let j = nalStart + 1; j < data.length - 2; j++) {
581
+ if (data[j] === 0 && data[j + 1] === 0 &&
582
+ (data[j + 2] === 1 || (data[j + 2] === 0 && j + 3 < data.length && data[j + 3] === 1))) {
583
+ nalEnd = j
584
+ break
585
+ }
586
+ }
587
+ if (nalEnd > nalStart) nalUnits.push(data.subarray(nalStart, nalEnd))
588
+ i = nalEnd
589
+ continue
590
+ }
591
+ }
592
+ i++
593
+ }
594
+ return nalUnits
595
+ }
596
+
597
+ /**
598
+ * Convert Annex-B (start-code delimited) to AVCC (4-byte length prefixed).
599
+ * Optionally prepends SPS/PPS NALs for keyframes.
600
+ */
601
+ export function annexBToAvcc(data: Uint8Array, spsPps?: SpsPps): Uint8Array {
602
+ const nalUnits = splitAnnexBNalUnits(data)
603
+ if (nalUnits.length === 0) return data
604
+
605
+ let extraSize = 0
606
+ if (spsPps) {
607
+ extraSize = 4 + spsPps.sps.length + 4 + spsPps.pps.length
608
+ }
609
+ const frameSize = nalUnits.reduce((sum, nal) => sum + 4 + nal.length, 0)
610
+ const output = new Uint8Array(extraSize + frameSize)
611
+ let w = 0
612
+
613
+ if (spsPps) {
614
+ w = writeNalLength(output, w, spsPps.sps.length)
615
+ output.set(spsPps.sps, w); w += spsPps.sps.length
616
+ w = writeNalLength(output, w, spsPps.pps.length)
617
+ output.set(spsPps.pps, w); w += spsPps.pps.length
618
+ }
619
+
620
+ for (const nal of nalUnits) {
621
+ w = writeNalLength(output, w, nal.length)
622
+ output.set(nal, w); w += nal.length
623
+ }
624
+ return output
625
+ }
626
+
627
+ export interface H264KeyframeDetectionOptions {
628
+ /**
629
+ * Allow classifying a length-prefixed NAL when the declared NAL length runs
630
+ * beyond this buffer. MMTP routing can see only the first packet fragment of
631
+ * a sample, where the NAL header is present but the full NAL is not.
632
+ */
633
+ allowPartialAvcc?: boolean
634
+ }
635
+
636
+ /**
637
+ * Classify H.264 keyframe status by scanning NAL units. Returns undefined when
638
+ * the payload is not H.264-classifiable. Handles Annex-B and AVCC formats.
639
+ */
640
+ export function classifyH264Keyframe(
641
+ data: Uint8Array,
642
+ options: H264KeyframeDetectionOptions = {},
643
+ ): boolean | undefined {
644
+ const nals = h264NalUnitsForKeyframeDetection(data, options)
645
+ if (nals.length === 0) return undefined
646
+ let sawIntraSlice = false
647
+ let sawNonIntraVcl = false
648
+ let sawNal = false
649
+ for (const nal of nals) {
650
+ sawNal = true
651
+ const nalType = nal[0] !== undefined ? nal[0] & 0x1f : -1
652
+ if (nalType === 5 || nalType === 7) return true
653
+ if (nalType === 1) {
654
+ if (isH264IntraSlice(nal)) sawIntraSlice = true
655
+ else sawNonIntraVcl = true
656
+ }
657
+ }
658
+ if (sawIntraSlice && !sawNonIntraVcl) return true
659
+ return sawNal ? false : undefined
660
+ }
661
+
662
+ /**
663
+ * Detect H.264 keyframe by scanning for IDR/SPS or intra non-IDR slices.
664
+ * Handles both Annex-B and AVCC formats.
665
+ */
666
+ export function detectH264Keyframe(
667
+ data: Uint8Array,
668
+ options: H264KeyframeDetectionOptions = {},
669
+ ): boolean {
670
+ return classifyH264Keyframe(data, options) === true
671
+ }
672
+
673
+ /**
674
+ * Convert MMTP timestamp (NTP short format) to monotonic microseconds.
675
+ * Per ISO 23008-1 §9.2.2 / RFC 5905: upper 16 bits = seconds (lower 16
676
+ * of NTP seconds), lower 16 bits = fractional seconds.
677
+ * Returns a monotonically increasing µs value within an ~18-hour window.
678
+ */
679
+ export function mmtpTimestampToMicros(rawTs: bigint | number): number {
680
+ const ts = Number(rawTs) >>> 0 // ensure unsigned 32-bit
681
+ if (ts === 0) return 0
682
+ const seconds = ts >>> 16
683
+ const frac = ts & 0xFFFF
684
+ return seconds * 1_000_000 + Math.floor(frac * 1_000_000 / 65536)
685
+ }
686
+
687
+ /** Validate the CMSF/MMTP catalog initData contract: complete top-level ftyp+moov. */
688
+ export function isFmp4InitSegment(data: Uint8Array): boolean {
689
+ let offset = 0
690
+ let sawFtyp = false
691
+ let sawMoov = false
692
+
693
+ while (offset + 8 <= data.byteLength) {
694
+ const view = new DataView(data.buffer, data.byteOffset + offset, data.byteLength - offset)
695
+ let size = view.getUint32(0, false)
696
+ const type = String.fromCharCode(data[offset + 4]!, data[offset + 5]!, data[offset + 6]!, data[offset + 7]!)
697
+ let headerSize = 8
698
+
699
+ if (size === 1) {
700
+ if (offset + 16 > data.byteLength) return false
701
+ const largeSize = view.getBigUint64(8, false)
702
+ if (largeSize > BigInt(Number.MAX_SAFE_INTEGER)) return false
703
+ size = Number(largeSize)
704
+ headerSize = 16
705
+ } else if (size === 0) {
706
+ size = data.byteLength - offset
707
+ }
708
+
709
+ if (size < headerSize || offset + size > data.byteLength) return false
710
+ if (type === 'ftyp') sawFtyp = true
711
+ if (type === 'moov') {
712
+ if (!sawFtyp) return false
713
+ sawMoov = true
714
+ }
715
+ offset += size
716
+ }
717
+
718
+ return offset === data.byteLength && sawFtyp && sawMoov
719
+ }
720
+
721
+ // ─── Internal Helpers ────────────────────────────────────────────
722
+
723
+ function hasIsoBmffMagic(data: Uint8Array): boolean {
724
+ for (let i = 0; i + 8 <= data.length && i < 32; i += 4) {
725
+ const tag = String.fromCharCode(data[i + 4], data[i + 5], data[i + 6], data[i + 7])
726
+ if (tag === 'ftyp' || tag === 'moov') return true
727
+ }
728
+ return false
729
+ }
730
+
731
+ function findTrackStsdBoxes(data: Uint8Array): Array<{ o: number; sz: number }> {
732
+ const moov = findBox(data, 0, data.length, 'moov')
733
+ if (!moov) return []
734
+
735
+ const out: Array<{ o: number; sz: number }> = []
736
+ for (const trak of findBoxes(data, moov.o + 8, moov.o + moov.sz, 'trak')) {
737
+ const stsd = findNestedBox(data, trak.o + 8, trak.o + trak.sz, ['mdia', 'minf', 'stbl', 'stsd'])
738
+ if (stsd) out.push(stsd)
739
+ }
740
+ return out
741
+ }
742
+
743
+ function findNestedBox(
744
+ buf: Uint8Array,
745
+ start: number,
746
+ end: number,
747
+ path: string[],
748
+ ): { o: number; sz: number } | undefined {
749
+ let s = start
750
+ let e = end
751
+ let current: { o: number; sz: number } | undefined
752
+ for (const type of path) {
753
+ current = findBox(buf, s, e, type)
754
+ if (!current) return undefined
755
+ s = current.o + 8
756
+ e = current.o + current.sz
757
+ }
758
+ return current
759
+ }
760
+
761
+ function findBox(buf: Uint8Array, start: number, end: number, type: string): { o: number; sz: number } | undefined {
762
+ return findBoxes(buf, start, end, type)[0]
763
+ }
764
+
765
+ function findBoxes(buf: Uint8Array, start: number, end: number, type: string): Array<{ o: number; sz: number }> {
766
+ const boxes: Array<{ o: number; sz: number }> = []
767
+ let o = start
768
+ while (o + 8 <= end) {
769
+ let sz = (buf[o] << 24) | (buf[o + 1] << 16) | (buf[o + 2] << 8) | buf[o + 3]
770
+ const t = String.fromCharCode(buf[o + 4], buf[o + 5], buf[o + 6], buf[o + 7])
771
+ if (sz === 0) sz = end - o
772
+ if (sz < 8 || o + sz > end) break
773
+ if (t === type) boxes.push({ o, sz })
774
+ o += sz
775
+ }
776
+ return boxes
777
+ }
778
+
779
+ function writeNalLength(buf: Uint8Array, offset: number, len: number): number {
780
+ buf[offset] = (len >> 24) & 0xff
781
+ buf[offset + 1] = (len >> 16) & 0xff
782
+ buf[offset + 2] = (len >> 8) & 0xff
783
+ buf[offset + 3] = len & 0xff
784
+ return offset + 4
785
+ }
786
+
787
+ function h264NalUnitsForKeyframeDetection(
788
+ data: Uint8Array,
789
+ options: H264KeyframeDetectionOptions,
790
+ ): Uint8Array[] {
791
+ return splitH264SampleNalUnits(data, options).nals
792
+ }
793
+
794
+ function splitPartialAvccNalUnits(data: Uint8Array, lengthSize = 4): Uint8Array[] {
795
+ const nals: Uint8Array[] = []
796
+ let i = 0
797
+ while (i + lengthSize < data.length) {
798
+ let len = 0
799
+ for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]
800
+ i += lengthSize
801
+ if (len <= 0 || i >= data.length) break
802
+ const end = Math.min(i + len, data.length)
803
+ nals.push(data.subarray(i, end))
804
+ if (end < i + len) break
805
+ i = end
806
+ }
807
+ return nals
808
+ }
809
+
810
+ function isLikelyRawH264NalUnit(data: Uint8Array): boolean {
811
+ if (data.length === 0) return false
812
+ const header = data[0]!
813
+ if ((header & 0x80) !== 0) return false
814
+ const nalType = header & 0x1f
815
+ return nalType > 0 && nalType < 24
816
+ }
817
+
818
+ function rawH264NalToAvcc(data: Uint8Array): Uint8Array {
819
+ const out = new Uint8Array(4 + data.length)
820
+ new DataView(out.buffer, out.byteOffset, out.byteLength).setUint32(0, data.length)
821
+ out.set(data, 4)
822
+ return out
823
+ }
824
+
825
+ function isH264IntraSlice(nal: Uint8Array): boolean {
826
+ const sliceType = readH264SliceType(nal)
827
+ return sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9
828
+ }
829
+
830
+ function readH264SliceType(nal: Uint8Array): number | null {
831
+ if (nal.length < 2 || (nal[0]! & 0x1f) !== 1) return null
832
+ const rbsp = removeEmulationPrevention(nal.subarray(1))
833
+ const state = { bitOffset: 0 }
834
+ if (readUnsignedExpGolomb(rbsp, state) === null) return null
835
+ return readUnsignedExpGolomb(rbsp, state)
836
+ }
837
+
838
+ function readUnsignedExpGolomb(data: Uint8Array, state: { bitOffset: number }): number | null {
839
+ let leadingZeroBits = 0
840
+ while (true) {
841
+ const bit = readBit(data, state)
842
+ if (bit === null) return null
843
+ if (bit === 1) break
844
+ leadingZeroBits++
845
+ if (leadingZeroBits > 30) return null
846
+ }
847
+
848
+ let suffix = 0
849
+ for (let i = 0; i < leadingZeroBits; i++) {
850
+ const bit = readBit(data, state)
851
+ if (bit === null) return null
852
+ suffix = (suffix << 1) | bit
853
+ }
854
+ return (1 << leadingZeroBits) - 1 + suffix
855
+ }
856
+
857
+ function readBit(data: Uint8Array, state: { bitOffset: number }): 0 | 1 | null {
858
+ if (state.bitOffset >= data.length * 8) return null
859
+ const byte = data[state.bitOffset >> 3]!
860
+ const bit = (byte >> (7 - (state.bitOffset & 7))) & 1
861
+ state.bitOffset++
862
+ return bit as 0 | 1
863
+ }
864
+
865
+ function hex2(n: number): string {
866
+ return n.toString(16).padStart(2, '0')
867
+ }
868
+
869
+ function pad2(n: number): string {
870
+ return String(n).padStart(2, '0')
871
+ }
872
+
873
+ function codecStringFromAv1C(av1C: Uint8Array): string {
874
+ if (av1C.length < 4) return 'av01.0.01M.08'
875
+ const profile = (av1C[1]! >>> 5) & 0x07
876
+ const level = av1C[1]! & 0x1f
877
+ const tier = (av1C[2]! & 0x80) !== 0 ? 'H' : 'M'
878
+ let bitDepth = 8
879
+ if ((av1C[2]! & 0x40) !== 0) {
880
+ bitDepth = (av1C[2]! & 0x20) !== 0 ? 12 : 10
881
+ }
882
+ return `av01.${profile}.${pad2(level)}${tier}.${pad2(bitDepth)}`
883
+ }
884
+
885
+ function codecStringFromVpcC(vpcC: Uint8Array): string {
886
+ if (vpcC.length < 3) return 'vp09.00.10.08'
887
+ // ISO/IEC 14496-15 defines vpcC as a FullBox. Some tooling hands callers
888
+ // the VPCodecConfigurationRecord after the 4-byte FullBox header; tolerate
889
+ // both shapes because WebCodecs callers only need a stable RFC 6381 fallback.
890
+ const offset = vpcC.length >= 7 && vpcC[0] <= 1 ? 4 : 0
891
+ if (offset + 2 >= vpcC.length) return 'vp09.00.10.08'
892
+ const profile = vpcC[offset]!
893
+ const level = vpcC[offset + 1]!
894
+ const bitDepth = (vpcC[offset + 2]! >>> 4) || 8
895
+ return `vp09.${pad2(profile)}.${pad2(level)}.${pad2(bitDepth)}`
896
+ }
897
+
898
+ /**
899
+ * Derive an RFC 6381 HEVC codec string (e.g. "hvc1.1.6.L93.90") from the
900
+ * payload of an hvcC box (the HEVCDecoderConfigurationRecord, ISO 14496-15 §8.3.3.1).
901
+ * Only the general_* fields in the fixed 23-byte header are needed:
902
+ * byte 1: general_profile_space(2) | general_tier_flag(1) | general_profile_idc(5)
903
+ * bytes 2-5: general_profile_compatibility_flags (32 bits)
904
+ * bytes 6-11: general_constraint_indicator_flags (48 bits)
905
+ * byte 12: general_level_idc
906
+ */
907
+ function hevcCodecStringFromHvcC(c: Uint8Array): string {
908
+ const profileSpace = (c[1] >> 6) & 0x3
909
+ const tierFlag = (c[1] >> 5) & 0x1
910
+ const profileIdc = c[1] & 0x1f
911
+ // Compatibility flags are emitted reversed bit-order as hex, leading zeros trimmed.
912
+ const compat = ((c[2] << 24) | (c[3] << 16) | (c[4] << 8) | c[5]) >>> 0
913
+ let rev = 0
914
+ let v = compat
915
+ for (let i = 0; i < 32; i++) {
916
+ rev = ((rev << 1) | (v & 1)) >>> 0
917
+ v >>>= 1
918
+ }
919
+ const compatStr = rev.toString(16)
920
+ // Constraint flags: dot-separated hex bytes, trailing zero bytes trimmed.
921
+ const constraint = c.subarray(6, 12)
922
+ let lastNonZero = -1
923
+ for (let i = 0; i < constraint.length; i++) if (constraint[i] !== 0) lastNonZero = i
924
+ const constraintParts: string[] = []
925
+ for (let i = 0; i <= lastNonZero; i++) constraintParts.push(constraint[i].toString(16))
926
+ const space = profileSpace === 0 ? '' : String.fromCharCode(64 + profileSpace) // 1→'A', 2→'B', 3→'C'
927
+ const tier = tierFlag === 0 ? 'L' : 'H'
928
+ let s = `hvc1.${space}${profileIdc}.${compatStr}.${tier}${c[12]}`
929
+ if (constraintParts.length > 0) s += '.' + constraintParts.join('.')
930
+ return s
931
+ }
932
+
933
+ /** Exp-Golomb bitstream writer for SPS/PPS generation. */
934
+ class BitWriter {
935
+ buf: number[] = []
936
+ byte = 0
937
+ bit = 7
938
+
939
+ u(val: number, bits: number) {
940
+ for (let i = bits - 1; i >= 0; i--) {
941
+ this.byte |= ((val >> i) & 1) << this.bit
942
+ if (--this.bit < 0) {
943
+ this.buf.push(this.byte)
944
+ this.byte = 0
945
+ this.bit = 7
946
+ }
947
+ }
948
+ }
949
+
950
+ ue(val: number) {
951
+ const v = val + 1
952
+ const bits = Math.floor(Math.log2(v)) + 1
953
+ this.u(0, bits - 1)
954
+ this.u(v, bits)
955
+ }
956
+
957
+ se(val: number) {
958
+ this.ue(val <= 0 ? -val * 2 : val * 2 - 1)
959
+ }
960
+
961
+ finish(): Uint8Array {
962
+ if (this.bit < 7) {
963
+ this.byte |= 1 << this.bit
964
+ this.buf.push(this.byte)
965
+ }
966
+ return new Uint8Array(this.buf)
967
+ }
968
+ }
969
+
970
+ function resolveResolution(
971
+ explicitW: number | undefined,
972
+ explicitH: number | undefined,
973
+ level: number,
974
+ ): { width: number; height: number } {
975
+ if (explicitW && explicitH) return { width: explicitW, height: explicitH }
976
+
977
+ const levelResolutions: Record<number, [number, number]> = {
978
+ 10: [176, 144], 11: [352, 288], 12: [352, 288], 13: [352, 288],
979
+ 20: [352, 288], 21: [720, 576], 22: [720, 576],
980
+ 30: [1280, 720], 31: [1280, 720], 32: [1280, 1024],
981
+ 40: [1920, 1088], 41: [1920, 1088], 42: [2048, 1088],
982
+ 50: [4096, 2160], 51: [4096, 2160], 52: [4096, 2160],
983
+ }
984
+
985
+ const [w, h] = levelResolutions[level] ?? [1920, 1088]
986
+ return { width: explicitW ?? w, height: explicitH ?? h }
987
+ }
988
+
989
+ function buildMinimalSps(
990
+ profile: number, constraint: number, level: number,
991
+ width: number, height: number,
992
+ ): Uint8Array {
993
+ const widthInMbs = Math.ceil(width / 16)
994
+ const heightInMapUnits = Math.ceil(height / 16)
995
+
996
+ const w = new BitWriter()
997
+ w.u(0, 1); w.u(3, 2); w.u(7, 5) // NAL header: SPS
998
+ w.u(profile, 8); w.u(constraint, 8); w.u(level, 8)
999
+ w.ue(0) // seq_parameter_set_id
1000
+
1001
+ if (profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44) {
1002
+ w.ue(1); w.ue(0); w.ue(0) // chroma + bit depth
1003
+ w.u(0, 1); w.u(0, 1) // transform bypass + scaling matrix
1004
+ }
1005
+
1006
+ w.ue(0) // log2_max_frame_num_minus4
1007
+ w.ue(2) // pic_order_cnt_type=2
1008
+ w.ue(1) // max_num_ref_frames
1009
+ w.u(0, 1) // gaps_in_frame_num
1010
+ w.ue(widthInMbs - 1)
1011
+ w.ue(heightInMapUnits - 1)
1012
+ w.u(1, 1) // frame_mbs_only
1013
+ w.u(1, 1) // direct_8x8_inference
1014
+
1015
+ const cropRight = widthInMbs * 16 - width
1016
+ const cropBottom = heightInMapUnits * 16 - height
1017
+ if (cropRight > 0 || cropBottom > 0) {
1018
+ w.u(1, 1)
1019
+ w.ue(0); w.ue(cropRight / 2); w.ue(0); w.ue(cropBottom / 2)
1020
+ } else {
1021
+ w.u(0, 1)
1022
+ }
1023
+
1024
+ w.u(0, 1) // vui_parameters_present
1025
+ return w.finish()
1026
+ }
1027
+
1028
+ function buildMinimalPps(profile: number): Uint8Array {
1029
+ const isHighProfile = profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44
1030
+ const w = new BitWriter()
1031
+ w.u(0, 1); w.u(3, 2); w.u(8, 5) // NAL header: PPS
1032
+
1033
+ w.ue(0); w.ue(0) // pps_id, sps_id
1034
+ w.u(isHighProfile ? 1 : 0, 1) // entropy_coding_mode
1035
+ w.u(0, 1) // bottom_field_pic_order
1036
+ w.ue(0); w.ue(0); w.ue(0) // slice groups, ref_idx defaults
1037
+ w.u(0, 1); w.u(0, 2) // weighted_pred
1038
+ w.se(0); w.se(0); w.se(0) // qp offsets
1039
+ w.u(1, 1); w.u(0, 1); w.u(0, 1) // deblock, constrained_intra, redundant_pic_cnt
1040
+
1041
+ if (isHighProfile) {
1042
+ w.u(1, 1); w.u(0, 1); w.se(0) // transform_8x8, scaling_matrix, chroma_qp_offset
1043
+ }
1044
+
1045
+ return w.finish()
1046
+ }
1047
+
1048
+ // ─── AAC / ADTS Utilities ──────────────────────────────────────
1049
+
1050
+ /** ISO 14496-3 sampling frequency index lookup. */
1051
+ const ADTS_SAMPLE_RATES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350] as const
1052
+
1053
+ /**
1054
+ * Prepend a 7-byte ADTS header to a raw AAC access unit.
1055
+ *
1056
+ * WebCodecs AudioDecoder configured WITHOUT an AudioSpecificConfig `description`
1057
+ * expects ADTS-framed AAC. MMTP carries raw AAC (no ADTS sync), so this wrapper
1058
+ * must be applied before calling `AudioDecoder.decode()`.
1059
+ */
1060
+ export function wrapAdtsFrame(raw: Uint8Array, sampleRate: number, channels: number): Uint8Array {
1061
+ const sri = ADTS_SAMPLE_RATES.indexOf(sampleRate as typeof ADTS_SAMPLE_RATES[number])
1062
+ const idx = sri >= 0 ? sri : 3 // default 48 kHz
1063
+ const len = 7 + raw.length
1064
+ const out = new Uint8Array(len)
1065
+ out[0] = 0xFF
1066
+ out[1] = 0xF1 // MPEG-4, Layer 0, no CRC
1067
+ out[2] = (1 << 6) | (idx << 2) | (channels >> 2) // profile=AAC-LC(1), freq, ch high
1068
+ out[3] = ((channels & 3) << 6) | ((len >> 11) & 3)
1069
+ out[4] = (len >> 3) & 0xFF
1070
+ out[5] = ((len & 7) << 5) | 0x1F
1071
+ out[6] = 0xFC
1072
+ out.set(raw, 7)
1073
+ return out
1074
+ }
1075
+
1076
+ /**
1077
+ * Extract AAC AudioSpecificConfig (ASC) from an fMP4 init segment.
1078
+ *
1079
+ * Walks moov > trak > mdia > minf > stbl > stsd > mp4a > esds, then descends
1080
+ * the MPEG-4 descriptor chain (ES_Descriptor 0x03 > DecoderConfigDescriptor
1081
+ * 0x04 > DecoderSpecificInfo 0x05) to return the ASC payload.
1082
+ *
1083
+ * Sibling of extractAvcCFromInitSegment, used by the same BLO-4830 / CMSF §3.1
1084
+ * path: when the catalog initData is the full ftyp+moov, WebCodecs' AudioDecoder
1085
+ * needs the raw ASC (~2 bytes for AAC-LC) as `description`, NOT the whole
1086
+ * segment. Without this extraction the configure() succeeds but every decode()
1087
+ * silently produces zero output (no error fires), exactly the symptom that
1088
+ * shows up as "Audio continuity FAILED: energy unstable CV=1.00" in the
1089
+ * 7-point E2E test (CV=1.00 is the meanEnergy=0 sentinel from audio-fft.mjs).
1090
+ *
1091
+ * Returns undefined when:
1092
+ * - data has no ftyp/moov magic in the first 32 bytes
1093
+ * - no mp4a sample entry exists in any trak's stbl
1094
+ * - the esds descriptor chain doesn't yield a DecoderSpecificInfo
1095
+ * - the ASC payload is shorter than 2 bytes (AOT + sampling-freq nibble)
1096
+ *
1097
+ * Walks every trak and returns the first AAC sample entry. This mirrors
1098
+ * extractAvcCFromInitSegment so multiplexed ftyp+moov init blobs work
1099
+ * regardless of track order.
1100
+ */
1101
+ export function extractAacAscFromInitSegment(data: Uint8Array): AacAscInfo | undefined {
1102
+ if (!hasIsoBmffMagic(data)) return undefined
1103
+
1104
+ for (const stsd of findTrackStsdBoxes(data)) {
1105
+ const info = extractAacAscFromStsd(data, stsd)
1106
+ if (info) return info
1107
+ }
1108
+
1109
+ return undefined
1110
+ }
1111
+
1112
+ function extractAacAscFromStsd(data: Uint8Array, stsd: { o: number; sz: number }): AacAscInfo | undefined {
1113
+ // stsd: 8 box header + 4 version+flags + 4 entry_count = 16 bytes before entries.
1114
+ const s = stsd.o + 16
1115
+ const e = stsd.o + stsd.sz
1116
+ // mp4a AudioSampleEntry minimum length: 8 box header + 28 fixed fields = 36 bytes
1117
+ // before any child boxes (esds lives here).
1118
+ const mp4a = findBox(data, s, e, 'mp4a')
1119
+ if (!mp4a || mp4a.sz < 36) return undefined
1120
+ const esds = findBox(data, mp4a.o + 36, mp4a.o + mp4a.sz, 'esds')
1121
+ // esds = 8 box header + 4 full-box version+flags = 12 bytes minimum before descriptors.
1122
+ if (!esds || esds.sz < 12) return undefined
1123
+ const esdsPayload = data.subarray(esds.o + 8, esds.o + esds.sz)
1124
+ const asc = extractAudioSpecificConfig(esdsPayload)
1125
+ if (!asc || asc.length < 2) return undefined
1126
+ // AudioObjectType = top 5 bits of ASC[0]. AAC-LC = 2, HE-AAC = 5, etc.
1127
+ const aot = asc[0] >> 3
1128
+ return { codec: `mp4a.40.${aot}`, description: asc }
1129
+ }
1130
+
1131
+ /**
1132
+ * Walk an esds descriptor chain to find DecoderSpecificInfo (tag 0x05).
1133
+ * Step over fixed-size headers of container descriptors (0x03, 0x04) so the
1134
+ * scan continues into their children rather than skipping their full payload.
1135
+ *
1136
+ * Assumes the ES_Descriptor flags byte is zero (no streamDependence, URL, OCR
1137
+ * ES_ID fields). FFmpeg's mov/mp4 muxer emits exactly that for all standard
1138
+ * AAC tracks; the assumption is the same one extractAvcCFromInitSegment makes
1139
+ * about the avc1 sample entry's reserved fields.
1140
+ *
1141
+ * Input is the esds box payload AFTER the 8-byte box header; the first 4 bytes
1142
+ * (full-box version+flags) are skipped here.
1143
+ */
1144
+ function extractAudioSpecificConfig(esds: Uint8Array): Uint8Array | undefined {
1145
+ let offset = 4 // skip esds version+flags
1146
+ while (offset < esds.length) {
1147
+ const tag = esds[offset++]
1148
+ let size = 0
1149
+ for (let i = 0; i < 4 && offset < esds.length; i++) {
1150
+ const b = esds[offset++]
1151
+ size = (size << 7) | (b & 0x7f)
1152
+ if ((b & 0x80) === 0) break
1153
+ }
1154
+ if (tag === 0x05) {
1155
+ if (offset + size > esds.length) return undefined
1156
+ return esds.subarray(offset, offset + size)
1157
+ }
1158
+ if (tag === 0x03) {
1159
+ offset += 3 // ES_ID (2) + flags (1)
1160
+ } else if (tag === 0x04) {
1161
+ offset += 13 // OTI (1) + streamType (1) + bufferSizeDB (3) + maxBitrate (4) + avgBitrate (4)
1162
+ } else {
1163
+ offset += size
1164
+ }
1165
+ }
1166
+ return undefined
1167
+ }
1168
+
1169
+ // =============================================================================
1170
+ // Inline Tests (vitest)
1171
+ // =============================================================================
1172
+
1173
+ if (import.meta.vitest) {
1174
+ const { describe, test, expect } = import.meta.vitest
1175
+
1176
+ describe('codec-utils', () => {
1177
+ describe('isAnnexB', () => {
1178
+ test('4-byte start code', () => {
1179
+ expect(isAnnexB(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(true)
1180
+ })
1181
+ test('3-byte start code', () => {
1182
+ expect(isAnnexB(new Uint8Array([0, 0, 1, 0x65]))).toBe(true)
1183
+ })
1184
+ test('rejects AVCC', () => {
1185
+ expect(isAnnexB(new Uint8Array([0, 0, 0, 10, 0x65]))).toBe(false)
1186
+ })
1187
+ })
1188
+
1189
+ describe('findAnnexBStartCode', () => {
1190
+ test('at offset 0', () => {
1191
+ expect(findAnnexBStartCode(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(0)
1192
+ })
1193
+ test('at offset 5', () => {
1194
+ expect(findAnnexBStartCode(new Uint8Array([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0, 0, 1, 0x65]))).toBe(5)
1195
+ })
1196
+ test('returns -1 when none', () => {
1197
+ expect(findAnnexBStartCode(new Uint8Array([0xAA, 0xBB, 0xCC]))).toBe(-1)
1198
+ })
1199
+ })
1200
+
1201
+ describe('splitAnnexBNalUnits', () => {
1202
+ test('splits two NALs', () => {
1203
+ const data = new Uint8Array([0, 0, 0, 1, 0x65, 0xAA, 0xBB, 0, 0, 0, 1, 0x41, 0xCC])
1204
+ const nals = splitAnnexBNalUnits(data)
1205
+ expect(nals.length).toBe(2)
1206
+ expect(nals[0]).toEqual(new Uint8Array([0x65, 0xAA, 0xBB]))
1207
+ expect(nals[1]).toEqual(new Uint8Array([0x41, 0xCC]))
1208
+ })
1209
+ test('handles 3-byte start codes', () => {
1210
+ const nals = splitAnnexBNalUnits(new Uint8Array([0, 0, 1, 0x67, 0x42, 0, 0, 1, 0x68, 0x01]))
1211
+ expect(nals.length).toBe(2)
1212
+ })
1213
+ })
1214
+
1215
+ describe('annexBToAvcc', () => {
1216
+ test('converts single NAL', () => {
1217
+ const annexB = new Uint8Array([0, 0, 0, 1, 0x41, 0xAA, 0xBB])
1218
+ const avcc = annexBToAvcc(annexB)
1219
+ expect(avcc[3]).toBe(3) // NAL length
1220
+ expect(avcc[4]).toBe(0x41)
1221
+ })
1222
+ test('prepends SPS/PPS', () => {
1223
+ const annexB = new Uint8Array([0, 0, 0, 1, 0x65, 0x01])
1224
+ const avcc = annexBToAvcc(annexB, { sps: new Uint8Array([0x67, 0x42]), pps: new Uint8Array([0x68, 0x01]) })
1225
+ expect(avcc[3]).toBe(2) // SPS length
1226
+ expect(avcc[4]).toBe(0x67)
1227
+ })
1228
+ })
1229
+
1230
+ describe('detectH264Keyframe', () => {
1231
+ test('IDR in Annex-B', () => {
1232
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(true)
1233
+ })
1234
+ test('SPS in Annex-B', () => {
1235
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x67]))).toBe(true)
1236
+ })
1237
+ test('IDR in AVCC', () => {
1238
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 5, 0x65, 1, 2, 3, 4]))).toBe(true)
1239
+ })
1240
+ test('IDR after AVCC AUD', () => {
1241
+ expect(detectH264Keyframe(new Uint8Array([
1242
+ 0, 0, 0, 1, 0x09,
1243
+ 0, 0, 0, 2, 0x65, 0xaa,
1244
+ ]))).toBe(true)
1245
+ })
1246
+ test('non-IDR AVC I-slice in AVCC', () => {
1247
+ expect(detectH264Keyframe(new Uint8Array([
1248
+ 0, 0, 0, 2, 0x41, 0x88,
1249
+ ]))).toBe(true)
1250
+ })
1251
+ test('raw H.264 IDR NAL', () => {
1252
+ expect(detectH264Keyframe(new Uint8Array([0x65, 0x88, 0x84]))).toBe(true)
1253
+ })
1254
+ test('raw H.264 non-IDR I-slice NAL', () => {
1255
+ expect(detectH264Keyframe(new Uint8Array([0x41, 0x88]))).toBe(true)
1256
+ })
1257
+ test('classifies partial length-prefixed IDR only when requested', () => {
1258
+ const partial = new Uint8Array([0, 0, 0x20, 0, 0x65])
1259
+ expect(classifyH264Keyframe(partial)).toBeUndefined()
1260
+ expect(classifyH264Keyframe(partial, { allowPartialAvcc: true })).toBe(true)
1261
+ })
1262
+ test('rejects P-frame', () => {
1263
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x41]))).toBe(false)
1264
+ })
1265
+ test('rejects AVCC sample with only non-IDR slices', () => {
1266
+ expect(detectH264Keyframe(new Uint8Array([
1267
+ 0, 0, 0, 2, 0x41, 0xc0,
1268
+ 0, 0, 0, 2, 0x41, 0xc0,
1269
+ ]))).toBe(false)
1270
+ })
1271
+ })
1272
+
1273
+ describe('avccToAnnexB', () => {
1274
+ test('converts with SPS/PPS on keyframe', () => {
1275
+ const avcc = new Uint8Array([0, 0, 0, 3, 0x65, 0xAA, 0xBB])
1276
+ const spsPps: SpsPps = { sps: new Uint8Array([0x67, 0x42]), pps: new Uint8Array([0x68, 0x01]) }
1277
+ const annexB = avccToAnnexB(avcc, true, spsPps)
1278
+ expect(annexB[3]).toBe(1) // start code
1279
+ expect(annexB[4]).toBe(0x67) // SPS
1280
+ })
1281
+ })
1282
+
1283
+ describe('codecStringFromAvcC', () => {
1284
+ test('derives avc1.64002a', () => {
1285
+ expect(codecStringFromAvcC(new Uint8Array([1, 100, 0, 42]))).toBe('avc1.64002a')
1286
+ })
1287
+ })
1288
+
1289
+ describe('extractAacAscFromInitSegment', () => {
1290
+ // Build a minimal valid ftyp+moov containing one mp4a sample entry whose
1291
+ // esds carries the supplied AudioSpecificConfig. The walker only needs
1292
+ // the box hierarchy moov > trak > mdia > minf > stbl > stsd > mp4a > esds;
1293
+ // mvhd/tkhd/mdhd/hdlr/smhd/dinf/stts/stsc/stsz/stco are spec-required for
1294
+ // a truly valid init but the extractor never reads them, so we omit them
1295
+ // to keep the fixture tight (~150 bytes vs ~500). FFmpeg's real output
1296
+ // includes them; the production path passes through `findBox` exactly
1297
+ // the same way.
1298
+ function buildMinimalAacInit(asc: Uint8Array): Uint8Array {
1299
+ const box = (type: string, payload: Uint8Array): Uint8Array => {
1300
+ const sz = 8 + payload.length
1301
+ const out = new Uint8Array(sz)
1302
+ out[0] = (sz >>> 24) & 0xff
1303
+ out[1] = (sz >>> 16) & 0xff
1304
+ out[2] = (sz >>> 8) & 0xff
1305
+ out[3] = sz & 0xff
1306
+ out[4] = type.charCodeAt(0); out[5] = type.charCodeAt(1)
1307
+ out[6] = type.charCodeAt(2); out[7] = type.charCodeAt(3)
1308
+ out.set(payload, 8)
1309
+ return out
1310
+ }
1311
+ const concat = (...arrs: Uint8Array[]): Uint8Array => {
1312
+ const total = arrs.reduce((n, a) => n + a.length, 0)
1313
+ const out = new Uint8Array(total)
1314
+ let off = 0
1315
+ for (const a of arrs) { out.set(a, off); off += a.length }
1316
+ return out
1317
+ }
1318
+ // ftyp: major_brand=iso5, minor=0x200, compat=iso5,iso6
1319
+ const ftyp = box('ftyp', new Uint8Array([
1320
+ 0x69, 0x73, 0x6f, 0x35, // 'iso5'
1321
+ 0x00, 0x00, 0x02, 0x00, // minor 0x200
1322
+ 0x69, 0x73, 0x6f, 0x35, // 'iso5'
1323
+ 0x69, 0x73, 0x6f, 0x36, // 'iso6'
1324
+ ]))
1325
+
1326
+ // ESDescriptor (0x03) > DecoderConfigDescriptor (0x04) > DecoderSpecificInfo (0x05) > ASC.
1327
+ // Use 1-byte BER sizes (all payloads < 128).
1328
+ const dsi = new Uint8Array(2 + asc.length)
1329
+ dsi[0] = 0x05; dsi[1] = asc.length
1330
+ dsi.set(asc, 2)
1331
+ // DCD = 13 fixed bytes + DSI: OTI=0x40 (AAC), streamType=0x05<<2|0x01 (audio, upStream=0)
1332
+ const dcd = new Uint8Array(2 + 13 + dsi.length)
1333
+ dcd[0] = 0x04; dcd[1] = 13 + dsi.length
1334
+ dcd[2] = 0x40 // objectTypeIndication: Audio ISO/IEC 14496-3
1335
+ dcd[3] = (0x05 << 2) | 1 // streamType=AudioStream(5), upStream=0, reserved=1
1336
+ // bufferSizeDB(3) + maxBitrate(4) + avgBitrate(4) all zero is fine for the walker.
1337
+ dcd.set(dsi, 15)
1338
+ // ESDescriptor: ES_ID=0, flags=0, then DCD
1339
+ const esd = new Uint8Array(2 + 3 + dcd.length)
1340
+ esd[0] = 0x03; esd[1] = 3 + dcd.length
1341
+ // ES_ID(2) + flags(1) all zero
1342
+ esd.set(dcd, 5)
1343
+ // esds box = full-box (4-byte version+flags) + ES_Descriptor chain
1344
+ const esdsPayload = new Uint8Array(4 + esd.length)
1345
+ esdsPayload.set(esd, 4)
1346
+ const esds = box('esds', esdsPayload)
1347
+
1348
+ // mp4a sample entry: 6 reserved + 2 dref_idx + 8 reserved + 2 channels + 2 samplesize +
1349
+ // 2 pre_defined + 2 reserved + 4 samplerate(48000<<16) = 28 bytes,
1350
+ // then esds child.
1351
+ const mp4aFixed = new Uint8Array(28)
1352
+ mp4aFixed[7] = 0x01 // data_reference_index = 1
1353
+ mp4aFixed[16] = 0x00; mp4aFixed[17] = 0x01 // channelcount = 1
1354
+ mp4aFixed[18] = 0x00; mp4aFixed[19] = 0x10 // samplesize = 16
1355
+ // samplerate at offset 24-27 (top 16 bits = sampleRate). 48000 = 0xBB80.
1356
+ mp4aFixed[24] = 0xBB; mp4aFixed[25] = 0x80
1357
+ const mp4a = box('mp4a', concat(mp4aFixed, esds))
1358
+
1359
+ // stsd: 4 ver+flags + 4 entry_count(=1) + entries
1360
+ const stsdPayload = new Uint8Array(8 + mp4a.length)
1361
+ stsdPayload[7] = 0x01 // entry_count = 1
1362
+ stsdPayload.set(mp4a, 8)
1363
+ const stsd = box('stsd', stsdPayload)
1364
+
1365
+ const stbl = box('stbl', stsd)
1366
+ const minf = box('minf', stbl)
1367
+ const mdia = box('mdia', minf)
1368
+ const trak = box('trak', mdia)
1369
+ const moov = box('moov', trak)
1370
+
1371
+ return concat(ftyp, moov)
1372
+ }
1373
+
1374
+ test('extracts AAC-LC mono 48k ASC and derives codec', () => {
1375
+ // AOT=2 (00010), samplingFrequencyIndex=3 (0011, 48000Hz), channelConfig=1 (0001),
1376
+ // GA: frameLengthFlag=0, dependsOnCoreCoder=0, extensionFlag=0 → 0x11 0x88.
1377
+ const asc = new Uint8Array([0x11, 0x88])
1378
+ const init = buildMinimalAacInit(asc)
1379
+ const result = extractAacAscFromInitSegment(init)
1380
+ expect(result).toBeDefined()
1381
+ expect(result!.codec).toBe('mp4a.40.2')
1382
+ expect(Array.from(result!.description)).toEqual([0x11, 0x88])
1383
+ })
1384
+
1385
+ test('derives AOT 5 (HE-AAC) from ASC[0] >> 3', () => {
1386
+ // AOT=5 (00101), rest don't matter for codec derivation.
1387
+ const asc = new Uint8Array([0x29, 0x88]) // 00101 001 1000 1000
1388
+ const init = buildMinimalAacInit(asc)
1389
+ expect(extractAacAscFromInitSegment(init)?.codec).toBe('mp4a.40.5')
1390
+ })
1391
+
1392
+ test('rejects data without ftyp/moov magic', () => {
1393
+ expect(extractAacAscFromInitSegment(new Uint8Array(64))).toBeUndefined()
1394
+ })
1395
+
1396
+ test('rejects ftyp+moov with no audio (no mp4a sample entry)', () => {
1397
+ // ftyp + moov with empty trak chain — no stsd, let alone mp4a.
1398
+ const ftyp = new Uint8Array([
1399
+ 0, 0, 0, 24, 0x66, 0x74, 0x79, 0x70,
1400
+ 0x69, 0x73, 0x6f, 0x35, 0, 0, 2, 0,
1401
+ 0x69, 0x73, 0x6f, 0x35, 0x69, 0x73, 0x6f, 0x36,
1402
+ ])
1403
+ const moov = new Uint8Array([0, 0, 0, 8, 0x6d, 0x6f, 0x6f, 0x76]) // empty moov
1404
+ const cat = new Uint8Array(ftyp.length + moov.length)
1405
+ cat.set(ftyp); cat.set(moov, ftyp.length)
1406
+ expect(extractAacAscFromInitSegment(cat)).toBeUndefined()
1407
+ })
1408
+ })
1409
+
1410
+ describe('hevc', () => {
1411
+ // Minimal HEVC NAL units: header byte0 = (type << 1). VPS=32(0x40), SPS=33(0x42), PPS=34(0x44).
1412
+ const vps = new Uint8Array([0x40, 0x01, 0x0c, 0x01, 0xff, 0xff])
1413
+ const sps = new Uint8Array([0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xa0, 0x05, 0x02])
1414
+ const pps = new Uint8Array([0x44, 0x01, 0xc1, 0x72, 0xb4, 0x62])
1415
+ const idr = new Uint8Array([0x26, 0x01, 0xaf, 0x06]) // type 19 (IDR_W_RADL)
1416
+ const lp = (...nals: Uint8Array[]) => {
1417
+ const parts: number[] = []
1418
+ for (const n of nals) { parts.push((n.length >> 24) & 0xff, (n.length >> 16) & 0xff, (n.length >> 8) & 0xff, n.length & 0xff, ...n) }
1419
+ return Uint8Array.from(parts)
1420
+ }
1421
+
1422
+ test('isHevcCodec', () => {
1423
+ expect(isHevcCodec('hev1.1.6.L93.B0')).toBe(true)
1424
+ expect(isHevcCodec('hvc1.1.6.L93.B0')).toBe(true)
1425
+ expect(isHevcCodec('avc1.640033')).toBe(false)
1426
+ })
1427
+
1428
+ test('removeEmulationPrevention strips 0x03', () => {
1429
+ expect(Array.from(removeEmulationPrevention(new Uint8Array([0, 0, 0x03, 1])))).toEqual([0, 0, 1])
1430
+ expect(Array.from(removeEmulationPrevention(new Uint8Array([0xaa, 0xbb])))).toEqual([0xaa, 0xbb])
1431
+ })
1432
+
1433
+ test('splitAvccNalUnits splits length-prefixed NALs', () => {
1434
+ const nals = splitAvccNalUnits(lp(vps, sps))
1435
+ expect(nals.length).toBe(2)
1436
+ expect(nals[0]).toEqual(vps)
1437
+ expect(nals[1]).toEqual(sps)
1438
+ })
1439
+
1440
+ test('isCompleteAvccSample accepts complete length-prefixed NAL samples', () => {
1441
+ expect(isCompleteAvccSample(lp(new Uint8Array([0x09]), idr))).toBe(true)
1442
+ })
1443
+
1444
+ test('isCompleteAvccSample rejects Annex-B start-code samples', () => {
1445
+ expect(isCompleteAvccSample(new Uint8Array([0, 0, 0, 1, 0x65, 0xaa]))).toBe(false)
1446
+ })
1447
+
1448
+ test('isCompleteAvccSample rejects truncated length-prefixed samples', () => {
1449
+ expect(isCompleteAvccSample(new Uint8Array([0, 0, 0, 4, 0x65, 0xaa]))).toBe(false)
1450
+ })
1451
+
1452
+ test('normalizeH264SampleToAvcc leaves complete AVCC samples unchanged', () => {
1453
+ const avcc = lp(new Uint8Array([0x09]), idr)
1454
+ const normalized = normalizeH264SampleToAvcc(avcc)
1455
+ expect(normalized.format).toBe('avcc')
1456
+ expect(normalized.data).toBe(avcc)
1457
+ })
1458
+
1459
+ test('normalizeH264SampleToAvcc converts a single raw H.264 NAL to AVCC', () => {
1460
+ const raw = new Uint8Array([0x65, 0x88, 0x84])
1461
+ const normalized = normalizeH264SampleToAvcc(raw)
1462
+ expect(normalized.format).toBe('raw-nal')
1463
+ expect(normalized.data).toEqual(new Uint8Array([0, 0, 0, 3, 0x65, 0x88, 0x84]))
1464
+ })
1465
+
1466
+ test('splitH264SampleNalUnits exposes Annex-B NALs without AVCC conversion', () => {
1467
+ const annexB = new Uint8Array([0, 0, 0, 1, 0x65, 0x88, 0x84])
1468
+ const sample = splitH264SampleNalUnits(annexB)
1469
+ expect(sample.format).toBe('annexb')
1470
+ expect(sample.nals).toHaveLength(1)
1471
+ expect(sample.nals[0]).toEqual(new Uint8Array([0x65, 0x88, 0x84]))
1472
+ })
1473
+
1474
+ test('splitH264SampleNalUnits exposes single raw H.264 NALs without wrapping', () => {
1475
+ const raw = new Uint8Array([0x65, 0x88, 0x84])
1476
+ const sample = splitH264SampleNalUnits(raw)
1477
+ expect(sample.format).toBe('raw-nal')
1478
+ expect(sample.nals).toEqual([raw])
1479
+ })
1480
+
1481
+ test('splitH264SampleNalUnits can expose partial AVCC NAL headers', () => {
1482
+ const partial = new Uint8Array([0, 0, 0, 5, 0x65, 0x88])
1483
+ const sample = splitH264SampleNalUnits(partial, { allowPartialAvcc: true })
1484
+ expect(sample.format).toBe('partial-avcc')
1485
+ expect(sample.nals).toHaveLength(1)
1486
+ expect(sample.nals[0]).toEqual(new Uint8Array([0x65, 0x88]))
1487
+ })
1488
+
1489
+ test('extractHevcParameterSets finds VPS/SPS/PPS (length-prefixed)', () => {
1490
+ const sets = extractHevcParameterSets(lp(vps, sps, pps, idr))
1491
+ expect(sets.vps).toEqual(vps)
1492
+ expect(sets.sps).toEqual(sps)
1493
+ expect(sets.pps).toEqual(pps)
1494
+ })
1495
+
1496
+ test('extractHevcParameterSets finds VPS/SPS/PPS (Annex-B)', () => {
1497
+ const sc = new Uint8Array([0, 0, 0, 1])
1498
+ const annexb = Uint8Array.from([...sc, ...vps, ...sc, ...sps, ...sc, ...pps, ...sc, ...idr])
1499
+ const sets = extractHevcParameterSets(annexb)
1500
+ expect(sets.vps).toEqual(vps)
1501
+ expect(sets.sps).toEqual(sps)
1502
+ expect(sets.pps).toEqual(pps)
1503
+ })
1504
+
1505
+ test('generateHvcCFromParameterSets builds a valid hvcC', () => {
1506
+ const hvcc = generateHvcCFromParameterSets({ vps, sps, pps })!
1507
+ expect(hvcc).not.toBeNull()
1508
+ expect(hvcc[0]).toBe(1) // configurationVersion
1509
+ expect(hvcc[1]).toBe(sps[3]) // general profile_tier_level byte 0 copied from SPS[3]
1510
+ expect(hvcc[22]).toBe(3) // numOfArrays (VPS+SPS+PPS)
1511
+ // first array entry: NAL_unit_type 32 (VPS), numNalus 1
1512
+ expect(hvcc[23] & 0x3f).toBe(32)
1513
+ expect((hvcc[24] << 8) | hvcc[25]).toBe(1)
1514
+ })
1515
+
1516
+ test('generateHvcCFromParameterSets returns null without SPS/PPS', () => {
1517
+ expect(generateHvcCFromParameterSets({ vps })).toBeNull()
1518
+ })
1519
+
1520
+ test('extractHevcCodecDescription end-to-end from keyframe', () => {
1521
+ const desc = extractHevcCodecDescription(lp(vps, sps, pps, idr))
1522
+ expect(desc).not.toBeNull()
1523
+ expect(desc![0]).toBe(1)
1524
+ })
1525
+
1526
+ test('hevcVclOnly strips parameter-set NALs, keeps VCL', () => {
1527
+ const stripped = hevcVclOnly(lp(vps, sps, pps, idr))
1528
+ const nals = splitAvccNalUnits(stripped)
1529
+ expect(nals.length).toBe(1) // only the IDR VCL NAL remains
1530
+ expect(nals[0]).toEqual(idr)
1531
+ })
1532
+
1533
+ test('hevcVclOnly is a no-op for VCL-only length-prefixed input', () => {
1534
+ const vclOnly = lp(idr)
1535
+ expect(hevcVclOnly(vclOnly)).toBe(vclOnly)
1536
+ })
1537
+ })
1538
+ })
1539
+ }