@osmo.inc/cli 0.1.15

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 (949) hide show
  1. package/DEPLOY.md +87 -0
  2. package/README.md +243 -0
  3. package/RELEASE.md +164 -0
  4. package/SETUP.md +60 -0
  5. package/THIRD_PARTY_NOTICES.md +18 -0
  6. package/bin/osmo.mjs +12 -0
  7. package/dist/osmo.js +7817 -0
  8. package/node_modules/@esbuild/darwin-arm64/README.md +3 -0
  9. package/node_modules/@esbuild/darwin-arm64/bin/esbuild +0 -0
  10. package/node_modules/@esbuild/darwin-arm64/package.json +20 -0
  11. package/node_modules/esbuild/LICENSE.md +21 -0
  12. package/node_modules/esbuild/README.md +3 -0
  13. package/node_modules/esbuild/bin/esbuild +223 -0
  14. package/node_modules/esbuild/install.js +289 -0
  15. package/node_modules/esbuild/lib/main.d.ts +716 -0
  16. package/node_modules/esbuild/lib/main.js +2532 -0
  17. package/node_modules/esbuild/package.json +49 -0
  18. package/node_modules/fflate/LICENSE +21 -0
  19. package/node_modules/fflate/esm/index.mjs +2710 -0
  20. package/node_modules/fflate/package.json +128 -0
  21. package/node_modules/typescript/LICENSE.txt +55 -0
  22. package/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
  23. package/node_modules/typescript/lib/lib.d.ts +22 -0
  24. package/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
  25. package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
  26. package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
  27. package/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
  28. package/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
  29. package/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
  30. package/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
  31. package/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
  32. package/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
  33. package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
  34. package/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
  35. package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
  36. package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
  37. package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
  38. package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
  39. package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
  40. package/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
  41. package/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
  42. package/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
  43. package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
  44. package/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
  45. package/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
  46. package/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
  47. package/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
  48. package/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
  49. package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
  50. package/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
  51. package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
  52. package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
  53. package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
  54. package/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
  55. package/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
  56. package/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
  57. package/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
  58. package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
  59. package/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
  60. package/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
  61. package/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
  62. package/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
  63. package/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
  64. package/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
  65. package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
  66. package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
  67. package/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
  68. package/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
  69. package/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
  70. package/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
  71. package/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
  72. package/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
  73. package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
  74. package/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
  75. package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
  76. package/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
  77. package/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
  78. package/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
  79. package/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
  80. package/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
  81. package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
  82. package/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
  83. package/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
  84. package/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
  85. package/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
  86. package/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
  87. package/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
  88. package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
  89. package/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
  90. package/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
  91. package/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
  92. package/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
  93. package/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
  94. package/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
  95. package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
  96. package/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
  97. package/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
  98. package/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
  99. package/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
  100. package/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
  101. package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
  102. package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
  103. package/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
  104. package/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
  105. package/node_modules/typescript/lib/lib.es6.d.ts +23 -0
  106. package/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
  107. package/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
  108. package/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
  109. package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
  110. package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
  111. package/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
  112. package/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
  113. package/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
  114. package/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
  115. package/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
  116. package/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
  117. package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
  118. package/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
  119. package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
  120. package/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
  121. package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
  122. package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
  123. package/node_modules/typescript/lib/typescript.js +382 -0
  124. package/node_modules/typescript/package.json +57 -0
  125. package/package.json +54 -0
  126. package/release.json +136 -0
  127. package/runtime/apps/native/tools/compile.mjs +217 -0
  128. package/runtime/packages/scene/scripts/validate-strict.mjs +246 -0
  129. package/runtime/packages/scene/src/clip.d.mts +14 -0
  130. package/runtime/packages/scene/src/clip.mjs +15 -0
  131. package/runtime/packages/scene/src/color-value.d.mts +5 -0
  132. package/runtime/packages/scene/src/color-value.mjs +19 -0
  133. package/runtime/packages/scene/src/color.ts +1 -0
  134. package/runtime/packages/scene/src/effects.ts +213 -0
  135. package/runtime/packages/scene/src/elements.ts +491 -0
  136. package/runtime/packages/scene/src/evaluate.ts +919 -0
  137. package/runtime/packages/scene/src/index.ts +69 -0
  138. package/runtime/packages/scene/src/jsx-runtime.ts +73 -0
  139. package/runtime/packages/scene/src/knob-schema.ts +115 -0
  140. package/runtime/packages/scene/src/knobs.ts +118 -0
  141. package/runtime/packages/scene/src/mask.d.mts +6 -0
  142. package/runtime/packages/scene/src/mask.mjs +17 -0
  143. package/runtime/packages/scene/src/metadata.d.mts +23 -0
  144. package/runtime/packages/scene/src/metadata.mjs +44 -0
  145. package/runtime/packages/scene/src/motion-sampling.d.mts +2 -0
  146. package/runtime/packages/scene/src/motion-sampling.mjs +50 -0
  147. package/runtime/packages/scene/src/motion.ts +121 -0
  148. package/runtime/packages/scene/src/shader.ts +57 -0
  149. package/src/resource-examples/example-image/landscape.svg +7 -0
  150. package/src/resource-examples/example-image/resource.json +8 -0
  151. package/src/resource-examples/example-model/gem.glb +0 -0
  152. package/src/resource-examples/example-model/preview.svg +1 -0
  153. package/src/resource-examples/example-model/resource.json +7 -0
  154. package/src/resource-examples/example-scene/resource.json +1 -0
  155. package/src/resource-examples/example-scene/scene.tsx +8 -0
  156. package/src/resource-examples/example-shader/effect.wgsl +7 -0
  157. package/src/resource-examples/example-shader/preview.svg +1 -0
  158. package/src/resource-examples/example-shader/resource.json +27 -0
  159. package/src/resource-examples/example-shader/tint.wgsl +7 -0
  160. package/src/resource-examples/example-video/ambient.mp4 +0 -0
  161. package/src/resource-examples/example-video/resource.json +9 -0
  162. package/src/starter/scene.tsx +20 -0
  163. package/studio/index.html +2 -0
  164. package/studio/public/check-circle-filled.svg +3 -0
  165. package/studio/public/comment-cursor/active-outer.svg +3 -0
  166. package/studio/public/comment-cursor/default.svg +6 -0
  167. package/studio/public/osmo-studio-logo.svg +12 -0
  168. package/studio/public/pencil-simple.svg +5 -0
  169. package/studio/src/active-outer-TVYUEURE.svg +3 -0
  170. package/studio/src/app.css +4 -0
  171. package/studio/src/app.js +705 -0
  172. package/studio/src/default-6CTRH4V5.svg +6 -0
  173. package/studio/src/engine_bg.wasm +0 -0
  174. package/studio/src/inter-cyrillic-ext-wght-normal-IYF56FF6.woff2 +0 -0
  175. package/studio/src/inter-cyrillic-wght-normal-JEOLYBOO.woff2 +0 -0
  176. package/studio/src/inter-greek-ext-wght-normal-EOVOK2B5.woff2 +0 -0
  177. package/studio/src/inter-greek-wght-normal-IRE366VL.woff2 +0 -0
  178. package/studio/src/inter-latin-ext-wght-normal-HA22NDSG.woff2 +0 -0
  179. package/studio/src/inter-latin-wght-normal-NRMW37G5.woff2 +0 -0
  180. package/studio/src/inter-vietnamese-wght-normal-CE5GGD3W.woff2 +0 -0
  181. package/studio/src/osmo-studio-logo-D5R2V77B.svg +12 -0
  182. package/third-party/cargo/adler2@2.0.1/Cargo.toml +91 -0
  183. package/third-party/cargo/adler2@2.0.1/LICENSE-0BSD +12 -0
  184. package/third-party/cargo/adler2@2.0.1/LICENSE-APACHE +201 -0
  185. package/third-party/cargo/adler2@2.0.1/LICENSE-MIT +23 -0
  186. package/third-party/cargo/allocator-api2@0.2.21/Cargo.toml +48 -0
  187. package/third-party/cargo/allocator-api2@0.2.21/LICENSE-APACHE +176 -0
  188. package/third-party/cargo/allocator-api2@0.2.21/LICENSE-MIT +23 -0
  189. package/third-party/cargo/arrayref@0.3.9/Cargo.toml +23 -0
  190. package/third-party/cargo/arrayref@0.3.9/LICENSE +26 -0
  191. package/third-party/cargo/arrayvec@0.7.8/Cargo.toml +112 -0
  192. package/third-party/cargo/arrayvec@0.7.8/LICENSE-APACHE +201 -0
  193. package/third-party/cargo/arrayvec@0.7.8/LICENSE-MIT +25 -0
  194. package/third-party/cargo/ascii@1.1.0/Cargo.toml +41 -0
  195. package/third-party/cargo/ascii@1.1.0/LICENSE-APACHE +201 -0
  196. package/third-party/cargo/ascii@1.1.0/LICENSE-MIT +22 -0
  197. package/third-party/cargo/ash@0.38.0+1.3.281/Cargo.toml +66 -0
  198. package/third-party/cargo/ash@0.38.0+1.3.281/LICENSE-APACHE +67 -0
  199. package/third-party/cargo/ash@0.38.0+1.3.281/LICENSE-MIT +25 -0
  200. package/third-party/cargo/autocfg@1.5.1/Cargo.toml +75 -0
  201. package/third-party/cargo/autocfg@1.5.1/LICENSE-APACHE +201 -0
  202. package/third-party/cargo/autocfg@1.5.1/LICENSE-MIT +25 -0
  203. package/third-party/cargo/base64@0.13.1/Cargo.toml +53 -0
  204. package/third-party/cargo/base64@0.13.1/LICENSE-APACHE +201 -0
  205. package/third-party/cargo/base64@0.13.1/LICENSE-MIT +21 -0
  206. package/third-party/cargo/base64@0.22.1/Cargo.toml +85 -0
  207. package/third-party/cargo/base64@0.22.1/LICENSE-APACHE +201 -0
  208. package/third-party/cargo/base64@0.22.1/LICENSE-MIT +21 -0
  209. package/third-party/cargo/bit-set@0.10.0/Cargo.toml +108 -0
  210. package/third-party/cargo/bit-set@0.10.0/LICENSE-APACHE +201 -0
  211. package/third-party/cargo/bit-set@0.10.0/LICENSE-MIT +25 -0
  212. package/third-party/cargo/bit-vec@0.9.1/Cargo.toml +94 -0
  213. package/third-party/cargo/bit-vec@0.9.1/LICENSE-APACHE +201 -0
  214. package/third-party/cargo/bit-vec@0.9.1/LICENSE-MIT +25 -0
  215. package/third-party/cargo/bitflags@2.13.0/Cargo.toml +120 -0
  216. package/third-party/cargo/bitflags@2.13.0/LICENSE-APACHE +201 -0
  217. package/third-party/cargo/bitflags@2.13.0/LICENSE-MIT +25 -0
  218. package/third-party/cargo/block2@0.6.2/Cargo.toml +115 -0
  219. package/third-party/cargo/block2@0.6.2/MIT-terms.txt +22 -0
  220. package/third-party/cargo/block2@0.6.2/madsmtm-objc2-b4167b582b2f-LICENSE.md +34 -0
  221. package/third-party/cargo/bumpalo@3.20.3/Cargo.toml +99 -0
  222. package/third-party/cargo/bumpalo@3.20.3/LICENSE-APACHE +201 -0
  223. package/third-party/cargo/bumpalo@3.20.3/LICENSE-MIT +25 -0
  224. package/third-party/cargo/bytemuck@1.25.1/Cargo.toml +153 -0
  225. package/third-party/cargo/bytemuck@1.25.1/LICENSE-APACHE +61 -0
  226. package/third-party/cargo/bytemuck@1.25.1/LICENSE-MIT +9 -0
  227. package/third-party/cargo/bytemuck@1.25.1/LICENSE-ZLIB +11 -0
  228. package/third-party/cargo/bytemuck_derive@1.11.0/Cargo.toml +60 -0
  229. package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-APACHE +61 -0
  230. package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-MIT +9 -0
  231. package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-ZLIB +11 -0
  232. package/third-party/cargo/byteorder-lite@0.1.0/Cargo.toml +57 -0
  233. package/third-party/cargo/byteorder-lite@0.1.0/LICENSE-MIT +21 -0
  234. package/third-party/cargo/byteorder-lite@0.1.0/UNLICENSE +24 -0
  235. package/third-party/cargo/byteorder@1.5.0/COPYING +3 -0
  236. package/third-party/cargo/byteorder@1.5.0/Cargo.toml +54 -0
  237. package/third-party/cargo/byteorder@1.5.0/LICENSE-MIT +21 -0
  238. package/third-party/cargo/byteorder@1.5.0/UNLICENSE +24 -0
  239. package/third-party/cargo/cfg-if@1.0.4/Cargo.toml +47 -0
  240. package/third-party/cargo/cfg-if@1.0.4/LICENSE-APACHE +201 -0
  241. package/third-party/cargo/cfg-if@1.0.4/LICENSE-MIT +25 -0
  242. package/third-party/cargo/cfg_aliases@0.2.1/Cargo.toml +43 -0
  243. package/third-party/cargo/cfg_aliases@0.2.1/LICENSE +9 -0
  244. package/third-party/cargo/chunked_transfer@1.5.0/Cargo.toml +27 -0
  245. package/third-party/cargo/chunked_transfer@1.5.0/LICENSE-APACHE +201 -0
  246. package/third-party/cargo/chunked_transfer@1.5.0/LICENSE-MIT +22 -0
  247. package/third-party/cargo/codespan-reporting@0.13.1/Cargo.toml +115 -0
  248. package/third-party/cargo/codespan-reporting@0.13.1/LICENSE +201 -0
  249. package/third-party/cargo/color_quant@1.1.0/Cargo.toml +20 -0
  250. package/third-party/cargo/color_quant@1.1.0/LICENSE +21 -0
  251. package/third-party/cargo/console_error_panic_hook@0.1.7/Cargo.toml +27 -0
  252. package/third-party/cargo/console_error_panic_hook@0.1.7/LICENSE-APACHE +201 -0
  253. package/third-party/cargo/console_error_panic_hook@0.1.7/LICENSE-MIT +25 -0
  254. package/third-party/cargo/core_maths@0.1.1/Cargo.toml +37 -0
  255. package/third-party/cargo/core_maths@0.1.1/LICENSE +21 -0
  256. package/third-party/cargo/cosmic-text@0.19.0/Cargo.toml +214 -0
  257. package/third-party/cargo/cosmic-text@0.19.0/LICENSE-APACHE +201 -0
  258. package/third-party/cargo/cosmic-text@0.19.0/LICENSE-MIT +21 -0
  259. package/third-party/cargo/crc32fast@1.5.0/Cargo.toml +68 -0
  260. package/third-party/cargo/crc32fast@1.5.0/LICENSE-APACHE +202 -0
  261. package/third-party/cargo/crc32fast@1.5.0/LICENSE-MIT +21 -0
  262. package/third-party/cargo/data-url@0.3.2/Cargo.toml +63 -0
  263. package/third-party/cargo/data-url@0.3.2/LICENSE-APACHE +201 -0
  264. package/third-party/cargo/data-url@0.3.2/LICENSE-MIT +25 -0
  265. package/third-party/cargo/dispatch2@0.3.1/Cargo.toml +122 -0
  266. package/third-party/cargo/dispatch2@0.3.1/MIT-terms.txt +22 -0
  267. package/third-party/cargo/dispatch2@0.3.1/madsmtm-objc2-8852b424193c-LICENSE.md +34 -0
  268. package/third-party/cargo/dlib@0.5.3/Cargo.toml +43 -0
  269. package/third-party/cargo/dlib@0.5.3/LICENSE.txt +19 -0
  270. package/third-party/cargo/document-features@0.2.12/Cargo.toml +51 -0
  271. package/third-party/cargo/document-features@0.2.12/LICENSE-APACHE +73 -0
  272. package/third-party/cargo/document-features@0.2.12/LICENSE-MIT +19 -0
  273. package/third-party/cargo/equivalent@1.0.2/Cargo.toml +37 -0
  274. package/third-party/cargo/equivalent@1.0.2/LICENSE-APACHE +201 -0
  275. package/third-party/cargo/equivalent@1.0.2/LICENSE-MIT +25 -0
  276. package/third-party/cargo/errno@0.3.14/Cargo.toml +67 -0
  277. package/third-party/cargo/errno@0.3.14/LICENSE-APACHE +201 -0
  278. package/third-party/cargo/errno@0.3.14/LICENSE-MIT +25 -0
  279. package/third-party/cargo/etagere@0.3.0/Cargo.toml +57 -0
  280. package/third-party/cargo/etagere@0.3.0/LICENSE-APACHE +13 -0
  281. package/third-party/cargo/etagere@0.3.0/LICENSE-MIT +20 -0
  282. package/third-party/cargo/euclid@0.22.14/COPYRIGHT +5 -0
  283. package/third-party/cargo/euclid@0.22.14/Cargo.toml +66 -0
  284. package/third-party/cargo/euclid@0.22.14/LICENSE-APACHE +201 -0
  285. package/third-party/cargo/euclid@0.22.14/LICENSE-MIT +25 -0
  286. package/third-party/cargo/fastrand@2.5.0/Cargo.toml +89 -0
  287. package/third-party/cargo/fastrand@2.5.0/LICENSE-APACHE +201 -0
  288. package/third-party/cargo/fastrand@2.5.0/LICENSE-MIT +23 -0
  289. package/third-party/cargo/fdeflate@0.3.7/Cargo.toml +55 -0
  290. package/third-party/cargo/fdeflate@0.3.7/LICENSE-APACHE +177 -0
  291. package/third-party/cargo/fdeflate@0.3.7/LICENSE-MIT +25 -0
  292. package/third-party/cargo/flate2@1.1.9/Cargo.toml +274 -0
  293. package/third-party/cargo/flate2@1.1.9/LICENSE-APACHE +201 -0
  294. package/third-party/cargo/flate2@1.1.9/LICENSE-MIT +25 -0
  295. package/third-party/cargo/float-cmp@0.9.0/Cargo.toml +39 -0
  296. package/third-party/cargo/float-cmp@0.9.0/LICENSE +19 -0
  297. package/third-party/cargo/float_next_after@1.0.0/Cargo.toml +43 -0
  298. package/third-party/cargo/float_next_after@1.0.0/LICENSE +23 -0
  299. package/third-party/cargo/foldhash@0.2.0/Cargo.toml +80 -0
  300. package/third-party/cargo/foldhash@0.2.0/LICENSE +19 -0
  301. package/third-party/cargo/font-types@0.11.3/Cargo.toml +55 -0
  302. package/third-party/cargo/font-types@0.11.3/LICENSE-APACHE +67 -0
  303. package/third-party/cargo/font-types@0.11.3/LICENSE-MIT +25 -0
  304. package/third-party/cargo/fontdb@0.23.0/Cargo.toml +83 -0
  305. package/third-party/cargo/fontdb@0.23.0/LICENSE +21 -0
  306. package/third-party/cargo/futures-core@0.3.32/Cargo.toml +66 -0
  307. package/third-party/cargo/futures-core@0.3.32/LICENSE-APACHE +202 -0
  308. package/third-party/cargo/futures-core@0.3.32/LICENSE-MIT +26 -0
  309. package/third-party/cargo/futures-task@0.3.32/Cargo.toml +58 -0
  310. package/third-party/cargo/futures-task@0.3.32/LICENSE-APACHE +202 -0
  311. package/third-party/cargo/futures-task@0.3.32/LICENSE-MIT +26 -0
  312. package/third-party/cargo/futures-util@0.3.32/Cargo.toml +181 -0
  313. package/third-party/cargo/futures-util@0.3.32/LICENSE-APACHE +202 -0
  314. package/third-party/cargo/futures-util@0.3.32/LICENSE-MIT +26 -0
  315. package/third-party/cargo/getrandom@0.4.3/Cargo.toml +165 -0
  316. package/third-party/cargo/getrandom@0.4.3/LICENSE-APACHE +201 -0
  317. package/third-party/cargo/getrandom@0.4.3/LICENSE-MIT +26 -0
  318. package/third-party/cargo/gif@0.14.2/Cargo.toml +84 -0
  319. package/third-party/cargo/gif@0.14.2/LICENSE-APACHE +201 -0
  320. package/third-party/cargo/gif@0.14.2/LICENSE-MIT +21 -0
  321. package/third-party/cargo/glam@0.33.2/Cargo.toml +354 -0
  322. package/third-party/cargo/glam@0.33.2/LICENSE-APACHE +201 -0
  323. package/third-party/cargo/glam@0.33.2/LICENSE-MIT +23 -0
  324. package/third-party/cargo/glow@0.17.0/Cargo.toml +122 -0
  325. package/third-party/cargo/glow@0.17.0/LICENSE-APACHE +201 -0
  326. package/third-party/cargo/glow@0.17.0/LICENSE-MIT +23 -0
  327. package/third-party/cargo/glow@0.17.0/LICENSE-ZLIB +15 -0
  328. package/third-party/cargo/gltf-derive@1.4.1/Cargo.toml +34 -0
  329. package/third-party/cargo/gltf-derive@1.4.1/LICENSE-APACHE +202 -0
  330. package/third-party/cargo/gltf-derive@1.4.1/LICENSE-MIT +26 -0
  331. package/third-party/cargo/gltf-json@1.4.1/Cargo.toml +50 -0
  332. package/third-party/cargo/gltf-json@1.4.1/LICENSE-APACHE +202 -0
  333. package/third-party/cargo/gltf-json@1.4.1/LICENSE-MIT +26 -0
  334. package/third-party/cargo/gltf@1.4.1/Cargo.toml +122 -0
  335. package/third-party/cargo/gltf@1.4.1/LICENSE-APACHE +202 -0
  336. package/third-party/cargo/gltf@1.4.1/LICENSE-MIT +26 -0
  337. package/third-party/cargo/glyphon@0.12.0/Cargo.toml +35 -0
  338. package/third-party/cargo/glyphon@0.12.0/LICENSE-APACHE +201 -0
  339. package/third-party/cargo/glyphon@0.12.0/LICENSE-MIT +23 -0
  340. package/third-party/cargo/glyphon@0.12.0/LICENSE-ZLIB +15 -0
  341. package/third-party/cargo/gpu-allocator@0.28.0/Cargo.toml +180 -0
  342. package/third-party/cargo/gpu-allocator@0.28.0/LICENSE-APACHE +201 -0
  343. package/third-party/cargo/gpu-allocator@0.28.0/LICENSE-MIT +7 -0
  344. package/third-party/cargo/grid@1.0.1/Cargo.toml +77 -0
  345. package/third-party/cargo/grid@1.0.1/LICENSE +21 -0
  346. package/third-party/cargo/half@2.7.1/Cargo.toml +137 -0
  347. package/third-party/cargo/half@2.7.1/LICENSE-APACHE +176 -0
  348. package/third-party/cargo/half@2.7.1/LICENSE-MIT +19 -0
  349. package/third-party/cargo/harfrust@0.5.2/Cargo.toml +115 -0
  350. package/third-party/cargo/harfrust@0.5.2/harfbuzz-harfrust-efdae3142ab7-LICENSE +22 -0
  351. package/third-party/cargo/hashbrown@0.16.1/Cargo.toml +180 -0
  352. package/third-party/cargo/hashbrown@0.16.1/LICENSE-APACHE +201 -0
  353. package/third-party/cargo/hashbrown@0.16.1/LICENSE-MIT +25 -0
  354. package/third-party/cargo/hashbrown@0.17.1/Cargo.toml +201 -0
  355. package/third-party/cargo/hashbrown@0.17.1/LICENSE-APACHE +201 -0
  356. package/third-party/cargo/hashbrown@0.17.1/LICENSE-MIT +25 -0
  357. package/third-party/cargo/httpdate@1.0.3/Cargo.toml +35 -0
  358. package/third-party/cargo/httpdate@1.0.3/LICENSE-APACHE +201 -0
  359. package/third-party/cargo/httpdate@1.0.3/LICENSE-MIT +19 -0
  360. package/third-party/cargo/image-webp@0.2.4/Cargo.toml +63 -0
  361. package/third-party/cargo/image-webp@0.2.4/LICENSE-APACHE +177 -0
  362. package/third-party/cargo/image-webp@0.2.4/LICENSE-MIT +25 -0
  363. package/third-party/cargo/image@0.25.10/Cargo.toml +248 -0
  364. package/third-party/cargo/image@0.25.10/LICENSE-APACHE +177 -0
  365. package/third-party/cargo/image@0.25.10/LICENSE-MIT +25 -0
  366. package/third-party/cargo/imagesize@0.14.0/Cargo.toml +104 -0
  367. package/third-party/cargo/imagesize@0.14.0/LICENSE +21 -0
  368. package/third-party/cargo/indexmap@2.14.0/Cargo.toml +167 -0
  369. package/third-party/cargo/indexmap@2.14.0/LICENSE-APACHE +201 -0
  370. package/third-party/cargo/indexmap@2.14.0/LICENSE-MIT +25 -0
  371. package/third-party/cargo/inflections@1.1.1/Cargo.toml +21 -0
  372. package/third-party/cargo/inflections@1.1.1/LICENSE +20 -0
  373. package/third-party/cargo/itoa@1.0.18/Cargo.toml +66 -0
  374. package/third-party/cargo/itoa@1.0.18/LICENSE-APACHE +176 -0
  375. package/third-party/cargo/itoa@1.0.18/LICENSE-MIT +23 -0
  376. package/third-party/cargo/js-sys@0.3.103/Cargo.toml +98 -0
  377. package/third-party/cargo/js-sys@0.3.103/LICENSE-APACHE +201 -0
  378. package/third-party/cargo/js-sys@0.3.103/LICENSE-MIT +25 -0
  379. package/third-party/cargo/khronos-egl@6.0.0/Cargo.toml +82 -0
  380. package/third-party/cargo/khronos-egl@6.0.0/LICENSE-APACHE +201 -0
  381. package/third-party/cargo/khronos-egl@6.0.0/LICENSE-MIT +23 -0
  382. package/third-party/cargo/kurbo@0.13.1/Cargo.toml +230 -0
  383. package/third-party/cargo/kurbo@0.13.1/LICENSE-APACHE +202 -0
  384. package/third-party/cargo/kurbo@0.13.1/LICENSE-MIT +25 -0
  385. package/third-party/cargo/lazy_static@1.5.0/Cargo.toml +46 -0
  386. package/third-party/cargo/lazy_static@1.5.0/LICENSE-APACHE +201 -0
  387. package/third-party/cargo/lazy_static@1.5.0/LICENSE-MIT +25 -0
  388. package/third-party/cargo/libc@0.2.186/Cargo.toml +229 -0
  389. package/third-party/cargo/libc@0.2.186/LICENSE-APACHE +176 -0
  390. package/third-party/cargo/libc@0.2.186/LICENSE-MIT +25 -0
  391. package/third-party/cargo/libloading@0.8.9/Cargo.toml +90 -0
  392. package/third-party/cargo/libloading@0.8.9/LICENSE +12 -0
  393. package/third-party/cargo/libm@0.2.16/Cargo.toml +61 -0
  394. package/third-party/cargo/libm@0.2.16/LICENSE.txt +258 -0
  395. package/third-party/cargo/linebender_resource_handle@0.1.1/Cargo.toml +107 -0
  396. package/third-party/cargo/linebender_resource_handle@0.1.1/LICENSE-APACHE +176 -0
  397. package/third-party/cargo/linebender_resource_handle@0.1.1/LICENSE-MIT +19 -0
  398. package/third-party/cargo/litrs@1.0.0/Cargo.toml +63 -0
  399. package/third-party/cargo/litrs@1.0.0/LICENSE-APACHE +176 -0
  400. package/third-party/cargo/litrs@1.0.0/LICENSE-MIT +25 -0
  401. package/third-party/cargo/lock_api@0.4.14/Cargo.toml +68 -0
  402. package/third-party/cargo/lock_api@0.4.14/LICENSE-APACHE +201 -0
  403. package/third-party/cargo/lock_api@0.4.14/LICENSE-MIT +25 -0
  404. package/third-party/cargo/log@0.4.33/Cargo.toml +152 -0
  405. package/third-party/cargo/log@0.4.33/LICENSE-APACHE +201 -0
  406. package/third-party/cargo/log@0.4.33/LICENSE-MIT +25 -0
  407. package/third-party/cargo/lru@0.16.4/Cargo.toml +47 -0
  408. package/third-party/cargo/lru@0.16.4/LICENSE +21 -0
  409. package/third-party/cargo/lyon_geom@1.0.19/Cargo.toml +69 -0
  410. package/third-party/cargo/lyon_geom@1.0.19/nical-lyon-56219c95c658-LICENSE-APACHE +13 -0
  411. package/third-party/cargo/lyon_geom@1.0.19/nical-lyon-56219c95c658-LICENSE-MIT +20 -0
  412. package/third-party/cargo/lyon_path@1.0.19/Cargo.toml +58 -0
  413. package/third-party/cargo/lyon_path@1.0.19/nical-lyon-56219c95c658-LICENSE-APACHE +13 -0
  414. package/third-party/cargo/lyon_path@1.0.19/nical-lyon-56219c95c658-LICENSE-MIT +20 -0
  415. package/third-party/cargo/lyon_tessellation@1.0.20/Cargo.toml +71 -0
  416. package/third-party/cargo/lyon_tessellation@1.0.20/nical-lyon-7fa75ea8eea3-LICENSE-APACHE +13 -0
  417. package/third-party/cargo/lyon_tessellation@1.0.20/nical-lyon-7fa75ea8eea3-LICENSE-MIT +20 -0
  418. package/third-party/cargo/memchr@2.8.3/COPYING +3 -0
  419. package/third-party/cargo/memchr@2.8.3/Cargo.toml +89 -0
  420. package/third-party/cargo/memchr@2.8.3/LICENSE-MIT +21 -0
  421. package/third-party/cargo/memchr@2.8.3/UNLICENSE +24 -0
  422. package/third-party/cargo/memmap2@0.9.11/Cargo.toml +59 -0
  423. package/third-party/cargo/memmap2@0.9.11/LICENSE-APACHE +201 -0
  424. package/third-party/cargo/memmap2@0.9.11/LICENSE-MIT +26 -0
  425. package/third-party/cargo/miniz_oxide@0.8.9/Cargo.toml +91 -0
  426. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE +25 -0
  427. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-APACHE.md +177 -0
  428. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-MIT.md +24 -0
  429. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-ZLIB.md +14 -0
  430. package/third-party/cargo/moxcms@0.8.1/Cargo.toml +113 -0
  431. package/third-party/cargo/moxcms@0.8.1/LICENSE-APACHE.md +201 -0
  432. package/third-party/cargo/moxcms@0.8.1/LICENSE.md +26 -0
  433. package/third-party/cargo/naga-types@30.0.0/Cargo.toml +94 -0
  434. package/third-party/cargo/naga-types@30.0.0/LICENSE.APACHE +176 -0
  435. package/third-party/cargo/naga-types@30.0.0/LICENSE.MIT +21 -0
  436. package/third-party/cargo/naga@30.0.0/Cargo.toml +246 -0
  437. package/third-party/cargo/naga@30.0.0/LICENSE.APACHE +176 -0
  438. package/third-party/cargo/naga@30.0.0/LICENSE.MIT +21 -0
  439. package/third-party/cargo/num-traits@0.2.19/Cargo.toml +54 -0
  440. package/third-party/cargo/num-traits@0.2.19/LICENSE-APACHE +201 -0
  441. package/third-party/cargo/num-traits@0.2.19/LICENSE-MIT +25 -0
  442. package/third-party/cargo/objc2-core-foundation@0.3.2/Cargo.toml +221 -0
  443. package/third-party/cargo/objc2-core-foundation@0.3.2/MIT-terms.txt +22 -0
  444. package/third-party/cargo/objc2-core-foundation@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
  445. package/third-party/cargo/objc2-core-graphics@0.3.2/Cargo.toml +331 -0
  446. package/third-party/cargo/objc2-core-graphics@0.3.2/MIT-terms.txt +22 -0
  447. package/third-party/cargo/objc2-core-graphics@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
  448. package/third-party/cargo/objc2-encode@4.1.0/Cargo.toml +81 -0
  449. package/third-party/cargo/objc2-encode@4.1.0/MIT-terms.txt +22 -0
  450. package/third-party/cargo/objc2-encode@4.1.0/madsmtm-objc2-8d214f547736-LICENSE.md +34 -0
  451. package/third-party/cargo/objc2-foundation@0.3.2/Cargo.toml +529 -0
  452. package/third-party/cargo/objc2-foundation@0.3.2/MIT-terms.txt +22 -0
  453. package/third-party/cargo/objc2-foundation@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
  454. package/third-party/cargo/objc2-io-surface@0.3.2/Cargo.toml +139 -0
  455. package/third-party/cargo/objc2-io-surface@0.3.2/MIT-terms.txt +22 -0
  456. package/third-party/cargo/objc2-io-surface@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
  457. package/third-party/cargo/objc2-metal@0.3.2/Cargo.toml +601 -0
  458. package/third-party/cargo/objc2-metal@0.3.2/MIT-terms.txt +22 -0
  459. package/third-party/cargo/objc2-metal@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
  460. package/third-party/cargo/objc2-quartz-core@0.3.2/Cargo.toml +302 -0
  461. package/third-party/cargo/objc2-quartz-core@0.3.2/MIT-terms.txt +22 -0
  462. package/third-party/cargo/objc2-quartz-core@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
  463. package/third-party/cargo/objc2@0.6.4/Cargo.toml +232 -0
  464. package/third-party/cargo/objc2@0.6.4/MIT-terms.txt +22 -0
  465. package/third-party/cargo/objc2@0.6.4/madsmtm-objc2-8852b424193c-LICENSE.md +34 -0
  466. package/third-party/cargo/once_cell@1.21.4/Cargo.toml +119 -0
  467. package/third-party/cargo/once_cell@1.21.4/LICENSE-APACHE +201 -0
  468. package/third-party/cargo/once_cell@1.21.4/LICENSE-MIT +23 -0
  469. package/third-party/cargo/ordered-float@5.3.0/Cargo.toml +162 -0
  470. package/third-party/cargo/ordered-float@5.3.0/LICENSE-MIT +25 -0
  471. package/third-party/cargo/parking_lot@0.12.5/Cargo.toml +87 -0
  472. package/third-party/cargo/parking_lot@0.12.5/LICENSE-APACHE +201 -0
  473. package/third-party/cargo/parking_lot@0.12.5/LICENSE-MIT +25 -0
  474. package/third-party/cargo/parking_lot_core@0.9.12/Cargo.toml +72 -0
  475. package/third-party/cargo/parking_lot_core@0.9.12/LICENSE-APACHE +201 -0
  476. package/third-party/cargo/parking_lot_core@0.9.12/LICENSE-MIT +25 -0
  477. package/third-party/cargo/pico-args@0.5.0/Cargo.toml +32 -0
  478. package/third-party/cargo/pico-args@0.5.0/LICENSE +20 -0
  479. package/third-party/cargo/pin-project-lite@0.2.17/Cargo.toml +198 -0
  480. package/third-party/cargo/pin-project-lite@0.2.17/LICENSE-APACHE +177 -0
  481. package/third-party/cargo/pin-project-lite@0.2.17/LICENSE-MIT +23 -0
  482. package/third-party/cargo/pkg-config@0.3.33/Cargo.toml +43 -0
  483. package/third-party/cargo/pkg-config@0.3.33/LICENSE-APACHE +201 -0
  484. package/third-party/cargo/pkg-config@0.3.33/LICENSE-MIT +25 -0
  485. package/third-party/cargo/png@0.18.1/Cargo.toml +130 -0
  486. package/third-party/cargo/png@0.18.1/LICENSE-APACHE +201 -0
  487. package/third-party/cargo/png@0.18.1/LICENSE-MIT +25 -0
  488. package/third-party/cargo/pollster@0.4.0/Cargo.toml +53 -0
  489. package/third-party/cargo/pollster@0.4.0/LICENSE-APACHE +201 -0
  490. package/third-party/cargo/pollster@0.4.0/LICENSE-MIT +25 -0
  491. package/third-party/cargo/polycool@0.4.0/Cargo.toml +83 -0
  492. package/third-party/cargo/polycool@0.4.0/LICENSE-APACHE +202 -0
  493. package/third-party/cargo/polycool@0.4.0/LICENSE-MIT +25 -0
  494. package/third-party/cargo/pp-rs@0.2.1/Cargo.toml +23 -0
  495. package/third-party/cargo/pp-rs@0.2.1/LICENSE +29 -0
  496. package/third-party/cargo/presser@0.3.1/Cargo.toml +58 -0
  497. package/third-party/cargo/presser@0.3.1/LICENSE-APACHE +201 -0
  498. package/third-party/cargo/presser@0.3.1/LICENSE-MIT +25 -0
  499. package/third-party/cargo/proc-macro2@1.0.106/Cargo.toml +105 -0
  500. package/third-party/cargo/proc-macro2@1.0.106/LICENSE-APACHE +176 -0
  501. package/third-party/cargo/proc-macro2@1.0.106/LICENSE-MIT +23 -0
  502. package/third-party/cargo/profiling@1.0.18/Cargo.toml +96 -0
  503. package/third-party/cargo/profiling@1.0.18/aclysma-profiling-8271551172eb-LICENSE-APACHE +201 -0
  504. package/third-party/cargo/profiling@1.0.18/aclysma-profiling-8271551172eb-LICENSE-MIT +21 -0
  505. package/third-party/cargo/pxfm@0.1.30/Cargo.toml +53 -0
  506. package/third-party/cargo/pxfm@0.1.30/LICENSE-APACHE.md +201 -0
  507. package/third-party/cargo/pxfm@0.1.30/LICENSE.md +26 -0
  508. package/third-party/cargo/quick-error@2.0.1/Cargo.toml +24 -0
  509. package/third-party/cargo/quick-error@2.0.1/LICENSE-APACHE +202 -0
  510. package/third-party/cargo/quick-error@2.0.1/LICENSE-MIT +19 -0
  511. package/third-party/cargo/quote@1.0.46/Cargo.toml +70 -0
  512. package/third-party/cargo/quote@1.0.46/LICENSE-APACHE +176 -0
  513. package/third-party/cargo/quote@1.0.46/LICENSE-MIT +23 -0
  514. package/third-party/cargo/rangemap@1.7.1/Cargo.toml +91 -0
  515. package/third-party/cargo/rangemap@1.7.1/LICENSE-APACHE +191 -0
  516. package/third-party/cargo/rangemap@1.7.1/LICENSE-MIT +7 -0
  517. package/third-party/cargo/raw-window-handle@0.6.2/Cargo.toml +56 -0
  518. package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-APACHE.md +177 -0
  519. package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-MIT.md +21 -0
  520. package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-ZLIB.md +11 -0
  521. package/third-party/cargo/raw-window-metal@1.1.0/Cargo.toml +111 -0
  522. package/third-party/cargo/raw-window-metal@1.1.0/LICENSE-APACHE +202 -0
  523. package/third-party/cargo/raw-window-metal@1.1.0/LICENSE-MIT +17 -0
  524. package/third-party/cargo/read-fonts@0.37.0/Cargo.toml +95 -0
  525. package/third-party/cargo/read-fonts@0.37.0/LICENSE-APACHE +67 -0
  526. package/third-party/cargo/read-fonts@0.37.0/LICENSE-MIT +25 -0
  527. package/third-party/cargo/read-fonts@0.39.2/Cargo.toml +96 -0
  528. package/third-party/cargo/read-fonts@0.39.2/LICENSE-APACHE +67 -0
  529. package/third-party/cargo/read-fonts@0.39.2/LICENSE-MIT +25 -0
  530. package/third-party/cargo/renderdoc-sys@1.1.0/Cargo.toml +39 -0
  531. package/third-party/cargo/renderdoc-sys@1.1.0/LICENSE-APACHE +201 -0
  532. package/third-party/cargo/renderdoc-sys@1.1.0/LICENSE-MIT +25 -0
  533. package/third-party/cargo/resvg@0.47.0/Cargo.toml +112 -0
  534. package/third-party/cargo/resvg@0.47.0/LICENSE-APACHE +176 -0
  535. package/third-party/cargo/resvg@0.47.0/LICENSE-MIT +25 -0
  536. package/third-party/cargo/rgb@0.8.53/Cargo.toml +110 -0
  537. package/third-party/cargo/rgb@0.8.53/LICENSE +21 -0
  538. package/third-party/cargo/roxmltree@0.21.1/Cargo.toml +75 -0
  539. package/third-party/cargo/roxmltree@0.21.1/LICENSE-APACHE +201 -0
  540. package/third-party/cargo/roxmltree@0.21.1/LICENSE-MIT +21 -0
  541. package/third-party/cargo/rustc-hash@1.1.0/Cargo.toml +25 -0
  542. package/third-party/cargo/rustc-hash@1.1.0/LICENSE-APACHE +201 -0
  543. package/third-party/cargo/rustc-hash@1.1.0/LICENSE-MIT +23 -0
  544. package/third-party/cargo/rustc-hash@2.1.3/Cargo.toml +50 -0
  545. package/third-party/cargo/rustc-hash@2.1.3/LICENSE-APACHE +176 -0
  546. package/third-party/cargo/rustc-hash@2.1.3/LICENSE-MIT +23 -0
  547. package/third-party/cargo/rustix@1.1.4/COPYRIGHT +29 -0
  548. package/third-party/cargo/rustix@1.1.4/Cargo.toml +292 -0
  549. package/third-party/cargo/rustix@1.1.4/LICENSE-APACHE +201 -0
  550. package/third-party/cargo/rustix@1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  551. package/third-party/cargo/rustix@1.1.4/LICENSE-MIT +23 -0
  552. package/third-party/cargo/rustversion@1.0.23/Cargo.toml +69 -0
  553. package/third-party/cargo/rustversion@1.0.23/LICENSE-APACHE +176 -0
  554. package/third-party/cargo/rustversion@1.0.23/LICENSE-MIT +23 -0
  555. package/third-party/cargo/rustybuzz@0.20.1/Cargo.toml +110 -0
  556. package/third-party/cargo/rustybuzz@0.20.1/LICENSE +22 -0
  557. package/third-party/cargo/scopeguard@1.2.0/Cargo.toml +43 -0
  558. package/third-party/cargo/scopeguard@1.2.0/LICENSE-APACHE +201 -0
  559. package/third-party/cargo/scopeguard@1.2.0/LICENSE-MIT +25 -0
  560. package/third-party/cargo/self_cell@1.2.2/Cargo.toml +33 -0
  561. package/third-party/cargo/self_cell@1.2.2/LICENSE-APACHE +201 -0
  562. package/third-party/cargo/self_cell@1.2.2/LICENSE-GPLv2 +339 -0
  563. package/third-party/cargo/self_cell@1.3.0/Cargo.toml +33 -0
  564. package/third-party/cargo/self_cell@1.3.0/LICENSE-APACHE +201 -0
  565. package/third-party/cargo/self_cell@1.3.0/LICENSE-GPLv2 +339 -0
  566. package/third-party/cargo/serde@1.0.228/Cargo.toml +84 -0
  567. package/third-party/cargo/serde@1.0.228/LICENSE-APACHE +176 -0
  568. package/third-party/cargo/serde@1.0.228/LICENSE-MIT +23 -0
  569. package/third-party/cargo/serde_core@1.0.228/Cargo.toml +87 -0
  570. package/third-party/cargo/serde_core@1.0.228/LICENSE-APACHE +176 -0
  571. package/third-party/cargo/serde_core@1.0.228/LICENSE-MIT +23 -0
  572. package/third-party/cargo/serde_derive@1.0.228/Cargo.toml +87 -0
  573. package/third-party/cargo/serde_derive@1.0.228/LICENSE-APACHE +176 -0
  574. package/third-party/cargo/serde_derive@1.0.228/LICENSE-MIT +23 -0
  575. package/third-party/cargo/serde_json@1.0.150/Cargo.toml +162 -0
  576. package/third-party/cargo/serde_json@1.0.150/LICENSE-APACHE +176 -0
  577. package/third-party/cargo/serde_json@1.0.150/LICENSE-MIT +23 -0
  578. package/third-party/cargo/simd-adler32@0.3.10/Cargo.toml +68 -0
  579. package/third-party/cargo/simd-adler32@0.3.10/LICENSE.md +21 -0
  580. package/third-party/cargo/simplecss@0.2.2/Cargo.toml +146 -0
  581. package/third-party/cargo/simplecss@0.2.2/LICENSE-APACHE +201 -0
  582. package/third-party/cargo/simplecss@0.2.2/LICENSE-MIT +21 -0
  583. package/third-party/cargo/siphasher@1.0.3/COPYING +7 -0
  584. package/third-party/cargo/siphasher@1.0.3/Cargo.toml +64 -0
  585. package/third-party/cargo/skrifa@0.40.0/Cargo.toml +86 -0
  586. package/third-party/cargo/skrifa@0.40.0/LICENSE-APACHE +67 -0
  587. package/third-party/cargo/skrifa@0.40.0/LICENSE-MIT +25 -0
  588. package/third-party/cargo/skrifa@0.42.1/Cargo.toml +86 -0
  589. package/third-party/cargo/skrifa@0.42.1/LICENSE-APACHE +67 -0
  590. package/third-party/cargo/skrifa@0.42.1/LICENSE-MIT +25 -0
  591. package/third-party/cargo/slab@0.4.12/Cargo.toml +67 -0
  592. package/third-party/cargo/slab@0.4.12/LICENSE +25 -0
  593. package/third-party/cargo/slotmap@1.1.1/Cargo.toml +83 -0
  594. package/third-party/cargo/slotmap@1.1.1/LICENSE +19 -0
  595. package/third-party/cargo/smallvec@1.15.2/Cargo.toml +103 -0
  596. package/third-party/cargo/smallvec@1.15.2/LICENSE-APACHE +201 -0
  597. package/third-party/cargo/smallvec@1.15.2/LICENSE-MIT +25 -0
  598. package/third-party/cargo/smol_str@0.3.6/Cargo.toml +155 -0
  599. package/third-party/cargo/smol_str@0.3.6/LICENSE-APACHE +201 -0
  600. package/third-party/cargo/smol_str@0.3.6/LICENSE-MIT +23 -0
  601. package/third-party/cargo/spirv@0.4.0+sdk-1.4.341.0/Cargo.toml +56 -0
  602. package/third-party/cargo/spirv@0.4.0+sdk-1.4.341.0/gfx-rs-rspirv-8afc3d0ac8e1-LICENSE +202 -0
  603. package/third-party/cargo/static_assertions@1.1.0/Cargo.toml +39 -0
  604. package/third-party/cargo/static_assertions@1.1.0/LICENSE-APACHE +202 -0
  605. package/third-party/cargo/static_assertions@1.1.0/LICENSE-MIT +21 -0
  606. package/third-party/cargo/strict-num@0.1.1/Cargo.toml +35 -0
  607. package/third-party/cargo/strict-num@0.1.1/LICENSE +19 -0
  608. package/third-party/cargo/svgtypes@0.16.1/Cargo.toml +57 -0
  609. package/third-party/cargo/svgtypes@0.16.1/LICENSE-APACHE +201 -0
  610. package/third-party/cargo/svgtypes@0.16.1/LICENSE-MIT +20 -0
  611. package/third-party/cargo/swash@0.2.9/Cargo.toml +93 -0
  612. package/third-party/cargo/swash@0.2.9/LICENSE-APACHE +201 -0
  613. package/third-party/cargo/swash@0.2.9/LICENSE-MIT +25 -0
  614. package/third-party/cargo/syn@2.0.118/Cargo.toml +272 -0
  615. package/third-party/cargo/syn@2.0.118/LICENSE-APACHE +176 -0
  616. package/third-party/cargo/syn@2.0.118/LICENSE-MIT +23 -0
  617. package/third-party/cargo/sys-locale@0.3.2/Cargo.toml +73 -0
  618. package/third-party/cargo/sys-locale@0.3.2/LICENSE-APACHE +201 -0
  619. package/third-party/cargo/sys-locale@0.3.2/LICENSE-MIT +21 -0
  620. package/third-party/cargo/taffy@0.12.1/Cargo.toml +171 -0
  621. package/third-party/cargo/taffy@0.12.1/DioxusLabs-taffy-1c9e37a0a1cf-LICENSE.md +25 -0
  622. package/third-party/cargo/tempfile@3.27.0/Cargo.toml +105 -0
  623. package/third-party/cargo/tempfile@3.27.0/LICENSE-APACHE +201 -0
  624. package/third-party/cargo/tempfile@3.27.0/LICENSE-MIT +25 -0
  625. package/third-party/cargo/termcolor@1.4.1/COPYING +3 -0
  626. package/third-party/cargo/termcolor@1.4.1/Cargo.toml +40 -0
  627. package/third-party/cargo/termcolor@1.4.1/LICENSE-MIT +21 -0
  628. package/third-party/cargo/termcolor@1.4.1/UNLICENSE +24 -0
  629. package/third-party/cargo/thiserror-impl@2.0.18/Cargo.toml +52 -0
  630. package/third-party/cargo/thiserror-impl@2.0.18/LICENSE-APACHE +176 -0
  631. package/third-party/cargo/thiserror-impl@2.0.18/LICENSE-MIT +23 -0
  632. package/third-party/cargo/thiserror@2.0.18/Cargo.toml +119 -0
  633. package/third-party/cargo/thiserror@2.0.18/LICENSE-APACHE +176 -0
  634. package/third-party/cargo/thiserror@2.0.18/LICENSE-MIT +23 -0
  635. package/third-party/cargo/tiny-skia-path@0.12.0/Cargo.toml +58 -0
  636. package/third-party/cargo/tiny-skia-path@0.12.0/LICENSE +30 -0
  637. package/third-party/cargo/tiny-skia@0.12.0/Cargo.toml +116 -0
  638. package/third-party/cargo/tiny-skia@0.12.0/LICENSE +30 -0
  639. package/third-party/cargo/tiny_http@0.12.0/Cargo.toml +82 -0
  640. package/third-party/cargo/tiny_http@0.12.0/LICENSE-APACHE +201 -0
  641. package/third-party/cargo/tiny_http@0.12.0/LICENSE-MIT +25 -0
  642. package/third-party/cargo/tinyvec@1.12.0/Cargo.toml +184 -0
  643. package/third-party/cargo/tinyvec@1.12.0/LICENSE-APACHE.md +202 -0
  644. package/third-party/cargo/tinyvec@1.12.0/LICENSE-MIT.md +5 -0
  645. package/third-party/cargo/tinyvec@1.12.0/LICENSE-ZLIB.md +11 -0
  646. package/third-party/cargo/tinyvec_macros@0.1.1/Cargo.toml +19 -0
  647. package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-APACHE.md +202 -0
  648. package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-MIT.md +21 -0
  649. package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-ZLIB.md +20 -0
  650. package/third-party/cargo/ttf-parser@0.25.1/Cargo.toml +101 -0
  651. package/third-party/cargo/ttf-parser@0.25.1/LICENSE-APACHE +201 -0
  652. package/third-party/cargo/ttf-parser@0.25.1/LICENSE-MIT +20 -0
  653. package/third-party/cargo/unicode-bidi-mirroring@0.4.0/Cargo.toml +22 -0
  654. package/third-party/cargo/unicode-bidi-mirroring@0.4.0/LICENSE-APACHE +201 -0
  655. package/third-party/cargo/unicode-bidi-mirroring@0.4.0/LICENSE-MIT +21 -0
  656. package/third-party/cargo/unicode-bidi@0.3.18/COPYRIGHT +8 -0
  657. package/third-party/cargo/unicode-bidi@0.3.18/Cargo.toml +90 -0
  658. package/third-party/cargo/unicode-bidi@0.3.18/LICENSE-APACHE +201 -0
  659. package/third-party/cargo/unicode-bidi@0.3.18/LICENSE-MIT +25 -0
  660. package/third-party/cargo/unicode-ccc@0.4.0/Cargo.toml +22 -0
  661. package/third-party/cargo/unicode-ccc@0.4.0/LICENSE-APACHE +201 -0
  662. package/third-party/cargo/unicode-ccc@0.4.0/LICENSE-MIT +21 -0
  663. package/third-party/cargo/unicode-ident@1.0.24/Cargo.toml +84 -0
  664. package/third-party/cargo/unicode-ident@1.0.24/LICENSE-APACHE +176 -0
  665. package/third-party/cargo/unicode-ident@1.0.24/LICENSE-MIT +23 -0
  666. package/third-party/cargo/unicode-ident@1.0.24/LICENSE-UNICODE +39 -0
  667. package/third-party/cargo/unicode-linebreak@0.1.5/Cargo.toml +32 -0
  668. package/third-party/cargo/unicode-linebreak@0.1.5/LICENSE +201 -0
  669. package/third-party/cargo/unicode-properties@0.1.4/COPYRIGHT +7 -0
  670. package/third-party/cargo/unicode-properties@0.1.4/Cargo.toml +65 -0
  671. package/third-party/cargo/unicode-properties@0.1.4/LICENSE-APACHE +201 -0
  672. package/third-party/cargo/unicode-properties@0.1.4/LICENSE-MIT +25 -0
  673. package/third-party/cargo/unicode-script@0.5.8/Cargo.toml +63 -0
  674. package/third-party/cargo/unicode-script@0.5.8/LICENSE-APACHE +201 -0
  675. package/third-party/cargo/unicode-script@0.5.8/LICENSE-MIT +27 -0
  676. package/third-party/cargo/unicode-segmentation@1.13.3/COPYRIGHT +7 -0
  677. package/third-party/cargo/unicode-segmentation@1.13.3/Cargo.toml +91 -0
  678. package/third-party/cargo/unicode-segmentation@1.13.3/LICENSE-APACHE +201 -0
  679. package/third-party/cargo/unicode-segmentation@1.13.3/LICENSE-MIT +25 -0
  680. package/third-party/cargo/unicode-vo@0.1.0/Cargo.toml +22 -0
  681. package/third-party/cargo/unicode-vo@0.1.0/LICENSE-APACHE +201 -0
  682. package/third-party/cargo/unicode-vo@0.1.0/LICENSE-MIT +21 -0
  683. package/third-party/cargo/unicode-width@0.2.2/COPYRIGHT +7 -0
  684. package/third-party/cargo/unicode-width@0.2.2/Cargo.toml +77 -0
  685. package/third-party/cargo/unicode-width@0.2.2/LICENSE-APACHE +201 -0
  686. package/third-party/cargo/unicode-width@0.2.2/LICENSE-MIT +25 -0
  687. package/third-party/cargo/unicode-xid@0.2.6/COPYRIGHT +7 -0
  688. package/third-party/cargo/unicode-xid@0.2.6/Cargo.toml +65 -0
  689. package/third-party/cargo/unicode-xid@0.2.6/LICENSE-APACHE +201 -0
  690. package/third-party/cargo/unicode-xid@0.2.6/LICENSE-MIT +25 -0
  691. package/third-party/cargo/urlencoding@2.1.3/Cargo.toml +41 -0
  692. package/third-party/cargo/urlencoding@2.1.3/LICENSE +20 -0
  693. package/third-party/cargo/usvg@0.47.0/Cargo.toml +138 -0
  694. package/third-party/cargo/usvg@0.47.0/LICENSE-APACHE +176 -0
  695. package/third-party/cargo/usvg@0.47.0/LICENSE-MIT +25 -0
  696. package/third-party/cargo/version_check@0.9.5/Cargo.toml +38 -0
  697. package/third-party/cargo/version_check@0.9.5/LICENSE-APACHE +201 -0
  698. package/third-party/cargo/version_check@0.9.5/LICENSE-MIT +19 -0
  699. package/third-party/cargo/wasm-bindgen-futures@0.4.76/Cargo.toml +88 -0
  700. package/third-party/cargo/wasm-bindgen-futures@0.4.76/LICENSE-APACHE +201 -0
  701. package/third-party/cargo/wasm-bindgen-futures@0.4.76/LICENSE-MIT +25 -0
  702. package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/Cargo.toml +83 -0
  703. package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/LICENSE-APACHE +201 -0
  704. package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/LICENSE-MIT +25 -0
  705. package/third-party/cargo/wasm-bindgen-macro@0.2.126/Cargo.toml +73 -0
  706. package/third-party/cargo/wasm-bindgen-macro@0.2.126/LICENSE-APACHE +201 -0
  707. package/third-party/cargo/wasm-bindgen-macro@0.2.126/LICENSE-MIT +25 -0
  708. package/third-party/cargo/wasm-bindgen-shared@0.2.126/Cargo.toml +66 -0
  709. package/third-party/cargo/wasm-bindgen-shared@0.2.126/LICENSE-APACHE +201 -0
  710. package/third-party/cargo/wasm-bindgen-shared@0.2.126/LICENSE-MIT +25 -0
  711. package/third-party/cargo/wasm-bindgen@0.2.126/Cargo.toml +122 -0
  712. package/third-party/cargo/wasm-bindgen@0.2.126/LICENSE-APACHE +201 -0
  713. package/third-party/cargo/wasm-bindgen@0.2.126/LICENSE-MIT +25 -0
  714. package/third-party/cargo/wayland-sys@0.31.11/Cargo.toml +81 -0
  715. package/third-party/cargo/wayland-sys@0.31.11/LICENSE.txt +19 -0
  716. package/third-party/cargo/web-sys@0.3.103/Cargo.toml +2823 -0
  717. package/third-party/cargo/web-sys@0.3.103/LICENSE-APACHE +201 -0
  718. package/third-party/cargo/web-sys@0.3.103/LICENSE-MIT +25 -0
  719. package/third-party/cargo/weezl@0.1.12/Cargo.toml +120 -0
  720. package/third-party/cargo/weezl@0.1.12/LICENSE-APACHE +201 -0
  721. package/third-party/cargo/weezl@0.1.12/LICENSE-MIT +22 -0
  722. package/third-party/cargo/wgpu-core-deps-apple@30.0.0/Cargo.toml +53 -0
  723. package/third-party/cargo/wgpu-core-deps-apple@30.0.0/LICENSE.APACHE +176 -0
  724. package/third-party/cargo/wgpu-core-deps-apple@30.0.0/LICENSE.MIT +21 -0
  725. package/third-party/cargo/wgpu-core@30.0.0/Cargo.toml +237 -0
  726. package/third-party/cargo/wgpu-core@30.0.0/LICENSE.APACHE +176 -0
  727. package/third-party/cargo/wgpu-core@30.0.0/LICENSE.MIT +21 -0
  728. package/third-party/cargo/wgpu-hal@30.0.0/Cargo.toml +541 -0
  729. package/third-party/cargo/wgpu-hal@30.0.0/LICENSE.APACHE +176 -0
  730. package/third-party/cargo/wgpu-hal@30.0.0/LICENSE.MIT +21 -0
  731. package/third-party/cargo/wgpu-naga-bridge@30.0.0/Cargo.toml +46 -0
  732. package/third-party/cargo/wgpu-naga-bridge@30.0.0/LICENSE.APACHE +176 -0
  733. package/third-party/cargo/wgpu-naga-bridge@30.0.0/LICENSE.MIT +21 -0
  734. package/third-party/cargo/wgpu-types@30.0.0/Cargo.toml +161 -0
  735. package/third-party/cargo/wgpu-types@30.0.0/LICENSE.APACHE +176 -0
  736. package/third-party/cargo/wgpu-types@30.0.0/LICENSE.MIT +21 -0
  737. package/third-party/cargo/wgpu@30.0.0/Cargo.toml +278 -0
  738. package/third-party/cargo/wgpu@30.0.0/LICENSE.APACHE +176 -0
  739. package/third-party/cargo/wgpu@30.0.0/LICENSE.MIT +21 -0
  740. package/third-party/cargo/xmlwriter@0.1.0/Cargo.toml +23 -0
  741. package/third-party/cargo/xmlwriter@0.1.0/LICENSE +21 -0
  742. package/third-party/cargo/yazi@0.2.1/Cargo.toml +45 -0
  743. package/third-party/cargo/yazi@0.2.1/LICENSE-APACHE +201 -0
  744. package/third-party/cargo/yazi@0.2.1/LICENSE-MIT +19 -0
  745. package/third-party/cargo/zeno@0.3.3/Cargo.toml +65 -0
  746. package/third-party/cargo/zeno@0.3.3/LICENSE-APACHE +201 -0
  747. package/third-party/cargo/zeno@0.3.3/LICENSE-MIT +25 -0
  748. package/third-party/cargo/zerocopy-derive@0.8.54/Cargo.toml +209 -0
  749. package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-APACHE +202 -0
  750. package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-BSD +24 -0
  751. package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-MIT +26 -0
  752. package/third-party/cargo/zerocopy@0.8.54/Cargo.toml +419 -0
  753. package/third-party/cargo/zerocopy@0.8.54/LICENSE-APACHE +202 -0
  754. package/third-party/cargo/zerocopy@0.8.54/LICENSE-BSD +24 -0
  755. package/third-party/cargo/zerocopy@0.8.54/LICENSE-MIT +26 -0
  756. package/third-party/cargo/zmij@1.0.23/Cargo.toml +90 -0
  757. package/third-party/cargo/zmij@1.0.23/LICENSE-MIT +23 -0
  758. package/third-party/cargo/zune-core@0.5.1/Cargo.toml +47 -0
  759. package/third-party/cargo/zune-core@0.5.1/LICENSE-APACHE +201 -0
  760. package/third-party/cargo/zune-core@0.5.1/LICENSE-MIT +21 -0
  761. package/third-party/cargo/zune-core@0.5.1/LICENSE-ZLIB +19 -0
  762. package/third-party/cargo/zune-jpeg@0.5.15/Cargo.toml +67 -0
  763. package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-APACHE +201 -0
  764. package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-MIT +21 -0
  765. package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-ZLIB +19 -0
  766. package/third-party/inventory.json +3614 -0
  767. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/esbuild-0.27.4-LICENSE.md +21 -0
  768. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/go-LICENSE +27 -0
  769. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/golang-x-sys-c0bba94af5f8-LICENSE +27 -0
  770. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/package.json +20 -0
  771. package/third-party/npm/@floating-ui__core@1.7.5/LICENSE +20 -0
  772. package/third-party/npm/@floating-ui__core@1.7.5/package.json +63 -0
  773. package/third-party/npm/@floating-ui__dom@1.7.6/LICENSE +20 -0
  774. package/third-party/npm/@floating-ui__dom@1.7.6/package.json +71 -0
  775. package/third-party/npm/@floating-ui__react-dom@2.1.8/LICENSE +20 -0
  776. package/third-party/npm/@floating-ui__react-dom@2.1.8/package.json +76 -0
  777. package/third-party/npm/@floating-ui__utils@0.2.11/LICENSE +20 -0
  778. package/third-party/npm/@floating-ui__utils@0.2.11/package.json +70 -0
  779. package/third-party/npm/@radix-ui__primitive@1.1.7/LICENSE +21 -0
  780. package/third-party/npm/@radix-ui__primitive@1.1.7/package.json +79 -0
  781. package/third-party/npm/@radix-ui__react-arrow@1.1.15/LICENSE +21 -0
  782. package/third-party/npm/@radix-ui__react-arrow@1.1.15/package.json +68 -0
  783. package/third-party/npm/@radix-ui__react-collection@1.1.15/LICENSE +21 -0
  784. package/third-party/npm/@radix-ui__react-collection@1.1.15/package.json +71 -0
  785. package/third-party/npm/@radix-ui__react-compose-refs@1.1.5/LICENSE +21 -0
  786. package/third-party/npm/@radix-ui__react-compose-refs@1.1.5/package.json +60 -0
  787. package/third-party/npm/@radix-ui__react-context-menu@2.3.7/LICENSE +21 -0
  788. package/third-party/npm/@radix-ui__react-context-menu@2.3.7/package.json +73 -0
  789. package/third-party/npm/@radix-ui__react-context@1.2.2/LICENSE +21 -0
  790. package/third-party/npm/@radix-ui__react-context@1.2.2/package.json +60 -0
  791. package/third-party/npm/@radix-ui__react-direction@1.1.4/LICENSE +21 -0
  792. package/third-party/npm/@radix-ui__react-direction@1.1.4/package.json +60 -0
  793. package/third-party/npm/@radix-ui__react-dismissable-layer@1.1.19/LICENSE +21 -0
  794. package/third-party/npm/@radix-ui__react-dismissable-layer@1.1.19/package.json +74 -0
  795. package/third-party/npm/@radix-ui__react-focus-guards@1.1.6/LICENSE +21 -0
  796. package/third-party/npm/@radix-ui__react-focus-guards@1.1.6/package.json +60 -0
  797. package/third-party/npm/@radix-ui__react-focus-scope@1.1.16/LICENSE +21 -0
  798. package/third-party/npm/@radix-ui__react-focus-scope@1.1.16/package.json +71 -0
  799. package/third-party/npm/@radix-ui__react-id@1.1.4/LICENSE +21 -0
  800. package/third-party/npm/@radix-ui__react-id@1.1.4/package.json +63 -0
  801. package/third-party/npm/@radix-ui__react-menu@2.1.24/LICENSE +21 -0
  802. package/third-party/npm/@radix-ui__react-menu@2.1.24/package.json +86 -0
  803. package/third-party/npm/@radix-ui__react-popper@1.3.7/LICENSE +21 -0
  804. package/third-party/npm/@radix-ui__react-popper@1.3.7/package.json +78 -0
  805. package/third-party/npm/@radix-ui__react-portal@1.1.17/LICENSE +21 -0
  806. package/third-party/npm/@radix-ui__react-portal@1.1.17/package.json +69 -0
  807. package/third-party/npm/@radix-ui__react-presence@1.1.10/LICENSE +21 -0
  808. package/third-party/npm/@radix-ui__react-presence@1.1.10/package.json +68 -0
  809. package/third-party/npm/@radix-ui__react-primitive@2.1.10/LICENSE +21 -0
  810. package/third-party/npm/@radix-ui__react-primitive@2.1.10/package.json +68 -0
  811. package/third-party/npm/@radix-ui__react-roving-focus@1.1.19/LICENSE +21 -0
  812. package/third-party/npm/@radix-ui__react-roving-focus@1.1.19/package.json +78 -0
  813. package/third-party/npm/@radix-ui__react-slot@1.3.3/LICENSE +21 -0
  814. package/third-party/npm/@radix-ui__react-slot@1.3.3/package.json +63 -0
  815. package/third-party/npm/@radix-ui__react-use-callback-ref@1.1.4/LICENSE +21 -0
  816. package/third-party/npm/@radix-ui__react-use-callback-ref@1.1.4/package.json +60 -0
  817. package/third-party/npm/@radix-ui__react-use-controllable-state@1.2.6/LICENSE +21 -0
  818. package/third-party/npm/@radix-ui__react-use-controllable-state@1.2.6/package.json +65 -0
  819. package/third-party/npm/@radix-ui__react-use-effect-event@0.0.5/LICENSE +21 -0
  820. package/third-party/npm/@radix-ui__react-use-effect-event@0.0.5/package.json +65 -0
  821. package/third-party/npm/@radix-ui__react-use-is-hydrated@0.1.3/LICENSE +21 -0
  822. package/third-party/npm/@radix-ui__react-use-is-hydrated@0.1.3/package.json +61 -0
  823. package/third-party/npm/@radix-ui__react-use-layout-effect@1.1.4/LICENSE +21 -0
  824. package/third-party/npm/@radix-ui__react-use-layout-effect@1.1.4/package.json +60 -0
  825. package/third-party/npm/@radix-ui__react-use-size@1.1.4/LICENSE +21 -0
  826. package/third-party/npm/@radix-ui__react-use-size@1.1.4/package.json +63 -0
  827. package/third-party/npm/aria-hidden@1.2.6/LICENSE +21 -0
  828. package/third-party/npm/aria-hidden@1.2.6/package.json +71 -0
  829. package/third-party/npm/detect-node-es@1.1.0/LICENSE +21 -0
  830. package/third-party/npm/detect-node-es@1.1.0/package.json +29 -0
  831. package/third-party/npm/esbuild@0.27.4/LICENSE.md +21 -0
  832. package/third-party/npm/esbuild@0.27.4/package.json +49 -0
  833. package/third-party/npm/fflate@0.8.3/LICENSE +21 -0
  834. package/third-party/npm/fflate@0.8.3/package.json +128 -0
  835. package/third-party/npm/get-nonce@1.0.1/LICENSE +21 -0
  836. package/third-party/npm/get-nonce@1.0.1/package.json +68 -0
  837. package/third-party/npm/mediabunny@1.52.2/LICENSE +373 -0
  838. package/third-party/npm/mediabunny@1.52.2/package.json +136 -0
  839. package/third-party/npm/mediabunny@1.52.2/source/LICENSE +373 -0
  840. package/third-party/npm/mediabunny@1.52.2/source/package.json +136 -0
  841. package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-demuxer.ts +400 -0
  842. package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-muxer.ts +124 -0
  843. package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-reader.ts +85 -0
  844. package/third-party/npm/mediabunny@1.52.2/source/src/aes.ts +307 -0
  845. package/third-party/npm/mediabunny@1.52.2/source/src/codec-data.ts +3178 -0
  846. package/third-party/npm/mediabunny@1.52.2/source/src/codec.ts +1154 -0
  847. package/third-party/npm/mediabunny@1.52.2/source/src/conversion.ts +2042 -0
  848. package/third-party/npm/mediabunny@1.52.2/source/src/custom-coder.ts +212 -0
  849. package/third-party/npm/mediabunny@1.52.2/source/src/decode.ts +263 -0
  850. package/third-party/npm/mediabunny@1.52.2/source/src/demuxer.ts +43 -0
  851. package/third-party/npm/mediabunny@1.52.2/source/src/encode.ts +1412 -0
  852. package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-demuxer.ts +813 -0
  853. package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-misc.ts +165 -0
  854. package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-muxer.ts +350 -0
  855. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-demuxer.ts +995 -0
  856. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-misc.ts +81 -0
  857. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-muxer.ts +1529 -0
  858. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-segmented-input.ts +772 -0
  859. package/third-party/npm/mediabunny@1.52.2/source/src/id3.ts +972 -0
  860. package/third-party/npm/mediabunny@1.52.2/source/src/index.ts +319 -0
  861. package/third-party/npm/mediabunny@1.52.2/source/src/input-format.ts +819 -0
  862. package/third-party/npm/mediabunny@1.52.2/source/src/input-track.ts +1141 -0
  863. package/third-party/npm/mediabunny@1.52.2/source/src/input.ts +571 -0
  864. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-boxes.ts +1911 -0
  865. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-demuxer.ts +3953 -0
  866. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-misc.ts +90 -0
  867. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-muxer.ts +1595 -0
  868. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-reader.ts +91 -0
  869. package/third-party/npm/mediabunny@1.52.2/source/src/logging.ts +120 -0
  870. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/ebml.ts +770 -0
  871. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-demuxer.ts +2598 -0
  872. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-misc.ts +29 -0
  873. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-muxer.ts +1360 -0
  874. package/third-party/npm/mediabunny@1.52.2/source/src/media-sink.ts +2589 -0
  875. package/third-party/npm/mediabunny@1.52.2/source/src/media-source.ts +3041 -0
  876. package/third-party/npm/mediabunny@1.52.2/source/src/metadata.ts +335 -0
  877. package/third-party/npm/mediabunny@1.52.2/source/src/misc.ts +1336 -0
  878. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-demuxer.ts +469 -0
  879. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-muxer.ts +169 -0
  880. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-reader.ts +61 -0
  881. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-writer.ts +121 -0
  882. package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-demuxer.ts +2519 -0
  883. package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-misc.ts +32 -0
  884. package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-muxer.ts +842 -0
  885. package/third-party/npm/mediabunny@1.52.2/source/src/muxer.ts +80 -0
  886. package/third-party/npm/mediabunny@1.52.2/source/src/node.ts +11 -0
  887. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-demuxer.ts +1087 -0
  888. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-misc.ts +116 -0
  889. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-muxer.ts +527 -0
  890. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-reader.ts +93 -0
  891. package/third-party/npm/mediabunny@1.52.2/source/src/output-format.ts +1425 -0
  892. package/third-party/npm/mediabunny@1.52.2/source/src/output.ts +940 -0
  893. package/third-party/npm/mediabunny@1.52.2/source/src/packet.ts +287 -0
  894. package/third-party/npm/mediabunny@1.52.2/source/src/pcm.ts +112 -0
  895. package/third-party/npm/mediabunny@1.52.2/source/src/reader.ts +391 -0
  896. package/third-party/npm/mediabunny@1.52.2/source/src/resample.ts +287 -0
  897. package/third-party/npm/mediabunny@1.52.2/source/src/sample.ts +3231 -0
  898. package/third-party/npm/mediabunny@1.52.2/source/src/segmented-input.ts +575 -0
  899. package/third-party/npm/mediabunny@1.52.2/source/src/source.ts +2588 -0
  900. package/third-party/npm/mediabunny@1.52.2/source/src/subtitles.ts +131 -0
  901. package/third-party/npm/mediabunny@1.52.2/source/src/target.ts +871 -0
  902. package/third-party/npm/mediabunny@1.52.2/source/src/tsconfig.json +16 -0
  903. package/third-party/npm/mediabunny@1.52.2/source/src/wave/riff-writer.ts +36 -0
  904. package/third-party/npm/mediabunny@1.52.2/source/src/wave/wave-demuxer.ts +583 -0
  905. package/third-party/npm/mediabunny@1.52.2/source/src/wave/wave-muxer.ts +375 -0
  906. package/third-party/npm/mediabunny@1.52.2/source/src/writer.ts +128 -0
  907. package/third-party/npm/react-dom@19.3.0/LICENSE +21 -0
  908. package/third-party/npm/react-dom@19.3.0/package.json +117 -0
  909. package/third-party/npm/react-remove-scroll-bar@2.3.8/package.json +85 -0
  910. package/third-party/npm/react-remove-scroll-bar@2.3.8/react-remove-scroll-bar-LICENSE +21 -0
  911. package/third-party/npm/react-remove-scroll@2.7.2/LICENSE +21 -0
  912. package/third-party/npm/react-remove-scroll@2.7.2/package.json +98 -0
  913. package/third-party/npm/react-style-singleton@2.2.3/LICENSE +21 -0
  914. package/third-party/npm/react-style-singleton@2.2.3/package.json +90 -0
  915. package/third-party/npm/react@19.3.0/LICENSE +21 -0
  916. package/third-party/npm/react@19.3.0/package.json +51 -0
  917. package/third-party/npm/scheduler@0.28.0/LICENSE +21 -0
  918. package/third-party/npm/scheduler@0.28.0/package.json +27 -0
  919. package/third-party/npm/tslib@2.8.1/LICENSE.txt +12 -0
  920. package/third-party/npm/tslib@2.8.1/package.json +47 -0
  921. package/third-party/npm/typescript@5.9.3/LICENSE.txt +55 -0
  922. package/third-party/npm/typescript@5.9.3/ThirdPartyNoticeText.txt +193 -0
  923. package/third-party/npm/typescript@5.9.3/package.json +120 -0
  924. package/third-party/npm/use-callback-ref@1.3.3/LICENSE +21 -0
  925. package/third-party/npm/use-callback-ref@1.3.3/package.json +85 -0
  926. package/third-party/npm/use-sidecar@1.1.3/LICENSE +21 -0
  927. package/third-party/npm/use-sidecar@1.1.3/package.json +93 -0
  928. package/third-party/packages/render-engine/assets/OFL.txt +93 -0
  929. package/third-party/packages/studio/public/fonts/OFL.txt +93 -0
  930. package/third-party/packages/studio/vendor/osmo-icons/LICENSE +21 -0
  931. package/third-party/packages/studio/vendor/shadcn/LICENSE +21 -0
  932. package/third-party/repository-notices.md +41 -0
  933. package/third-party/rust-standard-library/COPYRIGHT-library.html +5556 -0
  934. package/third-party/rust-standard-library/licenses/Apache-2.0.txt +73 -0
  935. package/third-party/rust-standard-library/licenses/BSD-2-Clause.txt +9 -0
  936. package/third-party/rust-standard-library/licenses/CC-BY-SA-4.0.txt +427 -0
  937. package/third-party/rust-standard-library/licenses/GCC-exception-3.1.txt +30 -0
  938. package/third-party/rust-standard-library/licenses/GPL-2.0-only.txt +133 -0
  939. package/third-party/rust-standard-library/licenses/GPL-3.0-or-later.txt +202 -0
  940. package/third-party/rust-standard-library/licenses/ISC.txt +7 -0
  941. package/third-party/rust-standard-library/licenses/LLVM-exception.txt +15 -0
  942. package/third-party/rust-standard-library/licenses/MIT.txt +9 -0
  943. package/third-party/rust-standard-library/licenses/NCSA.txt +29 -0
  944. package/third-party/rust-standard-library/licenses/OFL-1.1.txt +43 -0
  945. package/third-party/rust-standard-library/licenses/Unicode-3.0.txt +39 -0
  946. package/third-party/rust-standard-library/version.txt +1 -0
  947. package/vendor/osmo-lite +0 -0
  948. package/vendor/osmo-scene-0.1.0.tgz +0 -0
  949. package/vendor/platform.json +1 -0
@@ -0,0 +1,2589 @@
1
+ /*!
2
+ * Copyright (c) 2026-present, Vanilagy and contributors
3
+ *
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
+ */
8
+
9
+ import { parsePcmCodec, PCM_AUDIO_CODECS, PcmAudioCodec, VideoCodec, AudioCodec } from './codec';
10
+ import {
11
+ AvcNalUnitType,
12
+ concatAvcNalUnits,
13
+ deserializeAvcDecoderConfigurationRecord,
14
+ determineVideoPacketType,
15
+ extractNalUnitTypeForAvc,
16
+ extractNalUnitTypeForHevc,
17
+ HevcNalUnitType,
18
+ iterateAvcNalUnits,
19
+ iterateHevcNalUnits,
20
+ parseAvcSps,
21
+ sanitizeHevcPacketForChromium,
22
+ } from './codec-data';
23
+ import { CustomVideoDecoder, customVideoDecoders, CustomAudioDecoder, customAudioDecoders } from './custom-coder';
24
+ import { InputDisposedError } from './input';
25
+ import { InputAudioTrack, InputTrack, InputVideoTrack } from './input-track';
26
+ import {
27
+ AnyIterable,
28
+ assert,
29
+ assertNever,
30
+ CallSerializer,
31
+ clamp,
32
+ getInt24,
33
+ getUint24,
34
+ insertSorted,
35
+ isChromium,
36
+ isFirefox,
37
+ isNumber,
38
+ isWebKit,
39
+ last,
40
+ mapAsyncGenerator,
41
+ promiseWithResolvers,
42
+ removeItem,
43
+ Rotation,
44
+ toAsyncIterator,
45
+ toDataView,
46
+ toUint8Array,
47
+ validateAnyIterable,
48
+ } from './misc';
49
+ import { EncodedPacket } from './packet';
50
+ import { fromAlaw, fromUlaw } from './pcm';
51
+ import {
52
+ AudioSample,
53
+ clampCropRectangle,
54
+ CropRectangle,
55
+ validateCropRectangle,
56
+ VideoSample,
57
+ VideoSamplePixelFormat,
58
+ } from './sample';
59
+
60
+ /**
61
+ * Additional options for controlling packet retrieval.
62
+ * @group Media sinks
63
+ * @public
64
+ */
65
+ export type PacketRetrievalOptions = {
66
+ /**
67
+ * When set to `true`, only packet metadata (like timestamp) will be retrieved - the actual packet data will not
68
+ * be loaded.
69
+ */
70
+ metadataOnly?: boolean;
71
+
72
+ /**
73
+ * When set to `true`, key packets will be verified upon retrieval by looking into the packet's bitstream.
74
+ * If not enabled, the packet types will be determined solely by what's stored in the containing file and may be
75
+ * incorrect, potentially leading to decoder errors. Since determining a packet's actual type requires looking into
76
+ * its data, this option cannot be enabled together with `metadataOnly`.
77
+ */
78
+ verifyKeyPackets?: boolean;
79
+
80
+ /**
81
+ * When querying packets in live media that are in the future relative to the current live edge, Mediabunny will,
82
+ * by default, wait for the stream to advance until the query can be satisfied. In a sense, Mediabunny simply treats
83
+ * live streams as media files that are still being written, and any read that depends on future information will
84
+ * wait until it can be fulfilled.
85
+ *
86
+ * If you want to query packets based only on the currently known information, set this field to `true` - this way,
87
+ * Mediabunny will never wait for the live stream to catch up.
88
+ *
89
+ * For non-live media, this field has no effect.
90
+ */
91
+ skipLiveWait?: boolean;
92
+ };
93
+
94
+ const validatePacketRetrievalOptions = (options: PacketRetrievalOptions) => {
95
+ if (!options || typeof options !== 'object') {
96
+ throw new TypeError('options must be an object.');
97
+ }
98
+ if (options.metadataOnly !== undefined && typeof options.metadataOnly !== 'boolean') {
99
+ throw new TypeError('options.metadataOnly, when defined, must be a boolean.');
100
+ }
101
+ if (options.verifyKeyPackets !== undefined && typeof options.verifyKeyPackets !== 'boolean') {
102
+ throw new TypeError('options.verifyKeyPackets, when defined, must be a boolean.');
103
+ }
104
+ if (options.verifyKeyPackets && options.metadataOnly) {
105
+ throw new TypeError('options.verifyKeyPackets and options.metadataOnly cannot be enabled together.');
106
+ }
107
+ if (options.skipLiveWait !== undefined && typeof options.skipLiveWait !== 'boolean') {
108
+ throw new TypeError('options.skipLiveWait, when defined, must be a boolean.');
109
+ }
110
+ };
111
+
112
+ const validateTimestamp = (timestamp: number) => {
113
+ if (!isNumber(timestamp)) {
114
+ throw new TypeError('timestamp must be a number.'); // It can be non-finite, that's fine
115
+ }
116
+ };
117
+
118
+ const maybeFixPacketType = (
119
+ track: InputTrack,
120
+ promise: Promise<EncodedPacket | null>,
121
+ options: PacketRetrievalOptions,
122
+ ) => {
123
+ if (options.verifyKeyPackets) {
124
+ return promise.then(async (packet) => {
125
+ if (!packet || packet.type === 'delta') {
126
+ return packet;
127
+ }
128
+
129
+ const determinedType = await track.determinePacketType(packet);
130
+ if (determinedType) {
131
+ // @ts-expect-error Technically readonly
132
+ packet.type = determinedType;
133
+ }
134
+
135
+ return packet;
136
+ });
137
+ } else {
138
+ return promise;
139
+ }
140
+ };
141
+
142
+ /**
143
+ * Sink for retrieving encoded packets from an input track.
144
+ * @group Media sinks
145
+ * @public
146
+ */
147
+ export class EncodedPacketSink {
148
+ /** @internal */
149
+ _track: InputTrack;
150
+
151
+ /** Creates a new {@link EncodedPacketSink} for the given {@link InputTrack}. */
152
+ constructor(track: InputTrack) {
153
+ if (!(track instanceof InputTrack)) {
154
+ throw new TypeError('track must be an InputTrack.');
155
+ }
156
+
157
+ this._track = track;
158
+ }
159
+
160
+ /**
161
+ * Retrieves the track's first packet (in decode order), or null if it has no packets. The first packet is very
162
+ * likely to be a key packet, but it doesn't have to be.
163
+ */
164
+ async getFirstPacket(options: PacketRetrievalOptions = {}) {
165
+ validatePacketRetrievalOptions(options);
166
+
167
+ if (this._track.input._disposed) {
168
+ throw new InputDisposedError();
169
+ }
170
+
171
+ return maybeFixPacketType(this._track, this._track._backing.getFirstPacket(options), options);
172
+ }
173
+
174
+ /** Retrieves the track's first key packet (in decode order), or null if it has no key packets. */
175
+ async getFirstKeyPacket(options: PacketRetrievalOptions = {}) {
176
+ validatePacketRetrievalOptions(options);
177
+
178
+ const firstPacket = await this.getFirstPacket(options);
179
+ if (!firstPacket) {
180
+ return null;
181
+ }
182
+
183
+ if (firstPacket.type === 'key') {
184
+ // Great
185
+ return firstPacket;
186
+ }
187
+
188
+ return this.getNextKeyPacket(firstPacket, options);
189
+ }
190
+
191
+ /**
192
+ * Retrieves the packet corresponding to the given timestamp, in seconds. More specifically, returns the last packet
193
+ * (in presentation order) with a start timestamp less than or equal to the given timestamp. This method can be
194
+ * used to retrieve a track's last packet using `getPacket(Infinity)`. The method returns null if the timestamp
195
+ * is before the first packet in the track.
196
+ *
197
+ * @param timestamp - The timestamp used for retrieval, in seconds.
198
+ */
199
+ async getPacket(timestamp: number, options: PacketRetrievalOptions = {}) {
200
+ validateTimestamp(timestamp);
201
+ validatePacketRetrievalOptions(options);
202
+
203
+ if (this._track.input._disposed) {
204
+ throw new InputDisposedError();
205
+ }
206
+
207
+ return maybeFixPacketType(this._track, this._track._backing.getPacket(timestamp, options), options);
208
+ }
209
+
210
+ /**
211
+ * Retrieves the packet following the given packet (in decode order), or null if the given packet is the
212
+ * last packet.
213
+ */
214
+ async getNextPacket(packet: EncodedPacket, options: PacketRetrievalOptions = {}) {
215
+ if (!(packet instanceof EncodedPacket)) {
216
+ throw new TypeError('packet must be an EncodedPacket.');
217
+ }
218
+ validatePacketRetrievalOptions(options);
219
+
220
+ if (this._track.input._disposed) {
221
+ throw new InputDisposedError();
222
+ }
223
+
224
+ return maybeFixPacketType(this._track, this._track._backing.getNextPacket(packet, options), options);
225
+ }
226
+
227
+ /**
228
+ * Retrieves the key packet corresponding to the given timestamp, in seconds. More specifically, returns the last
229
+ * key packet (in presentation order) with a start timestamp less than or equal to the given timestamp. A key packet
230
+ * is a packet that doesn't require previous packets to be decoded. This method can be used to retrieve a track's
231
+ * last key packet using `getKeyPacket(Infinity)`. The method returns null if the timestamp is before the first
232
+ * key packet in the track.
233
+ *
234
+ * To ensure that the returned packet is guaranteed to be a real key frame, enable `options.verifyKeyPackets`.
235
+ *
236
+ * @param timestamp - The timestamp used for retrieval, in seconds.
237
+ */
238
+ async getKeyPacket(timestamp: number, options: PacketRetrievalOptions = {}): Promise<EncodedPacket | null> {
239
+ validateTimestamp(timestamp);
240
+ validatePacketRetrievalOptions(options);
241
+
242
+ if (this._track.input._disposed) {
243
+ throw new InputDisposedError();
244
+ }
245
+
246
+ if (!options.verifyKeyPackets) {
247
+ return this._track._backing.getKeyPacket(timestamp, options);
248
+ }
249
+
250
+ const packet = await this._track._backing.getKeyPacket(timestamp, options);
251
+ if (!packet) {
252
+ return packet;
253
+ }
254
+ assert(packet.type === 'key');
255
+
256
+ const determinedType = await this._track.determinePacketType(packet);
257
+ if (determinedType === 'delta') {
258
+ // Try returning the previous key packet (in hopes that it's actually a key packet)
259
+ return this.getKeyPacket(packet.timestamp - 1 / await this._track.getTimeResolution(), options);
260
+ }
261
+
262
+ return packet;
263
+ }
264
+
265
+ /**
266
+ * Retrieves the key packet following the given packet (in decode order), or null if the given packet is the last
267
+ * key packet.
268
+ *
269
+ * To ensure that the returned packet is guaranteed to be a real key frame, enable `options.verifyKeyPackets`.
270
+ */
271
+ async getNextKeyPacket(packet: EncodedPacket, options: PacketRetrievalOptions = {}): Promise<EncodedPacket | null> {
272
+ if (!(packet instanceof EncodedPacket)) {
273
+ throw new TypeError('packet must be an EncodedPacket.');
274
+ }
275
+ validatePacketRetrievalOptions(options);
276
+
277
+ if (this._track.input._disposed) {
278
+ throw new InputDisposedError();
279
+ }
280
+
281
+ if (!options.verifyKeyPackets) {
282
+ return this._track._backing.getNextKeyPacket(packet, options);
283
+ }
284
+
285
+ const nextPacket = await this._track._backing.getNextKeyPacket(packet, options);
286
+ if (!nextPacket) {
287
+ return nextPacket;
288
+ }
289
+ assert(nextPacket.type === 'key');
290
+
291
+ const determinedType = await this._track.determinePacketType(nextPacket);
292
+ if (determinedType === 'delta') {
293
+ // Try returning the next key packet (in hopes that it's actually a key packet)
294
+ return this.getNextKeyPacket(nextPacket, options);
295
+ }
296
+
297
+ return nextPacket;
298
+ }
299
+
300
+ /**
301
+ * Creates an async iterator that yields the packets in this track in decode order. To enable fast iteration, this
302
+ * method will intelligently preload packets based on the speed of the consumer.
303
+ *
304
+ * @param startPacket - (optional) The packet from which iteration should begin. This packet will also be yielded.
305
+ * @param endPacket - (optional) The packet at which iteration should end. This packet will _not_ be yielded.
306
+ */
307
+ packets(
308
+ startPacket?: EncodedPacket,
309
+ endPacket?: EncodedPacket,
310
+ options: PacketRetrievalOptions = {},
311
+ ): AsyncGenerator<EncodedPacket, void, unknown> {
312
+ if (startPacket !== undefined && !(startPacket instanceof EncodedPacket)) {
313
+ throw new TypeError('startPacket must be an EncodedPacket.');
314
+ }
315
+ if (startPacket !== undefined && startPacket.isMetadataOnly && !options?.metadataOnly) {
316
+ throw new TypeError('startPacket can only be metadata-only if options.metadataOnly is enabled.');
317
+ }
318
+ if (endPacket !== undefined && !(endPacket instanceof EncodedPacket)) {
319
+ throw new TypeError('endPacket must be an EncodedPacket.');
320
+ }
321
+ validatePacketRetrievalOptions(options);
322
+
323
+ if (this._track.input._disposed) {
324
+ throw new InputDisposedError();
325
+ }
326
+
327
+ const packetQueue: EncodedPacket[] = [];
328
+
329
+ let { promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers();
330
+ let { promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers();
331
+ let ended = false;
332
+ let terminated = false;
333
+
334
+ // This stores errors that are "out of band" in the sense that they didn't occur in the normal flow of this
335
+ // method but instead in a different context. This error should not go unnoticed and must be bubbled up to
336
+ // the consumer.
337
+ let outOfBandError = null as unknown;
338
+ let hasOutOfBandError = false;
339
+
340
+ const timestamps: number[] = [];
341
+ // The queue should always be big enough to hold 1 second worth of packets
342
+ const maxQueueSize = () => Math.max(2, timestamps.length);
343
+
344
+ // The following is the "pump" process that keeps pumping packets into the queue
345
+ (async () => {
346
+ let packet = startPacket ?? await this.getFirstPacket(options);
347
+
348
+ while (packet && !terminated && !this._track.input._disposed) {
349
+ if (endPacket && packet.sequenceNumber >= endPacket?.sequenceNumber) {
350
+ break;
351
+ }
352
+
353
+ if (packetQueue.length > maxQueueSize()) {
354
+ ({ promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers());
355
+ await queueDequeue;
356
+ continue;
357
+ }
358
+
359
+ packetQueue.push(packet);
360
+
361
+ onQueueNotEmpty();
362
+ ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers());
363
+
364
+ packet = await this.getNextPacket(packet, options);
365
+ }
366
+
367
+ ended = true;
368
+ onQueueNotEmpty();
369
+ })().catch((error) => {
370
+ if (!hasOutOfBandError) {
371
+ outOfBandError = error;
372
+ hasOutOfBandError = true;
373
+ onQueueNotEmpty();
374
+ }
375
+ });
376
+
377
+ const track = this._track;
378
+
379
+ return {
380
+ async next() {
381
+ while (true) {
382
+ if (track.input._disposed) {
383
+ throw new InputDisposedError();
384
+ } else if (terminated) {
385
+ return { value: undefined, done: true };
386
+ } else if (hasOutOfBandError) {
387
+ throw outOfBandError;
388
+ } else if (packetQueue.length > 0) {
389
+ const value = packetQueue.shift()!;
390
+ const now = performance.now();
391
+ timestamps.push(now);
392
+
393
+ while (timestamps.length > 0 && now - timestamps[0]! >= 1000) {
394
+ timestamps.shift();
395
+ }
396
+
397
+ onQueueDequeue();
398
+
399
+ return { value, done: false };
400
+ } else if (ended) {
401
+ return { value: undefined, done: true };
402
+ } else {
403
+ await queueNotEmpty;
404
+ }
405
+ }
406
+ },
407
+ async return() {
408
+ terminated = true;
409
+ onQueueDequeue();
410
+ onQueueNotEmpty();
411
+
412
+ return { value: undefined, done: true };
413
+ },
414
+ async throw(error) {
415
+ throw error;
416
+ },
417
+ [Symbol.asyncIterator]() {
418
+ return this;
419
+ },
420
+ };
421
+ }
422
+ }
423
+
424
+ abstract class DecoderWrapper<
425
+ MediaSample extends VideoSample | AudioSample,
426
+ > {
427
+ constructor(
428
+ public onSample: (sample: MediaSample) => unknown,
429
+ public onError: (error: unknown) => unknown,
430
+ ) {}
431
+
432
+ abstract getDecodeQueueSize(): number;
433
+ abstract decode(packet: EncodedPacket): void;
434
+ abstract flush(): Promise<void>;
435
+ abstract close(): void;
436
+ }
437
+
438
+ /**
439
+ * Base class for decoded media sample sinks.
440
+ * @group Media sinks
441
+ * @public
442
+ */
443
+ export abstract class BaseMediaSampleSink<
444
+ MediaSample extends VideoSample | AudioSample,
445
+ > {
446
+ /** @internal */
447
+ abstract _track: InputTrack;
448
+
449
+ /** @internal */
450
+ abstract _createDecoder(
451
+ onSample: (sample: MediaSample) => unknown,
452
+ onError: (error: unknown) => unknown
453
+ ): Promise<DecoderWrapper<MediaSample>>;
454
+ /** @internal */
455
+ abstract _createPacketSink(): EncodedPacketSink;
456
+
457
+ /** @internal */
458
+ protected mediaSamplesInRange(
459
+ startTimestamp = -Infinity,
460
+ endTimestamp = Infinity,
461
+ options: PacketRetrievalOptions,
462
+ ): AsyncGenerator<MediaSample, void, unknown> {
463
+ validateTimestamp(startTimestamp);
464
+ validateTimestamp(endTimestamp);
465
+
466
+ const sampleQueue: MediaSample[] = [];
467
+ let firstSampleQueued = false;
468
+ let lastSample: MediaSample | null = null;
469
+ let { promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers();
470
+ let { promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers();
471
+ let decoderIsFlushed = false;
472
+ let ended = false;
473
+ let terminated = false;
474
+ let decoder: DecoderWrapper<MediaSample> | null = null;
475
+
476
+ // This stores errors that are "out of band" in the sense that they didn't occur in the normal flow of this
477
+ // method but instead in a different context. This error should not go unnoticed and must be bubbled up to
478
+ // the consumer.
479
+ let outOfBandError = null as unknown;
480
+ let hasOutOfBandError = false;
481
+
482
+ const packetRetrievalOptions: PacketRetrievalOptions = {
483
+ ...options,
484
+ verifyKeyPackets: true,
485
+ metadataOnly: false,
486
+ };
487
+
488
+ // The following is the "pump" process that keeps pumping packets into the decoder
489
+ (async () => {
490
+ decoder = await this._createDecoder((sample) => {
491
+ onQueueDequeue();
492
+ if (sample.timestamp >= endTimestamp) {
493
+ ended = true;
494
+ }
495
+
496
+ if (ended) {
497
+ sample.close();
498
+ return;
499
+ }
500
+
501
+ if (lastSample) {
502
+ if (sample.timestamp > startTimestamp) {
503
+ // We don't know ahead of time what the first first is. This is because the first first is the
504
+ // last first whose timestamp is less than or equal to the start timestamp. Therefore we need to
505
+ // wait for the first first after the start timestamp, and then we'll know that the previous
506
+ // first was the first first.
507
+ sampleQueue.push(lastSample);
508
+ firstSampleQueued = true;
509
+ } else {
510
+ lastSample.close();
511
+ }
512
+ }
513
+
514
+ if (sample.timestamp >= startTimestamp) {
515
+ sampleQueue.push(sample);
516
+ firstSampleQueued = true;
517
+ }
518
+
519
+ lastSample = firstSampleQueued ? null : sample;
520
+
521
+ if (sampleQueue.length > 0) {
522
+ onQueueNotEmpty();
523
+ ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers());
524
+ }
525
+ }, (error) => {
526
+ if (!hasOutOfBandError) {
527
+ outOfBandError = error;
528
+ hasOutOfBandError = true;
529
+ onQueueNotEmpty();
530
+ }
531
+ });
532
+
533
+ const packetSink = this._createPacketSink();
534
+ const keyPacket = await packetSink.getKeyPacket(startTimestamp, packetRetrievalOptions)
535
+ ?? await packetSink.getFirstKeyPacket(packetRetrievalOptions);
536
+
537
+ let currentPacket: EncodedPacket | null = keyPacket;
538
+
539
+ // B-frames make it exceedingly difficult to properly define an upper bound for packet iteration if an end
540
+ // timestamp is set, so we just don't do it. The case that makes it especially tricky is when the frames
541
+ // following a key frame have a lower timestamp than the keyframe; something that quite frequently happens
542
+ // in HEVC streams. The price to pay for not upper-bounding the packet iterator is a slight increase in
543
+ // decoder work at the end of the range, but the added correctness and reliability makes this tradeoff worth
544
+ // it.
545
+ const endPacket = undefined;
546
+
547
+ const packets = packetSink.packets(keyPacket ?? undefined, endPacket, packetRetrievalOptions);
548
+ await packets.next(); // Skip the start packet as we already have it
549
+
550
+ while (currentPacket && !ended && !this._track.input._disposed) {
551
+ const maxQueueSize = computeMaxQueueSize(sampleQueue.length);
552
+ if (sampleQueue.length + decoder.getDecodeQueueSize() > maxQueueSize) {
553
+ ({ promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers());
554
+ await queueDequeue;
555
+ continue;
556
+ }
557
+
558
+ decoder.decode(currentPacket);
559
+
560
+ const packetResult = await packets.next();
561
+ if (packetResult.done) {
562
+ break;
563
+ }
564
+
565
+ currentPacket = packetResult.value;
566
+ }
567
+
568
+ await packets.return();
569
+
570
+ if (!terminated && !this._track.input._disposed) {
571
+ await decoder.flush();
572
+ }
573
+
574
+ if (!firstSampleQueued && lastSample) {
575
+ sampleQueue.push(lastSample);
576
+ }
577
+
578
+ decoderIsFlushed = true;
579
+ onQueueNotEmpty(); // To unstuck (unstick?) the generator
580
+ })().catch((error) => {
581
+ if (!hasOutOfBandError) {
582
+ outOfBandError = error;
583
+ hasOutOfBandError = true;
584
+ onQueueNotEmpty();
585
+ }
586
+ }).finally(() => {
587
+ decoder?.close();
588
+ });
589
+
590
+ const track = this._track;
591
+ const closeSamples = () => {
592
+ lastSample?.close();
593
+ for (const sample of sampleQueue) {
594
+ sample.close();
595
+ }
596
+ };
597
+
598
+ return {
599
+ async next() {
600
+ while (true) {
601
+ if (track.input._disposed) {
602
+ closeSamples();
603
+ throw new InputDisposedError();
604
+ } else if (terminated) {
605
+ return { value: undefined, done: true };
606
+ } else if (hasOutOfBandError) {
607
+ closeSamples();
608
+ throw outOfBandError;
609
+ } else if (sampleQueue.length > 0) {
610
+ const value = sampleQueue.shift()!;
611
+ onQueueDequeue();
612
+ return { value, done: false };
613
+ } else if (!decoderIsFlushed) {
614
+ await queueNotEmpty;
615
+ } else {
616
+ return { value: undefined, done: true };
617
+ }
618
+ }
619
+ },
620
+ async return() {
621
+ terminated = true;
622
+ ended = true;
623
+ onQueueDequeue();
624
+ onQueueNotEmpty();
625
+ closeSamples();
626
+
627
+ return { value: undefined, done: true };
628
+ },
629
+ async throw(error) {
630
+ throw error;
631
+ },
632
+ [Symbol.asyncIterator]() {
633
+ return this;
634
+ },
635
+ };
636
+ }
637
+
638
+ /** @internal */
639
+ protected mediaSamplesAtTimestamps(
640
+ timestamps: AnyIterable<number>,
641
+ options: PacketRetrievalOptions,
642
+ ): AsyncGenerator<MediaSample | null, void, unknown> {
643
+ validateAnyIterable(timestamps);
644
+ const timestampIterator = toAsyncIterator(timestamps);
645
+ const timestampsOfInterest: number[] = [];
646
+
647
+ const sampleQueue: (MediaSample | null)[] = [];
648
+ let { promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers();
649
+ let { promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers();
650
+ let decoderIsFlushed = false;
651
+ let terminated = false;
652
+ let decoder: DecoderWrapper<MediaSample> | null = null;
653
+
654
+ // This stores errors that are "out of band" in the sense that they didn't occur in the normal flow of this
655
+ // method but instead in a different context. This error should not go unnoticed and must be bubbled up to
656
+ // the consumer.
657
+ let outOfBandError = null as unknown;
658
+ let hasOutOfBandError = false;
659
+
660
+ const pushToQueue = (sample: MediaSample | null) => {
661
+ sampleQueue.push(sample);
662
+ onQueueNotEmpty();
663
+ ({ promise: queueNotEmpty, resolve: onQueueNotEmpty } = promiseWithResolvers());
664
+ };
665
+
666
+ const retrievalOptions: PacketRetrievalOptions = {
667
+ ...options,
668
+ verifyKeyPackets: true,
669
+ metadataOnly: false,
670
+ };
671
+
672
+ // The following is the "pump" process that keeps pumping packets into the decoder
673
+ (async () => {
674
+ decoder = await this._createDecoder((sample) => {
675
+ onQueueDequeue();
676
+
677
+ if (terminated) {
678
+ sample.close();
679
+ return;
680
+ }
681
+
682
+ let sampleUses = 0;
683
+ while (
684
+ timestampsOfInterest.length > 0
685
+ && sample.timestamp - timestampsOfInterest[0]! > -1e-10 // Give it a little epsilon
686
+ ) {
687
+ sampleUses++;
688
+ timestampsOfInterest.shift();
689
+ }
690
+
691
+ if (sampleUses > 0) {
692
+ for (let i = 0; i < sampleUses; i++) {
693
+ // Clone the sample if we need to emit it multiple times
694
+ pushToQueue((i < sampleUses - 1 ? sample.clone() : sample) as MediaSample);
695
+ }
696
+ } else {
697
+ sample.close();
698
+ }
699
+ }, (error) => {
700
+ if (!hasOutOfBandError) {
701
+ outOfBandError = error;
702
+ hasOutOfBandError = true;
703
+ onQueueNotEmpty();
704
+ }
705
+ });
706
+
707
+ const packetSink = this._createPacketSink();
708
+ let lastPacket: EncodedPacket | null = null;
709
+ let lastKeyPacket: EncodedPacket | null = null;
710
+
711
+ // The end sequence number (inclusive) in the next batch of packets that will be decoded. The batch starts
712
+ // at the last key frame and goes until this sequence number.
713
+ let maxSequenceNumber = -1;
714
+
715
+ const decodePackets = async () => {
716
+ assert(lastKeyPacket);
717
+ assert(decoder);
718
+
719
+ // Start at the current key packet
720
+ let currentPacket = lastKeyPacket;
721
+ decoder.decode(currentPacket);
722
+
723
+ while (currentPacket.sequenceNumber < maxSequenceNumber) {
724
+ const maxQueueSize = computeMaxQueueSize(sampleQueue.length);
725
+ while (sampleQueue.length + decoder.getDecodeQueueSize() > maxQueueSize && !terminated) {
726
+ ({ promise: queueDequeue, resolve: onQueueDequeue } = promiseWithResolvers());
727
+ await queueDequeue;
728
+ }
729
+
730
+ if (terminated) {
731
+ break;
732
+ }
733
+
734
+ const nextPacket = await packetSink.getNextPacket(currentPacket, retrievalOptions);
735
+ assert(nextPacket);
736
+
737
+ decoder.decode(nextPacket);
738
+ currentPacket = nextPacket;
739
+ }
740
+
741
+ maxSequenceNumber = -1;
742
+ };
743
+
744
+ const flushDecoder = async () => {
745
+ assert(decoder);
746
+ await decoder.flush();
747
+
748
+ // We don't expect this list to have any elements in it anymore, but in case it does, let's emit
749
+ // nulls for every remaining element, then clear it.
750
+ for (let i = 0; i < timestampsOfInterest.length; i++) {
751
+ pushToQueue(null);
752
+ }
753
+ timestampsOfInterest.length = 0;
754
+ };
755
+
756
+ for await (const timestamp of timestampIterator) {
757
+ validateTimestamp(timestamp);
758
+
759
+ if (terminated || this._track.input._disposed) {
760
+ break;
761
+ }
762
+
763
+ const targetPacket = await packetSink.getPacket(timestamp, retrievalOptions);
764
+ const keyPacket = targetPacket && await packetSink.getKeyPacket(timestamp, retrievalOptions);
765
+
766
+ if (!keyPacket) {
767
+ if (maxSequenceNumber !== -1) {
768
+ await decodePackets();
769
+ await flushDecoder();
770
+ }
771
+
772
+ pushToQueue(null);
773
+ lastPacket = null;
774
+ continue;
775
+ }
776
+
777
+ // Check if the key packet has changed or if we're going back in time
778
+ if (
779
+ lastPacket
780
+ && (
781
+ keyPacket.sequenceNumber !== lastKeyPacket!.sequenceNumber
782
+ || targetPacket.timestamp < lastPacket.timestamp
783
+ )
784
+ ) {
785
+ await decodePackets();
786
+ await flushDecoder(); // Always flush here, improves decoder compatibility
787
+ }
788
+
789
+ timestampsOfInterest.push(targetPacket.timestamp);
790
+ maxSequenceNumber = Math.max(targetPacket.sequenceNumber, maxSequenceNumber);
791
+
792
+ lastPacket = targetPacket;
793
+ lastKeyPacket = keyPacket;
794
+ }
795
+
796
+ if (!terminated && !this._track.input._disposed) {
797
+ if (maxSequenceNumber !== -1) {
798
+ // We still need to decode packets
799
+ await decodePackets();
800
+ }
801
+
802
+ await flushDecoder();
803
+ }
804
+
805
+ decoderIsFlushed = true;
806
+ onQueueNotEmpty(); // To unstuck the generator
807
+ })().catch((error) => {
808
+ if (!hasOutOfBandError) {
809
+ outOfBandError = error;
810
+ hasOutOfBandError = true;
811
+ onQueueNotEmpty();
812
+ }
813
+ }).finally(() => {
814
+ decoder?.close();
815
+ });
816
+
817
+ const track = this._track;
818
+ const closeSamples = () => {
819
+ for (const sample of sampleQueue) {
820
+ sample?.close();
821
+ }
822
+ };
823
+
824
+ return {
825
+ async next() {
826
+ while (true) {
827
+ if (track.input._disposed) {
828
+ closeSamples();
829
+ throw new InputDisposedError();
830
+ } else if (terminated) {
831
+ return { value: undefined, done: true };
832
+ } else if (hasOutOfBandError) {
833
+ closeSamples();
834
+ throw outOfBandError;
835
+ } else if (sampleQueue.length > 0) {
836
+ const value = sampleQueue.shift();
837
+ assert(value !== undefined);
838
+ onQueueDequeue();
839
+ return { value, done: false };
840
+ } else if (!decoderIsFlushed) {
841
+ await queueNotEmpty;
842
+ } else {
843
+ return { value: undefined, done: true };
844
+ }
845
+ }
846
+ },
847
+ async return() {
848
+ terminated = true;
849
+ onQueueDequeue();
850
+ onQueueNotEmpty();
851
+ closeSamples();
852
+
853
+ return { value: undefined, done: true };
854
+ },
855
+ async throw(error) {
856
+ throw error;
857
+ },
858
+ [Symbol.asyncIterator]() {
859
+ return this;
860
+ },
861
+ };
862
+ }
863
+ }
864
+
865
+ const computeMaxQueueSize = (decodedSampleQueueSize: number) => {
866
+ // If we have decoded samples lying around, limit the total queue size to a small value (decoded samples can use up
867
+ // a lot of memory). If not, we're fine with a much bigger queue of encoded packets waiting to be decoded. In fact,
868
+ // some decoders only start flushing out decoded chunks when the packet queue is large enough.
869
+ return decodedSampleQueueSize === 0 ? 40 : 8;
870
+ };
871
+
872
+ class VideoDecoderWrapper extends DecoderWrapper<VideoSample> {
873
+ decoder: VideoDecoder | null = null;
874
+
875
+ customDecoder: CustomVideoDecoder | null = null;
876
+ customDecoderCallSerializer = new CallSerializer();
877
+ customDecoderQueueSize = 0;
878
+
879
+ inputTimestamps: number[] = []; // Timestamps input into the decoder, sorted.
880
+ sampleQueue: VideoSample[] = []; // Safari-specific thing, check usage.
881
+ currentPacketIndex = 0;
882
+ raslSkipped = false; // For HEVC stuff
883
+
884
+ // Alpha stuff
885
+ alphaDecoder: VideoDecoder | null = null;
886
+ alphaHadKeyframe = false;
887
+ colorQueue: VideoFrame[] = [];
888
+ alphaQueue: (VideoFrame | null)[] = [];
889
+ merger: ColorAlphaMerger | null = null;
890
+ decodedAlphaChunkCount = 0;
891
+ alphaDecoderQueueSize = 0;
892
+ /** Each value is the number of decoded alpha chunks at which a null alpha frame should be added. */
893
+ nullAlphaFrameQueue: number[] = [];
894
+ currentAlphaPacketIndex = 0;
895
+ alphaRaslSkipped = false; // For HEVC stuff
896
+ finalSamples: { sample: VideoSample | null }[] = [];
897
+ mergeAlphaPromises: Promise<void>[] = [];
898
+
899
+ constructor(
900
+ onSample: (sample: VideoSample) => unknown,
901
+ onError: (error: unknown) => unknown,
902
+ public codec: VideoCodec,
903
+ public decoderConfig: VideoDecoderConfig,
904
+ public rotation: Rotation,
905
+ public timeResolution: number,
906
+ ) {
907
+ super(onSample, onError);
908
+
909
+ const MatchingCustomDecoder = customVideoDecoders.find(x => x.supports(codec, decoderConfig));
910
+ if (MatchingCustomDecoder) {
911
+ // @ts-expect-error "Can't create instance of abstract class 🤓"
912
+ this.customDecoder = new MatchingCustomDecoder() as CustomVideoDecoder;
913
+ // @ts-expect-error It's technically readonly
914
+ this.customDecoder.codec = codec;
915
+ // @ts-expect-error It's technically readonly
916
+ this.customDecoder.config = decoderConfig;
917
+ // @ts-expect-error It's technically readonly
918
+ this.customDecoder.onSample = (sample) => {
919
+ if (!(sample instanceof VideoSample)) {
920
+ throw new TypeError('The argument passed to onSample must be a VideoSample.');
921
+ }
922
+
923
+ this.finalizeAndEmitSample(sample);
924
+ };
925
+ // @ts-expect-error It's technically readonly
926
+ this.customDecoder.onError = (error) => {
927
+ onError(error);
928
+ };
929
+
930
+ void this.customDecoderCallSerializer
931
+ .call(() => this.customDecoder!.init())
932
+ .catch(error => onError(error));
933
+ } else {
934
+ const colorHandler = (frame: VideoFrame) => {
935
+ if (this.alphaQueue.length > 0) {
936
+ // Even when no alpha data is present (most of the time), there will be nulls in this queue
937
+ const alphaFrame = this.alphaQueue.shift();
938
+ assert(alphaFrame !== undefined);
939
+
940
+ void this.mergeAlpha(frame, alphaFrame);
941
+ } else {
942
+ this.colorQueue.push(frame);
943
+ }
944
+ };
945
+
946
+ if (codec === 'avc' && this.decoderConfig.description && isChromium()) {
947
+ // Chromium has/had a bug with playing interlaced AVC (https://issues.chromium.org/issues/456919096)
948
+ // which can be worked around by requesting that software decoding be used. So, here we peek into the
949
+ // AVC description, if present, and switch to software decoding if we find interlaced content.
950
+ const record = deserializeAvcDecoderConfigurationRecord(toUint8Array(this.decoderConfig.description));
951
+ if (record && record.sequenceParameterSets.length > 0) {
952
+ const sps = parseAvcSps(record.sequenceParameterSets[0]!);
953
+ if (sps && sps.frameMbsOnlyFlag === 0) {
954
+ this.decoderConfig = {
955
+ ...this.decoderConfig,
956
+ hardwareAcceleration: 'prefer-software',
957
+ };
958
+ }
959
+ }
960
+ }
961
+
962
+ const stack = new Error('Decoding error').stack;
963
+
964
+ this.decoder = new VideoDecoder({
965
+ output: (frame) => {
966
+ try {
967
+ colorHandler(frame);
968
+ } catch (error) {
969
+ this.onError(error);
970
+ }
971
+ },
972
+ error: (error) => {
973
+ error.stack = stack; // Provide a more useful stack trace, the default one sucks
974
+ this.onError(error);
975
+ },
976
+ });
977
+ this.decoder.configure(this.decoderConfig);
978
+ }
979
+ }
980
+
981
+ getDecodeQueueSize() {
982
+ if (this.customDecoder) {
983
+ return this.customDecoderQueueSize;
984
+ } else {
985
+ assert(this.decoder);
986
+
987
+ return Math.max(
988
+ this.decoder.decodeQueueSize,
989
+ this.alphaDecoder?.decodeQueueSize ?? 0,
990
+ );
991
+ }
992
+ }
993
+
994
+ decode(packet: EncodedPacket) {
995
+ if (this.codec === 'hevc' && this.currentPacketIndex > 0 && !this.raslSkipped) {
996
+ if (this.hasHevcRaslPicture(packet.data)) {
997
+ return; // Drop
998
+ }
999
+
1000
+ this.raslSkipped = true;
1001
+ }
1002
+
1003
+ if (this.customDecoder) {
1004
+ this.customDecoderQueueSize++;
1005
+ void this.customDecoderCallSerializer
1006
+ .call(() => this.customDecoder!.decode(packet))
1007
+ .catch(error => this.onError(error))
1008
+ .finally(() => this.customDecoderQueueSize--);
1009
+ } else {
1010
+ assert(this.decoder);
1011
+
1012
+ if (!isWebKit()) {
1013
+ insertSorted(this.inputTimestamps, packet.timestamp, x => x);
1014
+ }
1015
+
1016
+ if (isChromium() && this.currentPacketIndex === 0) {
1017
+ if (this.codec === 'avc') {
1018
+ // Workaround for https://issues.chromium.org/issues/470109459
1019
+ const filteredNalUnits: Uint8Array[] = [];
1020
+ let hasFrameData = false;
1021
+
1022
+ for (const loc of iterateAvcNalUnits(packet.data, this.decoderConfig)) {
1023
+ const type = extractNalUnitTypeForAvc(packet.data[loc.offset]!);
1024
+ hasFrameData ||= type >= 1 && type <= 5;
1025
+
1026
+ if (type === AvcNalUnitType.AUD) {
1027
+ if (hasFrameData) {
1028
+ // Already has actual frame data, so treat an AUD as simply the end of the packet
1029
+ break;
1030
+ } else {
1031
+ // If packets contain an AUD and have NALUs before it, this trips up Chromium's key
1032
+ // frame detector. Clear the NALUs if an AUD is encountered.
1033
+ // https://github.com/Vanilagy/mediabunny/issues/396
1034
+ filteredNalUnits.length = 0;
1035
+ }
1036
+ }
1037
+
1038
+ // These trip up Chromium's key frame detection, so let's strip them
1039
+ if (!(type >= 20 && type <= 31)) {
1040
+ filteredNalUnits.push(packet.data.subarray(loc.offset, loc.offset + loc.length));
1041
+ }
1042
+ }
1043
+
1044
+ const newData = concatAvcNalUnits(filteredNalUnits, this.decoderConfig);
1045
+ packet = new EncodedPacket(newData, packet.type, packet.timestamp, packet.duration);
1046
+ } else if (this.codec === 'hevc') {
1047
+ // Workaround for https://issues.chromium.org/issues/507611247
1048
+ const sanitizedData = sanitizeHevcPacketForChromium(packet.data, this.decoderConfig);
1049
+ if (sanitizedData) {
1050
+ packet = new EncodedPacket(sanitizedData, packet.type, packet.timestamp, packet.duration);
1051
+ }
1052
+ }
1053
+ }
1054
+
1055
+ this.decoder.decode(packet.toEncodedVideoChunk());
1056
+ this.decodeAlphaData(packet);
1057
+ }
1058
+
1059
+ this.currentPacketIndex++;
1060
+ }
1061
+
1062
+ decodeAlphaData(packet: EncodedPacket) {
1063
+ if (!packet.sideData.alpha) {
1064
+ // No alpha side data in the packet, most common case
1065
+ this.pushNullAlphaFrame();
1066
+ return;
1067
+ }
1068
+
1069
+ if (!this.merger) {
1070
+ this.merger = new ColorAlphaMerger();
1071
+ }
1072
+
1073
+ // Check if we need to set up the alpha decoder
1074
+ if (!this.alphaDecoder) {
1075
+ const alphaHandler = (frame: VideoFrame) => {
1076
+ if (this.colorQueue.length > 0) {
1077
+ const colorFrame = this.colorQueue.shift();
1078
+ assert(colorFrame !== undefined);
1079
+
1080
+ void this.mergeAlpha(colorFrame, frame);
1081
+ } else {
1082
+ this.alphaQueue.push(frame);
1083
+ }
1084
+
1085
+ // Check if any null frames have been queued for this point
1086
+ this.decodedAlphaChunkCount++;
1087
+ while (
1088
+ this.nullAlphaFrameQueue.length > 0
1089
+ && this.nullAlphaFrameQueue[0] === this.decodedAlphaChunkCount
1090
+ ) {
1091
+ this.nullAlphaFrameQueue.shift();
1092
+
1093
+ if (this.colorQueue.length > 0) {
1094
+ const colorFrame = this.colorQueue.shift();
1095
+ assert(colorFrame !== undefined);
1096
+
1097
+ void this.mergeAlpha(colorFrame, null);
1098
+ } else {
1099
+ this.alphaQueue.push(null);
1100
+ }
1101
+ }
1102
+
1103
+ this.alphaDecoderQueueSize--;
1104
+ };
1105
+
1106
+ const stack = new Error('Decoding error').stack;
1107
+
1108
+ this.alphaDecoder = new VideoDecoder({
1109
+ output: (frame) => {
1110
+ try {
1111
+ alphaHandler(frame);
1112
+ } catch (error) {
1113
+ this.onError(error);
1114
+ }
1115
+ },
1116
+ error: (error) => {
1117
+ error.stack = stack; // Provide a more useful stack trace, the default one sucks
1118
+ this.onError(error);
1119
+ },
1120
+ });
1121
+ this.alphaDecoder.configure(this.decoderConfig);
1122
+ }
1123
+
1124
+ const type = determineVideoPacketType(this.codec, this.decoderConfig, packet.sideData.alpha);
1125
+
1126
+ // Alpha packets might follow a different key frame rhythm than the main packets. Therefore, before we start
1127
+ // decoding, we must first find a packet that's actually a key frame. Until then, we treat the image as opaque.
1128
+ if (!this.alphaHadKeyframe) {
1129
+ this.alphaHadKeyframe = type === 'key';
1130
+ }
1131
+
1132
+ if (this.alphaHadKeyframe) {
1133
+ // Same RASL skipping logic as for color, unlikely to be hit (since who uses HEVC with separate alpha??) but
1134
+ // here for symmetry.
1135
+ if (this.codec === 'hevc' && this.currentAlphaPacketIndex > 0 && !this.alphaRaslSkipped) {
1136
+ if (this.hasHevcRaslPicture(packet.sideData.alpha)) {
1137
+ this.pushNullAlphaFrame();
1138
+ return;
1139
+ }
1140
+
1141
+ this.alphaRaslSkipped = true;
1142
+ }
1143
+
1144
+ this.currentAlphaPacketIndex++;
1145
+ this.alphaDecoder.decode(packet.alphaToEncodedVideoChunk(type ?? packet.type));
1146
+ this.alphaDecoderQueueSize++;
1147
+ } else {
1148
+ this.pushNullAlphaFrame();
1149
+ }
1150
+ }
1151
+
1152
+ pushNullAlphaFrame() {
1153
+ if (this.alphaDecoderQueueSize === 0) {
1154
+ // Easy
1155
+ this.alphaQueue.push(null);
1156
+ } else {
1157
+ // There are still alpha chunks being decoded, so pushing `null` immediately would result in out-of-order
1158
+ // data and be incorrect. Instead, we need to enqueue a "null frame" for when the current decoder workload
1159
+ // has finished.
1160
+ this.nullAlphaFrameQueue.push(this.decodedAlphaChunkCount + this.alphaDecoderQueueSize);
1161
+ }
1162
+ }
1163
+
1164
+ /**
1165
+ * If we're using HEVC, we need to make sure to skip any RASL slices that follow a non-IDR key frame such as
1166
+ * CRA_NUT. This is because RASL slices cannot be decoded without data before the CRA_NUT. Browsers behave
1167
+ * differently here: Chromium drops the packets, Safari throws a decoder error. Either way, it's not good
1168
+ * and causes bugs upstream. So, let's take the dropping into our own hands.
1169
+ */
1170
+ hasHevcRaslPicture(packetData: Uint8Array) {
1171
+ for (const loc of iterateHevcNalUnits(packetData, this.decoderConfig)) {
1172
+ const type = extractNalUnitTypeForHevc(packetData[loc.offset]!);
1173
+ if (type === HevcNalUnitType.RASL_N || type === HevcNalUnitType.RASL_R) {
1174
+ return true;
1175
+ }
1176
+ }
1177
+
1178
+ return false;
1179
+ }
1180
+
1181
+ /** Handler for the WebCodecs VideoDecoder for ironing out browser differences. */
1182
+ sampleHandler(sample: VideoSample) {
1183
+ if (isWebKit()) {
1184
+ // For correct B-frame handling, we don't just hand over the frames directly but instead add them to
1185
+ // a queue, because we want to ensure frames are emitted in presentation order. We flush the queue
1186
+ // each time we receive a frame with a timestamp larger than the highest we've seen so far, as we
1187
+ // can sure that is not a B-frame. Typically, WebCodecs automatically guarantees that frames are
1188
+ // emitted in presentation order, but Safari doesn't always follow this rule.
1189
+ if (this.sampleQueue.length > 0 && (sample.timestamp >= last(this.sampleQueue)!.timestamp)) {
1190
+ for (const sample of this.sampleQueue) {
1191
+ this.finalizeAndEmitSample(sample);
1192
+ }
1193
+
1194
+ this.sampleQueue.length = 0;
1195
+ }
1196
+
1197
+ insertSorted(this.sampleQueue, sample, x => x.timestamp);
1198
+ } else {
1199
+ // Assign it the next earliest timestamp from the input. We do this because browsers, by spec, are
1200
+ // required to emit decoded frames in presentation order *while* retaining the timestamp of their
1201
+ // originating EncodedVideoChunk. For files with B-frames but no out-of-order timestamps (like a
1202
+ // missing ctts box, for example), this causes a mismatch. We therefore fix the timestamps and
1203
+ // ensure they are sorted by doing this.
1204
+ const timestamp = this.inputTimestamps.shift();
1205
+
1206
+ // There's no way we'd have more decoded frames than encoded packets we passed in. Actually, the
1207
+ // correspondence should be 1:1.
1208
+ assert(timestamp !== undefined);
1209
+
1210
+ sample.setTimestamp(timestamp);
1211
+ this.finalizeAndEmitSample(sample);
1212
+ }
1213
+ }
1214
+
1215
+ finalizeAndEmitSample(sample: VideoSample) {
1216
+ // Round the timestamps to the time resolution
1217
+ sample.setTimestamp(Math.round(sample.timestamp * this.timeResolution) / this.timeResolution);
1218
+ sample.setDuration(Math.round(sample.duration * this.timeResolution) / this.timeResolution);
1219
+ sample.setRotation(this.rotation);
1220
+
1221
+ this.onSample(sample);
1222
+ }
1223
+
1224
+ async mergeAlpha(color: VideoFrame, alpha: VideoFrame | null) {
1225
+ const resolver = promiseWithResolvers();
1226
+ this.mergeAlphaPromises.push(resolver.promise);
1227
+
1228
+ // Alpha merging is concurrent but the samples must still be emitted in the same order in which the merging
1229
+ // began. Therefore, serialize the results in an array.
1230
+ const result: { sample: VideoSample | null } = { sample: null };
1231
+ this.finalSamples.push(result);
1232
+
1233
+ try {
1234
+ if (!alpha) {
1235
+ // Nothing needs to be merged
1236
+ result.sample = new VideoSample(color);
1237
+ } else {
1238
+ assert(this.merger);
1239
+
1240
+ // The merger takes ownership of the frames, so no need to close them ourselves
1241
+ const finalFrame = await this.merger.merge(color, alpha);
1242
+ result.sample = new VideoSample(finalFrame);
1243
+ }
1244
+
1245
+ // Emit any leading samples that are ready, preserving input order
1246
+ while (this.finalSamples.length > 0 && this.finalSamples[0]!.sample !== null) {
1247
+ const next = this.finalSamples.shift()!;
1248
+ this.sampleHandler(next.sample!);
1249
+ }
1250
+ } catch (error) {
1251
+ removeItem(this.finalSamples, result);
1252
+ this.onError(error);
1253
+ } finally {
1254
+ removeItem(this.mergeAlphaPromises, resolver.promise);
1255
+ resolver.resolve();
1256
+ }
1257
+ }
1258
+
1259
+ async flush() {
1260
+ if (this.customDecoder) {
1261
+ await this.customDecoderCallSerializer.call(() => this.customDecoder!.flush());
1262
+ } else {
1263
+ assert(this.decoder);
1264
+ await Promise.all([
1265
+ this.decoder.flush(),
1266
+ this.alphaDecoder?.flush(),
1267
+ ]);
1268
+ await Promise.all(this.mergeAlphaPromises);
1269
+
1270
+ this.colorQueue.forEach(x => x.close());
1271
+ this.colorQueue.length = 0;
1272
+ this.alphaQueue.forEach(x => x?.close());
1273
+ this.alphaQueue.length = 0;
1274
+
1275
+ this.alphaHadKeyframe = false;
1276
+ this.decodedAlphaChunkCount = 0;
1277
+ this.alphaDecoderQueueSize = 0;
1278
+ this.nullAlphaFrameQueue.length = 0;
1279
+ this.currentAlphaPacketIndex = 0;
1280
+ this.alphaRaslSkipped = false;
1281
+ }
1282
+
1283
+ if (isWebKit()) {
1284
+ for (const sample of this.sampleQueue) {
1285
+ this.finalizeAndEmitSample(sample);
1286
+ }
1287
+
1288
+ this.sampleQueue.length = 0;
1289
+ }
1290
+
1291
+ this.currentPacketIndex = 0;
1292
+ this.raslSkipped = false;
1293
+ }
1294
+
1295
+ close() {
1296
+ if (this.customDecoder) {
1297
+ void this.customDecoderCallSerializer.call(() => this.customDecoder!.close());
1298
+ } else {
1299
+ assert(this.decoder);
1300
+ this.decoder.close();
1301
+ this.alphaDecoder?.close();
1302
+
1303
+ this.colorQueue.forEach(x => x.close());
1304
+ this.colorQueue.length = 0;
1305
+ this.alphaQueue.forEach(x => x?.close());
1306
+ this.alphaQueue.length = 0;
1307
+
1308
+ this.merger?.close();
1309
+ }
1310
+
1311
+ for (const sample of this.sampleQueue) {
1312
+ sample.close();
1313
+ }
1314
+ this.sampleQueue.length = 0;
1315
+ }
1316
+ }
1317
+
1318
+ let mergerWorkerUrl: string | null = null;
1319
+
1320
+ /** Utility class that merges together color and alpha information on the CPU in a pool of workers. */
1321
+ export class ColorAlphaMerger {
1322
+ private workers: Worker[] = [];
1323
+ private nextWorkerIndex = 0;
1324
+ private pendingRequests = new Map<number, ReturnType<typeof promiseWithResolvers<VideoFrame>>>();
1325
+ private nextRequestId = 0;
1326
+
1327
+ merge(color: VideoFrame, alpha: VideoFrame): Promise<VideoFrame> {
1328
+ if (this.workers.length === 0) {
1329
+ if (!mergerWorkerUrl) {
1330
+ const blob = new Blob(
1331
+ [`(${colorAlphaMergerWorkerCode.toString()})()`],
1332
+ { type: 'application/javascript' },
1333
+ );
1334
+ mergerWorkerUrl = URL.createObjectURL(blob);
1335
+ }
1336
+
1337
+ const poolSize = clamp(navigator.hardwareConcurrency, 1, 4);
1338
+ for (let i = 0; i < poolSize; i++) {
1339
+ const worker = new Worker(mergerWorkerUrl);
1340
+
1341
+ worker.addEventListener('message', (event: MessageEvent<ColorAlphaMergerWorkerResponse>) => {
1342
+ const data = event.data;
1343
+ const pending = this.pendingRequests.get(data.id);
1344
+ if (!pending) {
1345
+ return;
1346
+ }
1347
+ this.pendingRequests.delete(data.id);
1348
+
1349
+ if ('error' in data) {
1350
+ pending.reject(new Error(data.error));
1351
+ } else {
1352
+ pending.resolve(data.frame);
1353
+ }
1354
+ });
1355
+
1356
+ worker.addEventListener('error', (event) => {
1357
+ const error = new Error(event.message || 'Color/alpha merge worker error.');
1358
+ for (const pending of this.pendingRequests.values()) {
1359
+ pending.reject(error);
1360
+ }
1361
+ this.pendingRequests.clear();
1362
+ });
1363
+
1364
+ this.workers.push(worker);
1365
+ }
1366
+ }
1367
+
1368
+ const id = this.nextRequestId++;
1369
+ const pending = promiseWithResolvers<VideoFrame>();
1370
+ this.pendingRequests.set(id, pending);
1371
+
1372
+ // Hand the job to the next worker in round-robin fashion
1373
+ const worker = this.workers[this.nextWorkerIndex]!;
1374
+ this.nextWorkerIndex = (this.nextWorkerIndex + 1) % this.workers.length;
1375
+ worker.postMessage({ id, color, alpha }, { transfer: [color, alpha] });
1376
+
1377
+ return pending.promise;
1378
+ }
1379
+
1380
+ close() {
1381
+ for (const worker of this.workers) {
1382
+ worker.terminate();
1383
+ }
1384
+ this.workers.length = 0;
1385
+
1386
+ const error = new Error('Color/alpha merger closed.');
1387
+ for (const pending of this.pendingRequests.values()) {
1388
+ pending.reject(error);
1389
+ }
1390
+ this.pendingRequests.clear();
1391
+ }
1392
+ }
1393
+
1394
+ type ColorAlphaMergerWorkerRequest = {
1395
+ id: number;
1396
+ color: VideoFrame;
1397
+ alpha: VideoFrame;
1398
+ };
1399
+
1400
+ type ColorAlphaMergerWorkerResponse =
1401
+ | { id: number; frame: VideoFrame }
1402
+ | { id: number; error: string };
1403
+
1404
+ const colorAlphaMergerWorkerCode = () => {
1405
+ // These buffers are reused across frames as long as the size matches, since consecutive frames usually share
1406
+ // dimensions
1407
+ let cpuAlphaBuffer: Uint8Array | null = null;
1408
+ let cpuColorBuffer: Uint8Array | null = null;
1409
+
1410
+ // Serialize execution internally so concurrent requests don't race on the shared cpu*Buffer state.
1411
+ let chain: Promise<void> = Promise.resolve();
1412
+ self.addEventListener('message', (event: MessageEvent<ColorAlphaMergerWorkerRequest>) => {
1413
+ const { id, color, alpha } = event.data;
1414
+ chain = chain.then(async () => {
1415
+ try {
1416
+ const frame = await merge(color, alpha);
1417
+ self.postMessage({ id, frame }, { transfer: [frame] });
1418
+ } catch (error) {
1419
+ self.postMessage({ id, error: (error as Error).message });
1420
+ } finally {
1421
+ // We took ownership of the inputs via transfer; close them now that the merge (or its error) is done.
1422
+ color.close();
1423
+ alpha.close();
1424
+ }
1425
+ });
1426
+ });
1427
+
1428
+ const merge = async (color: VideoFrame, alpha: VideoFrame): Promise<VideoFrame> => {
1429
+ const format = color.format as VideoSamplePixelFormat | null;
1430
+ const alphaFormat = alpha.format as VideoSamplePixelFormat | null;
1431
+ if (!format || !alphaFormat) {
1432
+ throw new Error('CPU color/alpha merging requires a known VideoFrame format.');
1433
+ }
1434
+
1435
+ // The alpha frame must have the same bit depth as the color frame
1436
+ const colorIs10 = format.includes('P10');
1437
+ const colorIs12 = format.includes('P12');
1438
+ const alphaIs10 = alphaFormat.includes('P10');
1439
+ const alphaIs12 = alphaFormat.includes('P12');
1440
+ if (alphaIs10 !== colorIs10 || alphaIs12 !== colorIs12) {
1441
+ throw new Error(
1442
+ `CPU color/alpha merging requires the alpha frame to have the same bit depth as the color frame`
1443
+ + ` (color: '${format}', alpha: '${alphaFormat}').`,
1444
+ );
1445
+ }
1446
+
1447
+ if (format === 'RGBX' || format === 'RGBA' || format === 'BGRX' || format === 'BGRA') {
1448
+ return await mergeInterleavedRgba(color, alpha, format);
1449
+ } else if (
1450
+ format === 'I420' || format === 'I420P10' || format === 'I420P12'
1451
+ || format === 'I422' || format === 'I422P10' || format === 'I422P12'
1452
+ || format === 'I444' || format === 'I444P10' || format === 'I444P12'
1453
+ ) {
1454
+ return await mergePlanarYuv(color, alpha, format);
1455
+ } else if (format === 'NV12') {
1456
+ return await mergeNv12(color, alpha);
1457
+ }
1458
+
1459
+ throw new Error(`CPU color/alpha merging does not support format '${format}'.`);
1460
+ };
1461
+
1462
+ const mergeInterleavedRgba = async (
1463
+ color: VideoFrame,
1464
+ alpha: VideoFrame,
1465
+ format: 'RGBX' | 'RGBA' | 'BGRX' | 'BGRA',
1466
+ ): Promise<VideoFrame> => {
1467
+ const width = color.visibleRect?.width ?? color.codedWidth;
1468
+ const height = color.visibleRect?.height ?? color.codedHeight;
1469
+
1470
+ const pixelCount = width * height;
1471
+ const output = new Uint8Array(pixelCount * 4);
1472
+
1473
+ // Color goes straight into the output buffer via copyTo, no intermediate copy needed
1474
+ await color.copyTo(output);
1475
+
1476
+ // And now add the alpha data
1477
+ const alphaY = await readAlpha(alpha, width, height, 1);
1478
+ for (let i = 0, j = 3; i < pixelCount; i++, j += 4) {
1479
+ output[j] = alphaY[i]!;
1480
+ }
1481
+
1482
+ const outputFormat = (format === 'RGBX' || format === 'RGBA') ? 'RGBA' : 'BGRA';
1483
+ const init = {
1484
+ format: outputFormat,
1485
+ codedWidth: width,
1486
+ codedHeight: height,
1487
+ timestamp: color.timestamp,
1488
+ duration: color.duration ?? undefined,
1489
+ transfer: [output.buffer],
1490
+ } as const;
1491
+
1492
+ return new VideoFrame(output, init);
1493
+ };
1494
+
1495
+ const mergePlanarYuv = async (
1496
+ color: VideoFrame,
1497
+ alpha: VideoFrame,
1498
+ format:
1499
+ | 'I420' | 'I420P10' | 'I420P12'
1500
+ | 'I422' | 'I422P10' | 'I422P12'
1501
+ | 'I444' | 'I444P10' | 'I444P12',
1502
+ ): Promise<VideoFrame> => {
1503
+ const width = color.visibleRect?.width ?? color.codedWidth;
1504
+ const height = color.visibleRect?.height ?? color.codedHeight;
1505
+
1506
+ const is10 = format.includes('P10');
1507
+ const is12 = format.includes('P12');
1508
+ const bytesPerSample = (is10 || is12) ? 2 : 1;
1509
+
1510
+ let chromaW: number;
1511
+ let chromaH: number;
1512
+ if (format.startsWith('I420')) {
1513
+ chromaW = Math.ceil(width / 2);
1514
+ chromaH = Math.ceil(height / 2);
1515
+ } else if (format.startsWith('I422')) {
1516
+ chromaW = Math.ceil(width / 2);
1517
+ chromaH = height;
1518
+ } else {
1519
+ chromaW = width;
1520
+ chromaH = height;
1521
+ }
1522
+
1523
+ const ySamples = width * height;
1524
+ const uvSamples = chromaW * chromaH;
1525
+ const yBytes = ySamples * bytesPerSample;
1526
+ const uvBytes = uvSamples * bytesPerSample;
1527
+ const aBytes = ySamples * bytesPerSample;
1528
+
1529
+ const outputBytes = yBytes + 2 * uvBytes + aBytes;
1530
+ const output = new Uint8Array(outputBytes);
1531
+
1532
+ // Write color planes directly into the output buffer via copyTo, no intermediate copy
1533
+ await color.copyTo(output);
1534
+
1535
+ const alphaY = await readAlpha(alpha, width, height, bytesPerSample);
1536
+ const aOffset = yBytes + 2 * uvBytes;
1537
+ output.set(alphaY, aOffset);
1538
+
1539
+ const outputFormat = (format.slice(0, 4) + 'A' + format.slice(4)) as VideoPixelFormat;
1540
+
1541
+ const init = {
1542
+ format: outputFormat,
1543
+ codedWidth: width,
1544
+ codedHeight: height,
1545
+ timestamp: color.timestamp,
1546
+ duration: color.duration ?? undefined,
1547
+ transfer: [output.buffer],
1548
+ };
1549
+
1550
+ return new VideoFrame(output, init);
1551
+ };
1552
+
1553
+ const mergeNv12 = async (
1554
+ color: VideoFrame,
1555
+ alpha: VideoFrame,
1556
+ ): Promise<VideoFrame> => {
1557
+ const width = color.visibleRect?.width ?? color.codedWidth;
1558
+ const height = color.visibleRect?.height ?? color.codedHeight;
1559
+
1560
+ const ySize = width * height;
1561
+ const chromaW = Math.ceil(width / 2);
1562
+ const chromaH = Math.ceil(height / 2);
1563
+ const uvSize = chromaW * chromaH;
1564
+
1565
+ const sourceSize = color.allocationSize();
1566
+ if (!cpuColorBuffer || cpuColorBuffer.byteLength !== sourceSize) {
1567
+ cpuColorBuffer = new Uint8Array(sourceSize);
1568
+ }
1569
+ await color.copyTo(cpuColorBuffer);
1570
+
1571
+ const output = new Uint8Array(ySize + 2 * uvSize + ySize);
1572
+
1573
+ // Y plane copies straight over
1574
+ output.set(cpuColorBuffer.subarray(0, ySize), 0);
1575
+
1576
+ // Deinterleave the UV plane into separate U and V planes
1577
+ const uOffset = ySize;
1578
+ const vOffset = ySize + uvSize;
1579
+ const uvStart = ySize;
1580
+ for (let i = 0; i < uvSize; i++) {
1581
+ output[uOffset + i] = cpuColorBuffer[uvStart + i * 2]!;
1582
+ output[vOffset + i] = cpuColorBuffer[uvStart + i * 2 + 1]!;
1583
+ }
1584
+
1585
+ const alphaY = await readAlpha(alpha, width, height, 1);
1586
+ output.set(alphaY, ySize + 2 * uvSize);
1587
+
1588
+ const init = {
1589
+ format: 'I420A',
1590
+ codedWidth: width,
1591
+ codedHeight: height,
1592
+ timestamp: color.timestamp,
1593
+ duration: color.duration ?? undefined,
1594
+ transfer: [output.buffer],
1595
+ } as const;
1596
+
1597
+ return new VideoFrame(output, init);
1598
+ };
1599
+
1600
+ const readAlpha = async (alpha: VideoFrame, width: number, height: number, bytesPerSample: number) => {
1601
+ const size = alpha.allocationSize();
1602
+ if (!cpuAlphaBuffer || cpuAlphaBuffer.byteLength !== size) {
1603
+ cpuAlphaBuffer = new Uint8Array(size);
1604
+ }
1605
+ await alpha.copyTo(cpuAlphaBuffer);
1606
+
1607
+ const format = alpha.format;
1608
+ if (format === 'RGBA' || format === 'BGRA' || format === 'RGBX' || format === 'BGRX') {
1609
+ // Pack alpha data tightly. Assume alpha is stored in RGB, so sample just from R for simplicity.
1610
+ const rOffset = (format === 'RGBA' || format === 'RGBX') ? 0 : 2;
1611
+ const pixelCount = width * height;
1612
+ for (let i = 0; i < pixelCount; i++) {
1613
+ cpuAlphaBuffer[i] = cpuAlphaBuffer[i * 4 + rOffset]!;
1614
+ }
1615
+ return cpuAlphaBuffer.subarray(0, pixelCount);
1616
+ } else {
1617
+ // For Y-plane-first formats (I*** and NV12), the leading width*height samples are the Y plane
1618
+ return cpuAlphaBuffer.subarray(0, width * height * bytesPerSample);
1619
+ }
1620
+ };
1621
+ };
1622
+
1623
+ /**
1624
+ * Describes additional decoder preferences for video sinks.
1625
+ * @group Media sinks
1626
+ * @public
1627
+ */
1628
+ export type VideoSinkDecoderOptions = {
1629
+ /**
1630
+ * A hint that configures the hardware acceleration method of the decoder. This is best left on `'no-preference'`,
1631
+ * the default.
1632
+ */
1633
+ hardwareAcceleration?: 'no-preference' | 'prefer-hardware' | 'prefer-software';
1634
+ /**
1635
+ * Hint that the selected decoder should be configured to minimize the number of packets that have to be decoded
1636
+ * before video frames are output.
1637
+ */
1638
+ optimizeForLatency?: boolean;
1639
+ };
1640
+
1641
+ const validateVideoSinkDecoderOptions = (decoderOptions: VideoSinkDecoderOptions) => {
1642
+ if (!decoderOptions || typeof decoderOptions !== 'object') {
1643
+ throw new TypeError('decoderOptions must be an object.');
1644
+ }
1645
+ if (
1646
+ decoderOptions.hardwareAcceleration !== undefined
1647
+ && !['no-preference', 'prefer-hardware', 'prefer-software'].includes(decoderOptions.hardwareAcceleration)
1648
+ ) {
1649
+ throw new TypeError(
1650
+ 'decoderOptions.hardwareAcceleration, when provided, must be \'no-preference\', \'prefer-hardware\' or'
1651
+ + ' \'prefer-software\'.',
1652
+ );
1653
+ }
1654
+ if (decoderOptions.optimizeForLatency !== undefined && typeof decoderOptions.optimizeForLatency !== 'boolean') {
1655
+ throw new TypeError('decoderOptions.optimizeForLatency, when provided, must be a boolean.');
1656
+ }
1657
+ };
1658
+
1659
+ /**
1660
+ * A sink that retrieves decoded video samples (video frames) from a video track.
1661
+ * @group Media sinks
1662
+ * @public
1663
+ */
1664
+ export class VideoSampleSink extends BaseMediaSampleSink<VideoSample> {
1665
+ /** @internal */
1666
+ _track: InputVideoTrack;
1667
+ /** @internal */
1668
+ _decoderOptions: VideoSinkDecoderOptions;
1669
+
1670
+ /** Creates a new {@link VideoSampleSink} for the given {@link InputVideoTrack}. */
1671
+ constructor(videoTrack: InputVideoTrack, decoderOptions: VideoSinkDecoderOptions = {}) {
1672
+ if (!(videoTrack instanceof InputVideoTrack)) {
1673
+ throw new TypeError('videoTrack must be an InputVideoTrack.');
1674
+ }
1675
+ validateVideoSinkDecoderOptions(decoderOptions);
1676
+
1677
+ super();
1678
+
1679
+ this._track = videoTrack;
1680
+ this._decoderOptions = decoderOptions;
1681
+ }
1682
+
1683
+ /** @internal */
1684
+ async _createDecoder(
1685
+ onSample: (sample: VideoSample) => unknown,
1686
+ onError: (error: unknown) => unknown,
1687
+ ) {
1688
+ if (!(await this._track.canDecode())) {
1689
+ throw new Error(
1690
+ 'This video track cannot be decoded by this browser. Make sure to check decodability before using'
1691
+ + ' a track.',
1692
+ );
1693
+ }
1694
+
1695
+ const codec = await this._track.getCodec();
1696
+ const rotation = await this._track.getRotation();
1697
+ let decoderConfig = await this._track.getDecoderConfig();
1698
+ const timeResolution = await this._track.getTimeResolution();
1699
+ assert(codec && decoderConfig);
1700
+
1701
+ decoderConfig = {
1702
+ ...decoderConfig,
1703
+ hardwareAcceleration: this._decoderOptions.hardwareAcceleration,
1704
+ optimizeForLatency: this._decoderOptions.optimizeForLatency,
1705
+ };
1706
+
1707
+ return new VideoDecoderWrapper(onSample, onError, codec, decoderConfig, rotation, timeResolution);
1708
+ }
1709
+
1710
+ /** @internal */
1711
+ _createPacketSink() {
1712
+ return new EncodedPacketSink(this._track);
1713
+ }
1714
+
1715
+ /**
1716
+ * Retrieves the video sample (frame) corresponding to the given timestamp, in seconds. More specifically, returns
1717
+ * the last video sample (in presentation order) with a start timestamp less than or equal to the given timestamp.
1718
+ * Returns null if the timestamp is before the track's first timestamp.
1719
+ *
1720
+ * @param timestamp - The timestamp used for retrieval, in seconds.
1721
+ * @param options - Options used for the underlying packet retrieval.
1722
+ */
1723
+ async getSample(timestamp: number, options: PacketRetrievalOptions = {}) {
1724
+ validateTimestamp(timestamp);
1725
+
1726
+ for await (const sample of this.mediaSamplesAtTimestamps([timestamp], options)) {
1727
+ return sample;
1728
+ }
1729
+ throw new Error('Internal error: Iterator returned nothing.');
1730
+ }
1731
+
1732
+ /**
1733
+ * Creates an async iterator that yields the video samples (frames) of this track in presentation order. This method
1734
+ * will intelligently pre-decode a few frames ahead to enable fast iteration.
1735
+ *
1736
+ * @param startTimestamp - The timestamp in seconds at which to start yielding samples (inclusive).
1737
+ * @param endTimestamp - The timestamp in seconds at which to stop yielding samples (exclusive).
1738
+ * @param options - Options used for the underlying packet retrieval.
1739
+ */
1740
+ samples(startTimestamp?: number, endTimestamp?: number, options: PacketRetrievalOptions = {}) {
1741
+ return this.mediaSamplesInRange(startTimestamp, endTimestamp, options);
1742
+ }
1743
+
1744
+ /**
1745
+ * Creates an async iterator that yields a video sample (frame) for each timestamp in the argument. This method
1746
+ * uses an optimized decoding pipeline if these timestamps are monotonically sorted, decoding each packet at most
1747
+ * once, and is therefore more efficient than manually getting the sample for every timestamp. The iterator may
1748
+ * yield null if no frame is available for a given timestamp.
1749
+ *
1750
+ * This method is good for sparse access of media data. If you want primarily sequential media access, prefer
1751
+ * {@link VideoSampleSink.samples} instead.
1752
+ *
1753
+ * @param timestamps - An iterable or async iterable of timestamps in seconds.
1754
+ * @param options - Options used for the underlying packet retrieval.
1755
+ */
1756
+ samplesAtTimestamps(timestamps: AnyIterable<number>, options: PacketRetrievalOptions = {}) {
1757
+ return this.mediaSamplesAtTimestamps(timestamps, options);
1758
+ }
1759
+ }
1760
+
1761
+ /**
1762
+ * A canvas with additional timing information (timestamp & duration).
1763
+ * @group Media sinks
1764
+ * @public
1765
+ */
1766
+ export type WrappedCanvas = {
1767
+ /** A canvas element or offscreen canvas. */
1768
+ canvas: HTMLCanvasElement | OffscreenCanvas;
1769
+ /** The timestamp of the corresponding video sample, in seconds. */
1770
+ timestamp: number;
1771
+ /** The duration of the corresponding video sample, in seconds. */
1772
+ duration: number;
1773
+ };
1774
+
1775
+ /**
1776
+ * Options for constructing a CanvasSink.
1777
+ * @group Media sinks
1778
+ * @public
1779
+ */
1780
+ export type CanvasSinkOptions = {
1781
+ /**
1782
+ * Whether the output canvases should have transparency instead of a black background. Defaults to `false`. Set
1783
+ * this to `true` when using this sink to read transparent videos.
1784
+ */
1785
+ alpha?: boolean;
1786
+ /**
1787
+ * The width of the output canvas in pixels, defaulting to the display width of the video track. If height is not
1788
+ * set, it will be deduced automatically based on aspect ratio.
1789
+ */
1790
+ width?: number;
1791
+ /**
1792
+ * The height of the output canvas in pixels, defaulting to the display height of the video track. If width is not
1793
+ * set, it will be deduced automatically based on aspect ratio.
1794
+ */
1795
+ height?: number;
1796
+ /**
1797
+ * The fitting algorithm in case both width and height are set.
1798
+ *
1799
+ * - `'fill'` will stretch the image to fill the entire box, potentially altering aspect ratio.
1800
+ * - `'contain'` will contain the entire image within the box while preserving aspect ratio. This may lead to
1801
+ * letterboxing.
1802
+ * - `'cover'` will scale the image until the entire box is filled, while preserving aspect ratio.
1803
+ */
1804
+ fit?: 'fill' | 'contain' | 'cover';
1805
+ /**
1806
+ * The clockwise rotation by which to rotate the raw video frame. Defaults to the rotation set in the file metadata.
1807
+ * Rotation is applied before resizing.
1808
+ */
1809
+ rotation?: Rotation;
1810
+ /**
1811
+ * Specifies the rectangular region of the input video to crop to. The crop region will automatically be clamped to
1812
+ * the dimensions of the input video track. Cropping is performed after rotation but before resizing. The crop
1813
+ * region is in the _display pixel space_ of the underlying video data.
1814
+ */
1815
+ crop?: CropRectangle;
1816
+ /**
1817
+ * When set, specifies the number of canvases in the pool. These canvases will be reused in a ring buffer /
1818
+ * round-robin type fashion. This keeps the amount of allocated VRAM constant and relieves the browser from
1819
+ * constantly allocating/deallocating canvases. A pool size of 0 or `undefined` disables the pool and means a new
1820
+ * canvas is created each time.
1821
+ */
1822
+ poolSize?: number;
1823
+ /** Additional preferences for the underlying video decoder. */
1824
+ decoderOptions?: VideoSinkDecoderOptions;
1825
+ };
1826
+
1827
+ /**
1828
+ * A sink that renders video samples (frames) of the given video track to canvases. This is often more useful than
1829
+ * directly retrieving frames, as it comes with common preprocessing steps such as resizing or applying rotation
1830
+ * metadata.
1831
+ *
1832
+ * This sink will yield `HTMLCanvasElement`s when in a DOM context, and `OffscreenCanvas`es otherwise.
1833
+ *
1834
+ * @group Media sinks
1835
+ * @public
1836
+ */
1837
+ export class CanvasSink {
1838
+ /** @internal */
1839
+ _videoTrack: InputVideoTrack;
1840
+ /** @internal */
1841
+ _alpha: boolean;
1842
+ /** @internal */
1843
+ _width!: number;
1844
+ /** @internal */
1845
+ _height!: number;
1846
+ /** @internal */
1847
+ _options: CanvasSinkOptions;
1848
+ /** @internal */
1849
+ _fit: 'fill' | 'contain' | 'cover';
1850
+ /** @internal */
1851
+ _rotation: Rotation = 0;
1852
+ /** @internal */
1853
+ _crop?: { left: number; top: number; width: number; height: number };
1854
+ /** @internal */
1855
+ _initPromise: Promise<void> | null = null;
1856
+ /** @internal */
1857
+ _videoSampleSink: VideoSampleSink;
1858
+ /** @internal */
1859
+ _canvasPool: (HTMLCanvasElement | OffscreenCanvas | null)[];
1860
+ /** @internal */
1861
+ _nextCanvasIndex = 0;
1862
+
1863
+ /** Creates a new {@link CanvasSink} for the given {@link InputVideoTrack}. */
1864
+ constructor(videoTrack: InputVideoTrack, options: CanvasSinkOptions = {}) {
1865
+ if (!(videoTrack instanceof InputVideoTrack)) {
1866
+ throw new TypeError('videoTrack must be an InputVideoTrack.');
1867
+ }
1868
+ if (options && typeof options !== 'object') {
1869
+ throw new TypeError('options must be an object.');
1870
+ }
1871
+ if (options.alpha !== undefined && typeof options.alpha !== 'boolean') {
1872
+ throw new TypeError('options.alpha, when provided, must be a boolean.');
1873
+ }
1874
+ if (options.width !== undefined && (!Number.isInteger(options.width) || options.width <= 0)) {
1875
+ throw new TypeError('options.width, when defined, must be a positive integer.');
1876
+ }
1877
+ if (options.height !== undefined && (!Number.isInteger(options.height) || options.height <= 0)) {
1878
+ throw new TypeError('options.height, when defined, must be a positive integer.');
1879
+ }
1880
+ if (options.fit !== undefined && !['fill', 'contain', 'cover'].includes(options.fit)) {
1881
+ throw new TypeError('options.fit, when provided, must be one of "fill", "contain", or "cover".');
1882
+ }
1883
+ if (
1884
+ options.width !== undefined
1885
+ && options.height !== undefined
1886
+ && options.fit === undefined
1887
+ ) {
1888
+ throw new TypeError(
1889
+ 'When both options.width and options.height are provided, options.fit must also be provided.',
1890
+ );
1891
+ }
1892
+ if (options.rotation !== undefined && ![0, 90, 180, 270].includes(options.rotation)) {
1893
+ throw new TypeError('options.rotation, when provided, must be 0, 90, 180 or 270.');
1894
+ }
1895
+ if (options.crop !== undefined) {
1896
+ validateCropRectangle(options.crop, 'options.');
1897
+ }
1898
+ if (
1899
+ options.poolSize !== undefined
1900
+ && (typeof options.poolSize !== 'number' || !Number.isInteger(options.poolSize) || options.poolSize < 0)
1901
+ ) {
1902
+ throw new TypeError('poolSize must be a non-negative integer.');
1903
+ }
1904
+ if (options.decoderOptions !== undefined) {
1905
+ validateVideoSinkDecoderOptions(options.decoderOptions);
1906
+ }
1907
+
1908
+ this._videoTrack = videoTrack;
1909
+ this._alpha = options.alpha ?? false;
1910
+ this._options = options;
1911
+ this._fit = options.fit ?? 'fill';
1912
+ this._videoSampleSink = new VideoSampleSink(videoTrack, options.decoderOptions);
1913
+ this._canvasPool = Array.from({ length: options.poolSize ?? 0 }, () => null);
1914
+ }
1915
+
1916
+ /** @internal */
1917
+ _ensureInit() {
1918
+ return this._initPromise ??= (async () => {
1919
+ const options = this._options;
1920
+ const videoTrack = this._videoTrack;
1921
+
1922
+ const rotation = options.rotation ?? await videoTrack.getRotation();
1923
+ const squarePixelWidth = await videoTrack.getSquarePixelWidth();
1924
+ const squarePixelHeight = await videoTrack.getSquarePixelHeight();
1925
+
1926
+ const [rotatedWidth, rotatedHeight] = rotation % 180 === 0
1927
+ ? [squarePixelWidth, squarePixelHeight]
1928
+ : [squarePixelHeight, squarePixelWidth];
1929
+
1930
+ let crop = options.crop;
1931
+ if (crop) {
1932
+ crop = clampCropRectangle(crop, rotatedWidth, rotatedHeight);
1933
+ }
1934
+
1935
+ let [width, height] = crop
1936
+ ? [crop.width, crop.height]
1937
+ : [rotatedWidth, rotatedHeight];
1938
+ const originalAspectRatio = width / height;
1939
+
1940
+ // If width and height aren't defined together, deduce the missing value using the aspect ratio
1941
+ if (options.width !== undefined && options.height === undefined) {
1942
+ width = options.width;
1943
+ height = Math.round(width / originalAspectRatio);
1944
+ } else if (options.width === undefined && options.height !== undefined) {
1945
+ height = options.height;
1946
+ width = Math.round(height * originalAspectRatio);
1947
+ } else if (options.width !== undefined && options.height !== undefined) {
1948
+ width = options.width;
1949
+ height = options.height;
1950
+ }
1951
+
1952
+ this._width = width;
1953
+ this._height = height;
1954
+ this._rotation = rotation;
1955
+ this._crop = crop;
1956
+ })();
1957
+ }
1958
+
1959
+ /** @internal */
1960
+ _videoSampleToWrappedCanvas(sample: VideoSample): WrappedCanvas {
1961
+ const width = this._width;
1962
+ const height = this._height;
1963
+ let canvas = this._canvasPool[this._nextCanvasIndex];
1964
+ let canvasIsNew = false;
1965
+
1966
+ if (!canvas) {
1967
+ if (typeof document !== 'undefined') {
1968
+ // Prefer an HTMLCanvasElement
1969
+ canvas = document.createElement('canvas');
1970
+ canvas.width = width;
1971
+ canvas.height = height;
1972
+ } else {
1973
+ canvas = new OffscreenCanvas(width, height);
1974
+ }
1975
+
1976
+ if (this._canvasPool.length > 0) {
1977
+ this._canvasPool[this._nextCanvasIndex] = canvas;
1978
+ }
1979
+
1980
+ canvasIsNew = true;
1981
+ }
1982
+
1983
+ if (this._canvasPool.length > 0) {
1984
+ this._nextCanvasIndex = (this._nextCanvasIndex + 1) % this._canvasPool.length;
1985
+ }
1986
+
1987
+ const context = canvas.getContext('2d', {
1988
+ alpha: this._alpha || isFirefox(), // Firefox has VideoFrame glitches with opaque canvases
1989
+ }) as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
1990
+ assert(context);
1991
+
1992
+ context.imageSmoothingQuality = 'high';
1993
+
1994
+ if (!canvasIsNew) {
1995
+ if (!this._alpha && isFirefox()) {
1996
+ context.fillStyle = 'black';
1997
+ context.fillRect(0, 0, width, height);
1998
+ } else {
1999
+ context.clearRect(0, 0, width, height);
2000
+ }
2001
+ }
2002
+
2003
+ sample.drawWithFit(context, {
2004
+ fit: this._fit,
2005
+ rotation: this._rotation,
2006
+ crop: this._crop,
2007
+ });
2008
+
2009
+ const result = {
2010
+ canvas,
2011
+ timestamp: sample.timestamp,
2012
+ duration: sample.duration,
2013
+ };
2014
+
2015
+ sample.close();
2016
+ return result;
2017
+ }
2018
+
2019
+ /**
2020
+ * Retrieves a canvas with the video frame corresponding to the given timestamp, in seconds. More specifically,
2021
+ * returns the last video frame (in presentation order) with a start timestamp less than or equal to the given
2022
+ * timestamp. Returns null if the timestamp is before the track's first timestamp.
2023
+ *
2024
+ * @param timestamp - The timestamp used for retrieval, in seconds.
2025
+ * @param options - Options used for the underlying packet retrieval.
2026
+ */
2027
+ async getCanvas(timestamp: number, options?: PacketRetrievalOptions) {
2028
+ validateTimestamp(timestamp);
2029
+ await this._ensureInit();
2030
+
2031
+ const sample = await this._videoSampleSink.getSample(timestamp, options);
2032
+ return sample && this._videoSampleToWrappedCanvas(sample);
2033
+ }
2034
+
2035
+ /**
2036
+ * Creates an async iterator that yields canvases with the video frames of this track in presentation order. This
2037
+ * method will intelligently pre-decode a few frames ahead to enable fast iteration.
2038
+ *
2039
+ * @param startTimestamp - The timestamp in seconds at which to start yielding canvases (inclusive).
2040
+ * @param endTimestamp - The timestamp in seconds at which to stop yielding canvases (exclusive).
2041
+ * @param options - Options used for the underlying packet retrieval.
2042
+ */
2043
+ async* canvases(startTimestamp?: number, endTimestamp?: number, options?: PacketRetrievalOptions) {
2044
+ await this._ensureInit();
2045
+ yield* mapAsyncGenerator(
2046
+ this._videoSampleSink.samples(startTimestamp, endTimestamp, options),
2047
+ sample => this._videoSampleToWrappedCanvas(sample),
2048
+ );
2049
+ }
2050
+
2051
+ /**
2052
+ * Creates an async iterator that yields a canvas for each timestamp in the argument. This method uses an optimized
2053
+ * decoding pipeline if these timestamps are monotonically sorted, decoding each packet at most once, and is
2054
+ * therefore more efficient than manually getting the canvas for every timestamp. The iterator may yield null if
2055
+ * no frame is available for a given timestamp.
2056
+ *
2057
+ * This method is good for sparse access of media data. If you want primarily sequential media access, prefer
2058
+ * {@link CanvasSink.canvases} instead.
2059
+ *
2060
+ * @param timestamps - An iterable or async iterable of timestamps in seconds.
2061
+ * @param options - Options used for the underlying packet retrieval.
2062
+ */
2063
+ async* canvasesAtTimestamps(timestamps: AnyIterable<number>, options?: PacketRetrievalOptions) {
2064
+ await this._ensureInit();
2065
+ yield* mapAsyncGenerator(
2066
+ this._videoSampleSink.samplesAtTimestamps(timestamps, options),
2067
+ sample => sample && this._videoSampleToWrappedCanvas(sample),
2068
+ );
2069
+ }
2070
+ }
2071
+
2072
+ class AudioDecoderWrapper extends DecoderWrapper<AudioSample> {
2073
+ decoder: AudioDecoder | null = null;
2074
+
2075
+ customDecoder: CustomAudioDecoder | null = null;
2076
+ customDecoderCallSerializer = new CallSerializer();
2077
+ customDecoderQueueSize = 0;
2078
+
2079
+ // Internal state to accumulate a precise current timestamp based on audio durations, not the (potentially
2080
+ // inaccurate) packet timestamps.
2081
+ currentTimestamp: number | null = null;
2082
+ // Chromium does not respect negative packet timestamps, so we must do the fixin' ourselves
2083
+ expectedFirstTimestamp: number | null = null;
2084
+ timestampOffset = 0;
2085
+
2086
+ constructor(
2087
+ onSample: (sample: AudioSample) => unknown,
2088
+ onError: (error: unknown) => unknown,
2089
+ codec: AudioCodec,
2090
+ decoderConfig: AudioDecoderConfig,
2091
+ ) {
2092
+ super(onSample, onError);
2093
+
2094
+ const sampleHandler = (sample: AudioSample) => {
2095
+ let sampleTimestamp = sample.timestamp;
2096
+
2097
+ if (this.expectedFirstTimestamp && this.currentTimestamp === null) {
2098
+ this.timestampOffset = this.expectedFirstTimestamp - sampleTimestamp; ;
2099
+ }
2100
+
2101
+ sampleTimestamp += this.timestampOffset;
2102
+
2103
+ if (
2104
+ this.currentTimestamp === null
2105
+ || Math.abs(sampleTimestamp - this.currentTimestamp) >= sample.duration
2106
+ ) {
2107
+ // We need to sync with the sample timestamp again
2108
+ this.currentTimestamp = sampleTimestamp;
2109
+ }
2110
+
2111
+ const preciseTimestamp = this.currentTimestamp;
2112
+ this.currentTimestamp += sample.duration;
2113
+
2114
+ if (sample.numberOfFrames === 0) {
2115
+ // We skip zero-data (empty) AudioSamples. These are sometimes emitted, for example, by Firefox when it
2116
+ // decodes Vorbis (at the start).
2117
+ sample.close();
2118
+ return;
2119
+ }
2120
+
2121
+ // Round the timestamp to the sample rate
2122
+ const sampleRate = decoderConfig.sampleRate;
2123
+ sample.setTimestamp(Math.round(preciseTimestamp * sampleRate) / sampleRate);
2124
+
2125
+ onSample(sample);
2126
+ };
2127
+
2128
+ const MatchingCustomDecoder = customAudioDecoders.find(x => x.supports(codec, decoderConfig));
2129
+ if (MatchingCustomDecoder) {
2130
+ // @ts-expect-error "Can't create instance of abstract class 🤓"
2131
+ this.customDecoder = new MatchingCustomDecoder() as CustomAudioDecoder;
2132
+ // @ts-expect-error It's technically readonly
2133
+ this.customDecoder.codec = codec;
2134
+ // @ts-expect-error It's technically readonly
2135
+ this.customDecoder.config = decoderConfig;
2136
+ // @ts-expect-error It's technically readonly
2137
+ this.customDecoder.onSample = (sample) => {
2138
+ if (!(sample instanceof AudioSample)) {
2139
+ throw new TypeError('The argument passed to onSample must be an AudioSample.');
2140
+ }
2141
+
2142
+ sampleHandler(sample);
2143
+ };
2144
+ // @ts-expect-error It's technically readonly
2145
+ this.customDecoder.onError = (error) => {
2146
+ onError(error);
2147
+ };
2148
+
2149
+ void this.customDecoderCallSerializer
2150
+ .call(() => this.customDecoder!.init())
2151
+ .catch(error => onError(error));
2152
+ } else {
2153
+ const stack = new Error('Decoding error').stack;
2154
+
2155
+ this.decoder = new AudioDecoder({
2156
+ output: (data) => {
2157
+ try {
2158
+ sampleHandler(new AudioSample(data));
2159
+ } catch (error) {
2160
+ this.onError(error);
2161
+ }
2162
+ },
2163
+ error: (error) => {
2164
+ error.stack = stack; // Provide a more useful stack trace, the default one sucks
2165
+ this.onError(error);
2166
+ },
2167
+ });
2168
+ this.decoder.configure(decoderConfig);
2169
+ }
2170
+ }
2171
+
2172
+ getDecodeQueueSize() {
2173
+ if (this.customDecoder) {
2174
+ return this.customDecoderQueueSize;
2175
+ } else {
2176
+ assert(this.decoder);
2177
+ return this.decoder.decodeQueueSize;
2178
+ }
2179
+ }
2180
+
2181
+ decode(packet: EncodedPacket) {
2182
+ if (this.customDecoder) {
2183
+ this.customDecoderQueueSize++;
2184
+ void this.customDecoderCallSerializer
2185
+ .call(() => this.customDecoder!.decode(packet))
2186
+ .catch(error => this.onError(error))
2187
+ .finally(() => this.customDecoderQueueSize--);
2188
+ } else {
2189
+ assert(this.decoder);
2190
+
2191
+ this.expectedFirstTimestamp ??= packet.timestamp;
2192
+ this.decoder.decode(packet.toEncodedAudioChunk());
2193
+ }
2194
+ }
2195
+
2196
+ async flush() {
2197
+ if (this.customDecoder) {
2198
+ await this.customDecoderCallSerializer.call(() => this.customDecoder!.flush());
2199
+ } else {
2200
+ assert(this.decoder);
2201
+ await this.decoder.flush();
2202
+ }
2203
+
2204
+ this.currentTimestamp = null;
2205
+ this.expectedFirstTimestamp = null;
2206
+ this.timestampOffset = 0;
2207
+ }
2208
+
2209
+ close() {
2210
+ if (this.customDecoder) {
2211
+ void this.customDecoderCallSerializer.call(() => this.customDecoder!.close());
2212
+ } else {
2213
+ assert(this.decoder);
2214
+ this.decoder.close();
2215
+ }
2216
+ }
2217
+ }
2218
+
2219
+ // There are a lot of PCM variants not natively supported by the browser and by AudioData. Therefore we need a simple
2220
+ // decoder that maps any input PCM format into a PCM format supported by the browser.
2221
+ class PcmAudioDecoderWrapper extends DecoderWrapper<AudioSample> {
2222
+ codec: PcmAudioCodec;
2223
+
2224
+ inputSampleSize: 1 | 2 | 3 | 4 | 8;
2225
+ readInputValue: (view: DataView, byteOffset: number) => number;
2226
+
2227
+ outputSampleSize: 1 | 2 | 4;
2228
+ outputFormat: 'u8' | 's16' | 's32' | 'f32';
2229
+ writeOutputValue: (view: DataView, byteOffset: number, value: number) => void;
2230
+
2231
+ // Internal state to accumulate a precise current timestamp based on audio durations, not the (potentially
2232
+ // inaccurate) packet timestamps.
2233
+ currentTimestamp: number | null = null;
2234
+
2235
+ constructor(
2236
+ onSample: (sample: AudioSample) => unknown,
2237
+ onError: (error: unknown) => unknown,
2238
+ public decoderConfig: AudioDecoderConfig,
2239
+ ) {
2240
+ super(onSample, onError);
2241
+
2242
+ assert((PCM_AUDIO_CODECS as readonly string[]).includes(decoderConfig.codec));
2243
+ this.codec = decoderConfig.codec as PcmAudioCodec;
2244
+
2245
+ const { dataType, sampleSize, littleEndian } = parsePcmCodec(this.codec);
2246
+ this.inputSampleSize = sampleSize;
2247
+
2248
+ switch (sampleSize) {
2249
+ case 1: {
2250
+ if (dataType === 'unsigned') {
2251
+ this.readInputValue = (view, byteOffset) => view.getUint8(byteOffset) - 2 ** 7;
2252
+ } else if (dataType === 'signed') {
2253
+ this.readInputValue = (view, byteOffset) => view.getInt8(byteOffset);
2254
+ } else if (dataType === 'ulaw') {
2255
+ this.readInputValue = (view, byteOffset) => fromUlaw(view.getUint8(byteOffset));
2256
+ } else if (dataType === 'alaw') {
2257
+ this.readInputValue = (view, byteOffset) => fromAlaw(view.getUint8(byteOffset));
2258
+ } else {
2259
+ assert(false);
2260
+ }
2261
+ }; break;
2262
+ case 2: {
2263
+ if (dataType === 'unsigned') {
2264
+ this.readInputValue = (view, byteOffset) => view.getUint16(byteOffset, littleEndian) - 2 ** 15;
2265
+ } else if (dataType === 'signed') {
2266
+ this.readInputValue = (view, byteOffset) => view.getInt16(byteOffset, littleEndian);
2267
+ } else {
2268
+ assert(false);
2269
+ }
2270
+ }; break;
2271
+ case 3: {
2272
+ if (dataType === 'unsigned') {
2273
+ this.readInputValue = (view, byteOffset) => getUint24(view, byteOffset, littleEndian) - 2 ** 23;
2274
+ } else if (dataType === 'signed') {
2275
+ this.readInputValue = (view, byteOffset) => getInt24(view, byteOffset, littleEndian);
2276
+ } else {
2277
+ assert(false);
2278
+ }
2279
+ }; break;
2280
+ case 4: {
2281
+ if (dataType === 'unsigned') {
2282
+ this.readInputValue = (view, byteOffset) => view.getUint32(byteOffset, littleEndian) - 2 ** 31;
2283
+ } else if (dataType === 'signed') {
2284
+ this.readInputValue = (view, byteOffset) => view.getInt32(byteOffset, littleEndian);
2285
+ } else if (dataType === 'float') {
2286
+ this.readInputValue = (view, byteOffset) => view.getFloat32(byteOffset, littleEndian);
2287
+ } else {
2288
+ assert(false);
2289
+ }
2290
+ }; break;
2291
+ case 8: {
2292
+ if (dataType === 'float') {
2293
+ this.readInputValue = (view, byteOffset) => view.getFloat64(byteOffset, littleEndian);
2294
+ } else {
2295
+ assert(false);
2296
+ }
2297
+ }; break;
2298
+ default: {
2299
+ assertNever(sampleSize);
2300
+ assert(false);
2301
+ };
2302
+ }
2303
+
2304
+ switch (sampleSize) {
2305
+ case 1: {
2306
+ if (dataType === 'ulaw' || dataType === 'alaw') {
2307
+ this.outputSampleSize = 2;
2308
+ this.outputFormat = 's16';
2309
+ this.writeOutputValue = (view, byteOffset, value) => view.setInt16(byteOffset, value, true);
2310
+ } else {
2311
+ this.outputSampleSize = 1;
2312
+ this.outputFormat = 'u8';
2313
+ this.writeOutputValue = (view, byteOffset, value) => view.setUint8(byteOffset, value + 2 ** 7);
2314
+ }
2315
+ }; break;
2316
+ case 2: {
2317
+ this.outputSampleSize = 2;
2318
+ this.outputFormat = 's16';
2319
+ this.writeOutputValue = (view, byteOffset, value) => view.setInt16(byteOffset, value, true);
2320
+ }; break;
2321
+ case 3: {
2322
+ this.outputSampleSize = 4;
2323
+ this.outputFormat = 's32';
2324
+ // From https://www.w3.org/TR/webcodecs:
2325
+ // AudioData containing 24-bit samples SHOULD store those samples in s32 or f32. When samples are
2326
+ // stored in s32, each sample MUST be left-shifted by 8 bits.
2327
+ this.writeOutputValue = (view, byteOffset, value) => view.setInt32(byteOffset, value << 8, true);
2328
+ }; break;
2329
+ case 4: {
2330
+ this.outputSampleSize = 4;
2331
+
2332
+ if (dataType === 'float') {
2333
+ this.outputFormat = 'f32';
2334
+ this.writeOutputValue = (view, byteOffset, value) => view.setFloat32(byteOffset, value, true);
2335
+ } else {
2336
+ this.outputFormat = 's32';
2337
+ this.writeOutputValue = (view, byteOffset, value) => view.setInt32(byteOffset, value, true);
2338
+ }
2339
+ }; break;
2340
+ case 8: {
2341
+ this.outputSampleSize = 4;
2342
+
2343
+ this.outputFormat = 'f32';
2344
+ this.writeOutputValue = (view, byteOffset, value) => view.setFloat32(byteOffset, value, true);
2345
+ }; break;
2346
+ default: {
2347
+ assertNever(sampleSize);
2348
+ assert(false);
2349
+ };
2350
+ };
2351
+ }
2352
+
2353
+ getDecodeQueueSize() {
2354
+ return 0;
2355
+ }
2356
+
2357
+ decode(packet: EncodedPacket) {
2358
+ const inputView = toDataView(packet.data);
2359
+
2360
+ const numberOfFrames = packet.byteLength / this.decoderConfig.numberOfChannels / this.inputSampleSize;
2361
+
2362
+ const outputBufferSize = numberOfFrames * this.decoderConfig.numberOfChannels * this.outputSampleSize;
2363
+ const outputBuffer = new ArrayBuffer(outputBufferSize);
2364
+ const outputView = new DataView(outputBuffer);
2365
+
2366
+ for (let i = 0; i < numberOfFrames * this.decoderConfig.numberOfChannels; i++) {
2367
+ const inputIndex = i * this.inputSampleSize;
2368
+ const outputIndex = i * this.outputSampleSize;
2369
+
2370
+ const value = this.readInputValue(inputView, inputIndex);
2371
+ this.writeOutputValue(outputView, outputIndex, value);
2372
+ }
2373
+
2374
+ const preciseDuration = numberOfFrames / this.decoderConfig.sampleRate;
2375
+ if (this.currentTimestamp === null || Math.abs(packet.timestamp - this.currentTimestamp) >= preciseDuration) {
2376
+ // We need to sync with the packet timestamp again
2377
+ this.currentTimestamp = packet.timestamp;
2378
+ }
2379
+
2380
+ const preciseTimestamp = this.currentTimestamp;
2381
+ this.currentTimestamp += preciseDuration;
2382
+
2383
+ const audioSample = new AudioSample({
2384
+ format: this.outputFormat,
2385
+ data: outputBuffer,
2386
+ numberOfChannels: this.decoderConfig.numberOfChannels,
2387
+ sampleRate: this.decoderConfig.sampleRate,
2388
+ numberOfFrames,
2389
+ timestamp: preciseTimestamp,
2390
+ });
2391
+
2392
+ this.onSample(audioSample);
2393
+ }
2394
+
2395
+ async flush() {
2396
+ // Do nothing
2397
+ }
2398
+
2399
+ close() {
2400
+ // Do nothing
2401
+ }
2402
+ }
2403
+
2404
+ /**
2405
+ * Sink for retrieving decoded audio samples from an audio track.
2406
+ * @group Media sinks
2407
+ * @public
2408
+ */
2409
+ export class AudioSampleSink extends BaseMediaSampleSink<AudioSample> {
2410
+ /** @internal */
2411
+ _track: InputAudioTrack;
2412
+
2413
+ /** Creates a new {@link AudioSampleSink} for the given {@link InputAudioTrack}. */
2414
+ constructor(audioTrack: InputAudioTrack) {
2415
+ if (!(audioTrack instanceof InputAudioTrack)) {
2416
+ throw new TypeError('audioTrack must be an InputAudioTrack.');
2417
+ }
2418
+
2419
+ super();
2420
+
2421
+ this._track = audioTrack;
2422
+ }
2423
+
2424
+ /** @internal */
2425
+ async _createDecoder(
2426
+ onSample: (sample: AudioSample) => unknown,
2427
+ onError: (error: unknown) => unknown,
2428
+ ) {
2429
+ if (!(await this._track.canDecode())) {
2430
+ throw new Error(
2431
+ 'This audio track cannot be decoded by this browser. Make sure to check decodability before using'
2432
+ + ' a track.',
2433
+ );
2434
+ }
2435
+
2436
+ const codec = await this._track.getCodec();
2437
+ const decoderConfig = await this._track.getDecoderConfig();
2438
+ assert(codec && decoderConfig);
2439
+
2440
+ if ((PCM_AUDIO_CODECS as readonly string[]).includes(decoderConfig.codec)) {
2441
+ return new PcmAudioDecoderWrapper(onSample, onError, decoderConfig);
2442
+ } else {
2443
+ return new AudioDecoderWrapper(onSample, onError, codec, decoderConfig);
2444
+ }
2445
+ }
2446
+
2447
+ /** @internal */
2448
+ _createPacketSink() {
2449
+ return new EncodedPacketSink(this._track);
2450
+ }
2451
+
2452
+ /**
2453
+ * Retrieves the audio sample corresponding to the given timestamp, in seconds. More specifically, returns
2454
+ * the last audio sample (in presentation order) with a start timestamp less than or equal to the given timestamp.
2455
+ * Returns null if the timestamp is before the track's first timestamp.
2456
+ *
2457
+ * @param timestamp - The timestamp used for retrieval, in seconds.
2458
+ * @param options - Options used for the underlying packet retrieval.
2459
+ */
2460
+ async getSample(timestamp: number, options: PacketRetrievalOptions = {}) {
2461
+ validateTimestamp(timestamp);
2462
+
2463
+ for await (const sample of this.mediaSamplesAtTimestamps([timestamp], options)) {
2464
+ return sample;
2465
+ }
2466
+ throw new Error('Internal error: Iterator returned nothing.');
2467
+ }
2468
+
2469
+ /**
2470
+ * Creates an async iterator that yields the audio samples of this track in presentation order. This method
2471
+ * will intelligently pre-decode a few samples ahead to enable fast iteration.
2472
+ *
2473
+ * @param startTimestamp - The timestamp in seconds at which to start yielding samples (inclusive).
2474
+ * @param endTimestamp - The timestamp in seconds at which to stop yielding samples (exclusive).
2475
+ * @param options - Options used for the underlying packet retrieval.
2476
+ */
2477
+ samples(startTimestamp?: number, endTimestamp?: number, options: PacketRetrievalOptions = {}) {
2478
+ return this.mediaSamplesInRange(startTimestamp, endTimestamp, options);
2479
+ }
2480
+
2481
+ /**
2482
+ * Creates an async iterator that yields an audio sample for each timestamp in the argument. This method
2483
+ * uses an optimized decoding pipeline if these timestamps are monotonically sorted, decoding each packet at most
2484
+ * once, and is therefore more efficient than manually getting the sample for every timestamp. The iterator may
2485
+ * yield null if no sample is available for a given timestamp.
2486
+ *
2487
+ * This method is good for sparse access of media data. If you want primarily sequential media access, prefer
2488
+ * {@link AudioSampleSink.samples} instead.
2489
+ *
2490
+ * @param timestamps - An iterable or async iterable of timestamps in seconds.
2491
+ * @param options - Options used for the underlying packet retrieval.
2492
+ */
2493
+ samplesAtTimestamps(timestamps: AnyIterable<number>, options: PacketRetrievalOptions = {}) {
2494
+ return this.mediaSamplesAtTimestamps(timestamps, options);
2495
+ }
2496
+ }
2497
+
2498
+ /**
2499
+ * An AudioBuffer with additional timing information (timestamp & duration).
2500
+ * @group Media sinks
2501
+ * @public
2502
+ */
2503
+ export type WrappedAudioBuffer = {
2504
+ /** An AudioBuffer. */
2505
+ buffer: AudioBuffer;
2506
+ /** The timestamp of the corresponding audio sample, in seconds. */
2507
+ timestamp: number;
2508
+ /** The duration of the corresponding audio sample, in seconds. */
2509
+ duration: number;
2510
+ };
2511
+
2512
+ /**
2513
+ * A sink that retrieves decoded audio samples from an audio track and converts them to `AudioBuffer` instances. This is
2514
+ * often more useful than directly retrieving audio samples, as audio buffers can be directly used with the
2515
+ * Web Audio API.
2516
+ * @group Media sinks
2517
+ * @public
2518
+ */
2519
+ export class AudioBufferSink {
2520
+ /** @internal */
2521
+ _audioSampleSink: AudioSampleSink;
2522
+
2523
+ /** Creates a new {@link AudioBufferSink} for the given {@link InputAudioTrack}. */
2524
+ constructor(audioTrack: InputAudioTrack) {
2525
+ if (!(audioTrack instanceof InputAudioTrack)) {
2526
+ throw new TypeError('audioTrack must be an InputAudioTrack.');
2527
+ }
2528
+
2529
+ this._audioSampleSink = new AudioSampleSink(audioTrack);
2530
+ }
2531
+
2532
+ /** @internal */
2533
+ _audioSampleToWrappedArrayBuffer(sample: AudioSample): WrappedAudioBuffer {
2534
+ const result: WrappedAudioBuffer = {
2535
+ buffer: sample.toAudioBuffer(),
2536
+ timestamp: sample.timestamp,
2537
+ duration: sample.duration,
2538
+ };
2539
+
2540
+ sample.close();
2541
+ return result;
2542
+ }
2543
+
2544
+ /**
2545
+ * Retrieves the audio buffer corresponding to the given timestamp, in seconds. More specifically, returns
2546
+ * the last audio buffer (in presentation order) with a start timestamp less than or equal to the given timestamp.
2547
+ * Returns null if the timestamp is before the track's first timestamp.
2548
+ *
2549
+ * @param timestamp - The timestamp used for retrieval, in seconds.
2550
+ * @param options - Options used for the underlying packet retrieval.
2551
+ */
2552
+ async getBuffer(timestamp: number, options?: PacketRetrievalOptions) {
2553
+ validateTimestamp(timestamp);
2554
+
2555
+ const data = await this._audioSampleSink.getSample(timestamp, options);
2556
+ return data && this._audioSampleToWrappedArrayBuffer(data);
2557
+ }
2558
+
2559
+ /**
2560
+ * Creates an async iterator that yields audio buffers of this track in presentation order. This method
2561
+ * will intelligently pre-decode a few buffers ahead to enable fast iteration.
2562
+ *
2563
+ * @param startTimestamp - The timestamp in seconds at which to start yielding buffers (inclusive).
2564
+ * @param endTimestamp - The timestamp in seconds at which to stop yielding buffers (exclusive).
2565
+ * @param options - Options used for the underlying packet retrieval.
2566
+ */
2567
+ buffers(startTimestamp?: number, endTimestamp?: number, options?: PacketRetrievalOptions) {
2568
+ return mapAsyncGenerator(
2569
+ this._audioSampleSink.samples(startTimestamp, endTimestamp, options),
2570
+ data => this._audioSampleToWrappedArrayBuffer(data),
2571
+ );
2572
+ }
2573
+
2574
+ /**
2575
+ * Creates an async iterator that yields an audio buffer for each timestamp in the argument. This method
2576
+ * uses an optimized decoding pipeline if these timestamps are monotonically sorted, decoding each packet at most
2577
+ * once, and is therefore more efficient than manually getting the buffer for every timestamp. The iterator may
2578
+ * yield null if no buffer is available for a given timestamp.
2579
+ *
2580
+ * @param timestamps - An iterable or async iterable of timestamps in seconds.
2581
+ * @param options - Options used for the underlying packet retrieval.
2582
+ */
2583
+ buffersAtTimestamps(timestamps: AnyIterable<number>, options?: PacketRetrievalOptions) {
2584
+ return mapAsyncGenerator(
2585
+ this._audioSampleSink.samplesAtTimestamps(timestamps, options),
2586
+ data => data && this._audioSampleToWrappedArrayBuffer(data),
2587
+ );
2588
+ }
2589
+ }