@blockcast/mmt-container 0.1.0 → 0.3.0-main.0bde85f7082f

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 (799) hide show
  1. package/README.md +524 -0
  2. package/dist/.build-stamp +0 -0
  3. package/dist/__tests__/test-utils.d.ts +71 -0
  4. package/dist/__tests__/test-utils.d.ts.map +1 -0
  5. package/dist/align.d.ts +152 -0
  6. package/dist/align.d.ts.map +1 -0
  7. package/dist/align.js +88 -0
  8. package/dist/align.js.map +7 -0
  9. package/dist/audio-init-parser.d.ts +66 -0
  10. package/dist/audio-init-parser.d.ts.map +1 -0
  11. package/dist/audio-init-parser.js +118 -0
  12. package/dist/audio-init-parser.js.map +7 -0
  13. package/dist/audio-ring-buffer.d.ts +50 -0
  14. package/dist/audio-ring-buffer.d.ts.map +1 -0
  15. package/dist/audio-ring-buffer.js +80 -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/block-policy.d.ts +314 -0
  22. package/dist/block-policy.d.ts.map +1 -0
  23. package/dist/block-policy.js +466 -0
  24. package/dist/block-policy.js.map +7 -0
  25. package/dist/browser-runtime.js +26549 -0
  26. package/dist/browser-runtime.js.map +7 -0
  27. package/dist/carp-catalog.d.ts +429 -0
  28. package/dist/carp-catalog.d.ts.map +1 -0
  29. package/dist/carp-catalog.js +985 -0
  30. package/dist/carp-catalog.js.map +7 -0
  31. package/dist/catalog-fec-params.d.ts +275 -0
  32. package/dist/catalog-fec-params.d.ts.map +1 -0
  33. package/dist/catalog-fec-params.js +702 -0
  34. package/dist/catalog-fec-params.js.map +7 -0
  35. package/dist/catalog-network-source.d.ts +42 -0
  36. package/dist/catalog-network-source.d.ts.map +1 -0
  37. package/dist/catalog-network-source.js +47 -0
  38. package/dist/catalog-network-source.js.map +7 -0
  39. package/dist/catalog-validator.d.ts +18 -0
  40. package/dist/catalog-validator.d.ts.map +1 -0
  41. package/dist/catalog-validator.js +7747 -0
  42. package/dist/catalog-validator.js.map +7 -0
  43. package/dist/cmaf-block-assembler.d.ts +285 -0
  44. package/dist/cmaf-block-assembler.d.ts.map +1 -0
  45. package/dist/cmaf-block-assembler.js +1966 -0
  46. package/dist/cmaf-block-assembler.js.map +7 -0
  47. package/dist/cmaf-group-batcher.d.ts +38 -0
  48. package/dist/cmaf-group-batcher.d.ts.map +1 -0
  49. package/dist/cmaf-group-batcher.js +176 -0
  50. package/dist/cmaf-group-batcher.js.map +7 -0
  51. package/dist/cmcd-producer.d.ts +104 -0
  52. package/dist/cmcd-producer.d.ts.map +1 -0
  53. package/dist/cmcd-producer.js +183 -0
  54. package/dist/cmcd-producer.js.map +7 -0
  55. package/dist/codec-utils.d.ts +384 -0
  56. package/dist/codec-utils.d.ts.map +1 -0
  57. package/dist/codec-utils.js +1546 -0
  58. package/dist/codec-utils.js.map +7 -0
  59. package/dist/conceal-timeline.d.ts +146 -0
  60. package/dist/conceal-timeline.d.ts.map +1 -0
  61. package/dist/conceal-timeline.js +137 -0
  62. package/dist/conceal-timeline.js.map +7 -0
  63. package/dist/delivery-gap.d.ts +75 -0
  64. package/dist/delivery-gap.d.ts.map +1 -0
  65. package/dist/delivery-gap.js +88 -0
  66. package/dist/delivery-gap.js.map +7 -0
  67. package/dist/duplicate-ledger.d.ts +197 -0
  68. package/dist/duplicate-ledger.d.ts.map +1 -0
  69. package/dist/duplicate-ledger.js +226 -0
  70. package/dist/duplicate-ledger.js.map +7 -0
  71. package/dist/fec-codec.d.ts +32 -0
  72. package/dist/fec-codec.d.ts.map +1 -0
  73. package/dist/fec-codec.js +47 -0
  74. package/dist/fec-codec.js.map +7 -0
  75. package/dist/fec-manager.d.ts +583 -0
  76. package/dist/fec-manager.d.ts.map +1 -0
  77. package/dist/fec-manager.js +3899 -0
  78. package/dist/fec-manager.js.map +7 -0
  79. package/dist/fec-starvation-detector.d.ts +59 -0
  80. package/dist/fec-starvation-detector.d.ts.map +1 -0
  81. package/dist/fec-starvation-detector.js +60 -0
  82. package/dist/fec-starvation-detector.js.map +7 -0
  83. package/dist/fec-state.d.ts +131 -0
  84. package/dist/fec-state.d.ts.map +1 -0
  85. package/dist/fec-state.js +87 -0
  86. package/dist/fec-state.js.map +7 -0
  87. package/dist/fec-worker-setup.d.ts +94 -0
  88. package/dist/fec-worker-setup.d.ts.map +1 -0
  89. package/dist/fec-worker-setup.js +673 -0
  90. package/dist/fec-worker-setup.js.map +7 -0
  91. package/dist/fmp4-completion.d.ts +30 -0
  92. package/dist/fmp4-completion.d.ts.map +1 -0
  93. package/dist/fmp4-completion.js +21 -0
  94. package/dist/fmp4-completion.js.map +7 -0
  95. package/dist/fmp4-reader.d.ts +37 -0
  96. package/dist/fmp4-reader.d.ts.map +1 -0
  97. package/dist/fmp4-reader.js +100 -0
  98. package/dist/fmp4-reader.js.map +7 -0
  99. package/dist/index.d.ts +44 -3
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +460 -14
  102. package/dist/index.js.map +7 -1
  103. package/dist/iso-block-locator.d.ts +118 -0
  104. package/dist/iso-block-locator.d.ts.map +1 -0
  105. package/dist/iso-block-locator.js +289 -0
  106. package/dist/iso-block-locator.js.map +7 -0
  107. package/dist/iso-bmff-timing.d.ts +78 -0
  108. package/dist/iso-bmff-timing.d.ts.map +1 -0
  109. package/dist/iso-bmff-timing.js +593 -0
  110. package/dist/iso-bmff-timing.js.map +7 -0
  111. package/dist/loc.d.ts +70 -75
  112. package/dist/loc.d.ts.map +1 -1
  113. package/dist/loc.js +106 -192
  114. package/dist/loc.js.map +7 -1
  115. package/dist/manifest-auth-plan.d.ts +30 -0
  116. package/dist/manifest-auth-plan.d.ts.map +1 -0
  117. package/dist/manifest-auth-plan.js +1266 -0
  118. package/dist/manifest-auth-plan.js.map +7 -0
  119. package/dist/media-router.d.ts +262 -0
  120. package/dist/media-router.d.ts.map +1 -0
  121. package/dist/media-router.js +584 -0
  122. package/dist/media-router.js.map +7 -0
  123. package/dist/mfu-reassembler.d.ts +313 -9
  124. package/dist/mfu-reassembler.d.ts.map +1 -1
  125. package/dist/mfu-reassembler.js +3051 -285
  126. package/dist/mfu-reassembler.js.map +7 -1
  127. package/dist/mfu-reassembly-policy.d.ts +24 -0
  128. package/dist/mfu-reassembly-policy.d.ts.map +1 -0
  129. package/dist/mfu-reassembly-policy.js +99 -0
  130. package/dist/mfu-reassembly-policy.js.map +7 -0
  131. package/dist/mmtp-container-parser.d.ts +568 -0
  132. package/dist/mmtp-container-parser.d.ts.map +1 -0
  133. package/dist/mmtp-container-parser.js +6580 -0
  134. package/dist/mmtp-container-parser.js.map +7 -0
  135. package/dist/mmtp-container-parser.no-modules.js +9616 -0
  136. package/dist/mmtp-container-parser.no-modules.js.map +7 -0
  137. package/dist/mmtp-decode-pipeline.d.ts +138 -0
  138. package/dist/mmtp-decode-pipeline.d.ts.map +1 -0
  139. package/dist/mmtp-decode-pipeline.js +6814 -0
  140. package/dist/mmtp-decode-pipeline.js.map +7 -0
  141. package/dist/mmtp-router.d.ts +679 -0
  142. package/dist/mmtp-router.d.ts.map +1 -0
  143. package/dist/mmtp-router.js +11278 -0
  144. package/dist/mmtp-router.js.map +7 -0
  145. package/dist/mmtp-to-cmaf.d.ts +187 -0
  146. package/dist/mmtp-to-cmaf.d.ts.map +1 -0
  147. package/dist/mmtp-to-cmaf.js +716 -0
  148. package/dist/mmtp-to-cmaf.js.map +7 -0
  149. package/dist/multicast-auth-catalog.d.ts +115 -0
  150. package/dist/multicast-auth-catalog.d.ts.map +1 -0
  151. package/dist/multicast-auth-catalog.js +202 -0
  152. package/dist/multicast-auth-catalog.js.map +7 -0
  153. package/dist/multicast-input.d.ts +141 -0
  154. package/dist/multicast-input.d.ts.map +1 -0
  155. package/dist/multicast-input.js +390 -0
  156. package/dist/multicast-input.js.map +7 -0
  157. package/dist/multicast-manager.d.ts +669 -0
  158. package/dist/multicast-manager.d.ts.map +1 -0
  159. package/dist/multicast-manager.js +19101 -0
  160. package/dist/multicast-manager.js.map +7 -0
  161. package/dist/multicast-transport.d.ts +271 -0
  162. package/dist/multicast-transport.d.ts.map +1 -0
  163. package/dist/multicast-transport.js +2264 -0
  164. package/dist/multicast-transport.js.map +7 -0
  165. package/dist/multicast-unwrap.d.ts +57 -0
  166. package/dist/multicast-unwrap.d.ts.map +1 -0
  167. package/dist/multicast-unwrap.js +418 -0
  168. package/dist/multicast-unwrap.js.map +7 -0
  169. package/dist/no-modules.d.ts +10 -0
  170. package/dist/no-modules.d.ts.map +1 -0
  171. package/dist/no-modules.js +9491 -0
  172. package/dist/no-modules.js.map +7 -0
  173. package/dist/phase14-catalog-override.d.ts +126 -0
  174. package/dist/phase14-catalog-override.d.ts.map +1 -0
  175. package/dist/phase14-catalog-override.js +155 -0
  176. package/dist/phase14-catalog-override.js.map +7 -0
  177. package/dist/repair-fetch.d.ts +270 -0
  178. package/dist/repair-fetch.d.ts.map +1 -0
  179. package/dist/repair-fetch.js +381 -0
  180. package/dist/repair-fetch.js.map +7 -0
  181. package/dist/repair-layers.d.ts +158 -0
  182. package/dist/repair-layers.d.ts.map +1 -0
  183. package/dist/repair-layers.js +344 -0
  184. package/dist/repair-layers.js.map +7 -0
  185. package/dist/sample-combiner.d.ts +212 -0
  186. package/dist/sample-combiner.d.ts.map +1 -0
  187. package/dist/sample-combiner.js +595 -0
  188. package/dist/sample-combiner.js.map +7 -0
  189. package/dist/signaling.d.ts +29 -0
  190. package/dist/signaling.d.ts.map +1 -0
  191. package/dist/signaling.js +572 -0
  192. package/dist/signaling.js.map +7 -0
  193. package/dist/source-auth.d.ts +2 -0
  194. package/dist/source-auth.d.ts.map +1 -0
  195. package/dist/source-auth.js +1209 -0
  196. package/dist/source-auth.js.map +7 -0
  197. package/dist/switching-set-alignment.d.ts +61 -0
  198. package/dist/switching-set-alignment.d.ts.map +1 -0
  199. package/dist/switching-set-alignment.js +70 -0
  200. package/dist/switching-set-alignment.js.map +7 -0
  201. package/dist/timestamp-codec.d.ts +34 -0
  202. package/dist/timestamp-codec.d.ts.map +1 -0
  203. package/dist/timestamp-codec.js +473 -0
  204. package/dist/timestamp-codec.js.map +7 -0
  205. package/dist/track-pipeline-setup.d.ts +83 -0
  206. package/dist/track-pipeline-setup.d.ts.map +1 -0
  207. package/dist/track-pipeline-setup.js +291 -0
  208. package/dist/track-pipeline-setup.js.map +7 -0
  209. package/dist/track-pipeline.d.ts +121 -0
  210. package/dist/track-pipeline.d.ts.map +1 -0
  211. package/dist/track-pipeline.js +209 -0
  212. package/dist/track-pipeline.js.map +7 -0
  213. package/dist/track-session.d.ts +414 -0
  214. package/dist/track-session.d.ts.map +1 -0
  215. package/dist/track-session.js +754 -0
  216. package/dist/track-session.js.map +7 -0
  217. package/dist/transport-pool.d.ts +69 -0
  218. package/dist/transport-pool.d.ts.map +1 -0
  219. package/dist/transport-pool.js +74 -0
  220. package/dist/transport-pool.js.map +7 -0
  221. package/dist/wasm-mfu-reassembler.d.ts +97 -0
  222. package/dist/wasm-mfu-reassembler.d.ts.map +1 -0
  223. package/dist/wasm-mfu-reassembler.js +2133 -0
  224. package/dist/wasm-mfu-reassembler.js.map +7 -0
  225. package/dist/wasm-mmtp-container-parser.d.ts +10 -0
  226. package/dist/wasm-mmtp-container-parser.d.ts.map +1 -0
  227. package/dist/wasm-mmtp-container-parser.js +6834 -0
  228. package/dist/wasm-mmtp-container-parser.js.map +7 -0
  229. package/node_modules/@blockcast/fec-worker/README.md +223 -0
  230. package/node_modules/@blockcast/fec-worker/dist/.build-stamp +0 -0
  231. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts +38 -0
  232. package/node_modules/@blockcast/fec-worker/dist/block-order.d.ts.map +1 -0
  233. package/node_modules/@blockcast/fec-worker/dist/block-order.js +78 -0
  234. package/node_modules/@blockcast/fec-worker/dist/block-order.js.map +1 -0
  235. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts +93 -0
  236. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.d.ts.map +1 -0
  237. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js +346 -0
  238. package/node_modules/@blockcast/fec-worker/dist/fec-worker-client.js.map +1 -0
  239. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts +380 -0
  240. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.d.ts.map +1 -0
  241. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js +11 -0
  242. package/node_modules/@blockcast/fec-worker/dist/fec-worker-types.js.map +1 -0
  243. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts +14 -0
  244. package/node_modules/@blockcast/fec-worker/dist/fec-worker.d.ts.map +1 -0
  245. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js +859 -0
  246. package/node_modules/@blockcast/fec-worker/dist/fec-worker.js.map +7 -0
  247. package/node_modules/@blockcast/fec-worker/dist/index.d.ts +10 -0
  248. package/node_modules/@blockcast/fec-worker/dist/index.d.ts.map +1 -0
  249. package/node_modules/@blockcast/fec-worker/dist/index.js +8 -0
  250. package/node_modules/@blockcast/fec-worker/dist/index.js.map +1 -0
  251. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts +20 -0
  252. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.d.ts.map +1 -0
  253. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js +370 -0
  254. package/node_modules/@blockcast/fec-worker/dist/shred-fec-worker.js.map +7 -0
  255. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts +104 -0
  256. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.d.ts.map +1 -0
  257. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js +181 -0
  258. package/node_modules/@blockcast/fec-worker/dist/shred-worker-client.js.map +1 -0
  259. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts +194 -0
  260. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.d.ts.map +1 -0
  261. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js +62 -0
  262. package/node_modules/@blockcast/fec-worker/dist/shred-worker-types.js.map +1 -0
  263. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts +9 -0
  264. package/node_modules/@blockcast/fec-worker/dist/transfer.d.ts.map +1 -0
  265. package/node_modules/@blockcast/fec-worker/dist/transfer.js +13 -0
  266. package/node_modules/@blockcast/fec-worker/dist/transfer.js.map +1 -0
  267. package/node_modules/@blockcast/fec-worker/package.json +59 -0
  268. package/node_modules/@blockcast/fec-worker/src/block-order.ts +93 -0
  269. package/node_modules/@blockcast/fec-worker/src/fec-worker-client.ts +392 -0
  270. package/node_modules/@blockcast/fec-worker/src/fec-worker-types.ts +399 -0
  271. package/node_modules/@blockcast/fec-worker/src/fec-worker.ts +1502 -0
  272. package/node_modules/@blockcast/fec-worker/src/index.ts +35 -0
  273. package/node_modules/@blockcast/fec-worker/src/shred-fec-worker.ts +589 -0
  274. package/node_modules/@blockcast/fec-worker/src/shred-worker-client.ts +222 -0
  275. package/node_modules/@blockcast/fec-worker/src/shred-worker-types.ts +209 -0
  276. package/node_modules/@blockcast/fec-worker/src/transfer.ts +12 -0
  277. package/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  278. package/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  279. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  280. package/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  281. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  282. package/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  283. package/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  284. package/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  285. package/node_modules/@blockcast/mmt-base/README.md +134 -0
  286. package/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  287. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
  288. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
  289. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
  290. package/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
  291. package/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
  292. package/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  293. package/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
  294. package/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  295. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
  296. package/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  297. package/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
  298. package/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  299. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
  300. package/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
  301. package/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
  302. package/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
  303. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
  304. package/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
  305. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
  306. package/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
  307. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
  308. package/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
  309. package/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
  310. package/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
  311. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
  312. package/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
  313. package/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
  314. package/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
  315. package/node_modules/@blockcast/mmt-base/package.json +49 -0
  316. package/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
  317. package/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
  318. package/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
  319. package/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
  320. package/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
  321. package/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
  322. package/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
  323. package/node_modules/@blockcast/mmt-fec/README.md +271 -0
  324. package/node_modules/@blockcast/mmt-fec/dist/.build-stamp +0 -0
  325. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts +82 -0
  326. package/node_modules/@blockcast/mmt-fec/dist/block-manager.d.ts.map +1 -0
  327. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js +296 -0
  328. package/node_modules/@blockcast/mmt-fec/dist/block-manager.js.map +1 -0
  329. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts +106 -0
  330. package/node_modules/@blockcast/mmt-fec/dist/block-processor.d.ts.map +1 -0
  331. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js +288 -0
  332. package/node_modules/@blockcast/mmt-fec/dist/block-processor.js.map +1 -0
  333. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts +46 -0
  334. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.d.ts.map +1 -0
  335. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js +96 -0
  336. package/node_modules/@blockcast/mmt-fec/dist/cmaf-recovery.js.map +1 -0
  337. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts +33 -0
  338. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.d.ts.map +1 -0
  339. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js +57 -0
  340. package/node_modules/@blockcast/mmt-fec/dist/cmaf-symbols.js.map +1 -0
  341. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts +205 -0
  342. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.d.ts.map +1 -0
  343. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js +240 -0
  344. package/node_modules/@blockcast/mmt-fec/dist/fec-block-mapper.js.map +1 -0
  345. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts +28 -0
  346. package/node_modules/@blockcast/mmt-fec/dist/index.d.ts.map +1 -0
  347. package/node_modules/@blockcast/mmt-fec/dist/index.js +28 -0
  348. package/node_modules/@blockcast/mmt-fec/dist/index.js.map +1 -0
  349. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts +20 -0
  350. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.d.ts.map +1 -0
  351. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js +28 -0
  352. package/node_modules/@blockcast/mmt-fec/dist/loc-symbols.js.map +1 -0
  353. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts +258 -0
  354. package/node_modules/@blockcast/mmt-fec/dist/oti.d.ts.map +1 -0
  355. package/node_modules/@blockcast/mmt-fec/dist/oti.js +382 -0
  356. package/node_modules/@blockcast/mmt-fec/dist/oti.js.map +1 -0
  357. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts +41 -0
  358. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.d.ts.map +1 -0
  359. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js +81 -0
  360. package/node_modules/@blockcast/mmt-fec/dist/reorder-buffer.js.map +1 -0
  361. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts +82 -0
  362. package/node_modules/@blockcast/mmt-fec/dist/repair-group.d.ts.map +1 -0
  363. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js +170 -0
  364. package/node_modules/@blockcast/mmt-fec/dist/repair-group.js.map +1 -0
  365. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/README.md +181 -0
  366. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/.build-stamp +0 -0
  367. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts +57 -0
  368. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.d.ts.map +1 -0
  369. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js +115 -0
  370. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/dist/index.js.map +1 -0
  371. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/package.json +50 -0
  372. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-alta-parse/src/index.ts +136 -0
  373. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/README.md +134 -0
  374. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/.build-stamp +0 -0
  375. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts +55 -0
  376. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.d.ts.map +1 -0
  377. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js +128 -0
  378. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/fec-block-profile.js.map +1 -0
  379. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts +7 -0
  380. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.d.ts.map +1 -0
  381. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js +7 -0
  382. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/index.js.map +1 -0
  383. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts +292 -0
  384. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.d.ts.map +1 -0
  385. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js +612 -0
  386. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/mmtp.js.map +1 -0
  387. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts +18 -0
  388. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.d.ts.map +1 -0
  389. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js +18 -0
  390. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/net-constants.js.map +1 -0
  391. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts +47 -0
  392. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.d.ts.map +1 -0
  393. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js +54 -0
  394. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/ntp-short.js.map +1 -0
  395. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts +57 -0
  396. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.d.ts.map +1 -0
  397. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js +98 -0
  398. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/serial-number.js.map +1 -0
  399. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts +312 -0
  400. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.d.ts.map +1 -0
  401. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js +637 -0
  402. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/dist/signalling.js.map +1 -0
  403. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/package.json +49 -0
  404. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/fec-block-profile.ts +145 -0
  405. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/index.ts +6 -0
  406. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/mmtp.ts +892 -0
  407. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/net-constants.ts +17 -0
  408. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/ntp-short.ts +67 -0
  409. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/serial-number.ts +107 -0
  410. package/node_modules/@blockcast/mmt-fec/node_modules/@blockcast/mmt-base/src/signalling.ts +862 -0
  411. package/node_modules/@blockcast/mmt-fec/package.json +79 -0
  412. package/node_modules/@blockcast/mmt-fec/src/block-manager.ts +371 -0
  413. package/node_modules/@blockcast/mmt-fec/src/block-processor.ts +439 -0
  414. package/node_modules/@blockcast/mmt-fec/src/cmaf-recovery.ts +127 -0
  415. package/node_modules/@blockcast/mmt-fec/src/cmaf-symbols.ts +68 -0
  416. package/node_modules/@blockcast/mmt-fec/src/fec-block-mapper.ts +310 -0
  417. package/node_modules/@blockcast/mmt-fec/src/index.ts +94 -0
  418. package/node_modules/@blockcast/mmt-fec/src/loc-symbols.ts +34 -0
  419. package/node_modules/@blockcast/mmt-fec/src/oti.ts +463 -0
  420. package/node_modules/@blockcast/mmt-fec/src/reorder-buffer.ts +101 -0
  421. package/node_modules/@blockcast/mmt-fec/src/repair-group.ts +195 -0
  422. package/node_modules/@blockcast/mmt-manifest-verify/dist/.build-stamp +0 -0
  423. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts +28 -0
  424. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.d.ts.map +1 -0
  425. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js +128 -0
  426. package/node_modules/@blockcast/mmt-manifest-verify/dist/admission.js.map +1 -0
  427. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts +4 -0
  428. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.d.ts.map +1 -0
  429. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js +4 -0
  430. package/node_modules/@blockcast/mmt-manifest-verify/dist/index.js.map +1 -0
  431. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts +37 -0
  432. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.d.ts.map +1 -0
  433. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js +179 -0
  434. package/node_modules/@blockcast/mmt-manifest-verify/dist/source-auth.js.map +1 -0
  435. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts +99 -0
  436. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.d.ts.map +1 -0
  437. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js +492 -0
  438. package/node_modules/@blockcast/mmt-manifest-verify/dist/verifier.js.map +1 -0
  439. package/node_modules/@blockcast/mmt-manifest-verify/package.json +45 -0
  440. package/node_modules/@blockcast/mmt-manifest-verify/src/admission.ts +171 -0
  441. package/node_modules/@blockcast/mmt-manifest-verify/src/index.ts +3 -0
  442. package/node_modules/@blockcast/mmt-manifest-verify/src/source-auth.ts +230 -0
  443. package/node_modules/@blockcast/mmt-manifest-verify/src/verifier.ts +669 -0
  444. package/node_modules/@noble/hashes/LICENSE +21 -0
  445. package/node_modules/@noble/hashes/README.md +521 -0
  446. package/node_modules/@noble/hashes/_assert.d.ts +17 -0
  447. package/node_modules/@noble/hashes/_assert.d.ts.map +1 -0
  448. package/node_modules/@noble/hashes/_assert.js +18 -0
  449. package/node_modules/@noble/hashes/_assert.js.map +1 -0
  450. package/node_modules/@noble/hashes/_blake.d.ts +14 -0
  451. package/node_modules/@noble/hashes/_blake.d.ts.map +1 -0
  452. package/node_modules/@noble/hashes/_blake.js +50 -0
  453. package/node_modules/@noble/hashes/_blake.js.map +1 -0
  454. package/node_modules/@noble/hashes/_md.d.ts +51 -0
  455. package/node_modules/@noble/hashes/_md.d.ts.map +1 -0
  456. package/node_modules/@noble/hashes/_md.js +162 -0
  457. package/node_modules/@noble/hashes/_md.js.map +1 -0
  458. package/node_modules/@noble/hashes/_u64.d.ts +55 -0
  459. package/node_modules/@noble/hashes/_u64.d.ts.map +1 -0
  460. package/node_modules/@noble/hashes/_u64.js +90 -0
  461. package/node_modules/@noble/hashes/_u64.js.map +1 -0
  462. package/node_modules/@noble/hashes/argon2.d.ts +32 -0
  463. package/node_modules/@noble/hashes/argon2.d.ts.map +1 -0
  464. package/node_modules/@noble/hashes/argon2.js +401 -0
  465. package/node_modules/@noble/hashes/argon2.js.map +1 -0
  466. package/node_modules/@noble/hashes/blake1.d.ts +106 -0
  467. package/node_modules/@noble/hashes/blake1.d.ts.map +1 -0
  468. package/node_modules/@noble/hashes/blake1.js +459 -0
  469. package/node_modules/@noble/hashes/blake1.js.map +1 -0
  470. package/node_modules/@noble/hashes/blake2.d.ts +116 -0
  471. package/node_modules/@noble/hashes/blake2.d.ts.map +1 -0
  472. package/node_modules/@noble/hashes/blake2.js +420 -0
  473. package/node_modules/@noble/hashes/blake2.js.map +1 -0
  474. package/node_modules/@noble/hashes/blake2b.d.ts +11 -0
  475. package/node_modules/@noble/hashes/blake2b.d.ts.map +1 -0
  476. package/node_modules/@noble/hashes/blake2b.js +14 -0
  477. package/node_modules/@noble/hashes/blake2b.js.map +1 -0
  478. package/node_modules/@noble/hashes/blake2s.d.ts +20 -0
  479. package/node_modules/@noble/hashes/blake2s.d.ts.map +1 -0
  480. package/node_modules/@noble/hashes/blake2s.js +24 -0
  481. package/node_modules/@noble/hashes/blake2s.js.map +1 -0
  482. package/node_modules/@noble/hashes/blake3.d.ts +54 -0
  483. package/node_modules/@noble/hashes/blake3.d.ts.map +1 -0
  484. package/node_modules/@noble/hashes/blake3.js +255 -0
  485. package/node_modules/@noble/hashes/blake3.js.map +1 -0
  486. package/node_modules/@noble/hashes/crypto.d.ts +2 -0
  487. package/node_modules/@noble/hashes/crypto.d.ts.map +1 -0
  488. package/node_modules/@noble/hashes/crypto.js +5 -0
  489. package/node_modules/@noble/hashes/crypto.js.map +1 -0
  490. package/node_modules/@noble/hashes/cryptoNode.d.ts +2 -0
  491. package/node_modules/@noble/hashes/cryptoNode.d.ts.map +1 -0
  492. package/node_modules/@noble/hashes/cryptoNode.js +18 -0
  493. package/node_modules/@noble/hashes/cryptoNode.js.map +1 -0
  494. package/node_modules/@noble/hashes/eskdf.d.ts +47 -0
  495. package/node_modules/@noble/hashes/eskdf.d.ts.map +1 -0
  496. package/node_modules/@noble/hashes/eskdf.js +166 -0
  497. package/node_modules/@noble/hashes/eskdf.js.map +1 -0
  498. package/node_modules/@noble/hashes/esm/_assert.d.ts +17 -0
  499. package/node_modules/@noble/hashes/esm/_assert.d.ts.map +1 -0
  500. package/node_modules/@noble/hashes/esm/_assert.js +15 -0
  501. package/node_modules/@noble/hashes/esm/_assert.js.map +1 -0
  502. package/node_modules/@noble/hashes/esm/_blake.d.ts +14 -0
  503. package/node_modules/@noble/hashes/esm/_blake.d.ts.map +1 -0
  504. package/node_modules/@noble/hashes/esm/_blake.js +45 -0
  505. package/node_modules/@noble/hashes/esm/_blake.js.map +1 -0
  506. package/node_modules/@noble/hashes/esm/_md.d.ts +51 -0
  507. package/node_modules/@noble/hashes/esm/_md.d.ts.map +1 -0
  508. package/node_modules/@noble/hashes/esm/_md.js +155 -0
  509. package/node_modules/@noble/hashes/esm/_md.js.map +1 -0
  510. package/node_modules/@noble/hashes/esm/_u64.d.ts +55 -0
  511. package/node_modules/@noble/hashes/esm/_u64.d.ts.map +1 -0
  512. package/node_modules/@noble/hashes/esm/_u64.js +67 -0
  513. package/node_modules/@noble/hashes/esm/_u64.js.map +1 -0
  514. package/node_modules/@noble/hashes/esm/argon2.d.ts +32 -0
  515. package/node_modules/@noble/hashes/esm/argon2.d.ts.map +1 -0
  516. package/node_modules/@noble/hashes/esm/argon2.js +392 -0
  517. package/node_modules/@noble/hashes/esm/argon2.js.map +1 -0
  518. package/node_modules/@noble/hashes/esm/blake1.d.ts +106 -0
  519. package/node_modules/@noble/hashes/esm/blake1.d.ts.map +1 -0
  520. package/node_modules/@noble/hashes/esm/blake1.js +452 -0
  521. package/node_modules/@noble/hashes/esm/blake1.js.map +1 -0
  522. package/node_modules/@noble/hashes/esm/blake2.d.ts +116 -0
  523. package/node_modules/@noble/hashes/esm/blake2.d.ts.map +1 -0
  524. package/node_modules/@noble/hashes/esm/blake2.js +413 -0
  525. package/node_modules/@noble/hashes/esm/blake2.js.map +1 -0
  526. package/node_modules/@noble/hashes/esm/blake2b.d.ts +11 -0
  527. package/node_modules/@noble/hashes/esm/blake2b.d.ts.map +1 -0
  528. package/node_modules/@noble/hashes/esm/blake2b.js +11 -0
  529. package/node_modules/@noble/hashes/esm/blake2b.js.map +1 -0
  530. package/node_modules/@noble/hashes/esm/blake2s.d.ts +20 -0
  531. package/node_modules/@noble/hashes/esm/blake2s.d.ts.map +1 -0
  532. package/node_modules/@noble/hashes/esm/blake2s.js +21 -0
  533. package/node_modules/@noble/hashes/esm/blake2s.js.map +1 -0
  534. package/node_modules/@noble/hashes/esm/blake3.d.ts +54 -0
  535. package/node_modules/@noble/hashes/esm/blake3.d.ts.map +1 -0
  536. package/node_modules/@noble/hashes/esm/blake3.js +251 -0
  537. package/node_modules/@noble/hashes/esm/blake3.js.map +1 -0
  538. package/node_modules/@noble/hashes/esm/crypto.d.ts +2 -0
  539. package/node_modules/@noble/hashes/esm/crypto.d.ts.map +1 -0
  540. package/node_modules/@noble/hashes/esm/crypto.js +2 -0
  541. package/node_modules/@noble/hashes/esm/crypto.js.map +1 -0
  542. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts +2 -0
  543. package/node_modules/@noble/hashes/esm/cryptoNode.d.ts.map +1 -0
  544. package/node_modules/@noble/hashes/esm/cryptoNode.js +15 -0
  545. package/node_modules/@noble/hashes/esm/cryptoNode.js.map +1 -0
  546. package/node_modules/@noble/hashes/esm/eskdf.d.ts +47 -0
  547. package/node_modules/@noble/hashes/esm/eskdf.d.ts.map +1 -0
  548. package/node_modules/@noble/hashes/esm/eskdf.js +160 -0
  549. package/node_modules/@noble/hashes/esm/eskdf.js.map +1 -0
  550. package/node_modules/@noble/hashes/esm/hkdf.d.ts +36 -0
  551. package/node_modules/@noble/hashes/esm/hkdf.d.ts.map +1 -0
  552. package/node_modules/@noble/hashes/esm/hkdf.js +82 -0
  553. package/node_modules/@noble/hashes/esm/hkdf.js.map +1 -0
  554. package/node_modules/@noble/hashes/esm/hmac.d.ts +35 -0
  555. package/node_modules/@noble/hashes/esm/hmac.d.ts.map +1 -0
  556. package/node_modules/@noble/hashes/esm/hmac.js +86 -0
  557. package/node_modules/@noble/hashes/esm/hmac.js.map +1 -0
  558. package/node_modules/@noble/hashes/esm/index.d.ts +2 -0
  559. package/node_modules/@noble/hashes/esm/index.d.ts.map +1 -0
  560. package/node_modules/@noble/hashes/esm/index.js +33 -0
  561. package/node_modules/@noble/hashes/esm/index.js.map +1 -0
  562. package/node_modules/@noble/hashes/esm/legacy.d.ts +71 -0
  563. package/node_modules/@noble/hashes/esm/legacy.d.ts.map +1 -0
  564. package/node_modules/@noble/hashes/esm/legacy.js +281 -0
  565. package/node_modules/@noble/hashes/esm/legacy.js.map +1 -0
  566. package/node_modules/@noble/hashes/esm/package.json +10 -0
  567. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts +23 -0
  568. package/node_modules/@noble/hashes/esm/pbkdf2.d.ts.map +1 -0
  569. package/node_modules/@noble/hashes/esm/pbkdf2.js +97 -0
  570. package/node_modules/@noble/hashes/esm/pbkdf2.js.map +1 -0
  571. package/node_modules/@noble/hashes/esm/ripemd160.d.ts +13 -0
  572. package/node_modules/@noble/hashes/esm/ripemd160.d.ts.map +1 -0
  573. package/node_modules/@noble/hashes/esm/ripemd160.js +13 -0
  574. package/node_modules/@noble/hashes/esm/ripemd160.js.map +1 -0
  575. package/node_modules/@noble/hashes/esm/scrypt.d.ts +34 -0
  576. package/node_modules/@noble/hashes/esm/scrypt.d.ts.map +1 -0
  577. package/node_modules/@noble/hashes/esm/scrypt.js +228 -0
  578. package/node_modules/@noble/hashes/esm/scrypt.js.map +1 -0
  579. package/node_modules/@noble/hashes/esm/sha1.d.ts +11 -0
  580. package/node_modules/@noble/hashes/esm/sha1.d.ts.map +1 -0
  581. package/node_modules/@noble/hashes/esm/sha1.js +11 -0
  582. package/node_modules/@noble/hashes/esm/sha1.js.map +1 -0
  583. package/node_modules/@noble/hashes/esm/sha2.d.ts +159 -0
  584. package/node_modules/@noble/hashes/esm/sha2.d.ts.map +1 -0
  585. package/node_modules/@noble/hashes/esm/sha2.js +375 -0
  586. package/node_modules/@noble/hashes/esm/sha2.js.map +1 -0
  587. package/node_modules/@noble/hashes/esm/sha256.d.ts +20 -0
  588. package/node_modules/@noble/hashes/esm/sha256.d.ts.map +1 -0
  589. package/node_modules/@noble/hashes/esm/sha256.js +20 -0
  590. package/node_modules/@noble/hashes/esm/sha256.js.map +1 -0
  591. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts +142 -0
  592. package/node_modules/@noble/hashes/esm/sha3-addons.d.ts.map +1 -0
  593. package/node_modules/@noble/hashes/esm/sha3-addons.js +393 -0
  594. package/node_modules/@noble/hashes/esm/sha3-addons.js.map +1 -0
  595. package/node_modules/@noble/hashes/esm/sha3.d.ts +53 -0
  596. package/node_modules/@noble/hashes/esm/sha3.d.ts.map +1 -0
  597. package/node_modules/@noble/hashes/esm/sha3.js +234 -0
  598. package/node_modules/@noble/hashes/esm/sha3.js.map +1 -0
  599. package/node_modules/@noble/hashes/esm/sha512.d.ts +26 -0
  600. package/node_modules/@noble/hashes/esm/sha512.d.ts.map +1 -0
  601. package/node_modules/@noble/hashes/esm/sha512.js +26 -0
  602. package/node_modules/@noble/hashes/esm/sha512.js.map +1 -0
  603. package/node_modules/@noble/hashes/esm/utils.d.ts +161 -0
  604. package/node_modules/@noble/hashes/esm/utils.d.ts.map +1 -0
  605. package/node_modules/@noble/hashes/esm/utils.js +281 -0
  606. package/node_modules/@noble/hashes/esm/utils.js.map +1 -0
  607. package/node_modules/@noble/hashes/hkdf.d.ts +36 -0
  608. package/node_modules/@noble/hashes/hkdf.d.ts.map +1 -0
  609. package/node_modules/@noble/hashes/hkdf.js +88 -0
  610. package/node_modules/@noble/hashes/hkdf.js.map +1 -0
  611. package/node_modules/@noble/hashes/hmac.d.ts +35 -0
  612. package/node_modules/@noble/hashes/hmac.d.ts.map +1 -0
  613. package/node_modules/@noble/hashes/hmac.js +91 -0
  614. package/node_modules/@noble/hashes/hmac.js.map +1 -0
  615. package/node_modules/@noble/hashes/index.d.ts +1 -0
  616. package/node_modules/@noble/hashes/index.d.ts.map +1 -0
  617. package/node_modules/@noble/hashes/index.js +33 -0
  618. package/node_modules/@noble/hashes/index.js.map +1 -0
  619. package/node_modules/@noble/hashes/legacy.d.ts +71 -0
  620. package/node_modules/@noble/hashes/legacy.d.ts.map +1 -0
  621. package/node_modules/@noble/hashes/legacy.js +287 -0
  622. package/node_modules/@noble/hashes/legacy.js.map +1 -0
  623. package/node_modules/@noble/hashes/package.json +266 -0
  624. package/node_modules/@noble/hashes/pbkdf2.d.ts +23 -0
  625. package/node_modules/@noble/hashes/pbkdf2.d.ts.map +1 -0
  626. package/node_modules/@noble/hashes/pbkdf2.js +101 -0
  627. package/node_modules/@noble/hashes/pbkdf2.js.map +1 -0
  628. package/node_modules/@noble/hashes/ripemd160.d.ts +13 -0
  629. package/node_modules/@noble/hashes/ripemd160.d.ts.map +1 -0
  630. package/node_modules/@noble/hashes/ripemd160.js +16 -0
  631. package/node_modules/@noble/hashes/ripemd160.js.map +1 -0
  632. package/node_modules/@noble/hashes/scrypt.d.ts +34 -0
  633. package/node_modules/@noble/hashes/scrypt.d.ts.map +1 -0
  634. package/node_modules/@noble/hashes/scrypt.js +232 -0
  635. package/node_modules/@noble/hashes/scrypt.js.map +1 -0
  636. package/node_modules/@noble/hashes/sha1.d.ts +11 -0
  637. package/node_modules/@noble/hashes/sha1.d.ts.map +1 -0
  638. package/node_modules/@noble/hashes/sha1.js +14 -0
  639. package/node_modules/@noble/hashes/sha1.js.map +1 -0
  640. package/node_modules/@noble/hashes/sha2.d.ts +159 -0
  641. package/node_modules/@noble/hashes/sha2.d.ts.map +1 -0
  642. package/node_modules/@noble/hashes/sha2.js +384 -0
  643. package/node_modules/@noble/hashes/sha2.js.map +1 -0
  644. package/node_modules/@noble/hashes/sha256.d.ts +20 -0
  645. package/node_modules/@noble/hashes/sha256.d.ts.map +1 -0
  646. package/node_modules/@noble/hashes/sha256.js +23 -0
  647. package/node_modules/@noble/hashes/sha256.js.map +1 -0
  648. package/node_modules/@noble/hashes/sha3-addons.d.ts +142 -0
  649. package/node_modules/@noble/hashes/sha3-addons.d.ts.map +1 -0
  650. package/node_modules/@noble/hashes/sha3-addons.js +402 -0
  651. package/node_modules/@noble/hashes/sha3-addons.js.map +1 -0
  652. package/node_modules/@noble/hashes/sha3.d.ts +53 -0
  653. package/node_modules/@noble/hashes/sha3.d.ts.map +1 -0
  654. package/node_modules/@noble/hashes/sha3.js +239 -0
  655. package/node_modules/@noble/hashes/sha3.js.map +1 -0
  656. package/node_modules/@noble/hashes/sha512.d.ts +26 -0
  657. package/node_modules/@noble/hashes/sha512.d.ts.map +1 -0
  658. package/node_modules/@noble/hashes/sha512.js +29 -0
  659. package/node_modules/@noble/hashes/sha512.js.map +1 -0
  660. package/node_modules/@noble/hashes/src/_assert.ts +22 -0
  661. package/node_modules/@noble/hashes/src/_blake.ts +50 -0
  662. package/node_modules/@noble/hashes/src/_md.ts +176 -0
  663. package/node_modules/@noble/hashes/src/_u64.ts +91 -0
  664. package/node_modules/@noble/hashes/src/argon2.ts +497 -0
  665. package/node_modules/@noble/hashes/src/blake1.ts +534 -0
  666. package/node_modules/@noble/hashes/src/blake2.ts +486 -0
  667. package/node_modules/@noble/hashes/src/blake2b.ts +10 -0
  668. package/node_modules/@noble/hashes/src/blake2s.ts +20 -0
  669. package/node_modules/@noble/hashes/src/blake3.ts +272 -0
  670. package/node_modules/@noble/hashes/src/crypto.ts +9 -0
  671. package/node_modules/@noble/hashes/src/cryptoNode.ts +15 -0
  672. package/node_modules/@noble/hashes/src/eskdf.ts +187 -0
  673. package/node_modules/@noble/hashes/src/hkdf.ts +88 -0
  674. package/node_modules/@noble/hashes/src/hmac.ts +94 -0
  675. package/node_modules/@noble/hashes/src/index.ts +31 -0
  676. package/node_modules/@noble/hashes/src/legacy.ts +293 -0
  677. package/node_modules/@noble/hashes/src/pbkdf2.ts +122 -0
  678. package/node_modules/@noble/hashes/src/ripemd160.ts +12 -0
  679. package/node_modules/@noble/hashes/src/scrypt.ts +257 -0
  680. package/node_modules/@noble/hashes/src/sha1.ts +10 -0
  681. package/node_modules/@noble/hashes/src/sha2.ts +402 -0
  682. package/node_modules/@noble/hashes/src/sha256.ts +24 -0
  683. package/node_modules/@noble/hashes/src/sha3-addons.ts +499 -0
  684. package/node_modules/@noble/hashes/src/sha3.ts +258 -0
  685. package/node_modules/@noble/hashes/src/sha512.ts +34 -0
  686. package/node_modules/@noble/hashes/src/utils.ts +395 -0
  687. package/node_modules/@noble/hashes/utils.d.ts +161 -0
  688. package/node_modules/@noble/hashes/utils.d.ts.map +1 -0
  689. package/node_modules/@noble/hashes/utils.js +313 -0
  690. package/node_modules/@noble/hashes/utils.js.map +1 -0
  691. package/package.json +179 -8
  692. package/schemas/catalog.schema.json +357 -0
  693. package/src/align.ts +239 -0
  694. package/src/audio-init-parser.ts +189 -0
  695. package/src/audio-ring-buffer.ts +120 -0
  696. package/src/authored-timing.ts +63 -0
  697. package/src/block-policy.ts +903 -0
  698. package/src/carp-catalog.ts +1065 -0
  699. package/src/catalog-fec-params.ts +729 -0
  700. package/src/catalog-network-source.ts +74 -0
  701. package/src/catalog-validator.ts +34 -0
  702. package/src/cmaf-block-assembler.ts +1255 -0
  703. package/src/cmaf-group-batcher.ts +104 -0
  704. package/src/cmcd-producer.ts +329 -0
  705. package/src/codec-utils.ts +1984 -0
  706. package/src/conceal-timeline.ts +293 -0
  707. package/src/delivery-gap.ts +147 -0
  708. package/src/duplicate-ledger.ts +372 -0
  709. package/src/fec-codec.ts +61 -0
  710. package/src/fec-manager.ts +4298 -0
  711. package/src/fec-starvation-detector.ts +128 -0
  712. package/src/fec-state.ts +219 -0
  713. package/src/fec-worker-setup.ts +283 -0
  714. package/src/fmp4-completion.ts +47 -0
  715. package/src/fmp4-reader.ts +112 -0
  716. package/src/index.ts +590 -21
  717. package/src/iso-block-locator.ts +329 -0
  718. package/src/iso-bmff-timing.ts +813 -0
  719. package/src/loc.ts +128 -154
  720. package/src/manifest-auth-plan.ts +107 -0
  721. package/src/media-router.ts +841 -0
  722. package/src/mfu-reassembler.ts +3480 -155
  723. package/src/mfu-reassembly-policy.ts +134 -0
  724. package/src/mmtp-container-parser.ts +2380 -0
  725. package/src/mmtp-decode-pipeline.ts +406 -0
  726. package/src/mmtp-router.ts +4860 -0
  727. package/src/mmtp-to-cmaf.ts +977 -0
  728. package/src/multicast-auth-catalog.ts +340 -0
  729. package/src/multicast-input.ts +793 -0
  730. package/src/multicast-manager.ts +2783 -0
  731. package/src/multicast-transport.ts +1058 -0
  732. package/src/multicast-unwrap.ts +132 -0
  733. package/src/no-modules.ts +21 -0
  734. package/src/phase14-catalog-override.ts +336 -0
  735. package/src/repair-fetch.ts +772 -0
  736. package/src/repair-layers.ts +533 -0
  737. package/src/sample-combiner.ts +938 -0
  738. package/src/signaling.ts +78 -0
  739. package/src/source-auth.ts +1 -0
  740. package/src/switching-set-alignment.ts +109 -0
  741. package/src/timestamp-codec.ts +265 -0
  742. package/src/track-pipeline-setup.ts +205 -0
  743. package/src/track-pipeline.ts +332 -0
  744. package/src/track-session.ts +1010 -0
  745. package/src/transport-pool.ts +128 -0
  746. package/src/wasm-mfu-reassembler.ts +405 -0
  747. package/src/wasm-mmtp-container-parser.ts +23 -0
  748. package/test-vectors/align.json +199 -0
  749. package/test-vectors/catalog/README.md +106 -0
  750. package/test-vectors/catalog/negative/archive-origin-with-path.json +33 -0
  751. package/test-vectors/catalog/negative/archive-unknown-field.json +34 -0
  752. package/test-vectors/catalog/negative/fec-block-profile-missing.json +51 -0
  753. package/test-vectors/catalog/negative/fec-block-profile-unknown.json +52 -0
  754. package/test-vectors/catalog/negative/fec-enhancement-repair-removed.json +28 -0
  755. package/test-vectors/catalog/negative/fec-repair-layer-geometry.json +33 -0
  756. package/test-vectors/catalog/negative/fec-repair-layer-missing-symbols.json +31 -0
  757. package/test-vectors/catalog/negative/fec-repair-multiple-depends.json +32 -0
  758. package/test-vectors/catalog/negative/fec-repair-overlay-with-layer.json +34 -0
  759. package/test-vectors/catalog/negative/fec-repair-priority-out-of-band.json +24 -0
  760. package/test-vectors/catalog/negative/fec-repair-source-symbols-without-scope.json +33 -0
  761. package/test-vectors/catalog/negative/legacy-selection-params.json +15 -0
  762. package/test-vectors/catalog/negative/media-track-repair-layer.json +16 -0
  763. package/test-vectors/catalog/negative/missing-mmtp-fields.json +12 -0
  764. package/test-vectors/catalog/negative/multicast-auth-padded-key.json +37 -0
  765. package/test-vectors/catalog/negative/multicast-auth-reused-track-format.json +53 -0
  766. package/test-vectors/catalog/negative/multicast-auth-trailing-pad-bits.json +37 -0
  767. package/test-vectors/catalog/negative/multicast-endpoint-missing-protocol-and-source.json +24 -0
  768. package/test-vectors/catalog/negative/multicast-network-source-empty.json +44 -0
  769. package/test-vectors/catalog/negative/multicast-packet-id-signaling.json +26 -0
  770. package/test-vectors/catalog/negative/network-source-object.json +25 -0
  771. package/test-vectors/catalog/negative/raptorq-source-symbols-over-max.json +24 -0
  772. package/test-vectors/catalog/negative/raptorq-unaligned-symbol.json +27 -0
  773. package/test-vectors/catalog/negative/repair-track-legacy-shape.json +19 -0
  774. package/test-vectors/catalog/negative/track-role-empty.json +16 -0
  775. package/test-vectors/catalog/negative/track-role-non-string.json +16 -0
  776. package/test-vectors/catalog/positive/archive-origin.json +34 -0
  777. package/test-vectors/catalog/positive/fec-iso-ssbg1.json +52 -0
  778. package/test-vectors/catalog/positive/fec-layered-repair.json +106 -0
  779. package/test-vectors/catalog/positive/fec-multicast.json +52 -0
  780. package/test-vectors/catalog/positive/flat-av.json +30 -0
  781. package/test-vectors/catalog/positive/multicast-auth-rotation.json +55 -0
  782. package/test-vectors/catalog/positive/multicast-native-ssm-no-network-source.json +43 -0
  783. package/test-vectors/catalog/positive/ticks-and-mixed-timescales.json +39 -0
  784. package/test-vectors/catalog/positive/track-role.json +49 -0
  785. package/test-vectors/catalog/releases/moqcast-native-v16-v1/catalog.json +65 -0
  786. package/test-vectors/catalog/releases/moqcast-native-v16-v1.archive.sha256 +1 -0
  787. package/test-vectors/catalog-multicast-auth-v1.json +26 -0
  788. package/test-vectors/catalog-validation.json +732 -0
  789. package/test-vectors/catalog-validator-public-api.mjs +25 -0
  790. package/test-vectors/generate-source-auth-fixture.mjs +114 -0
  791. package/test-vectors/hevc-x265-repeat-headers-v1.json +40 -0
  792. package/test-vectors/source-auth-v1.json +61 -0
  793. package/test-vectors/source-contiguity-v1.json +131 -0
  794. package/test-vectors/subsample-placement-v1.json +230 -0
  795. package/dist/mmtp.d.ts +0 -73
  796. package/dist/mmtp.d.ts.map +0 -1
  797. package/dist/mmtp.js +0 -210
  798. package/dist/mmtp.js.map +0 -1
  799. package/src/mmtp.ts +0 -277
@@ -0,0 +1,1984 @@
1
+ /**
2
+ * H.264/AVC and H.265/HEVC 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
+ * HEVC keyframe (IRAP) detection, HEVC codec strings and hvcC records, and MMTP
7
+ * timestamp conversion. The video codec family is always selected from the
8
+ * catalog codec string ({@link videoSampleEntryFromCodecString}), never sniffed.
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+
13
+ // ─── Public Types ────────────────────────────────────────────────
14
+
15
+ /** Result of extracting a video codec configuration record from an fMP4 init segment. */
16
+ export interface AvcCInfo {
17
+ codec: string
18
+ description: Uint8Array
19
+ }
20
+
21
+ /** Result of extracting AAC AudioSpecificConfig from an fMP4 init segment. */
22
+ export interface AacAscInfo {
23
+ /** Codec string like "mp4a.40.2" (AAC-LC). Derived from AOT in ASC[0] >> 3. */
24
+ codec: string
25
+ /** AudioSpecificConfig bytes — what AudioDecoder.configure({description}) needs for raw AAC. */
26
+ description: Uint8Array
27
+ }
28
+
29
+ /** SPS and PPS NAL units extracted from an avcC record. */
30
+ export interface SpsPps {
31
+ sps: Uint8Array
32
+ pps: Uint8Array
33
+ }
34
+
35
+ /** Options for fallback avcC generation. */
36
+ export interface FallbackAvcCOptions {
37
+ /** Codec string like "avc1.640033". Profile/constraint/level parsed from hex triplet. */
38
+ codec?: string
39
+ /** Coded width in pixels. Used in SPS pic_width_in_mbs. */
40
+ width?: number
41
+ /** Coded height in pixels. Used in SPS pic_height_in_map_units. */
42
+ height?: number
43
+ }
44
+
45
+ // ─── Public Functions ────────────────────────────────────────────
46
+
47
+ /**
48
+ * Extract the codec configuration record (avcC/hvcC/av1C/vpcC) from
49
+ * an fMP4 init segment (moov box).
50
+ * MMTP FT=0 packets contain a complete fMP4: moov > trak > mdia > minf > stbl > stsd > sample entry > codec config.
51
+ *
52
+ * Walks every trak and returns the first supported video sample entry. This
53
+ * handles both per-track CMSF init segments and multiplexed ftyp+moov blobs
54
+ * where the audio trak may appear before video.
55
+ */
56
+ export function extractAvcCFromInitSegment(data: Uint8Array): AvcCInfo | undefined {
57
+ if (!hasIsoBmffMagic(data)) return undefined
58
+
59
+ for (const stsd of findTrackStsdBoxes(data)) {
60
+ const info = extractVideoConfigFromStsd(data, stsd)
61
+ if (info) return info
62
+ }
63
+
64
+ return undefined
65
+ }
66
+
67
+ function extractVideoConfigFromStsd(data: Uint8Array, stsd: { o: number; sz: number }): AvcCInfo | undefined {
68
+ let s = stsd.o + 16
69
+ const e = stsd.o + stsd.sz
70
+
71
+ // H.264: avc1 VisualSampleEntry → avcC.
72
+ const avc1 = findBox(data, s, e, 'avc1')
73
+ if (avc1 && avc1.sz >= 86) {
74
+ const avcC = findBox(data, avc1.o + 86, avc1.o + avc1.sz, 'avcC')
75
+ if (avcC && avcC.sz >= 12) {
76
+ const d = data.subarray(avcC.o + 8, avcC.o + avcC.sz)
77
+ const codec = `avc1.${hex2(d[1])}${hex2(d[2])}${hex2(d[3])}`
78
+ return { codec, description: d }
79
+ }
80
+ }
81
+ // HEVC: hvc1/hev1 VisualSampleEntry → hvcC. Same +86 child-box offset as
82
+ // avc1 (identical VisualSampleEntry layout). Without this branch, HEVC
83
+ // ftyp+moov inits yield no descriptor, so the WebCodecs path configures the
84
+ // VideoDecoder with no `description` and the first length-prefixed hvc1
85
+ // keyframe decode() throws DataError ("must fill out the description field"),
86
+ // which the player's MoQ pump catch swallows → video track silently 0-fps
87
+ // (BLO-8054).
88
+ // The codec string's first element is the sample entry actually present
89
+ // (ISO/IEC 14496-15 Annex E.3), so an hev1 entry yields "hev1.*".
90
+ for (const sampleEntry of ['hvc1', 'hev1'] as const) {
91
+ const hvc = findBox(data, s, e, sampleEntry)
92
+ if (!hvc || hvc.sz < 86) continue
93
+ const hvcC = findBox(data, hvc.o + 86, hvc.o + hvc.sz, 'hvcC')
94
+ // HEVCDecoderConfigurationRecord is ≥23 bytes (+8 box header).
95
+ if (hvcC && hvcC.sz >= 8 + 23) {
96
+ const d = data.subarray(hvcC.o + 8, hvcC.o + hvcC.sz)
97
+ return { codec: hevcCodecStringFromHvcC(d, sampleEntry), description: d }
98
+ }
99
+ }
100
+ // AV1: av01 VisualSampleEntry -> av1C. Real FFmpeg MMTP inits can carry an
101
+ // empty av1C box (size=8) while still providing the authoritative sample
102
+ // entry/codec signal. Return an empty description for that shape so callers
103
+ // can open their FT=0 gate without passing a zero-byte WebCodecs description.
104
+ const av01 = findBox(data, s, e, 'av01')
105
+ if (av01 && av01.sz >= 86) {
106
+ const av1C = findBox(data, av01.o + 86, av01.o + av01.sz, 'av1C')
107
+ if (av1C && av1C.sz >= 8) {
108
+ const d = data.subarray(av1C.o + 8, av1C.o + av1C.sz)
109
+ return { codec: codecStringFromAv1C(d), description: d }
110
+ }
111
+ }
112
+ // VP9: vp09 VisualSampleEntry -> vpcC. Some encoders omit the VP codec record
113
+ // payload; use the sample entry as readiness evidence and fall back to a
114
+ // conservative vp09 codec string in that case.
115
+ const vp09 = findBox(data, s, e, 'vp09')
116
+ if (vp09 && vp09.sz >= 86) {
117
+ const vpcC = findBox(data, vp09.o + 86, vp09.o + vp09.sz, 'vpcC')
118
+ if (vpcC && vpcC.sz >= 8) {
119
+ const d = data.subarray(vpcC.o + 8, vpcC.o + vpcC.sz)
120
+ return { codec: codecStringFromVpcC(d), description: d }
121
+ }
122
+ }
123
+ return undefined
124
+ }
125
+
126
+ /**
127
+ * Generate a minimal avcC (AVCDecoderConfigurationRecord) from codec parameters.
128
+ * Parses "avc1.XXYYZZ" → profile_idc=0xXX, constraint_flags=0xYY, level_idc=0xZZ.
129
+ * Width/height are encoded in the SPS; if not provided, they're inferred from the
130
+ * H.264 level's max frame size (e.g. level 4.2 → 2048x1088).
131
+ * Used when late MoQ subscribers miss the FT=0 init segment (moov/avcC).
132
+ */
133
+ export function generateFallbackAvcC(options?: FallbackAvcCOptions | string): Uint8Array {
134
+ const opts: FallbackAvcCOptions = typeof options === 'string' ? { codec: options } : (options ?? {})
135
+
136
+ let profile = 0x64
137
+ let constraint = 0x00
138
+ let level = 0x2a
139
+
140
+ if (opts.codec) {
141
+ const match = opts.codec.match(/^avc[13]\.([0-9a-fA-F]{6})$/)
142
+ if (match) {
143
+ profile = Number.parseInt(match[1].substring(0, 2), 16)
144
+ constraint = Number.parseInt(match[1].substring(2, 4), 16)
145
+ level = Number.parseInt(match[1].substring(4, 6), 16)
146
+ }
147
+ }
148
+
149
+ const { width, height } = resolveResolution(opts.width, opts.height, level)
150
+ const sps = buildMinimalSps(profile, constraint, level, width, height)
151
+ const pps = buildMinimalPps(profile)
152
+
153
+ const avcC = new Uint8Array(5 + 1 + 2 + sps.length + 1 + 2 + pps.length)
154
+ let w = 0
155
+ avcC[w++] = 0x01
156
+ avcC[w++] = profile
157
+ avcC[w++] = constraint
158
+ avcC[w++] = level
159
+ avcC[w++] = 0xff
160
+ avcC[w++] = 0xe1
161
+ avcC[w++] = (sps.length >> 8) & 0xff
162
+ avcC[w++] = sps.length & 0xff
163
+ avcC.set(sps, w)
164
+ w += sps.length
165
+ avcC[w++] = 0x01
166
+ avcC[w++] = (pps.length >> 8) & 0xff
167
+ avcC[w++] = pps.length & 0xff
168
+ avcC.set(pps, w)
169
+ return avcC
170
+ }
171
+
172
+ /**
173
+ * Extract raw SPS and PPS NAL unit bytes from an avcC (AVCDecoderConfigurationRecord).
174
+ * Returns the first SPS and first PPS found.
175
+ */
176
+ export function extractSpsPpsFromAvcC(avcC: Uint8Array): SpsPps | undefined {
177
+ if (avcC.length < 8) return undefined
178
+ const numSPS = avcC[5] & 0x1f
179
+ let offset = 6
180
+ let sps: Uint8Array | undefined
181
+ for (let i = 0; i < numSPS && offset + 2 <= avcC.length; i++) {
182
+ const spsLen = (avcC[offset] << 8) | avcC[offset + 1]
183
+ offset += 2
184
+ if (offset + spsLen <= avcC.length) {
185
+ if (!sps) sps = avcC.subarray(offset, offset + spsLen)
186
+ offset += spsLen
187
+ }
188
+ }
189
+ let pps: Uint8Array | undefined
190
+ if (offset < avcC.length) {
191
+ const numPPS = avcC[offset] & 0x1f
192
+ offset++
193
+ for (let i = 0; i < numPPS && offset + 2 <= avcC.length; i++) {
194
+ const ppsLen = (avcC[offset] << 8) | avcC[offset + 1]
195
+ offset += 2
196
+ if (offset + ppsLen <= avcC.length) {
197
+ if (!pps) pps = avcC.subarray(offset, offset + ppsLen)
198
+ offset += ppsLen
199
+ }
200
+ }
201
+ }
202
+ return sps && pps ? { sps, pps } : undefined
203
+ }
204
+
205
+ /**
206
+ * Convert AVCC (4-byte NAL length prefixed) to Annex-B (start code prefixed).
207
+ * Prepends SPS/PPS NALs before keyframes.
208
+ */
209
+ export function avccToAnnexB(avccData: Uint8Array, isKeyframe: boolean, spsPps: SpsPps): Uint8Array {
210
+ let offset = 0
211
+ let totalNalSize = 0
212
+ let nalCount = 0
213
+ while (offset + 4 <= avccData.length) {
214
+ const nalLen =
215
+ (avccData[offset] << 24) |
216
+ (avccData[offset + 1] << 16) |
217
+ (avccData[offset + 2] << 8) |
218
+ avccData[offset + 3]
219
+ offset += 4
220
+ if (nalLen <= 0 || offset + nalLen > avccData.length) break
221
+ totalNalSize += nalLen
222
+ nalCount++
223
+ offset += nalLen
224
+ }
225
+ if (nalCount === 0) return avccData
226
+
227
+ let outSize = nalCount * 4 + totalNalSize
228
+ if (isKeyframe) {
229
+ outSize += 4 + spsPps.sps.length + 4 + spsPps.pps.length
230
+ }
231
+ const out = new Uint8Array(outSize)
232
+ let w = 0
233
+
234
+ if (isKeyframe) {
235
+ out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1
236
+ out.set(spsPps.sps, w); w += spsPps.sps.length
237
+ out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1
238
+ out.set(spsPps.pps, w); w += spsPps.pps.length
239
+ }
240
+
241
+ offset = 0
242
+ while (offset + 4 <= avccData.length) {
243
+ const nalLen =
244
+ (avccData[offset] << 24) |
245
+ (avccData[offset + 1] << 16) |
246
+ (avccData[offset + 2] << 8) |
247
+ avccData[offset + 3]
248
+ offset += 4
249
+ if (nalLen <= 0 || offset + nalLen > avccData.length) break
250
+ out[w++] = 0; out[w++] = 0; out[w++] = 0; out[w++] = 1
251
+ out.set(avccData.subarray(offset, offset + nalLen), w)
252
+ w += nalLen
253
+ offset += nalLen
254
+ }
255
+ return out
256
+ }
257
+
258
+ /**
259
+ * Build an avcC record by parsing Annex-B data to extract SPS (NAL type 7) and PPS (NAL type 8).
260
+ * Returns the avcC record, or null if SPS/PPS not found.
261
+ */
262
+ export function extractCodecDescriptionFromAnnexB(data: Uint8Array): Uint8Array | null {
263
+ const nalUnits: Array<{ type: number; data: Uint8Array }> = []
264
+ let i = 0
265
+ while (i < data.length - 4) {
266
+ if (data[i] === 0 && data[i + 1] === 0) {
267
+ let nalStart = 0
268
+ if (data[i + 2] === 1) {
269
+ nalStart = i + 3
270
+ } else if (data[i + 2] === 0 && data[i + 3] === 1) {
271
+ nalStart = i + 4
272
+ }
273
+ if (nalStart > 0 && nalStart < data.length) {
274
+ let nalEnd = data.length
275
+ for (let j = nalStart; j < data.length - 3; j++) {
276
+ if (data[j] === 0 && data[j + 1] === 0 &&
277
+ (data[j + 2] === 1 || (data[j + 2] === 0 && data[j + 3] === 1))) {
278
+ nalEnd = j
279
+ break
280
+ }
281
+ }
282
+ const nalType = data[nalStart] & 0x1f
283
+ nalUnits.push({ type: nalType, data: data.slice(nalStart, nalEnd) })
284
+ i = nalEnd
285
+ continue
286
+ }
287
+ }
288
+ i++
289
+ }
290
+
291
+ const sps = nalUnits.find(n => n.type === 7)
292
+ const pps = nalUnits.find(n => n.type === 8)
293
+ if (!sps || !pps) return null
294
+
295
+ const avcCLength = 6 + 2 + sps.data.length + 1 + 2 + pps.data.length
296
+ const avcC = new Uint8Array(avcCLength)
297
+ let offset = 0
298
+ avcC[offset++] = 1
299
+ avcC[offset++] = sps.data[1]
300
+ avcC[offset++] = sps.data[2]
301
+ avcC[offset++] = sps.data[3]
302
+ avcC[offset++] = 0xff
303
+ avcC[offset++] = 0xe1
304
+ avcC[offset++] = (sps.data.length >> 8) & 0xff
305
+ avcC[offset++] = sps.data.length & 0xff
306
+ avcC.set(sps.data, offset); offset += sps.data.length
307
+ avcC[offset++] = 1
308
+ avcC[offset++] = (pps.data.length >> 8) & 0xff
309
+ avcC[offset++] = pps.data.length & 0xff
310
+ avcC.set(pps.data, offset)
311
+ return avcC
312
+ }
313
+
314
+ /**
315
+ * Derive codec string from avcC record (e.g. "avc1.640033").
316
+ */
317
+ export function codecStringFromAvcC(avcC: Uint8Array): string {
318
+ if (avcC.length < 4) return 'avc1.640033'
319
+ return `avc1.${hex2(avcC[1])}${hex2(avcC[2])}${hex2(avcC[3])}`
320
+ }
321
+
322
+ /** VPS/SPS/PPS NAL units extracted from an HEVC keyframe (raw NAL bytes, no start code/length prefix). */
323
+ export interface HevcParameterSets {
324
+ vps?: Uint8Array
325
+ sps?: Uint8Array
326
+ pps?: Uint8Array
327
+ }
328
+
329
+ /** True for HEVC/H.265 codec strings (hev1.* / hvc1.*). */
330
+ export function isHevcCodec(codec: string): boolean {
331
+ return codec.startsWith('hev1') || codec.startsWith('hvc1')
332
+ }
333
+
334
+ /** Video sample entries this package decodes (ISO/IEC 14496-15 5.4.2, 8.4.1). */
335
+ export type VideoSampleEntry = 'avc1' | 'avc3' | 'hev1' | 'hvc1'
336
+
337
+ /** Video codec family of a sample entry. */
338
+ export type VideoCodecFamily = 'avc' | 'hevc'
339
+
340
+ /**
341
+ * Select the video sample entry named by a catalog codec string's first
342
+ * element (RFC 6381 §3.3). The match is exact and case-sensitive; any other
343
+ * string, including an empty one, throws. There is no default family and the
344
+ * bitstream is never sniffed.
345
+ */
346
+ export function videoSampleEntryFromCodecString(codec: string): VideoSampleEntry {
347
+ const fourCc = codec.split('.')[0]
348
+ if (fourCc === 'avc1' || fourCc === 'avc3' || fourCc === 'hev1' || fourCc === 'hvc1') return fourCc
349
+ throw new Error(`Unsupported video codec family in codec string ${JSON.stringify(codec)}`)
350
+ }
351
+
352
+ /** Codec family of a sample entry. */
353
+ export function videoCodecFamily(sampleEntry: VideoSampleEntry): VideoCodecFamily {
354
+ return sampleEntry === 'avc1' || sampleEntry === 'avc3' ? 'avc' : 'hevc'
355
+ }
356
+
357
+ /**
358
+ * HEVC `nal_unit_type` codes (ITU-T H.265 Table 7-1). The HEVC NAL unit header
359
+ * is two bytes (H.265 §7.3.1.2): forbidden_zero_bit u(1) | nal_unit_type u(6) |
360
+ * nuh_layer_id u(6) | nuh_temporal_id_plus1 u(3).
361
+ */
362
+ export const HEVC_NAL_UNIT_TYPE = {
363
+ TRAIL_R: 1,
364
+ /** First IRAP type. */
365
+ BLA_W_LP: 16,
366
+ IDR_W_RADL: 19,
367
+ IDR_N_LP: 20,
368
+ CRA_NUT: 21,
369
+ /** Last IRAP type (reserved IRAP VCL). */
370
+ RSV_IRAP_VCL23: 23,
371
+ /** Last VCL type; 32..=63 are non-VCL. */
372
+ LAST_VCL: 31,
373
+ VPS_NUT: 32,
374
+ SPS_NUT: 33,
375
+ PPS_NUT: 34,
376
+ AUD_NUT: 35,
377
+ PREFIX_SEI_NUT: 39,
378
+ SUFFIX_SEI_NUT: 40,
379
+ } as const
380
+
381
+ /** Bytes in the HEVC NAL unit header (H.265 §7.3.1.2). */
382
+ export const HEVC_NAL_HEADER_LENGTH = 2
383
+
384
+ /**
385
+ * Highest `bitDepth{Luma,Chroma}Minus8` an `hvcC` can carry: both are u(3)
386
+ * (ISO/IEC 14496-15 §8.3.3.1.2). H.265 allows the SPS fields up to 8.
387
+ */
388
+ const HVCC_MAX_BIT_DEPTH_MINUS8 = 7
389
+
390
+ /** HEVC `nal_unit_type` from the first NAL header byte: (byte >> 1) & 0x3f. */
391
+ export function hevcNalUnitType(nal: Uint8Array): number {
392
+ return (nal[0]! >> 1) & 0x3f
393
+ }
394
+
395
+ /**
396
+ * True for an IRAP picture's `nal_unit_type`: BLA_W_LP..=RSV_IRAP_VCL23
397
+ * (16..=23, H.265 Table 7-1 / §7.4.2.2), including the reserved IRAP types 22
398
+ * and 23. An IRAP picture is the HEVC keyframe.
399
+ */
400
+ export function isHevcIrapNalUnitType(nalUnitType: number): boolean {
401
+ return nalUnitType >= HEVC_NAL_UNIT_TYPE.BLA_W_LP && nalUnitType <= HEVC_NAL_UNIT_TYPE.RSV_IRAP_VCL23
402
+ }
403
+
404
+ /** Internal alias kept for the existing helpers below. */
405
+ function hevcNalType(nal: Uint8Array): number {
406
+ return hevcNalUnitType(nal)
407
+ }
408
+
409
+ /**
410
+ * Remove H.26x emulation-prevention bytes from a NAL unit so fixed-position
411
+ * fields (e.g. HEVC profile_tier_level) can be read by byte offset.
412
+ *
413
+ * H.265 §7.3.1.1 / §7.4.2: every 0x03 that follows two zero bytes inside a NAL
414
+ * unit is an emulation_prevention_three_byte and is discarded, including one
415
+ * that ends the NAL unit (the cabac_zero_words case).
416
+ */
417
+ export function removeEmulationPrevention(rbsp: Uint8Array): Uint8Array {
418
+ const out: number[] = []
419
+ let zeros = 0
420
+ for (let i = 0; i < rbsp.length; i++) {
421
+ const b = rbsp[i]!
422
+ if (zeros >= 2 && b === 0x03) {
423
+ zeros = 0
424
+ continue
425
+ }
426
+ out.push(b)
427
+ zeros = b === 0 ? zeros + 1 : 0
428
+ }
429
+ return Uint8Array.from(out)
430
+ }
431
+
432
+ /**
433
+ * Fields of an HEVC SPS (H.265 §7.3.2.2.1) needed for the codec string and the
434
+ * hvcC record. `profileCompatibilityFlags` holds the 32 flags in bitstream
435
+ * order (flag 0 is the most significant bit); `constraintIndicatorFlags` holds
436
+ * the 48 bits from general_progressive_source_flag onward.
437
+ */
438
+ export interface HevcSpsInfo {
439
+ profileSpace: number
440
+ tierFlag: boolean
441
+ profileIdc: number
442
+ profileCompatibilityFlags: number
443
+ constraintIndicatorFlags: Uint8Array
444
+ levelIdc: number
445
+ maxSubLayersMinus1: number
446
+ temporalIdNestingFlag: boolean
447
+ seqParameterSetId: number
448
+ chromaFormatIdc: number
449
+ picWidthInLumaSamples: number
450
+ picHeightInLumaSamples: number
451
+ bitDepthLumaMinus8: number
452
+ bitDepthChromaMinus8: number
453
+ }
454
+
455
+ /** MSB-first RBSP bit reader (H.265 §7.2 read_bits, §9.2 ue(v)). */
456
+ class RbspReader {
457
+ #bit = 0
458
+ constructor(private readonly data: Uint8Array) {}
459
+
460
+ bits(count: number): number {
461
+ if (this.#bit + count > this.data.length * 8) throw new Error('HEVC SPS: RBSP truncated')
462
+ let value = 0
463
+ for (let i = 0; i < count; i++, this.#bit++) {
464
+ value = value * 2 + ((this.data[this.#bit >> 3]! >> (7 - (this.#bit & 7))) & 1)
465
+ }
466
+ return value
467
+ }
468
+
469
+ skip(count: number): void {
470
+ if (this.#bit + count > this.data.length * 8) throw new Error('HEVC SPS: RBSP truncated')
471
+ this.#bit += count
472
+ }
473
+
474
+ ue(): number {
475
+ let leadingZeros = 0
476
+ while (this.bits(1) === 0) {
477
+ if (++leadingZeros > 31) throw new Error('HEVC SPS: ue(v) exceeds 32 bits')
478
+ }
479
+ return leadingZeros === 0 ? 0 : 2 ** leadingZeros - 1 + this.bits(leadingZeros)
480
+ }
481
+ }
482
+
483
+ /**
484
+ * Parse the leading fields of an HEVC SPS NAL unit (2-byte header included, no
485
+ * start code). Emulation prevention is removed first. Throws on a non-SPS or
486
+ * non-base-layer NAL unit, truncation, or a value outside its H.265 §7.4.3.2.1
487
+ * range: a malformed SPS must not yield a guessed record.
488
+ */
489
+ export function parseHevcSps(sps: Uint8Array): HevcSpsInfo {
490
+ if (sps.length < HEVC_NAL_HEADER_LENGTH || (sps[0]! & 0x80) !== 0 || (sps[1]! & 0x07) === 0) {
491
+ throw new Error('HEVC SPS: invalid NAL unit header')
492
+ }
493
+ if (hevcNalUnitType(sps) !== HEVC_NAL_UNIT_TYPE.SPS_NUT) throw new Error('HEVC SPS: NAL unit is not an SPS')
494
+ if (((sps[0]! & 0x01) << 5 | sps[1]! >> 3) !== 0) {
495
+ throw new Error('HEVC SPS: not in the base layer (nuh_layer_id != 0)')
496
+ }
497
+ const r = new RbspReader(removeEmulationPrevention(sps).subarray(HEVC_NAL_HEADER_LENGTH))
498
+ r.bits(4) // sps_video_parameter_set_id
499
+ const maxSubLayersMinus1 = r.bits(3)
500
+ if (maxSubLayersMinus1 > 6) throw new Error('HEVC SPS: sps_max_sub_layers_minus1 is greater than 6')
501
+ const temporalIdNestingFlag = r.bits(1) === 1
502
+ // profile_tier_level(1, sps_max_sub_layers_minus1), H.265 §7.3.3.
503
+ const profileSpace = r.bits(2)
504
+ const tierFlag = r.bits(1) === 1
505
+ const profileIdc = r.bits(5)
506
+ const profileCompatibilityFlags = r.bits(32) >>> 0
507
+ const constraintIndicatorFlags = new Uint8Array(6)
508
+ for (let i = 0; i < 6; i++) constraintIndicatorFlags[i] = r.bits(8)
509
+ const levelIdc = r.bits(8)
510
+ const subLayerProfilePresent: boolean[] = []
511
+ const subLayerLevelPresent: boolean[] = []
512
+ for (let i = 0; i < maxSubLayersMinus1; i++) {
513
+ subLayerProfilePresent.push(r.bits(1) === 1)
514
+ subLayerLevelPresent.push(r.bits(1) === 1)
515
+ }
516
+ if (maxSubLayersMinus1 > 0) {
517
+ for (let i = maxSubLayersMinus1; i < 8; i++) r.skip(2) // reserved_zero_2bits
518
+ }
519
+ for (let i = 0; i < maxSubLayersMinus1; i++) {
520
+ if (subLayerProfilePresent[i]) r.skip(88) // sub-layer profile fields
521
+ if (subLayerLevelPresent[i]) r.skip(8) // sub_layer_level_idc
522
+ }
523
+ const seqParameterSetId = r.ue()
524
+ if (seqParameterSetId > 15) throw new Error('HEVC SPS: sps_seq_parameter_set_id is greater than 15')
525
+ const chromaFormatIdc = r.ue()
526
+ if (chromaFormatIdc > 3) throw new Error('HEVC SPS: chroma_format_idc is greater than 3')
527
+ if (chromaFormatIdc === 3) r.skip(1) // separate_colour_plane_flag
528
+ const picWidthInLumaSamples = r.ue()
529
+ const picHeightInLumaSamples = r.ue()
530
+ if (picWidthInLumaSamples === 0 || picHeightInLumaSamples === 0) {
531
+ throw new Error('HEVC SPS: picture dimension is zero')
532
+ }
533
+ if (r.bits(1) === 1) for (let i = 0; i < 4; i++) r.ue() // conformance window offsets
534
+ const bitDepthLumaMinus8 = r.ue()
535
+ const bitDepthChromaMinus8 = r.ue()
536
+ if (bitDepthLumaMinus8 > 8 || bitDepthChromaMinus8 > 8) throw new Error('HEVC SPS: bit depth is greater than 16')
537
+ return {
538
+ profileSpace,
539
+ tierFlag,
540
+ profileIdc,
541
+ profileCompatibilityFlags,
542
+ constraintIndicatorFlags,
543
+ levelIdc,
544
+ maxSubLayersMinus1,
545
+ temporalIdNestingFlag,
546
+ seqParameterSetId,
547
+ chromaFormatIdc,
548
+ picWidthInLumaSamples,
549
+ picHeightInLumaSamples,
550
+ bitDepthLumaMinus8,
551
+ bitDepthChromaMinus8,
552
+ }
553
+ }
554
+
555
+ /**
556
+ * RFC 6381 codec string of an HEVC stream from its SPS, per ISO/IEC 14496-15
557
+ * Annex E.3 (e.g. "hev1.1.6.L93.B0"); the first element is `sampleEntry`.
558
+ */
559
+ export function hevcCodecStringFromSps(sps: Uint8Array, sampleEntry: 'hev1' | 'hvc1'): string {
560
+ const info = parseHevcSps(sps)
561
+ return formatHevcCodecString(
562
+ sampleEntry,
563
+ info.profileSpace,
564
+ info.tierFlag,
565
+ info.profileIdc,
566
+ info.profileCompatibilityFlags,
567
+ info.constraintIndicatorFlags,
568
+ info.levelIdc,
569
+ )
570
+ }
571
+
572
+ /**
573
+ * ISO/IEC 14496-15 Annex E.3 codec string: sample entry; profile space
574
+ * (none/A/B/C) + decimal profile_idc; the 32 compatibility flags in reverse bit
575
+ * order as hex without leading zeros; L/H tier + decimal level_idc; each
576
+ * constraint byte as hex, trailing zero bytes omitted. Hex digits are upper
577
+ * case, as in the Annex E.3 examples.
578
+ */
579
+ function formatHevcCodecString(
580
+ sampleEntry: 'hev1' | 'hvc1',
581
+ profileSpace: number,
582
+ tierFlag: boolean,
583
+ profileIdc: number,
584
+ profileCompatibilityFlags: number,
585
+ constraintIndicatorFlags: Uint8Array,
586
+ levelIdc: number,
587
+ ): string {
588
+ let reversed = 0
589
+ let v = profileCompatibilityFlags >>> 0
590
+ for (let i = 0; i < 32; i++) {
591
+ reversed = ((reversed << 1) | (v & 1)) >>> 0
592
+ v >>>= 1
593
+ }
594
+ let lastNonZero = -1
595
+ for (let i = 0; i < constraintIndicatorFlags.length; i++) if (constraintIndicatorFlags[i] !== 0) lastNonZero = i
596
+ const space = profileSpace === 0 ? '' : String.fromCharCode(64 + profileSpace) // 1→'A', 2→'B', 3→'C'
597
+ let codec = `${sampleEntry}.${space}${profileIdc}.${reversed.toString(16).toUpperCase()}.${tierFlag ? 'H' : 'L'}${levelIdc}`
598
+ for (let i = 0; i <= lastNonZero; i++) codec += `.${constraintIndicatorFlags[i]!.toString(16).toUpperCase()}`
599
+ return codec
600
+ }
601
+
602
+ /**
603
+ * The NAL units of an HEVC sample, split by its framing: a complete 4-byte
604
+ * length-prefixed sample is read as such, otherwise Annex B. Null when the
605
+ * sample is neither — there is no third framing to read it as.
606
+ */
607
+ function splitHevcSample(data: Uint8Array): { nals: Uint8Array[]; lengthPrefixed: boolean } | null {
608
+ if (isCompleteAvccSample(data)) return { nals: splitAvccNalUnits(data), lengthPrefixed: true }
609
+ if (isAnnexB(data)) return { nals: splitAnnexBNalUnits(data), lengthPrefixed: false }
610
+ return null
611
+ }
612
+
613
+ /** IRAP classification of split NAL units; undefined when none has a header. */
614
+ function hevcNalUnitsContainIrap(nals: Uint8Array[]): boolean | undefined {
615
+ let sawNal = false
616
+ for (const nal of nals) {
617
+ if (nal.length < HEVC_NAL_HEADER_LENGTH) continue
618
+ sawNal = true
619
+ if (isHevcIrapNalUnitType(hevcNalUnitType(nal))) return true
620
+ }
621
+ return sawNal ? false : undefined
622
+ }
623
+
624
+ /**
625
+ * Classify an HEVC access unit as a keyframe (contains an IRAP slice,
626
+ * {@link isHevcIrapNalUnitType}). Complete 4-byte length-prefixed samples are
627
+ * read as such; otherwise Annex B. Returns undefined when no NAL unit is found
628
+ * — an unreadable sample, which is not the same as a delta frame.
629
+ */
630
+ export function classifyHevcKeyframe(data: Uint8Array): boolean | undefined {
631
+ const split = splitHevcSample(data)
632
+ return split === null ? undefined : hevcNalUnitsContainIrap(split.nals)
633
+ }
634
+
635
+ /**
636
+ * True when an HEVC access unit contains an IRAP slice. Collapses "unreadable"
637
+ * into false; a caller that must tell the two apart (anything about to write
638
+ * the sample into an mdat) uses {@link prepareHevcCmafSample} instead.
639
+ */
640
+ export function detectHevcKeyframe(data: Uint8Array): boolean {
641
+ return classifyHevcKeyframe(data) === true
642
+ }
643
+
644
+ /** An HEVC sample ready for an fMP4 mdat whose hvcC has lengthSizeMinusOne = 3. */
645
+ export interface HevcCmafSample {
646
+ /** The access unit contains an IRAP slice ({@link isHevcIrapNalUnitType}). */
647
+ keyframe: boolean
648
+ /** 4-byte length-prefixed NAL units (the input itself when already so). */
649
+ data: Uint8Array
650
+ }
651
+
652
+ /**
653
+ * Classify and normalize an HEVC sample for an fMP4 mdat from one split of its
654
+ * NAL units. Returns null — the sample must not be written — when its framing
655
+ * is neither a complete 4-byte length-prefixed sample nor Annex B, when it
656
+ * holds no NAL unit, or when nothing is left after `hvc1` drops the parameter
657
+ * sets: bytes the hvcC's lengthSizeMinusOne = 3 does not describe would be
658
+ * misparsed by the decoder, and an unreadable sample is not a delta frame.
659
+ * Normalization as in {@link normalizeHevcSampleToLengthPrefixed}.
660
+ */
661
+ export function prepareHevcCmafSample(data: Uint8Array, sampleEntry: 'hev1' | 'hvc1'): HevcCmafSample | null {
662
+ const split = splitHevcSample(data)
663
+ if (split === null) return null
664
+ const keyframe = hevcNalUnitsContainIrap(split.nals)
665
+ if (keyframe === undefined) return null
666
+ const normalized = hevcNalUnitsToLengthPrefixed(data, split.nals, split.lengthPrefixed, sampleEntry)
667
+ return normalized === null ? null : { keyframe, data: normalized }
668
+ }
669
+
670
+ /**
671
+ * Normalize an HEVC sample to 4-byte length-prefixed NAL units for an fMP4
672
+ * mdat whose hvcC declares lengthSizeMinusOne = 3. Complete length-prefixed
673
+ * samples pass through; Annex B is converted. For `hvc1` the VPS/SPS/PPS are
674
+ * dropped (ISO/IEC 14496-15 8.4.1.1: they live only in the hvcC); for `hev1`
675
+ * they are kept in-band. Throws when the sample cannot be read
676
+ * ({@link prepareHevcCmafSample} returns null) rather than pass it through.
677
+ */
678
+ export function normalizeHevcSampleToLengthPrefixed(data: Uint8Array, sampleEntry: 'hev1' | 'hvc1'): Uint8Array {
679
+ const prepared = prepareHevcCmafSample(data, sampleEntry)
680
+ if (prepared === null) {
681
+ throw new Error('HEVC sample is neither 4-byte length-prefixed nor Annex B, or holds no NAL unit to write')
682
+ }
683
+ return prepared.data
684
+ }
685
+
686
+ function hevcNalUnitsToLengthPrefixed(
687
+ data: Uint8Array,
688
+ nals: Uint8Array[],
689
+ lengthPrefixed: boolean,
690
+ sampleEntry: 'hev1' | 'hvc1',
691
+ ): Uint8Array | null {
692
+ const kept = sampleEntry === 'hvc1'
693
+ ? nals.filter((nal) => {
694
+ const t = hevcNalUnitType(nal)
695
+ return t !== HEVC_NAL_UNIT_TYPE.VPS_NUT && t !== HEVC_NAL_UNIT_TYPE.SPS_NUT && t !== HEVC_NAL_UNIT_TYPE.PPS_NUT
696
+ })
697
+ : nals
698
+ if (kept.length === 0) return null
699
+ if (lengthPrefixed && kept.length === nals.length) return data
700
+ const out = new Uint8Array(kept.reduce((sum, nal) => sum + 4 + nal.length, 0))
701
+ let w = 0
702
+ for (const nal of kept) {
703
+ w = writeNalLength(out, w, nal.length)
704
+ out.set(nal, w)
705
+ w += nal.length
706
+ }
707
+ return out
708
+ }
709
+
710
+ /**
711
+ * Split an AVCC/hvcC bitstream (length-prefixed NAL units) into raw NAL byte slices.
712
+ * `lengthSize` is the NAL length prefix width (4 for FFmpeg LOC / fMP4 sample data).
713
+ */
714
+ export function splitAvccNalUnits(data: Uint8Array, lengthSize = 4): Uint8Array[] {
715
+ const nals: Uint8Array[] = []
716
+ let i = 0
717
+ while (i + lengthSize <= data.length) {
718
+ let len = 0
719
+ for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]
720
+ i += lengthSize
721
+ if (len <= 0 || i + len > data.length) break
722
+ nals.push(data.subarray(i, i + len))
723
+ i += len
724
+ }
725
+ return nals
726
+ }
727
+
728
+ /**
729
+ * True when `data` is exactly one or more length-prefixed NAL units.
730
+ *
731
+ * This is intentionally stricter than "starts with a plausible length": a
732
+ * H.264 AVCC access unit may begin with bytes `00 00 00 01` when the first
733
+ * NAL is a one-byte AUD. That byte pattern is also an Annex-B start code, so
734
+ * callers must prove the whole buffer is complete AVCC before falling back to
735
+ * Annex-B detection/conversion.
736
+ */
737
+ export function isCompleteAvccSample(data: Uint8Array, lengthSize = 4): boolean {
738
+ let i = 0
739
+ let count = 0
740
+ while (i + lengthSize <= data.length) {
741
+ let len = 0
742
+ for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]
743
+ i += lengthSize
744
+ if (len <= 0 || i + len > data.length) return false
745
+ i += len
746
+ count++
747
+ }
748
+ return count > 0 && i === data.length
749
+ }
750
+
751
+ export type H264SampleFormat = 'avcc' | 'annexb' | 'raw-nal' | 'unknown'
752
+ export type H264NalUnitFormat = H264SampleFormat | 'partial-avcc'
753
+
754
+ export interface H264AvccSample {
755
+ data: Uint8Array
756
+ format: H264SampleFormat
757
+ }
758
+
759
+ export interface H264NalUnitSample {
760
+ nals: Uint8Array[]
761
+ format: H264NalUnitFormat
762
+ }
763
+
764
+ /**
765
+ * Normalize a H.264 access unit/sample to length-prefixed AVCC for fMP4 mdat
766
+ * payloads. Already-AVCC samples are returned as-is. Annex-B and single raw
767
+ * NAL samples are converted one-way to AVCC at the MSE/fMP4 boundary.
768
+ */
769
+ export function normalizeH264SampleToAvcc(data: Uint8Array): H264AvccSample {
770
+ if (isCompleteAvccSample(data)) return { data, format: 'avcc' }
771
+ if (isAnnexB(data)) return { data: annexBToAvcc(data), format: 'annexb' }
772
+ if (isLikelyRawH264NalUnit(data)) return { data: rawH264NalToAvcc(data), format: 'raw-nal' }
773
+ return { data, format: 'unknown' }
774
+ }
775
+
776
+ /**
777
+ * Split a H.264 access unit/sample into NAL units without changing its framing.
778
+ * Use this for routing, keyframe gates, and diagnostics. Conversion to AVCC is
779
+ * reserved for `normalizeH264SampleToAvcc` at the fMP4/MSE boundary.
780
+ */
781
+ export function splitH264SampleNalUnits(
782
+ data: Uint8Array,
783
+ options: H264KeyframeDetectionOptions = {},
784
+ ): H264NalUnitSample {
785
+ if (isCompleteAvccSample(data)) return { nals: splitAvccNalUnits(data), format: 'avcc' }
786
+ if (isAnnexB(data)) return { nals: splitAnnexBNalUnits(data), format: 'annexb' }
787
+ if (options.allowPartialAvcc === true) {
788
+ const partial = splitPartialAvccNalUnits(data)
789
+ if (partial.length > 0) return { nals: partial, format: 'partial-avcc' }
790
+ }
791
+ if (isLikelyRawH264NalUnit(data)) return { nals: [data], format: 'raw-nal' }
792
+ const nals = splitAvccNalUnits(data)
793
+ return { nals, format: nals.length > 0 ? 'avcc' : 'unknown' }
794
+ }
795
+
796
+ /**
797
+ * Extract HEVC VPS (type 32) / SPS (33) / PPS (34) NAL units from a keyframe access
798
+ * unit. Accepts either Annex-B (start-code delimited) or AVCC/hvcC (length-prefixed)
799
+ * framing — FFmpeg LOC delivers the latter.
800
+ */
801
+ export function extractHevcParameterSets(keyframe: Uint8Array): HevcParameterSets {
802
+ const nals = isAnnexB(keyframe) ? splitAnnexBNalUnits(keyframe) : splitAvccNalUnits(keyframe)
803
+ const out: HevcParameterSets = {}
804
+ for (const nal of nals) {
805
+ if (nal.length === 0) continue
806
+ const t = hevcNalType(nal)
807
+ if (t === 32 && !out.vps) out.vps = nal
808
+ else if (t === 33 && !out.sps) out.sps = nal
809
+ else if (t === 34 && !out.pps) out.pps = nal
810
+ }
811
+ return out
812
+ }
813
+
814
+ /**
815
+ * Build an hvcC (HEVCDecoderConfigurationRecord, ISO/IEC 14496-15 §8.3.3.1) from
816
+ * VPS/SPS/PPS NAL units. Suitable as the `description` for a WebCodecs HEVC
817
+ * VideoDecoderConfig and as the fMP4 `hvcC` box payload.
818
+ *
819
+ * Field sources (§8.3.3.1.2): general profile/tier/level, chromaFormat,
820
+ * bitDepth{Luma,Chroma}Minus8, numTemporalLayers (= sps_max_sub_layers_minus1
821
+ * + 1) and temporalIdNested come from the parsed SPS. min_spatial_segmentation
822
+ * _idc = 0, parallelismType = 0, avgFrameRate = 0 and constantFrameRate = 0
823
+ * are the record's "no restriction / unknown / unspecified" encodings (the VUI
824
+ * and PPS fields that could tighten them are not parsed). lengthSizeMinusOne =
825
+ * 3 (4-byte NAL lengths). array_completeness = 0: the record is derived from
826
+ * in-band parameter sets, which is the `hev1` arrangement (§8.4.1.1).
827
+ *
828
+ * Returns null when the SPS or PPS is absent; throws when the SPS is malformed
829
+ * or its bit depth (16) is not representable in the record.
830
+ *
831
+ * WebCodecs REQUIRES a description for length-prefixed HEVC — unlike H.264, Chrome will
832
+ * not self-configure HEVC from the bitstream (BLO-7764).
833
+ */
834
+ export function generateHvcCFromParameterSets(sets: HevcParameterSets): Uint8Array | null {
835
+ const { vps, sps, pps } = sets
836
+ if (!sps || !pps) return null
837
+ const info = parseHevcSps(sps)
838
+ // bitDepth{Luma,Chroma}Minus8 are u(3) in the record (§8.3.3.1.2), so 0..7.
839
+ // H.265 also allows 8 (16-bit), which `0xf8 | 8` would write as 0 — a
840
+ // record declaring 8-bit — so a 16-bit SPS is refused, not mis-described.
841
+ if (info.bitDepthLumaMinus8 > HVCC_MAX_BIT_DEPTH_MINUS8 || info.bitDepthChromaMinus8 > HVCC_MAX_BIT_DEPTH_MINUS8) {
842
+ throw new Error('HEVC SPS: bit depth of 16 is not representable in an hvcC record')
843
+ }
844
+ const arrays = [
845
+ { type: 32, nal: vps },
846
+ { type: 33, nal: sps },
847
+ { type: 34, nal: pps },
848
+ ].filter((a): a is { type: number; nal: Uint8Array } => a.nal !== undefined)
849
+
850
+ let size = 23
851
+ for (const a of arrays) size += 5 + a.nal.length
852
+ const h = new Uint8Array(size)
853
+ let o = 0
854
+ h[o++] = 1 // configurationVersion
855
+ h[o++] = (info.profileSpace << 6) | ((info.tierFlag ? 1 : 0) << 5) | info.profileIdc
856
+ h[o++] = (info.profileCompatibilityFlags >>> 24) & 0xff
857
+ h[o++] = (info.profileCompatibilityFlags >>> 16) & 0xff
858
+ h[o++] = (info.profileCompatibilityFlags >>> 8) & 0xff
859
+ h[o++] = info.profileCompatibilityFlags & 0xff
860
+ h.set(info.constraintIndicatorFlags, o); o += 6 // general_constraint_indicator_flags
861
+ h[o++] = info.levelIdc // general_level_idc
862
+ h[o++] = 0xf0; h[o++] = 0x00 // reserved(1111) + min_spatial_segmentation_idc(0)
863
+ h[o++] = 0xfc // reserved(111111) + parallelismType(0)
864
+ h[o++] = 0xfc | info.chromaFormatIdc // reserved(111111) + chromaFormat
865
+ h[o++] = 0xf8 | info.bitDepthLumaMinus8 // reserved(11111) + bitDepthLumaMinus8
866
+ h[o++] = 0xf8 | info.bitDepthChromaMinus8 // reserved(11111) + bitDepthChromaMinus8
867
+ h[o++] = 0x00; h[o++] = 0x00 // avgFrameRate(0 = unspecified)
868
+ // constantFrameRate(0) | numTemporalLayers | temporalIdNested | lengthSizeMinusOne(3)
869
+ h[o++] = ((info.maxSubLayersMinus1 + 1) << 3) | ((info.temporalIdNestingFlag ? 1 : 0) << 2) | 3
870
+ h[o++] = arrays.length // numOfArrays
871
+ for (const a of arrays) {
872
+ h[o++] = a.type & 0x3f // array_completeness(0) + NAL_unit_type
873
+ h[o++] = 0; h[o++] = 1 // numNalus = 1
874
+ h[o++] = (a.nal.length >> 8) & 0xff
875
+ h[o++] = a.nal.length & 0xff
876
+ h.set(a.nal, o); o += a.nal.length
877
+ }
878
+ return h.subarray(0, o)
879
+ }
880
+
881
+ /**
882
+ * Build an hvcC `description` for a WebCodecs HEVC VideoDecoderConfig directly from a
883
+ * keyframe access unit (Annex-B or length-prefixed). Returns null if VPS/SPS/PPS aren't
884
+ * present in-band. HEVC analogue of {@link extractCodecDescriptionFromAnnexB}.
885
+ */
886
+ export function extractHevcCodecDescription(keyframe: Uint8Array): Uint8Array | null {
887
+ return generateHvcCFromParameterSets(extractHevcParameterSets(keyframe))
888
+ }
889
+
890
+ /**
891
+ * Strip non-VCL NAL units (VPS/SPS/PPS/SEI/AUD, type >= 32) from an HEVC access unit,
892
+ * returning only the VCL NAL units (type <= 31) as a 4-byte length-prefixed buffer.
893
+ *
894
+ * Required when decoding `hvc1` with an explicit hvcC `description`: WebCodecs rejects a
895
+ * sample whose data still carries in-band parameter sets ("Unable to determine size of
896
+ * bitstream buffer"). Accepts Annex-B or length-prefixed input. Returns the input
897
+ * unchanged if it already contains only VCL NALs (cheap no-op for delta frames).
898
+ */
899
+ export function hevcVclOnly(data: Uint8Array): Uint8Array {
900
+ const nals = isAnnexB(data) ? splitAnnexBNalUnits(data) : splitAvccNalUnits(data)
901
+ const vcl = nals.filter((n) => n.length > 0 && hevcNalType(n) <= 31)
902
+ if (vcl.length === nals.length && !isAnnexB(data)) return data
903
+ let size = 0
904
+ for (const n of vcl) size += 4 + n.length
905
+ const out = new Uint8Array(size)
906
+ let o = 0
907
+ for (const n of vcl) {
908
+ out[o++] = (n.length >> 24) & 0xff
909
+ out[o++] = (n.length >> 16) & 0xff
910
+ out[o++] = (n.length >> 8) & 0xff
911
+ out[o++] = n.length & 0xff
912
+ out.set(n, o); o += n.length
913
+ }
914
+ return out
915
+ }
916
+
917
+ /**
918
+ * Check if data starts with an Annex-B start code (00 00 01 or 00 00 00 01).
919
+ */
920
+ export function isAnnexB(data: Uint8Array): boolean {
921
+ if (data.length >= 4 && data[0] === 0 && data[1] === 0 &&
922
+ (data[2] === 1 || (data[2] === 0 && data[3] === 1))) {
923
+ return true
924
+ }
925
+ return false
926
+ }
927
+
928
+ /**
929
+ * Find the byte offset of the first Annex-B start code in data.
930
+ * Returns -1 if no start code found.
931
+ */
932
+ export function findAnnexBStartCode(data: Uint8Array): number {
933
+ for (let i = 0; i < data.length - 3; i++) {
934
+ if (data[i] === 0 && data[i + 1] === 0) {
935
+ if (data[i + 2] === 1) return i
936
+ if (data[i + 2] === 0 && data[i + 3] === 1) return i
937
+ }
938
+ }
939
+ return -1
940
+ }
941
+
942
+ /**
943
+ * Split Annex-B bitstream into NAL units.
944
+ * Returns array of NAL unit byte slices (without start codes).
945
+ */
946
+ export function splitAnnexBNalUnits(data: Uint8Array): Uint8Array[] {
947
+ const nalUnits: Uint8Array[] = []
948
+ let i = 0
949
+ while (i < data.length - 3) {
950
+ if (data[i] === 0 && data[i + 1] === 0) {
951
+ let scLen = 0
952
+ if (data[i + 2] === 1) scLen = 3
953
+ else if (data[i + 2] === 0 && i + 3 < data.length && data[i + 3] === 1) scLen = 4
954
+ if (scLen > 0) {
955
+ const nalStart = i + scLen
956
+ let nalEnd = data.length
957
+ for (let j = nalStart + 1; j < data.length - 2; j++) {
958
+ if (data[j] === 0 && data[j + 1] === 0 &&
959
+ (data[j + 2] === 1 || (data[j + 2] === 0 && j + 3 < data.length && data[j + 3] === 1))) {
960
+ nalEnd = j
961
+ break
962
+ }
963
+ }
964
+ if (nalEnd > nalStart) nalUnits.push(data.subarray(nalStart, nalEnd))
965
+ i = nalEnd
966
+ continue
967
+ }
968
+ }
969
+ i++
970
+ }
971
+ return nalUnits
972
+ }
973
+
974
+ /**
975
+ * Convert Annex-B (start-code delimited) to AVCC (4-byte length prefixed).
976
+ * Optionally prepends SPS/PPS NALs for keyframes.
977
+ */
978
+ export function annexBToAvcc(data: Uint8Array, spsPps?: SpsPps): Uint8Array {
979
+ const nalUnits = splitAnnexBNalUnits(data)
980
+ if (nalUnits.length === 0) return data
981
+
982
+ let extraSize = 0
983
+ if (spsPps) {
984
+ extraSize = 4 + spsPps.sps.length + 4 + spsPps.pps.length
985
+ }
986
+ const frameSize = nalUnits.reduce((sum, nal) => sum + 4 + nal.length, 0)
987
+ const output = new Uint8Array(extraSize + frameSize)
988
+ let w = 0
989
+
990
+ if (spsPps) {
991
+ w = writeNalLength(output, w, spsPps.sps.length)
992
+ output.set(spsPps.sps, w); w += spsPps.sps.length
993
+ w = writeNalLength(output, w, spsPps.pps.length)
994
+ output.set(spsPps.pps, w); w += spsPps.pps.length
995
+ }
996
+
997
+ for (const nal of nalUnits) {
998
+ w = writeNalLength(output, w, nal.length)
999
+ output.set(nal, w); w += nal.length
1000
+ }
1001
+ return output
1002
+ }
1003
+
1004
+ export interface H264KeyframeDetectionOptions {
1005
+ /**
1006
+ * Allow classifying a length-prefixed NAL when the declared NAL length runs
1007
+ * beyond this buffer. MMTP routing can see only the first packet fragment of
1008
+ * a sample, where the NAL header is present but the full NAL is not.
1009
+ */
1010
+ allowPartialAvcc?: boolean
1011
+ }
1012
+
1013
+ /**
1014
+ * Classify H.264 keyframe status by scanning NAL units. Returns undefined when
1015
+ * the payload is not H.264-classifiable. Handles Annex-B and AVCC formats.
1016
+ */
1017
+ export function classifyH264Keyframe(
1018
+ data: Uint8Array,
1019
+ options: H264KeyframeDetectionOptions = {},
1020
+ ): boolean | undefined {
1021
+ const nals = h264NalUnitsForKeyframeDetection(data, options)
1022
+ if (nals.length === 0) return undefined
1023
+ let sawIntraSlice = false
1024
+ let sawNonIntraVcl = false
1025
+ let sawNal = false
1026
+ for (const nal of nals) {
1027
+ sawNal = true
1028
+ const nalType = nal[0] !== undefined ? nal[0] & 0x1f : -1
1029
+ if (nalType === 5 || nalType === 7) return true
1030
+ if (nalType === 1) {
1031
+ if (isH264IntraSlice(nal)) sawIntraSlice = true
1032
+ else sawNonIntraVcl = true
1033
+ }
1034
+ }
1035
+ if (sawIntraSlice && !sawNonIntraVcl) return true
1036
+ return sawNal ? false : undefined
1037
+ }
1038
+
1039
+ /**
1040
+ * Detect H.264 keyframe by scanning for IDR/SPS or intra non-IDR slices.
1041
+ * Handles both Annex-B and AVCC formats.
1042
+ */
1043
+ export function detectH264Keyframe(
1044
+ data: Uint8Array,
1045
+ options: H264KeyframeDetectionOptions = {},
1046
+ ): boolean {
1047
+ return classifyH264Keyframe(data, options) === true
1048
+ }
1049
+
1050
+ /**
1051
+ * Convert MMTP timestamp (NTP short format) to monotonic microseconds.
1052
+ * Per ISO 23008-1 §9.2.2 / RFC 5905: upper 16 bits = seconds (lower 16
1053
+ * of NTP seconds), lower 16 bits = fractional seconds.
1054
+ * Returns a monotonically increasing µs value within an ~18-hour window.
1055
+ */
1056
+ export function mmtpTimestampToMicros(rawTs: bigint | number): number {
1057
+ // MMTP carries an NTP short timestamp as an unsigned 32-bit integer. Do not
1058
+ // use `Number(rawTs) >>> 0` here: bitwise coercion silently turns malformed
1059
+ // values such as -1, 2**32, fractions, and strings into plausible timestamps
1060
+ // and can poison downstream latency observations.
1061
+ const ts = typeof rawTs === 'bigint'
1062
+ ? rawTs >= 0n && rawTs <= 0xffff_ffffn
1063
+ ? Number(rawTs)
1064
+ : undefined
1065
+ : Number.isSafeInteger(rawTs) && rawTs >= 0 && rawTs <= 0xffff_ffff
1066
+ ? rawTs
1067
+ : undefined
1068
+ if (ts === undefined) {
1069
+ throw new RangeError(`MMTP timestamp must be an unsigned 32-bit integer, got ${String(rawTs)}`)
1070
+ }
1071
+ if (ts === 0) return 0
1072
+ const seconds = ts >>> 16
1073
+ const frac = ts & 0xFFFF
1074
+ return seconds * 1_000_000 + Math.floor(frac * 1_000_000 / 65536)
1075
+ }
1076
+
1077
+ /** Validate the CMSF/MMTP catalog initData contract: complete top-level ftyp+moov. */
1078
+ export function isFmp4InitSegment(data: Uint8Array): boolean {
1079
+ let offset = 0
1080
+ let sawFtyp = false
1081
+ let sawMoov = false
1082
+
1083
+ while (offset + 8 <= data.byteLength) {
1084
+ const view = new DataView(data.buffer, data.byteOffset + offset, data.byteLength - offset)
1085
+ let size = view.getUint32(0, false)
1086
+ const type = String.fromCharCode(data[offset + 4]!, data[offset + 5]!, data[offset + 6]!, data[offset + 7]!)
1087
+ let headerSize = 8
1088
+
1089
+ if (size === 1) {
1090
+ if (offset + 16 > data.byteLength) return false
1091
+ const largeSize = view.getBigUint64(8, false)
1092
+ if (largeSize > BigInt(Number.MAX_SAFE_INTEGER)) return false
1093
+ size = Number(largeSize)
1094
+ headerSize = 16
1095
+ } else if (size === 0) {
1096
+ size = data.byteLength - offset
1097
+ }
1098
+
1099
+ if (size < headerSize || offset + size > data.byteLength) return false
1100
+ // `mmpu` is the ISO MMT MPU metadata box. It is valid inside an MMTP
1101
+ // FT=0 payload but is not an ISO-BMFF initialization box that an MSE
1102
+ // SourceBuffer can append as a top-level box.
1103
+ if (type === 'mmpu') return false
1104
+ if (type === 'ftyp') sawFtyp = true
1105
+ if (type === 'moov') {
1106
+ if (!sawFtyp) return false
1107
+ sawMoov = true
1108
+ }
1109
+ offset += size
1110
+ }
1111
+
1112
+ return offset === data.byteLength && sawFtyp && sawMoov
1113
+ }
1114
+
1115
+ /**
1116
+ * Return the fMP4 initialization segment carried by MMTP MPU metadata.
1117
+ *
1118
+ * ISO MMTP FT=0 metadata may contain an `mmpu` box between the top-level
1119
+ * `ftyp` and `moov` boxes (`ftyp + mmpu + moov`). `mmpu` is an MMT metadata
1120
+ * box, not an ISO-BMFF initialization box accepted by an MSE SourceBuffer;
1121
+ * appending it produces Chromium's `Invalid top-level ISO BMFF box type mmpu`
1122
+ * error, most visibly when a rendition switch delivers a fresh FT=0 packet.
1123
+ * The returned bytes retain every other top-level box, in wire order, and are
1124
+ * only returned when the resulting bytes are a complete `ftyp` + `moov` init.
1125
+ * Inputs that are already a valid fMP4 init are returned unchanged.
1126
+ */
1127
+ export function normalizeMmtpInitSegment(data: Uint8Array): Uint8Array | undefined {
1128
+ if (isFmp4InitSegment(data)) return data
1129
+
1130
+ const parts: Uint8Array[] = []
1131
+ let offset = 0
1132
+ let removedMpu = false
1133
+
1134
+ while (offset + 8 <= data.byteLength) {
1135
+ const view = new DataView(data.buffer, data.byteOffset + offset, data.byteLength - offset)
1136
+ let size = view.getUint32(0, false)
1137
+ let headerSize = 8
1138
+ if (size === 1) {
1139
+ if (offset + 16 > data.byteLength) return undefined
1140
+ const largeSize = view.getBigUint64(8, false)
1141
+ if (largeSize > BigInt(Number.MAX_SAFE_INTEGER)) return undefined
1142
+ size = Number(largeSize)
1143
+ headerSize = 16
1144
+ } else if (size === 0) {
1145
+ size = data.byteLength - offset
1146
+ }
1147
+ if (size < headerSize || offset + size > data.byteLength) return undefined
1148
+
1149
+ const type = String.fromCharCode(
1150
+ data[offset + 4]!,
1151
+ data[offset + 5]!,
1152
+ data[offset + 6]!,
1153
+ data[offset + 7]!,
1154
+ )
1155
+ if (type === 'mmpu') removedMpu = true
1156
+ else parts.push(data.subarray(offset, offset + size))
1157
+ offset += size
1158
+ }
1159
+
1160
+ if (!removedMpu || offset !== data.byteLength) return undefined
1161
+ const normalized = new Uint8Array(parts.reduce((total, part) => total + part.byteLength, 0))
1162
+ let writeOffset = 0
1163
+ for (const part of parts) {
1164
+ normalized.set(part, writeOffset)
1165
+ writeOffset += part.byteLength
1166
+ }
1167
+ return isFmp4InitSegment(normalized) ? normalized : undefined
1168
+ }
1169
+
1170
+ // ─── Internal Helpers ────────────────────────────────────────────
1171
+
1172
+ function hasIsoBmffMagic(data: Uint8Array): boolean {
1173
+ for (let i = 0; i + 8 <= data.length && i < 32; i += 4) {
1174
+ const tag = String.fromCharCode(data[i + 4], data[i + 5], data[i + 6], data[i + 7])
1175
+ if (tag === 'ftyp' || tag === 'moov') return true
1176
+ }
1177
+ return false
1178
+ }
1179
+
1180
+ function findTrackStsdBoxes(data: Uint8Array): Array<{ o: number; sz: number }> {
1181
+ const moov = findBox(data, 0, data.length, 'moov')
1182
+ if (!moov) return []
1183
+
1184
+ const out: Array<{ o: number; sz: number }> = []
1185
+ for (const trak of findBoxes(data, moov.o + 8, moov.o + moov.sz, 'trak')) {
1186
+ const stsd = findNestedBox(data, trak.o + 8, trak.o + trak.sz, ['mdia', 'minf', 'stbl', 'stsd'])
1187
+ if (stsd) out.push(stsd)
1188
+ }
1189
+ return out
1190
+ }
1191
+
1192
+ function findNestedBox(
1193
+ buf: Uint8Array,
1194
+ start: number,
1195
+ end: number,
1196
+ path: string[],
1197
+ ): { o: number; sz: number } | undefined {
1198
+ let s = start
1199
+ let e = end
1200
+ let current: { o: number; sz: number } | undefined
1201
+ for (const type of path) {
1202
+ current = findBox(buf, s, e, type)
1203
+ if (!current) return undefined
1204
+ s = current.o + 8
1205
+ e = current.o + current.sz
1206
+ }
1207
+ return current
1208
+ }
1209
+
1210
+ function findBox(buf: Uint8Array, start: number, end: number, type: string): { o: number; sz: number } | undefined {
1211
+ return findBoxes(buf, start, end, type)[0]
1212
+ }
1213
+
1214
+ function findBoxes(buf: Uint8Array, start: number, end: number, type: string): Array<{ o: number; sz: number }> {
1215
+ const boxes: Array<{ o: number; sz: number }> = []
1216
+ let o = start
1217
+ while (o + 8 <= end) {
1218
+ let sz = (buf[o] << 24) | (buf[o + 1] << 16) | (buf[o + 2] << 8) | buf[o + 3]
1219
+ const t = String.fromCharCode(buf[o + 4], buf[o + 5], buf[o + 6], buf[o + 7])
1220
+ if (sz === 0) sz = end - o
1221
+ if (sz < 8 || o + sz > end) break
1222
+ if (t === type) boxes.push({ o, sz })
1223
+ o += sz
1224
+ }
1225
+ return boxes
1226
+ }
1227
+
1228
+ function writeNalLength(buf: Uint8Array, offset: number, len: number): number {
1229
+ buf[offset] = (len >> 24) & 0xff
1230
+ buf[offset + 1] = (len >> 16) & 0xff
1231
+ buf[offset + 2] = (len >> 8) & 0xff
1232
+ buf[offset + 3] = len & 0xff
1233
+ return offset + 4
1234
+ }
1235
+
1236
+ function h264NalUnitsForKeyframeDetection(
1237
+ data: Uint8Array,
1238
+ options: H264KeyframeDetectionOptions,
1239
+ ): Uint8Array[] {
1240
+ return splitH264SampleNalUnits(data, options).nals
1241
+ }
1242
+
1243
+ function splitPartialAvccNalUnits(data: Uint8Array, lengthSize = 4): Uint8Array[] {
1244
+ const nals: Uint8Array[] = []
1245
+ let i = 0
1246
+ while (i + lengthSize < data.length) {
1247
+ let len = 0
1248
+ for (let k = 0; k < lengthSize; k++) len = (len << 8) | data[i + k]
1249
+ i += lengthSize
1250
+ if (len <= 0 || i >= data.length) break
1251
+ const end = Math.min(i + len, data.length)
1252
+ nals.push(data.subarray(i, end))
1253
+ if (end < i + len) break
1254
+ i = end
1255
+ }
1256
+ return nals
1257
+ }
1258
+
1259
+ function isLikelyRawH264NalUnit(data: Uint8Array): boolean {
1260
+ if (data.length === 0) return false
1261
+ const header = data[0]!
1262
+ if ((header & 0x80) !== 0) return false
1263
+ const nalType = header & 0x1f
1264
+ return nalType > 0 && nalType < 24
1265
+ }
1266
+
1267
+ function rawH264NalToAvcc(data: Uint8Array): Uint8Array {
1268
+ const out = new Uint8Array(4 + data.length)
1269
+ new DataView(out.buffer, out.byteOffset, out.byteLength).setUint32(0, data.length)
1270
+ out.set(data, 4)
1271
+ return out
1272
+ }
1273
+
1274
+ function isH264IntraSlice(nal: Uint8Array): boolean {
1275
+ const sliceType = readH264SliceType(nal)
1276
+ return sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9
1277
+ }
1278
+
1279
+ function readH264SliceType(nal: Uint8Array): number | null {
1280
+ if (nal.length < 2 || (nal[0]! & 0x1f) !== 1) return null
1281
+ const rbsp = removeEmulationPrevention(nal.subarray(1))
1282
+ const state = { bitOffset: 0 }
1283
+ if (readUnsignedExpGolomb(rbsp, state) === null) return null
1284
+ return readUnsignedExpGolomb(rbsp, state)
1285
+ }
1286
+
1287
+ function readUnsignedExpGolomb(data: Uint8Array, state: { bitOffset: number }): number | null {
1288
+ let leadingZeroBits = 0
1289
+ while (true) {
1290
+ const bit = readBit(data, state)
1291
+ if (bit === null) return null
1292
+ if (bit === 1) break
1293
+ leadingZeroBits++
1294
+ if (leadingZeroBits > 30) return null
1295
+ }
1296
+
1297
+ let suffix = 0
1298
+ for (let i = 0; i < leadingZeroBits; i++) {
1299
+ const bit = readBit(data, state)
1300
+ if (bit === null) return null
1301
+ suffix = (suffix << 1) | bit
1302
+ }
1303
+ return (1 << leadingZeroBits) - 1 + suffix
1304
+ }
1305
+
1306
+ function readBit(data: Uint8Array, state: { bitOffset: number }): 0 | 1 | null {
1307
+ if (state.bitOffset >= data.length * 8) return null
1308
+ const byte = data[state.bitOffset >> 3]!
1309
+ const bit = (byte >> (7 - (state.bitOffset & 7))) & 1
1310
+ state.bitOffset++
1311
+ return bit as 0 | 1
1312
+ }
1313
+
1314
+ function hex2(n: number): string {
1315
+ return n.toString(16).padStart(2, '0')
1316
+ }
1317
+
1318
+ function pad2(n: number): string {
1319
+ return String(n).padStart(2, '0')
1320
+ }
1321
+
1322
+ function codecStringFromAv1C(av1C: Uint8Array): string {
1323
+ if (av1C.length < 4) return 'av01.0.01M.08'
1324
+ const profile = (av1C[1]! >>> 5) & 0x07
1325
+ const level = av1C[1]! & 0x1f
1326
+ const tier = (av1C[2]! & 0x80) !== 0 ? 'H' : 'M'
1327
+ let bitDepth = 8
1328
+ if ((av1C[2]! & 0x40) !== 0) {
1329
+ bitDepth = (av1C[2]! & 0x20) !== 0 ? 12 : 10
1330
+ }
1331
+ return `av01.${profile}.${pad2(level)}${tier}.${pad2(bitDepth)}`
1332
+ }
1333
+
1334
+ function codecStringFromVpcC(vpcC: Uint8Array): string {
1335
+ if (vpcC.length < 3) return 'vp09.00.10.08'
1336
+ // ISO/IEC 14496-15 defines vpcC as a FullBox. Some tooling hands callers
1337
+ // the VPCodecConfigurationRecord after the 4-byte FullBox header; tolerate
1338
+ // both shapes because WebCodecs callers only need a stable RFC 6381 fallback.
1339
+ const offset = vpcC.length >= 7 && vpcC[0] <= 1 ? 4 : 0
1340
+ if (offset + 2 >= vpcC.length) return 'vp09.00.10.08'
1341
+ const profile = vpcC[offset]!
1342
+ const level = vpcC[offset + 1]!
1343
+ const bitDepth = (vpcC[offset + 2]! >>> 4) || 8
1344
+ return `vp09.${pad2(profile)}.${pad2(level)}.${pad2(bitDepth)}`
1345
+ }
1346
+
1347
+ /**
1348
+ * Derive an RFC 6381 HEVC codec string (e.g. "hvc1.1.6.L93.90") from the
1349
+ * payload of an hvcC box (the HEVCDecoderConfigurationRecord, ISO 14496-15 §8.3.3.1).
1350
+ * Only the general_* fields in the fixed 23-byte header are needed:
1351
+ * byte 1: general_profile_space(2) | general_tier_flag(1) | general_profile_idc(5)
1352
+ * bytes 2-5: general_profile_compatibility_flags (32 bits)
1353
+ * bytes 6-11: general_constraint_indicator_flags (48 bits)
1354
+ * byte 12: general_level_idc
1355
+ */
1356
+ function hevcCodecStringFromHvcC(c: Uint8Array, sampleEntry: 'hev1' | 'hvc1'): string {
1357
+ return formatHevcCodecString(
1358
+ sampleEntry,
1359
+ (c[1]! >> 6) & 0x3,
1360
+ ((c[1]! >> 5) & 0x1) === 1,
1361
+ c[1]! & 0x1f,
1362
+ ((c[2]! << 24) | (c[3]! << 16) | (c[4]! << 8) | c[5]!) >>> 0,
1363
+ c.subarray(6, 12),
1364
+ c[12]!,
1365
+ )
1366
+ }
1367
+
1368
+ /** Exp-Golomb bitstream writer for SPS/PPS generation. */
1369
+ class BitWriter {
1370
+ buf: number[] = []
1371
+ byte = 0
1372
+ bit = 7
1373
+
1374
+ u(val: number, bits: number) {
1375
+ for (let i = bits - 1; i >= 0; i--) {
1376
+ this.byte |= ((val >> i) & 1) << this.bit
1377
+ if (--this.bit < 0) {
1378
+ this.buf.push(this.byte)
1379
+ this.byte = 0
1380
+ this.bit = 7
1381
+ }
1382
+ }
1383
+ }
1384
+
1385
+ ue(val: number) {
1386
+ const v = val + 1
1387
+ const bits = Math.floor(Math.log2(v)) + 1
1388
+ this.u(0, bits - 1)
1389
+ this.u(v, bits)
1390
+ }
1391
+
1392
+ se(val: number) {
1393
+ this.ue(val <= 0 ? -val * 2 : val * 2 - 1)
1394
+ }
1395
+
1396
+ finish(): Uint8Array {
1397
+ if (this.bit < 7) {
1398
+ this.byte |= 1 << this.bit
1399
+ this.buf.push(this.byte)
1400
+ }
1401
+ return new Uint8Array(this.buf)
1402
+ }
1403
+ }
1404
+
1405
+ function resolveResolution(
1406
+ explicitW: number | undefined,
1407
+ explicitH: number | undefined,
1408
+ level: number,
1409
+ ): { width: number; height: number } {
1410
+ if (explicitW && explicitH) return { width: explicitW, height: explicitH }
1411
+
1412
+ const levelResolutions: Record<number, [number, number]> = {
1413
+ 10: [176, 144], 11: [352, 288], 12: [352, 288], 13: [352, 288],
1414
+ 20: [352, 288], 21: [720, 576], 22: [720, 576],
1415
+ 30: [1280, 720], 31: [1280, 720], 32: [1280, 1024],
1416
+ 40: [1920, 1088], 41: [1920, 1088], 42: [2048, 1088],
1417
+ 50: [4096, 2160], 51: [4096, 2160], 52: [4096, 2160],
1418
+ }
1419
+
1420
+ const [w, h] = levelResolutions[level] ?? [1920, 1088]
1421
+ return { width: explicitW ?? w, height: explicitH ?? h }
1422
+ }
1423
+
1424
+ function buildMinimalSps(
1425
+ profile: number, constraint: number, level: number,
1426
+ width: number, height: number,
1427
+ ): Uint8Array {
1428
+ const widthInMbs = Math.ceil(width / 16)
1429
+ const heightInMapUnits = Math.ceil(height / 16)
1430
+
1431
+ const w = new BitWriter()
1432
+ w.u(0, 1); w.u(3, 2); w.u(7, 5) // NAL header: SPS
1433
+ w.u(profile, 8); w.u(constraint, 8); w.u(level, 8)
1434
+ w.ue(0) // seq_parameter_set_id
1435
+
1436
+ if (profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44) {
1437
+ w.ue(1); w.ue(0); w.ue(0) // chroma + bit depth
1438
+ w.u(0, 1); w.u(0, 1) // transform bypass + scaling matrix
1439
+ }
1440
+
1441
+ w.ue(0) // log2_max_frame_num_minus4
1442
+ w.ue(2) // pic_order_cnt_type=2
1443
+ w.ue(1) // max_num_ref_frames
1444
+ w.u(0, 1) // gaps_in_frame_num
1445
+ w.ue(widthInMbs - 1)
1446
+ w.ue(heightInMapUnits - 1)
1447
+ w.u(1, 1) // frame_mbs_only
1448
+ w.u(1, 1) // direct_8x8_inference
1449
+
1450
+ const cropRight = widthInMbs * 16 - width
1451
+ const cropBottom = heightInMapUnits * 16 - height
1452
+ if (cropRight > 0 || cropBottom > 0) {
1453
+ w.u(1, 1)
1454
+ w.ue(0); w.ue(cropRight / 2); w.ue(0); w.ue(cropBottom / 2)
1455
+ } else {
1456
+ w.u(0, 1)
1457
+ }
1458
+
1459
+ w.u(0, 1) // vui_parameters_present
1460
+ return w.finish()
1461
+ }
1462
+
1463
+ function buildMinimalPps(profile: number): Uint8Array {
1464
+ const isHighProfile = profile === 100 || profile === 110 || profile === 122 || profile === 244 || profile === 44
1465
+ const w = new BitWriter()
1466
+ w.u(0, 1); w.u(3, 2); w.u(8, 5) // NAL header: PPS
1467
+
1468
+ w.ue(0); w.ue(0) // pps_id, sps_id
1469
+ w.u(isHighProfile ? 1 : 0, 1) // entropy_coding_mode
1470
+ w.u(0, 1) // bottom_field_pic_order
1471
+ w.ue(0); w.ue(0); w.ue(0) // slice groups, ref_idx defaults
1472
+ w.u(0, 1); w.u(0, 2) // weighted_pred
1473
+ w.se(0); w.se(0); w.se(0) // qp offsets
1474
+ w.u(1, 1); w.u(0, 1); w.u(0, 1) // deblock, constrained_intra, redundant_pic_cnt
1475
+
1476
+ if (isHighProfile) {
1477
+ w.u(1, 1); w.u(0, 1); w.se(0) // transform_8x8, scaling_matrix, chroma_qp_offset
1478
+ }
1479
+
1480
+ return w.finish()
1481
+ }
1482
+
1483
+ // ─── AAC / ADTS Utilities ──────────────────────────────────────
1484
+
1485
+ /** ISO 14496-3 sampling frequency index lookup. */
1486
+ const ADTS_SAMPLE_RATES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350] as const
1487
+
1488
+ /**
1489
+ * Prepend a 7-byte ADTS header to a raw AAC access unit.
1490
+ *
1491
+ * WebCodecs AudioDecoder configured WITHOUT an AudioSpecificConfig `description`
1492
+ * expects ADTS-framed AAC. MMTP carries raw AAC (no ADTS sync), so this wrapper
1493
+ * must be applied before calling `AudioDecoder.decode()`.
1494
+ */
1495
+ export function wrapAdtsFrame(raw: Uint8Array, sampleRate: number, channels: number): Uint8Array {
1496
+ const sri = ADTS_SAMPLE_RATES.indexOf(sampleRate as typeof ADTS_SAMPLE_RATES[number])
1497
+ const idx = sri >= 0 ? sri : 3 // default 48 kHz
1498
+ const len = 7 + raw.length
1499
+ const out = new Uint8Array(len)
1500
+ out[0] = 0xFF
1501
+ out[1] = 0xF1 // MPEG-4, Layer 0, no CRC
1502
+ out[2] = (1 << 6) | (idx << 2) | (channels >> 2) // profile=AAC-LC(1), freq, ch high
1503
+ out[3] = ((channels & 3) << 6) | ((len >> 11) & 3)
1504
+ out[4] = (len >> 3) & 0xFF
1505
+ out[5] = ((len & 7) << 5) | 0x1F
1506
+ out[6] = 0xFC
1507
+ out.set(raw, 7)
1508
+ return out
1509
+ }
1510
+
1511
+ /**
1512
+ * Extract AAC AudioSpecificConfig (ASC) from an fMP4 init segment.
1513
+ *
1514
+ * Walks moov > trak > mdia > minf > stbl > stsd > mp4a > esds, then descends
1515
+ * the MPEG-4 descriptor chain (ES_Descriptor 0x03 > DecoderConfigDescriptor
1516
+ * 0x04 > DecoderSpecificInfo 0x05) to return the ASC payload.
1517
+ *
1518
+ * Sibling of extractAvcCFromInitSegment, used by the same BLO-4830 / CMSF §3.1
1519
+ * path: when the catalog initData is the full ftyp+moov, WebCodecs' AudioDecoder
1520
+ * needs the raw ASC (~2 bytes for AAC-LC) as `description`, NOT the whole
1521
+ * segment. Without this extraction the configure() succeeds but every decode()
1522
+ * silently produces zero output (no error fires), exactly the symptom that
1523
+ * shows up as "Audio continuity FAILED: energy unstable CV=1.00" in the
1524
+ * 7-point E2E test (CV=1.00 is the meanEnergy=0 sentinel from audio-fft.mjs).
1525
+ *
1526
+ * Returns undefined when:
1527
+ * - data has no ftyp/moov magic in the first 32 bytes
1528
+ * - no mp4a sample entry exists in any trak's stbl
1529
+ * - the esds descriptor chain doesn't yield a DecoderSpecificInfo
1530
+ * - the ASC payload is shorter than 2 bytes (AOT + sampling-freq nibble)
1531
+ *
1532
+ * Walks every trak and returns the first AAC sample entry. This mirrors
1533
+ * extractAvcCFromInitSegment so multiplexed ftyp+moov init blobs work
1534
+ * regardless of track order.
1535
+ */
1536
+ export function extractAacAscFromInitSegment(data: Uint8Array): AacAscInfo | undefined {
1537
+ if (!hasIsoBmffMagic(data)) return undefined
1538
+
1539
+ for (const stsd of findTrackStsdBoxes(data)) {
1540
+ const info = extractAacAscFromStsd(data, stsd)
1541
+ if (info) return info
1542
+ }
1543
+
1544
+ return undefined
1545
+ }
1546
+
1547
+ function extractAacAscFromStsd(data: Uint8Array, stsd: { o: number; sz: number }): AacAscInfo | undefined {
1548
+ // stsd: 8 box header + 4 version+flags + 4 entry_count = 16 bytes before entries.
1549
+ const s = stsd.o + 16
1550
+ const e = stsd.o + stsd.sz
1551
+ // mp4a AudioSampleEntry minimum length: 8 box header + 28 fixed fields = 36 bytes
1552
+ // before any child boxes (esds lives here).
1553
+ const mp4a = findBox(data, s, e, 'mp4a')
1554
+ if (!mp4a || mp4a.sz < 36) return undefined
1555
+ const esds = findBox(data, mp4a.o + 36, mp4a.o + mp4a.sz, 'esds')
1556
+ // esds = 8 box header + 4 full-box version+flags = 12 bytes minimum before descriptors.
1557
+ if (!esds || esds.sz < 12) return undefined
1558
+ const esdsPayload = data.subarray(esds.o + 8, esds.o + esds.sz)
1559
+ const asc = extractAudioSpecificConfig(esdsPayload)
1560
+ if (!asc || asc.length < 2) return undefined
1561
+ // AudioObjectType = top 5 bits of ASC[0]. AAC-LC = 2, HE-AAC = 5, etc.
1562
+ const aot = asc[0] >> 3
1563
+ return { codec: `mp4a.40.${aot}`, description: asc }
1564
+ }
1565
+
1566
+ /**
1567
+ * Walk an esds descriptor chain to find DecoderSpecificInfo (tag 0x05).
1568
+ * Step over fixed-size headers of container descriptors (0x03, 0x04) so the
1569
+ * scan continues into their children rather than skipping their full payload.
1570
+ *
1571
+ * Assumes the ES_Descriptor flags byte is zero (no streamDependence, URL, OCR
1572
+ * ES_ID fields). FFmpeg's mov/mp4 muxer emits exactly that for all standard
1573
+ * AAC tracks; the assumption is the same one extractAvcCFromInitSegment makes
1574
+ * about the avc1 sample entry's reserved fields.
1575
+ *
1576
+ * Input is the esds box payload AFTER the 8-byte box header; the first 4 bytes
1577
+ * (full-box version+flags) are skipped here.
1578
+ */
1579
+ function extractAudioSpecificConfig(esds: Uint8Array): Uint8Array | undefined {
1580
+ let offset = 4 // skip esds version+flags
1581
+ while (offset < esds.length) {
1582
+ const tag = esds[offset++]
1583
+ let size = 0
1584
+ for (let i = 0; i < 4 && offset < esds.length; i++) {
1585
+ const b = esds[offset++]
1586
+ size = (size << 7) | (b & 0x7f)
1587
+ if ((b & 0x80) === 0) break
1588
+ }
1589
+ if (tag === 0x05) {
1590
+ if (offset + size > esds.length) return undefined
1591
+ return esds.subarray(offset, offset + size)
1592
+ }
1593
+ if (tag === 0x03) {
1594
+ offset += 3 // ES_ID (2) + flags (1)
1595
+ } else if (tag === 0x04) {
1596
+ offset += 13 // OTI (1) + streamType (1) + bufferSizeDB (3) + maxBitrate (4) + avgBitrate (4)
1597
+ } else {
1598
+ offset += size
1599
+ }
1600
+ }
1601
+ return undefined
1602
+ }
1603
+
1604
+ // =============================================================================
1605
+ // Inline Tests (vitest)
1606
+ // =============================================================================
1607
+
1608
+ if (import.meta.vitest) {
1609
+ const { describe, test, expect } = import.meta.vitest
1610
+
1611
+ describe('codec-utils', () => {
1612
+ describe('isAnnexB', () => {
1613
+ test('4-byte start code', () => {
1614
+ expect(isAnnexB(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(true)
1615
+ })
1616
+ test('3-byte start code', () => {
1617
+ expect(isAnnexB(new Uint8Array([0, 0, 1, 0x65]))).toBe(true)
1618
+ })
1619
+ test('rejects AVCC', () => {
1620
+ expect(isAnnexB(new Uint8Array([0, 0, 0, 10, 0x65]))).toBe(false)
1621
+ })
1622
+ })
1623
+
1624
+ describe('findAnnexBStartCode', () => {
1625
+ test('at offset 0', () => {
1626
+ expect(findAnnexBStartCode(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(0)
1627
+ })
1628
+ test('at offset 5', () => {
1629
+ expect(findAnnexBStartCode(new Uint8Array([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0, 0, 1, 0x65]))).toBe(5)
1630
+ })
1631
+ test('returns -1 when none', () => {
1632
+ expect(findAnnexBStartCode(new Uint8Array([0xAA, 0xBB, 0xCC]))).toBe(-1)
1633
+ })
1634
+ })
1635
+
1636
+ describe('splitAnnexBNalUnits', () => {
1637
+ test('splits two NALs', () => {
1638
+ const data = new Uint8Array([0, 0, 0, 1, 0x65, 0xAA, 0xBB, 0, 0, 0, 1, 0x41, 0xCC])
1639
+ const nals = splitAnnexBNalUnits(data)
1640
+ expect(nals.length).toBe(2)
1641
+ expect(nals[0]).toEqual(new Uint8Array([0x65, 0xAA, 0xBB]))
1642
+ expect(nals[1]).toEqual(new Uint8Array([0x41, 0xCC]))
1643
+ })
1644
+ test('handles 3-byte start codes', () => {
1645
+ const nals = splitAnnexBNalUnits(new Uint8Array([0, 0, 1, 0x67, 0x42, 0, 0, 1, 0x68, 0x01]))
1646
+ expect(nals.length).toBe(2)
1647
+ })
1648
+ })
1649
+
1650
+ describe('annexBToAvcc', () => {
1651
+ test('converts single NAL', () => {
1652
+ const annexB = new Uint8Array([0, 0, 0, 1, 0x41, 0xAA, 0xBB])
1653
+ const avcc = annexBToAvcc(annexB)
1654
+ expect(avcc[3]).toBe(3) // NAL length
1655
+ expect(avcc[4]).toBe(0x41)
1656
+ })
1657
+ test('prepends SPS/PPS', () => {
1658
+ const annexB = new Uint8Array([0, 0, 0, 1, 0x65, 0x01])
1659
+ const avcc = annexBToAvcc(annexB, { sps: new Uint8Array([0x67, 0x42]), pps: new Uint8Array([0x68, 0x01]) })
1660
+ expect(avcc[3]).toBe(2) // SPS length
1661
+ expect(avcc[4]).toBe(0x67)
1662
+ })
1663
+ })
1664
+
1665
+ describe('detectH264Keyframe', () => {
1666
+ test('IDR in Annex-B', () => {
1667
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x65]))).toBe(true)
1668
+ })
1669
+ test('SPS in Annex-B', () => {
1670
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x67]))).toBe(true)
1671
+ })
1672
+ test('IDR in AVCC', () => {
1673
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 5, 0x65, 1, 2, 3, 4]))).toBe(true)
1674
+ })
1675
+ test('IDR after AVCC AUD', () => {
1676
+ expect(detectH264Keyframe(new Uint8Array([
1677
+ 0, 0, 0, 1, 0x09,
1678
+ 0, 0, 0, 2, 0x65, 0xaa,
1679
+ ]))).toBe(true)
1680
+ })
1681
+ test('non-IDR AVC I-slice in AVCC', () => {
1682
+ expect(detectH264Keyframe(new Uint8Array([
1683
+ 0, 0, 0, 2, 0x41, 0x88,
1684
+ ]))).toBe(true)
1685
+ })
1686
+ test('raw H.264 IDR NAL', () => {
1687
+ expect(detectH264Keyframe(new Uint8Array([0x65, 0x88, 0x84]))).toBe(true)
1688
+ })
1689
+ test('raw H.264 non-IDR I-slice NAL', () => {
1690
+ expect(detectH264Keyframe(new Uint8Array([0x41, 0x88]))).toBe(true)
1691
+ })
1692
+ test('classifies partial length-prefixed IDR only when requested', () => {
1693
+ const partial = new Uint8Array([0, 0, 0x20, 0, 0x65])
1694
+ expect(classifyH264Keyframe(partial)).toBeUndefined()
1695
+ expect(classifyH264Keyframe(partial, { allowPartialAvcc: true })).toBe(true)
1696
+ })
1697
+ test('rejects P-frame', () => {
1698
+ expect(detectH264Keyframe(new Uint8Array([0, 0, 0, 1, 0x41]))).toBe(false)
1699
+ })
1700
+ test('rejects AVCC sample with only non-IDR slices', () => {
1701
+ expect(detectH264Keyframe(new Uint8Array([
1702
+ 0, 0, 0, 2, 0x41, 0xc0,
1703
+ 0, 0, 0, 2, 0x41, 0xc0,
1704
+ ]))).toBe(false)
1705
+ })
1706
+ })
1707
+
1708
+ describe('avccToAnnexB', () => {
1709
+ test('converts with SPS/PPS on keyframe', () => {
1710
+ const avcc = new Uint8Array([0, 0, 0, 3, 0x65, 0xAA, 0xBB])
1711
+ const spsPps: SpsPps = { sps: new Uint8Array([0x67, 0x42]), pps: new Uint8Array([0x68, 0x01]) }
1712
+ const annexB = avccToAnnexB(avcc, true, spsPps)
1713
+ expect(annexB[3]).toBe(1) // start code
1714
+ expect(annexB[4]).toBe(0x67) // SPS
1715
+ })
1716
+ })
1717
+
1718
+ describe('codecStringFromAvcC', () => {
1719
+ test('derives avc1.64002a', () => {
1720
+ expect(codecStringFromAvcC(new Uint8Array([1, 100, 0, 42]))).toBe('avc1.64002a')
1721
+ })
1722
+ })
1723
+
1724
+ describe('extractAacAscFromInitSegment', () => {
1725
+ // Build a minimal valid ftyp+moov containing one mp4a sample entry whose
1726
+ // esds carries the supplied AudioSpecificConfig. The walker only needs
1727
+ // the box hierarchy moov > trak > mdia > minf > stbl > stsd > mp4a > esds;
1728
+ // mvhd/tkhd/mdhd/hdlr/smhd/dinf/stts/stsc/stsz/stco are spec-required for
1729
+ // a truly valid init but the extractor never reads them, so we omit them
1730
+ // to keep the fixture tight (~150 bytes vs ~500). FFmpeg's real output
1731
+ // includes them; the production path passes through `findBox` exactly
1732
+ // the same way.
1733
+ function buildMinimalAacInit(asc: Uint8Array): Uint8Array {
1734
+ const box = (type: string, payload: Uint8Array): Uint8Array => {
1735
+ const sz = 8 + payload.length
1736
+ const out = new Uint8Array(sz)
1737
+ out[0] = (sz >>> 24) & 0xff
1738
+ out[1] = (sz >>> 16) & 0xff
1739
+ out[2] = (sz >>> 8) & 0xff
1740
+ out[3] = sz & 0xff
1741
+ out[4] = type.charCodeAt(0); out[5] = type.charCodeAt(1)
1742
+ out[6] = type.charCodeAt(2); out[7] = type.charCodeAt(3)
1743
+ out.set(payload, 8)
1744
+ return out
1745
+ }
1746
+ const concat = (...arrs: Uint8Array[]): Uint8Array => {
1747
+ const total = arrs.reduce((n, a) => n + a.length, 0)
1748
+ const out = new Uint8Array(total)
1749
+ let off = 0
1750
+ for (const a of arrs) { out.set(a, off); off += a.length }
1751
+ return out
1752
+ }
1753
+ // ftyp: major_brand=iso5, minor=0x200, compat=iso5,iso6
1754
+ const ftyp = box('ftyp', new Uint8Array([
1755
+ 0x69, 0x73, 0x6f, 0x35, // 'iso5'
1756
+ 0x00, 0x00, 0x02, 0x00, // minor 0x200
1757
+ 0x69, 0x73, 0x6f, 0x35, // 'iso5'
1758
+ 0x69, 0x73, 0x6f, 0x36, // 'iso6'
1759
+ ]))
1760
+
1761
+ // ESDescriptor (0x03) > DecoderConfigDescriptor (0x04) > DecoderSpecificInfo (0x05) > ASC.
1762
+ // Use 1-byte BER sizes (all payloads < 128).
1763
+ const dsi = new Uint8Array(2 + asc.length)
1764
+ dsi[0] = 0x05; dsi[1] = asc.length
1765
+ dsi.set(asc, 2)
1766
+ // DCD = 13 fixed bytes + DSI: OTI=0x40 (AAC), streamType=0x05<<2|0x01 (audio, upStream=0)
1767
+ const dcd = new Uint8Array(2 + 13 + dsi.length)
1768
+ dcd[0] = 0x04; dcd[1] = 13 + dsi.length
1769
+ dcd[2] = 0x40 // objectTypeIndication: Audio ISO/IEC 14496-3
1770
+ dcd[3] = (0x05 << 2) | 1 // streamType=AudioStream(5), upStream=0, reserved=1
1771
+ // bufferSizeDB(3) + maxBitrate(4) + avgBitrate(4) all zero is fine for the walker.
1772
+ dcd.set(dsi, 15)
1773
+ // ESDescriptor: ES_ID=0, flags=0, then DCD
1774
+ const esd = new Uint8Array(2 + 3 + dcd.length)
1775
+ esd[0] = 0x03; esd[1] = 3 + dcd.length
1776
+ // ES_ID(2) + flags(1) all zero
1777
+ esd.set(dcd, 5)
1778
+ // esds box = full-box (4-byte version+flags) + ES_Descriptor chain
1779
+ const esdsPayload = new Uint8Array(4 + esd.length)
1780
+ esdsPayload.set(esd, 4)
1781
+ const esds = box('esds', esdsPayload)
1782
+
1783
+ // mp4a sample entry: 6 reserved + 2 dref_idx + 8 reserved + 2 channels + 2 samplesize +
1784
+ // 2 pre_defined + 2 reserved + 4 samplerate(48000<<16) = 28 bytes,
1785
+ // then esds child.
1786
+ const mp4aFixed = new Uint8Array(28)
1787
+ mp4aFixed[7] = 0x01 // data_reference_index = 1
1788
+ mp4aFixed[16] = 0x00; mp4aFixed[17] = 0x01 // channelcount = 1
1789
+ mp4aFixed[18] = 0x00; mp4aFixed[19] = 0x10 // samplesize = 16
1790
+ // samplerate at offset 24-27 (top 16 bits = sampleRate). 48000 = 0xBB80.
1791
+ mp4aFixed[24] = 0xBB; mp4aFixed[25] = 0x80
1792
+ const mp4a = box('mp4a', concat(mp4aFixed, esds))
1793
+
1794
+ // stsd: 4 ver+flags + 4 entry_count(=1) + entries
1795
+ const stsdPayload = new Uint8Array(8 + mp4a.length)
1796
+ stsdPayload[7] = 0x01 // entry_count = 1
1797
+ stsdPayload.set(mp4a, 8)
1798
+ const stsd = box('stsd', stsdPayload)
1799
+
1800
+ const stbl = box('stbl', stsd)
1801
+ const minf = box('minf', stbl)
1802
+ const mdia = box('mdia', minf)
1803
+ const trak = box('trak', mdia)
1804
+ const moov = box('moov', trak)
1805
+
1806
+ return concat(ftyp, moov)
1807
+ }
1808
+
1809
+ test('extracts AAC-LC mono 48k ASC and derives codec', () => {
1810
+ // AOT=2 (00010), samplingFrequencyIndex=3 (0011, 48000Hz), channelConfig=1 (0001),
1811
+ // GA: frameLengthFlag=0, dependsOnCoreCoder=0, extensionFlag=0 → 0x11 0x88.
1812
+ const asc = new Uint8Array([0x11, 0x88])
1813
+ const init = buildMinimalAacInit(asc)
1814
+ const result = extractAacAscFromInitSegment(init)
1815
+ expect(result).toBeDefined()
1816
+ expect(result!.codec).toBe('mp4a.40.2')
1817
+ expect(Array.from(result!.description)).toEqual([0x11, 0x88])
1818
+ })
1819
+
1820
+ test('derives AOT 5 (HE-AAC) from ASC[0] >> 3', () => {
1821
+ // AOT=5 (00101), rest don't matter for codec derivation.
1822
+ const asc = new Uint8Array([0x29, 0x88]) // 00101 001 1000 1000
1823
+ const init = buildMinimalAacInit(asc)
1824
+ expect(extractAacAscFromInitSegment(init)?.codec).toBe('mp4a.40.5')
1825
+ })
1826
+
1827
+ test('rejects data without ftyp/moov magic', () => {
1828
+ expect(extractAacAscFromInitSegment(new Uint8Array(64))).toBeUndefined()
1829
+ })
1830
+
1831
+ test('rejects ftyp+moov with no audio (no mp4a sample entry)', () => {
1832
+ // ftyp + moov with empty trak chain — no stsd, let alone mp4a.
1833
+ const ftyp = new Uint8Array([
1834
+ 0, 0, 0, 24, 0x66, 0x74, 0x79, 0x70,
1835
+ 0x69, 0x73, 0x6f, 0x35, 0, 0, 2, 0,
1836
+ 0x69, 0x73, 0x6f, 0x35, 0x69, 0x73, 0x6f, 0x36,
1837
+ ])
1838
+ const moov = new Uint8Array([0, 0, 0, 8, 0x6d, 0x6f, 0x6f, 0x76]) // empty moov
1839
+ const cat = new Uint8Array(ftyp.length + moov.length)
1840
+ cat.set(ftyp); cat.set(moov, ftyp.length)
1841
+ expect(extractAacAscFromInitSegment(cat)).toBeUndefined()
1842
+ })
1843
+ })
1844
+
1845
+ describe('hevc', () => {
1846
+ // Minimal HEVC NAL units: header byte0 = (type << 1). VPS=32(0x40), SPS=33(0x42), PPS=34(0x44).
1847
+ // Real x265 repeat-headers=1 parameter sets (the cast encoder profile); the
1848
+ // SPS carries emulation-prevention bytes inside profile_tier_level. See
1849
+ // test-vectors/hevc-x265-repeat-headers-v1.json for the full access units.
1850
+ const vps = new Uint8Array([
1851
+ 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00,
1852
+ 0x03, 0x00, 0x00, 0x03, 0x00, 0x3c, 0x92, 0x80, 0x90,
1853
+ ])
1854
+ const sps = new Uint8Array([
1855
+ 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00,
1856
+ 0x03, 0x00, 0x3c, 0xa0, 0x0a, 0x08, 0x0f, 0x16, 0x59, 0x2a, 0x49, 0x32, 0xbc, 0x05, 0xa0,
1857
+ 0x20, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x03, 0x03, 0xc1,
1858
+ ])
1859
+ const pps = new Uint8Array([0x44, 0x01, 0xc1, 0x72, 0xb4, 0x62, 0x40])
1860
+ const idr = new Uint8Array([0x26, 0x01, 0xaf, 0x06]) // type 19 (IDR_W_RADL)
1861
+ const lp = (...nals: Uint8Array[]) => {
1862
+ const parts: number[] = []
1863
+ for (const n of nals) { parts.push((n.length >> 24) & 0xff, (n.length >> 16) & 0xff, (n.length >> 8) & 0xff, n.length & 0xff, ...n) }
1864
+ return Uint8Array.from(parts)
1865
+ }
1866
+
1867
+ test('isHevcCodec', () => {
1868
+ expect(isHevcCodec('hev1.1.6.L93.B0')).toBe(true)
1869
+ expect(isHevcCodec('hvc1.1.6.L93.B0')).toBe(true)
1870
+ expect(isHevcCodec('avc1.640033')).toBe(false)
1871
+ })
1872
+
1873
+ test('removeEmulationPrevention strips 0x03', () => {
1874
+ expect(Array.from(removeEmulationPrevention(new Uint8Array([0, 0, 0x03, 1])))).toEqual([0, 0, 1])
1875
+ expect(Array.from(removeEmulationPrevention(new Uint8Array([0xaa, 0xbb])))).toEqual([0xaa, 0xbb])
1876
+ })
1877
+
1878
+ test('splitAvccNalUnits splits length-prefixed NALs', () => {
1879
+ const nals = splitAvccNalUnits(lp(vps, sps))
1880
+ expect(nals.length).toBe(2)
1881
+ expect(nals[0]).toEqual(vps)
1882
+ expect(nals[1]).toEqual(sps)
1883
+ })
1884
+
1885
+ test('isCompleteAvccSample accepts complete length-prefixed NAL samples', () => {
1886
+ expect(isCompleteAvccSample(lp(new Uint8Array([0x09]), idr))).toBe(true)
1887
+ })
1888
+
1889
+ test('isCompleteAvccSample rejects Annex-B start-code samples', () => {
1890
+ expect(isCompleteAvccSample(new Uint8Array([0, 0, 0, 1, 0x65, 0xaa]))).toBe(false)
1891
+ })
1892
+
1893
+ test('isCompleteAvccSample rejects truncated length-prefixed samples', () => {
1894
+ expect(isCompleteAvccSample(new Uint8Array([0, 0, 0, 4, 0x65, 0xaa]))).toBe(false)
1895
+ })
1896
+
1897
+ test('normalizeH264SampleToAvcc leaves complete AVCC samples unchanged', () => {
1898
+ const avcc = lp(new Uint8Array([0x09]), idr)
1899
+ const normalized = normalizeH264SampleToAvcc(avcc)
1900
+ expect(normalized.format).toBe('avcc')
1901
+ expect(normalized.data).toBe(avcc)
1902
+ })
1903
+
1904
+ test('normalizeH264SampleToAvcc converts a single raw H.264 NAL to AVCC', () => {
1905
+ const raw = new Uint8Array([0x65, 0x88, 0x84])
1906
+ const normalized = normalizeH264SampleToAvcc(raw)
1907
+ expect(normalized.format).toBe('raw-nal')
1908
+ expect(normalized.data).toEqual(new Uint8Array([0, 0, 0, 3, 0x65, 0x88, 0x84]))
1909
+ })
1910
+
1911
+ test('splitH264SampleNalUnits exposes Annex-B NALs without AVCC conversion', () => {
1912
+ const annexB = new Uint8Array([0, 0, 0, 1, 0x65, 0x88, 0x84])
1913
+ const sample = splitH264SampleNalUnits(annexB)
1914
+ expect(sample.format).toBe('annexb')
1915
+ expect(sample.nals).toHaveLength(1)
1916
+ expect(sample.nals[0]).toEqual(new Uint8Array([0x65, 0x88, 0x84]))
1917
+ })
1918
+
1919
+ test('splitH264SampleNalUnits exposes single raw H.264 NALs without wrapping', () => {
1920
+ const raw = new Uint8Array([0x65, 0x88, 0x84])
1921
+ const sample = splitH264SampleNalUnits(raw)
1922
+ expect(sample.format).toBe('raw-nal')
1923
+ expect(sample.nals).toEqual([raw])
1924
+ })
1925
+
1926
+ test('splitH264SampleNalUnits can expose partial AVCC NAL headers', () => {
1927
+ const partial = new Uint8Array([0, 0, 0, 5, 0x65, 0x88])
1928
+ const sample = splitH264SampleNalUnits(partial, { allowPartialAvcc: true })
1929
+ expect(sample.format).toBe('partial-avcc')
1930
+ expect(sample.nals).toHaveLength(1)
1931
+ expect(sample.nals[0]).toEqual(new Uint8Array([0x65, 0x88]))
1932
+ })
1933
+
1934
+ test('extractHevcParameterSets finds VPS/SPS/PPS (length-prefixed)', () => {
1935
+ const sets = extractHevcParameterSets(lp(vps, sps, pps, idr))
1936
+ expect(sets.vps).toEqual(vps)
1937
+ expect(sets.sps).toEqual(sps)
1938
+ expect(sets.pps).toEqual(pps)
1939
+ })
1940
+
1941
+ test('extractHevcParameterSets finds VPS/SPS/PPS (Annex-B)', () => {
1942
+ const sc = new Uint8Array([0, 0, 0, 1])
1943
+ const annexb = Uint8Array.from([...sc, ...vps, ...sc, ...sps, ...sc, ...pps, ...sc, ...idr])
1944
+ const sets = extractHevcParameterSets(annexb)
1945
+ expect(sets.vps).toEqual(vps)
1946
+ expect(sets.sps).toEqual(sps)
1947
+ expect(sets.pps).toEqual(pps)
1948
+ })
1949
+
1950
+ test('generateHvcCFromParameterSets builds a valid hvcC', () => {
1951
+ const hvcc = generateHvcCFromParameterSets({ vps, sps, pps })!
1952
+ expect(hvcc).not.toBeNull()
1953
+ expect(hvcc[0]).toBe(1) // configurationVersion
1954
+ expect(hvcc[1]).toBe(sps[3]) // general profile_tier_level byte 0 copied from SPS[3]
1955
+ expect(hvcc[22]).toBe(3) // numOfArrays (VPS+SPS+PPS)
1956
+ // first array entry: NAL_unit_type 32 (VPS), numNalus 1
1957
+ expect(hvcc[23] & 0x3f).toBe(32)
1958
+ expect((hvcc[24] << 8) | hvcc[25]).toBe(1)
1959
+ })
1960
+
1961
+ test('generateHvcCFromParameterSets returns null without SPS/PPS', () => {
1962
+ expect(generateHvcCFromParameterSets({ vps })).toBeNull()
1963
+ })
1964
+
1965
+ test('extractHevcCodecDescription end-to-end from keyframe', () => {
1966
+ const desc = extractHevcCodecDescription(lp(vps, sps, pps, idr))
1967
+ expect(desc).not.toBeNull()
1968
+ expect(desc![0]).toBe(1)
1969
+ })
1970
+
1971
+ test('hevcVclOnly strips parameter-set NALs, keeps VCL', () => {
1972
+ const stripped = hevcVclOnly(lp(vps, sps, pps, idr))
1973
+ const nals = splitAvccNalUnits(stripped)
1974
+ expect(nals.length).toBe(1) // only the IDR VCL NAL remains
1975
+ expect(nals[0]).toEqual(idr)
1976
+ })
1977
+
1978
+ test('hevcVclOnly is a no-op for VCL-only length-prefixed input', () => {
1979
+ const vclOnly = lp(idr)
1980
+ expect(hevcVclOnly(vclOnly)).toBe(vclOnly)
1981
+ })
1982
+ })
1983
+ })
1984
+ }