@osmo.inc/cli 0.1.18 → 0.1.20

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 (943) hide show
  1. package/DEPLOY.md +62 -16
  2. package/README.md +31 -15
  3. package/SETUP.md +19 -7
  4. package/THIRD_PARTY_NOTICES.md +5 -16
  5. package/bin/osmo.mjs +12 -0
  6. package/dist/osmo.js +2500 -8905
  7. package/package.json +13 -26
  8. package/release.json +49 -84
  9. package/third-party/inventory.json +0 -3601
  10. package/node_modules/@esbuild/darwin-arm64/README.md +0 -3
  11. package/node_modules/@esbuild/darwin-arm64/bin/esbuild +0 -0
  12. package/node_modules/@esbuild/darwin-arm64/package.json +0 -20
  13. package/node_modules/esbuild/LICENSE.md +0 -21
  14. package/node_modules/esbuild/README.md +0 -3
  15. package/node_modules/esbuild/bin/esbuild +0 -223
  16. package/node_modules/esbuild/install.js +0 -289
  17. package/node_modules/esbuild/lib/main.d.ts +0 -716
  18. package/node_modules/esbuild/lib/main.js +0 -2532
  19. package/node_modules/esbuild/package.json +0 -49
  20. package/node_modules/typescript/LICENSE.txt +0 -55
  21. package/node_modules/typescript/ThirdPartyNoticeText.txt +0 -193
  22. package/node_modules/typescript/lib/lib.d.ts +0 -22
  23. package/node_modules/typescript/lib/lib.decorators.d.ts +0 -384
  24. package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +0 -22
  25. package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +0 -41
  26. package/node_modules/typescript/lib/lib.dom.d.ts +0 -39429
  27. package/node_modules/typescript/lib/lib.dom.iterable.d.ts +0 -571
  28. package/node_modules/typescript/lib/lib.es2015.collection.d.ts +0 -147
  29. package/node_modules/typescript/lib/lib.es2015.core.d.ts +0 -597
  30. package/node_modules/typescript/lib/lib.es2015.d.ts +0 -28
  31. package/node_modules/typescript/lib/lib.es2015.generator.d.ts +0 -77
  32. package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +0 -605
  33. package/node_modules/typescript/lib/lib.es2015.promise.d.ts +0 -81
  34. package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +0 -128
  35. package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +0 -144
  36. package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +0 -46
  37. package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +0 -326
  38. package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +0 -116
  39. package/node_modules/typescript/lib/lib.es2016.d.ts +0 -21
  40. package/node_modules/typescript/lib/lib.es2016.full.d.ts +0 -23
  41. package/node_modules/typescript/lib/lib.es2016.intl.d.ts +0 -31
  42. package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +0 -21
  43. package/node_modules/typescript/lib/lib.es2017.d.ts +0 -26
  44. package/node_modules/typescript/lib/lib.es2017.date.d.ts +0 -31
  45. package/node_modules/typescript/lib/lib.es2017.full.d.ts +0 -23
  46. package/node_modules/typescript/lib/lib.es2017.intl.d.ts +0 -44
  47. package/node_modules/typescript/lib/lib.es2017.object.d.ts +0 -49
  48. package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +0 -135
  49. package/node_modules/typescript/lib/lib.es2017.string.d.ts +0 -45
  50. package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +0 -53
  51. package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +0 -77
  52. package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +0 -53
  53. package/node_modules/typescript/lib/lib.es2018.d.ts +0 -24
  54. package/node_modules/typescript/lib/lib.es2018.full.d.ts +0 -24
  55. package/node_modules/typescript/lib/lib.es2018.intl.d.ts +0 -83
  56. package/node_modules/typescript/lib/lib.es2018.promise.d.ts +0 -30
  57. package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +0 -37
  58. package/node_modules/typescript/lib/lib.es2019.array.d.ts +0 -79
  59. package/node_modules/typescript/lib/lib.es2019.d.ts +0 -24
  60. package/node_modules/typescript/lib/lib.es2019.full.d.ts +0 -24
  61. package/node_modules/typescript/lib/lib.es2019.intl.d.ts +0 -23
  62. package/node_modules/typescript/lib/lib.es2019.object.d.ts +0 -33
  63. package/node_modules/typescript/lib/lib.es2019.string.d.ts +0 -37
  64. package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +0 -24
  65. package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +0 -765
  66. package/node_modules/typescript/lib/lib.es2020.d.ts +0 -27
  67. package/node_modules/typescript/lib/lib.es2020.date.d.ts +0 -42
  68. package/node_modules/typescript/lib/lib.es2020.full.d.ts +0 -24
  69. package/node_modules/typescript/lib/lib.es2020.intl.d.ts +0 -474
  70. package/node_modules/typescript/lib/lib.es2020.number.d.ts +0 -28
  71. package/node_modules/typescript/lib/lib.es2020.promise.d.ts +0 -47
  72. package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +0 -99
  73. package/node_modules/typescript/lib/lib.es2020.string.d.ts +0 -44
  74. package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +0 -41
  75. package/node_modules/typescript/lib/lib.es2021.d.ts +0 -23
  76. package/node_modules/typescript/lib/lib.es2021.full.d.ts +0 -24
  77. package/node_modules/typescript/lib/lib.es2021.intl.d.ts +0 -166
  78. package/node_modules/typescript/lib/lib.es2021.promise.d.ts +0 -48
  79. package/node_modules/typescript/lib/lib.es2021.string.d.ts +0 -33
  80. package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +0 -78
  81. package/node_modules/typescript/lib/lib.es2022.array.d.ts +0 -121
  82. package/node_modules/typescript/lib/lib.es2022.d.ts +0 -25
  83. package/node_modules/typescript/lib/lib.es2022.error.d.ts +0 -75
  84. package/node_modules/typescript/lib/lib.es2022.full.d.ts +0 -24
  85. package/node_modules/typescript/lib/lib.es2022.intl.d.ts +0 -145
  86. package/node_modules/typescript/lib/lib.es2022.object.d.ts +0 -26
  87. package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +0 -39
  88. package/node_modules/typescript/lib/lib.es2022.string.d.ts +0 -25
  89. package/node_modules/typescript/lib/lib.es2023.array.d.ts +0 -924
  90. package/node_modules/typescript/lib/lib.es2023.collection.d.ts +0 -21
  91. package/node_modules/typescript/lib/lib.es2023.d.ts +0 -22
  92. package/node_modules/typescript/lib/lib.es2023.full.d.ts +0 -24
  93. package/node_modules/typescript/lib/lib.es2023.intl.d.ts +0 -56
  94. package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +0 -65
  95. package/node_modules/typescript/lib/lib.es2024.collection.d.ts +0 -29
  96. package/node_modules/typescript/lib/lib.es2024.d.ts +0 -26
  97. package/node_modules/typescript/lib/lib.es2024.full.d.ts +0 -24
  98. package/node_modules/typescript/lib/lib.es2024.object.d.ts +0 -29
  99. package/node_modules/typescript/lib/lib.es2024.promise.d.ts +0 -35
  100. package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +0 -25
  101. package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +0 -68
  102. package/node_modules/typescript/lib/lib.es2024.string.d.ts +0 -29
  103. package/node_modules/typescript/lib/lib.es5.d.ts +0 -4601
  104. package/node_modules/typescript/lib/lib.es6.d.ts +0 -23
  105. package/node_modules/typescript/lib/lib.esnext.array.d.ts +0 -35
  106. package/node_modules/typescript/lib/lib.esnext.collection.d.ts +0 -96
  107. package/node_modules/typescript/lib/lib.esnext.d.ts +0 -29
  108. package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +0 -28
  109. package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +0 -193
  110. package/node_modules/typescript/lib/lib.esnext.error.d.ts +0 -24
  111. package/node_modules/typescript/lib/lib.esnext.float16.d.ts +0 -445
  112. package/node_modules/typescript/lib/lib.esnext.full.d.ts +0 -24
  113. package/node_modules/typescript/lib/lib.esnext.intl.d.ts +0 -21
  114. package/node_modules/typescript/lib/lib.esnext.iterator.d.ts +0 -148
  115. package/node_modules/typescript/lib/lib.esnext.promise.d.ts +0 -34
  116. package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +0 -25
  117. package/node_modules/typescript/lib/lib.scripthost.d.ts +0 -322
  118. package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +0 -41
  119. package/node_modules/typescript/lib/lib.webworker.d.ts +0 -13150
  120. package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +0 -23
  121. package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +0 -340
  122. package/node_modules/typescript/lib/typescript.js +0 -382
  123. package/node_modules/typescript/package.json +0 -57
  124. package/runtime/apps/native/tools/compile.mjs +0 -217
  125. package/runtime/packages/scene/scripts/validate-strict.mjs +0 -246
  126. package/runtime/packages/scene/src/clip.d.mts +0 -14
  127. package/runtime/packages/scene/src/clip.mjs +0 -15
  128. package/runtime/packages/scene/src/color-value.d.mts +0 -5
  129. package/runtime/packages/scene/src/color-value.mjs +0 -19
  130. package/runtime/packages/scene/src/color.ts +0 -1
  131. package/runtime/packages/scene/src/effects.ts +0 -213
  132. package/runtime/packages/scene/src/elements.ts +0 -491
  133. package/runtime/packages/scene/src/evaluate.ts +0 -919
  134. package/runtime/packages/scene/src/index.ts +0 -69
  135. package/runtime/packages/scene/src/jsx-runtime.ts +0 -73
  136. package/runtime/packages/scene/src/knob-schema.ts +0 -115
  137. package/runtime/packages/scene/src/knobs.ts +0 -118
  138. package/runtime/packages/scene/src/mask.d.mts +0 -6
  139. package/runtime/packages/scene/src/mask.mjs +0 -17
  140. package/runtime/packages/scene/src/metadata.d.mts +0 -23
  141. package/runtime/packages/scene/src/metadata.mjs +0 -44
  142. package/runtime/packages/scene/src/motion-sampling.d.mts +0 -2
  143. package/runtime/packages/scene/src/motion-sampling.mjs +0 -50
  144. package/runtime/packages/scene/src/motion.ts +0 -121
  145. package/runtime/packages/scene/src/shader.ts +0 -57
  146. package/src/resource-examples/example-image/landscape.svg +0 -7
  147. package/src/resource-examples/example-image/resource.json +0 -8
  148. package/src/resource-examples/example-model/gem.glb +0 -0
  149. package/src/resource-examples/example-model/preview.svg +0 -1
  150. package/src/resource-examples/example-model/resource.json +0 -7
  151. package/src/resource-examples/example-scene/resource.json +0 -1
  152. package/src/resource-examples/example-scene/scene.tsx +0 -8
  153. package/src/resource-examples/example-shader/effect.wgsl +0 -7
  154. package/src/resource-examples/example-shader/preview.svg +0 -1
  155. package/src/resource-examples/example-shader/resource.json +0 -27
  156. package/src/resource-examples/example-shader/tint.wgsl +0 -7
  157. package/src/resource-examples/example-video/ambient.mp4 +0 -0
  158. package/src/resource-examples/example-video/resource.json +0 -9
  159. package/src/starter/scene.tsx +0 -20
  160. package/studio/index.html +0 -2
  161. package/studio/public/check-circle-filled.svg +0 -3
  162. package/studio/public/comment-cursor/active-outer.svg +0 -3
  163. package/studio/public/comment-cursor/default.svg +0 -6
  164. package/studio/public/osmo-studio-logo.svg +0 -12
  165. package/studio/public/pencil-simple.svg +0 -5
  166. package/studio/src/active-outer-TVYUEURE.svg +0 -3
  167. package/studio/src/app.css +0 -4
  168. package/studio/src/app.js +0 -714
  169. package/studio/src/default-6CTRH4V5.svg +0 -6
  170. package/studio/src/engine_bg.wasm +0 -0
  171. package/studio/src/inter-cyrillic-ext-wght-normal-IYF56FF6.woff2 +0 -0
  172. package/studio/src/inter-cyrillic-wght-normal-JEOLYBOO.woff2 +0 -0
  173. package/studio/src/inter-greek-ext-wght-normal-EOVOK2B5.woff2 +0 -0
  174. package/studio/src/inter-greek-wght-normal-IRE366VL.woff2 +0 -0
  175. package/studio/src/inter-latin-ext-wght-normal-HA22NDSG.woff2 +0 -0
  176. package/studio/src/inter-latin-wght-normal-NRMW37G5.woff2 +0 -0
  177. package/studio/src/inter-vietnamese-wght-normal-CE5GGD3W.woff2 +0 -0
  178. package/studio/src/osmo-studio-logo-D5R2V77B.svg +0 -12
  179. package/third-party/cargo/adler2@2.0.1/Cargo.toml +0 -91
  180. package/third-party/cargo/adler2@2.0.1/LICENSE-0BSD +0 -12
  181. package/third-party/cargo/adler2@2.0.1/LICENSE-APACHE +0 -201
  182. package/third-party/cargo/adler2@2.0.1/LICENSE-MIT +0 -23
  183. package/third-party/cargo/allocator-api2@0.2.21/Cargo.toml +0 -48
  184. package/third-party/cargo/allocator-api2@0.2.21/LICENSE-APACHE +0 -176
  185. package/third-party/cargo/allocator-api2@0.2.21/LICENSE-MIT +0 -23
  186. package/third-party/cargo/arrayref@0.3.9/Cargo.toml +0 -23
  187. package/third-party/cargo/arrayref@0.3.9/LICENSE +0 -26
  188. package/third-party/cargo/arrayvec@0.7.8/Cargo.toml +0 -112
  189. package/third-party/cargo/arrayvec@0.7.8/LICENSE-APACHE +0 -201
  190. package/third-party/cargo/arrayvec@0.7.8/LICENSE-MIT +0 -25
  191. package/third-party/cargo/ascii@1.1.0/Cargo.toml +0 -41
  192. package/third-party/cargo/ascii@1.1.0/LICENSE-APACHE +0 -201
  193. package/third-party/cargo/ascii@1.1.0/LICENSE-MIT +0 -22
  194. package/third-party/cargo/ash@0.38.0+1.3.281/Cargo.toml +0 -66
  195. package/third-party/cargo/ash@0.38.0+1.3.281/LICENSE-APACHE +0 -67
  196. package/third-party/cargo/ash@0.38.0+1.3.281/LICENSE-MIT +0 -25
  197. package/third-party/cargo/autocfg@1.5.1/Cargo.toml +0 -75
  198. package/third-party/cargo/autocfg@1.5.1/LICENSE-APACHE +0 -201
  199. package/third-party/cargo/autocfg@1.5.1/LICENSE-MIT +0 -25
  200. package/third-party/cargo/base64@0.13.1/Cargo.toml +0 -53
  201. package/third-party/cargo/base64@0.13.1/LICENSE-APACHE +0 -201
  202. package/third-party/cargo/base64@0.13.1/LICENSE-MIT +0 -21
  203. package/third-party/cargo/base64@0.22.1/Cargo.toml +0 -85
  204. package/third-party/cargo/base64@0.22.1/LICENSE-APACHE +0 -201
  205. package/third-party/cargo/base64@0.22.1/LICENSE-MIT +0 -21
  206. package/third-party/cargo/bit-set@0.10.0/Cargo.toml +0 -108
  207. package/third-party/cargo/bit-set@0.10.0/LICENSE-APACHE +0 -201
  208. package/third-party/cargo/bit-set@0.10.0/LICENSE-MIT +0 -25
  209. package/third-party/cargo/bit-vec@0.9.1/Cargo.toml +0 -94
  210. package/third-party/cargo/bit-vec@0.9.1/LICENSE-APACHE +0 -201
  211. package/third-party/cargo/bit-vec@0.9.1/LICENSE-MIT +0 -25
  212. package/third-party/cargo/bitflags@2.13.0/Cargo.toml +0 -120
  213. package/third-party/cargo/bitflags@2.13.0/LICENSE-APACHE +0 -201
  214. package/third-party/cargo/bitflags@2.13.0/LICENSE-MIT +0 -25
  215. package/third-party/cargo/block2@0.6.2/Cargo.toml +0 -115
  216. package/third-party/cargo/block2@0.6.2/MIT-terms.txt +0 -22
  217. package/third-party/cargo/block2@0.6.2/madsmtm-objc2-b4167b582b2f-LICENSE.md +0 -34
  218. package/third-party/cargo/bumpalo@3.20.3/Cargo.toml +0 -99
  219. package/third-party/cargo/bumpalo@3.20.3/LICENSE-APACHE +0 -201
  220. package/third-party/cargo/bumpalo@3.20.3/LICENSE-MIT +0 -25
  221. package/third-party/cargo/bytemuck@1.25.1/Cargo.toml +0 -153
  222. package/third-party/cargo/bytemuck@1.25.1/LICENSE-APACHE +0 -61
  223. package/third-party/cargo/bytemuck@1.25.1/LICENSE-MIT +0 -9
  224. package/third-party/cargo/bytemuck@1.25.1/LICENSE-ZLIB +0 -11
  225. package/third-party/cargo/bytemuck_derive@1.11.0/Cargo.toml +0 -60
  226. package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-APACHE +0 -61
  227. package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-MIT +0 -9
  228. package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-ZLIB +0 -11
  229. package/third-party/cargo/byteorder-lite@0.1.0/Cargo.toml +0 -57
  230. package/third-party/cargo/byteorder-lite@0.1.0/LICENSE-MIT +0 -21
  231. package/third-party/cargo/byteorder-lite@0.1.0/UNLICENSE +0 -24
  232. package/third-party/cargo/byteorder@1.5.0/COPYING +0 -3
  233. package/third-party/cargo/byteorder@1.5.0/Cargo.toml +0 -54
  234. package/third-party/cargo/byteorder@1.5.0/LICENSE-MIT +0 -21
  235. package/third-party/cargo/byteorder@1.5.0/UNLICENSE +0 -24
  236. package/third-party/cargo/cfg-if@1.0.4/Cargo.toml +0 -47
  237. package/third-party/cargo/cfg-if@1.0.4/LICENSE-APACHE +0 -201
  238. package/third-party/cargo/cfg-if@1.0.4/LICENSE-MIT +0 -25
  239. package/third-party/cargo/cfg_aliases@0.2.1/Cargo.toml +0 -43
  240. package/third-party/cargo/cfg_aliases@0.2.1/LICENSE +0 -9
  241. package/third-party/cargo/chunked_transfer@1.5.0/Cargo.toml +0 -27
  242. package/third-party/cargo/chunked_transfer@1.5.0/LICENSE-APACHE +0 -201
  243. package/third-party/cargo/chunked_transfer@1.5.0/LICENSE-MIT +0 -22
  244. package/third-party/cargo/codespan-reporting@0.13.1/Cargo.toml +0 -115
  245. package/third-party/cargo/codespan-reporting@0.13.1/LICENSE +0 -201
  246. package/third-party/cargo/color_quant@1.1.0/Cargo.toml +0 -20
  247. package/third-party/cargo/color_quant@1.1.0/LICENSE +0 -21
  248. package/third-party/cargo/console_error_panic_hook@0.1.7/Cargo.toml +0 -27
  249. package/third-party/cargo/console_error_panic_hook@0.1.7/LICENSE-APACHE +0 -201
  250. package/third-party/cargo/console_error_panic_hook@0.1.7/LICENSE-MIT +0 -25
  251. package/third-party/cargo/core_maths@0.1.1/Cargo.toml +0 -37
  252. package/third-party/cargo/core_maths@0.1.1/LICENSE +0 -21
  253. package/third-party/cargo/cosmic-text@0.19.0/Cargo.toml +0 -214
  254. package/third-party/cargo/cosmic-text@0.19.0/LICENSE-APACHE +0 -201
  255. package/third-party/cargo/cosmic-text@0.19.0/LICENSE-MIT +0 -21
  256. package/third-party/cargo/crc32fast@1.5.0/Cargo.toml +0 -68
  257. package/third-party/cargo/crc32fast@1.5.0/LICENSE-APACHE +0 -202
  258. package/third-party/cargo/crc32fast@1.5.0/LICENSE-MIT +0 -21
  259. package/third-party/cargo/data-url@0.3.2/Cargo.toml +0 -63
  260. package/third-party/cargo/data-url@0.3.2/LICENSE-APACHE +0 -201
  261. package/third-party/cargo/data-url@0.3.2/LICENSE-MIT +0 -25
  262. package/third-party/cargo/dispatch2@0.3.1/Cargo.toml +0 -122
  263. package/third-party/cargo/dispatch2@0.3.1/MIT-terms.txt +0 -22
  264. package/third-party/cargo/dispatch2@0.3.1/madsmtm-objc2-8852b424193c-LICENSE.md +0 -34
  265. package/third-party/cargo/dlib@0.5.3/Cargo.toml +0 -43
  266. package/third-party/cargo/dlib@0.5.3/LICENSE.txt +0 -19
  267. package/third-party/cargo/document-features@0.2.12/Cargo.toml +0 -51
  268. package/third-party/cargo/document-features@0.2.12/LICENSE-APACHE +0 -73
  269. package/third-party/cargo/document-features@0.2.12/LICENSE-MIT +0 -19
  270. package/third-party/cargo/equivalent@1.0.2/Cargo.toml +0 -37
  271. package/third-party/cargo/equivalent@1.0.2/LICENSE-APACHE +0 -201
  272. package/third-party/cargo/equivalent@1.0.2/LICENSE-MIT +0 -25
  273. package/third-party/cargo/errno@0.3.14/Cargo.toml +0 -67
  274. package/third-party/cargo/errno@0.3.14/LICENSE-APACHE +0 -201
  275. package/third-party/cargo/errno@0.3.14/LICENSE-MIT +0 -25
  276. package/third-party/cargo/etagere@0.3.0/Cargo.toml +0 -57
  277. package/third-party/cargo/etagere@0.3.0/LICENSE-APACHE +0 -13
  278. package/third-party/cargo/etagere@0.3.0/LICENSE-MIT +0 -20
  279. package/third-party/cargo/euclid@0.22.14/COPYRIGHT +0 -5
  280. package/third-party/cargo/euclid@0.22.14/Cargo.toml +0 -66
  281. package/third-party/cargo/euclid@0.22.14/LICENSE-APACHE +0 -201
  282. package/third-party/cargo/euclid@0.22.14/LICENSE-MIT +0 -25
  283. package/third-party/cargo/fastrand@2.5.0/Cargo.toml +0 -89
  284. package/third-party/cargo/fastrand@2.5.0/LICENSE-APACHE +0 -201
  285. package/third-party/cargo/fastrand@2.5.0/LICENSE-MIT +0 -23
  286. package/third-party/cargo/fdeflate@0.3.7/Cargo.toml +0 -55
  287. package/third-party/cargo/fdeflate@0.3.7/LICENSE-APACHE +0 -177
  288. package/third-party/cargo/fdeflate@0.3.7/LICENSE-MIT +0 -25
  289. package/third-party/cargo/flate2@1.1.9/Cargo.toml +0 -274
  290. package/third-party/cargo/flate2@1.1.9/LICENSE-APACHE +0 -201
  291. package/third-party/cargo/flate2@1.1.9/LICENSE-MIT +0 -25
  292. package/third-party/cargo/float-cmp@0.9.0/Cargo.toml +0 -39
  293. package/third-party/cargo/float-cmp@0.9.0/LICENSE +0 -19
  294. package/third-party/cargo/float_next_after@1.0.0/Cargo.toml +0 -43
  295. package/third-party/cargo/float_next_after@1.0.0/LICENSE +0 -23
  296. package/third-party/cargo/foldhash@0.2.0/Cargo.toml +0 -80
  297. package/third-party/cargo/foldhash@0.2.0/LICENSE +0 -19
  298. package/third-party/cargo/font-types@0.11.3/Cargo.toml +0 -55
  299. package/third-party/cargo/font-types@0.11.3/LICENSE-APACHE +0 -67
  300. package/third-party/cargo/font-types@0.11.3/LICENSE-MIT +0 -25
  301. package/third-party/cargo/fontdb@0.23.0/Cargo.toml +0 -83
  302. package/third-party/cargo/fontdb@0.23.0/LICENSE +0 -21
  303. package/third-party/cargo/futures-core@0.3.32/Cargo.toml +0 -66
  304. package/third-party/cargo/futures-core@0.3.32/LICENSE-APACHE +0 -202
  305. package/third-party/cargo/futures-core@0.3.32/LICENSE-MIT +0 -26
  306. package/third-party/cargo/futures-task@0.3.32/Cargo.toml +0 -58
  307. package/third-party/cargo/futures-task@0.3.32/LICENSE-APACHE +0 -202
  308. package/third-party/cargo/futures-task@0.3.32/LICENSE-MIT +0 -26
  309. package/third-party/cargo/futures-util@0.3.32/Cargo.toml +0 -181
  310. package/third-party/cargo/futures-util@0.3.32/LICENSE-APACHE +0 -202
  311. package/third-party/cargo/futures-util@0.3.32/LICENSE-MIT +0 -26
  312. package/third-party/cargo/getrandom@0.4.3/Cargo.toml +0 -165
  313. package/third-party/cargo/getrandom@0.4.3/LICENSE-APACHE +0 -201
  314. package/third-party/cargo/getrandom@0.4.3/LICENSE-MIT +0 -26
  315. package/third-party/cargo/gif@0.14.2/Cargo.toml +0 -84
  316. package/third-party/cargo/gif@0.14.2/LICENSE-APACHE +0 -201
  317. package/third-party/cargo/gif@0.14.2/LICENSE-MIT +0 -21
  318. package/third-party/cargo/glam@0.33.2/Cargo.toml +0 -354
  319. package/third-party/cargo/glam@0.33.2/LICENSE-APACHE +0 -201
  320. package/third-party/cargo/glam@0.33.2/LICENSE-MIT +0 -23
  321. package/third-party/cargo/glow@0.17.0/Cargo.toml +0 -122
  322. package/third-party/cargo/glow@0.17.0/LICENSE-APACHE +0 -201
  323. package/third-party/cargo/glow@0.17.0/LICENSE-MIT +0 -23
  324. package/third-party/cargo/glow@0.17.0/LICENSE-ZLIB +0 -15
  325. package/third-party/cargo/gltf-derive@1.4.1/Cargo.toml +0 -34
  326. package/third-party/cargo/gltf-derive@1.4.1/LICENSE-APACHE +0 -202
  327. package/third-party/cargo/gltf-derive@1.4.1/LICENSE-MIT +0 -26
  328. package/third-party/cargo/gltf-json@1.4.1/Cargo.toml +0 -50
  329. package/third-party/cargo/gltf-json@1.4.1/LICENSE-APACHE +0 -202
  330. package/third-party/cargo/gltf-json@1.4.1/LICENSE-MIT +0 -26
  331. package/third-party/cargo/gltf@1.4.1/Cargo.toml +0 -122
  332. package/third-party/cargo/gltf@1.4.1/LICENSE-APACHE +0 -202
  333. package/third-party/cargo/gltf@1.4.1/LICENSE-MIT +0 -26
  334. package/third-party/cargo/glyphon@0.12.0/Cargo.toml +0 -35
  335. package/third-party/cargo/glyphon@0.12.0/LICENSE-APACHE +0 -201
  336. package/third-party/cargo/glyphon@0.12.0/LICENSE-MIT +0 -23
  337. package/third-party/cargo/glyphon@0.12.0/LICENSE-ZLIB +0 -15
  338. package/third-party/cargo/gpu-allocator@0.28.0/Cargo.toml +0 -180
  339. package/third-party/cargo/gpu-allocator@0.28.0/LICENSE-APACHE +0 -201
  340. package/third-party/cargo/gpu-allocator@0.28.0/LICENSE-MIT +0 -7
  341. package/third-party/cargo/grid@1.0.1/Cargo.toml +0 -77
  342. package/third-party/cargo/grid@1.0.1/LICENSE +0 -21
  343. package/third-party/cargo/half@2.7.1/Cargo.toml +0 -137
  344. package/third-party/cargo/half@2.7.1/LICENSE-APACHE +0 -176
  345. package/third-party/cargo/half@2.7.1/LICENSE-MIT +0 -19
  346. package/third-party/cargo/harfrust@0.5.2/Cargo.toml +0 -115
  347. package/third-party/cargo/harfrust@0.5.2/harfbuzz-harfrust-efdae3142ab7-LICENSE +0 -22
  348. package/third-party/cargo/hashbrown@0.16.1/Cargo.toml +0 -180
  349. package/third-party/cargo/hashbrown@0.16.1/LICENSE-APACHE +0 -201
  350. package/third-party/cargo/hashbrown@0.16.1/LICENSE-MIT +0 -25
  351. package/third-party/cargo/hashbrown@0.17.1/Cargo.toml +0 -201
  352. package/third-party/cargo/hashbrown@0.17.1/LICENSE-APACHE +0 -201
  353. package/third-party/cargo/hashbrown@0.17.1/LICENSE-MIT +0 -25
  354. package/third-party/cargo/httpdate@1.0.3/Cargo.toml +0 -35
  355. package/third-party/cargo/httpdate@1.0.3/LICENSE-APACHE +0 -201
  356. package/third-party/cargo/httpdate@1.0.3/LICENSE-MIT +0 -19
  357. package/third-party/cargo/image-webp@0.2.4/Cargo.toml +0 -63
  358. package/third-party/cargo/image-webp@0.2.4/LICENSE-APACHE +0 -177
  359. package/third-party/cargo/image-webp@0.2.4/LICENSE-MIT +0 -25
  360. package/third-party/cargo/image@0.25.10/Cargo.toml +0 -248
  361. package/third-party/cargo/image@0.25.10/LICENSE-APACHE +0 -177
  362. package/third-party/cargo/image@0.25.10/LICENSE-MIT +0 -25
  363. package/third-party/cargo/imagesize@0.14.0/Cargo.toml +0 -104
  364. package/third-party/cargo/imagesize@0.14.0/LICENSE +0 -21
  365. package/third-party/cargo/indexmap@2.14.0/Cargo.toml +0 -167
  366. package/third-party/cargo/indexmap@2.14.0/LICENSE-APACHE +0 -201
  367. package/third-party/cargo/indexmap@2.14.0/LICENSE-MIT +0 -25
  368. package/third-party/cargo/inflections@1.1.1/Cargo.toml +0 -21
  369. package/third-party/cargo/inflections@1.1.1/LICENSE +0 -20
  370. package/third-party/cargo/itoa@1.0.18/Cargo.toml +0 -66
  371. package/third-party/cargo/itoa@1.0.18/LICENSE-APACHE +0 -176
  372. package/third-party/cargo/itoa@1.0.18/LICENSE-MIT +0 -23
  373. package/third-party/cargo/js-sys@0.3.103/Cargo.toml +0 -98
  374. package/third-party/cargo/js-sys@0.3.103/LICENSE-APACHE +0 -201
  375. package/third-party/cargo/js-sys@0.3.103/LICENSE-MIT +0 -25
  376. package/third-party/cargo/khronos-egl@6.0.0/Cargo.toml +0 -82
  377. package/third-party/cargo/khronos-egl@6.0.0/LICENSE-APACHE +0 -201
  378. package/third-party/cargo/khronos-egl@6.0.0/LICENSE-MIT +0 -23
  379. package/third-party/cargo/kurbo@0.13.1/Cargo.toml +0 -230
  380. package/third-party/cargo/kurbo@0.13.1/LICENSE-APACHE +0 -202
  381. package/third-party/cargo/kurbo@0.13.1/LICENSE-MIT +0 -25
  382. package/third-party/cargo/lazy_static@1.5.0/Cargo.toml +0 -46
  383. package/third-party/cargo/lazy_static@1.5.0/LICENSE-APACHE +0 -201
  384. package/third-party/cargo/lazy_static@1.5.0/LICENSE-MIT +0 -25
  385. package/third-party/cargo/libc@0.2.186/Cargo.toml +0 -229
  386. package/third-party/cargo/libc@0.2.186/LICENSE-APACHE +0 -176
  387. package/third-party/cargo/libc@0.2.186/LICENSE-MIT +0 -25
  388. package/third-party/cargo/libloading@0.8.9/Cargo.toml +0 -90
  389. package/third-party/cargo/libloading@0.8.9/LICENSE +0 -12
  390. package/third-party/cargo/libm@0.2.16/Cargo.toml +0 -61
  391. package/third-party/cargo/libm@0.2.16/LICENSE.txt +0 -258
  392. package/third-party/cargo/linebender_resource_handle@0.1.1/Cargo.toml +0 -107
  393. package/third-party/cargo/linebender_resource_handle@0.1.1/LICENSE-APACHE +0 -176
  394. package/third-party/cargo/linebender_resource_handle@0.1.1/LICENSE-MIT +0 -19
  395. package/third-party/cargo/litrs@1.0.0/Cargo.toml +0 -63
  396. package/third-party/cargo/litrs@1.0.0/LICENSE-APACHE +0 -176
  397. package/third-party/cargo/litrs@1.0.0/LICENSE-MIT +0 -25
  398. package/third-party/cargo/lock_api@0.4.14/Cargo.toml +0 -68
  399. package/third-party/cargo/lock_api@0.4.14/LICENSE-APACHE +0 -201
  400. package/third-party/cargo/lock_api@0.4.14/LICENSE-MIT +0 -25
  401. package/third-party/cargo/log@0.4.33/Cargo.toml +0 -152
  402. package/third-party/cargo/log@0.4.33/LICENSE-APACHE +0 -201
  403. package/third-party/cargo/log@0.4.33/LICENSE-MIT +0 -25
  404. package/third-party/cargo/lru@0.16.4/Cargo.toml +0 -47
  405. package/third-party/cargo/lru@0.16.4/LICENSE +0 -21
  406. package/third-party/cargo/lyon_geom@1.0.19/Cargo.toml +0 -69
  407. package/third-party/cargo/lyon_geom@1.0.19/nical-lyon-56219c95c658-LICENSE-APACHE +0 -13
  408. package/third-party/cargo/lyon_geom@1.0.19/nical-lyon-56219c95c658-LICENSE-MIT +0 -20
  409. package/third-party/cargo/lyon_path@1.0.19/Cargo.toml +0 -58
  410. package/third-party/cargo/lyon_path@1.0.19/nical-lyon-56219c95c658-LICENSE-APACHE +0 -13
  411. package/third-party/cargo/lyon_path@1.0.19/nical-lyon-56219c95c658-LICENSE-MIT +0 -20
  412. package/third-party/cargo/lyon_tessellation@1.0.20/Cargo.toml +0 -71
  413. package/third-party/cargo/lyon_tessellation@1.0.20/nical-lyon-7fa75ea8eea3-LICENSE-APACHE +0 -13
  414. package/third-party/cargo/lyon_tessellation@1.0.20/nical-lyon-7fa75ea8eea3-LICENSE-MIT +0 -20
  415. package/third-party/cargo/memchr@2.8.3/COPYING +0 -3
  416. package/third-party/cargo/memchr@2.8.3/Cargo.toml +0 -89
  417. package/third-party/cargo/memchr@2.8.3/LICENSE-MIT +0 -21
  418. package/third-party/cargo/memchr@2.8.3/UNLICENSE +0 -24
  419. package/third-party/cargo/memmap2@0.9.11/Cargo.toml +0 -59
  420. package/third-party/cargo/memmap2@0.9.11/LICENSE-APACHE +0 -201
  421. package/third-party/cargo/memmap2@0.9.11/LICENSE-MIT +0 -26
  422. package/third-party/cargo/miniz_oxide@0.8.9/Cargo.toml +0 -91
  423. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE +0 -25
  424. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-APACHE.md +0 -177
  425. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-MIT.md +0 -24
  426. package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-ZLIB.md +0 -14
  427. package/third-party/cargo/moxcms@0.8.1/Cargo.toml +0 -113
  428. package/third-party/cargo/moxcms@0.8.1/LICENSE-APACHE.md +0 -201
  429. package/third-party/cargo/moxcms@0.8.1/LICENSE.md +0 -26
  430. package/third-party/cargo/naga-types@30.0.0/Cargo.toml +0 -94
  431. package/third-party/cargo/naga-types@30.0.0/LICENSE.APACHE +0 -176
  432. package/third-party/cargo/naga-types@30.0.0/LICENSE.MIT +0 -21
  433. package/third-party/cargo/naga@30.0.0/Cargo.toml +0 -246
  434. package/third-party/cargo/naga@30.0.0/LICENSE.APACHE +0 -176
  435. package/third-party/cargo/naga@30.0.0/LICENSE.MIT +0 -21
  436. package/third-party/cargo/num-traits@0.2.19/Cargo.toml +0 -54
  437. package/third-party/cargo/num-traits@0.2.19/LICENSE-APACHE +0 -201
  438. package/third-party/cargo/num-traits@0.2.19/LICENSE-MIT +0 -25
  439. package/third-party/cargo/objc2-core-foundation@0.3.2/Cargo.toml +0 -221
  440. package/third-party/cargo/objc2-core-foundation@0.3.2/MIT-terms.txt +0 -22
  441. package/third-party/cargo/objc2-core-foundation@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +0 -34
  442. package/third-party/cargo/objc2-core-graphics@0.3.2/Cargo.toml +0 -331
  443. package/third-party/cargo/objc2-core-graphics@0.3.2/MIT-terms.txt +0 -22
  444. package/third-party/cargo/objc2-core-graphics@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +0 -34
  445. package/third-party/cargo/objc2-encode@4.1.0/Cargo.toml +0 -81
  446. package/third-party/cargo/objc2-encode@4.1.0/MIT-terms.txt +0 -22
  447. package/third-party/cargo/objc2-encode@4.1.0/madsmtm-objc2-8d214f547736-LICENSE.md +0 -34
  448. package/third-party/cargo/objc2-foundation@0.3.2/Cargo.toml +0 -529
  449. package/third-party/cargo/objc2-foundation@0.3.2/MIT-terms.txt +0 -22
  450. package/third-party/cargo/objc2-foundation@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +0 -34
  451. package/third-party/cargo/objc2-io-surface@0.3.2/Cargo.toml +0 -139
  452. package/third-party/cargo/objc2-io-surface@0.3.2/MIT-terms.txt +0 -22
  453. package/third-party/cargo/objc2-io-surface@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +0 -34
  454. package/third-party/cargo/objc2-metal@0.3.2/Cargo.toml +0 -601
  455. package/third-party/cargo/objc2-metal@0.3.2/MIT-terms.txt +0 -22
  456. package/third-party/cargo/objc2-metal@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +0 -34
  457. package/third-party/cargo/objc2-quartz-core@0.3.2/Cargo.toml +0 -302
  458. package/third-party/cargo/objc2-quartz-core@0.3.2/MIT-terms.txt +0 -22
  459. package/third-party/cargo/objc2-quartz-core@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +0 -34
  460. package/third-party/cargo/objc2@0.6.4/Cargo.toml +0 -232
  461. package/third-party/cargo/objc2@0.6.4/MIT-terms.txt +0 -22
  462. package/third-party/cargo/objc2@0.6.4/madsmtm-objc2-8852b424193c-LICENSE.md +0 -34
  463. package/third-party/cargo/once_cell@1.21.4/Cargo.toml +0 -119
  464. package/third-party/cargo/once_cell@1.21.4/LICENSE-APACHE +0 -201
  465. package/third-party/cargo/once_cell@1.21.4/LICENSE-MIT +0 -23
  466. package/third-party/cargo/ordered-float@5.3.0/Cargo.toml +0 -162
  467. package/third-party/cargo/ordered-float@5.3.0/LICENSE-MIT +0 -25
  468. package/third-party/cargo/parking_lot@0.12.5/Cargo.toml +0 -87
  469. package/third-party/cargo/parking_lot@0.12.5/LICENSE-APACHE +0 -201
  470. package/third-party/cargo/parking_lot@0.12.5/LICENSE-MIT +0 -25
  471. package/third-party/cargo/parking_lot_core@0.9.12/Cargo.toml +0 -72
  472. package/third-party/cargo/parking_lot_core@0.9.12/LICENSE-APACHE +0 -201
  473. package/third-party/cargo/parking_lot_core@0.9.12/LICENSE-MIT +0 -25
  474. package/third-party/cargo/pico-args@0.5.0/Cargo.toml +0 -32
  475. package/third-party/cargo/pico-args@0.5.0/LICENSE +0 -20
  476. package/third-party/cargo/pin-project-lite@0.2.17/Cargo.toml +0 -198
  477. package/third-party/cargo/pin-project-lite@0.2.17/LICENSE-APACHE +0 -177
  478. package/third-party/cargo/pin-project-lite@0.2.17/LICENSE-MIT +0 -23
  479. package/third-party/cargo/pkg-config@0.3.33/Cargo.toml +0 -43
  480. package/third-party/cargo/pkg-config@0.3.33/LICENSE-APACHE +0 -201
  481. package/third-party/cargo/pkg-config@0.3.33/LICENSE-MIT +0 -25
  482. package/third-party/cargo/png@0.18.1/Cargo.toml +0 -130
  483. package/third-party/cargo/png@0.18.1/LICENSE-APACHE +0 -201
  484. package/third-party/cargo/png@0.18.1/LICENSE-MIT +0 -25
  485. package/third-party/cargo/pollster@0.4.0/Cargo.toml +0 -53
  486. package/third-party/cargo/pollster@0.4.0/LICENSE-APACHE +0 -201
  487. package/third-party/cargo/pollster@0.4.0/LICENSE-MIT +0 -25
  488. package/third-party/cargo/polycool@0.4.0/Cargo.toml +0 -83
  489. package/third-party/cargo/polycool@0.4.0/LICENSE-APACHE +0 -202
  490. package/third-party/cargo/polycool@0.4.0/LICENSE-MIT +0 -25
  491. package/third-party/cargo/pp-rs@0.2.1/Cargo.toml +0 -23
  492. package/third-party/cargo/pp-rs@0.2.1/LICENSE +0 -29
  493. package/third-party/cargo/presser@0.3.1/Cargo.toml +0 -58
  494. package/third-party/cargo/presser@0.3.1/LICENSE-APACHE +0 -201
  495. package/third-party/cargo/presser@0.3.1/LICENSE-MIT +0 -25
  496. package/third-party/cargo/proc-macro2@1.0.106/Cargo.toml +0 -105
  497. package/third-party/cargo/proc-macro2@1.0.106/LICENSE-APACHE +0 -176
  498. package/third-party/cargo/proc-macro2@1.0.106/LICENSE-MIT +0 -23
  499. package/third-party/cargo/profiling@1.0.18/Cargo.toml +0 -96
  500. package/third-party/cargo/profiling@1.0.18/aclysma-profiling-8271551172eb-LICENSE-APACHE +0 -201
  501. package/third-party/cargo/profiling@1.0.18/aclysma-profiling-8271551172eb-LICENSE-MIT +0 -21
  502. package/third-party/cargo/pxfm@0.1.30/Cargo.toml +0 -53
  503. package/third-party/cargo/pxfm@0.1.30/LICENSE-APACHE.md +0 -201
  504. package/third-party/cargo/pxfm@0.1.30/LICENSE.md +0 -26
  505. package/third-party/cargo/quick-error@2.0.1/Cargo.toml +0 -24
  506. package/third-party/cargo/quick-error@2.0.1/LICENSE-APACHE +0 -202
  507. package/third-party/cargo/quick-error@2.0.1/LICENSE-MIT +0 -19
  508. package/third-party/cargo/quote@1.0.46/Cargo.toml +0 -70
  509. package/third-party/cargo/quote@1.0.46/LICENSE-APACHE +0 -176
  510. package/third-party/cargo/quote@1.0.46/LICENSE-MIT +0 -23
  511. package/third-party/cargo/rangemap@1.7.1/Cargo.toml +0 -91
  512. package/third-party/cargo/rangemap@1.7.1/LICENSE-APACHE +0 -191
  513. package/third-party/cargo/rangemap@1.7.1/LICENSE-MIT +0 -7
  514. package/third-party/cargo/raw-window-handle@0.6.2/Cargo.toml +0 -56
  515. package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-APACHE.md +0 -177
  516. package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-MIT.md +0 -21
  517. package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-ZLIB.md +0 -11
  518. package/third-party/cargo/raw-window-metal@1.1.0/Cargo.toml +0 -111
  519. package/third-party/cargo/raw-window-metal@1.1.0/LICENSE-APACHE +0 -202
  520. package/third-party/cargo/raw-window-metal@1.1.0/LICENSE-MIT +0 -17
  521. package/third-party/cargo/read-fonts@0.37.0/Cargo.toml +0 -95
  522. package/third-party/cargo/read-fonts@0.37.0/LICENSE-APACHE +0 -67
  523. package/third-party/cargo/read-fonts@0.37.0/LICENSE-MIT +0 -25
  524. package/third-party/cargo/read-fonts@0.39.2/Cargo.toml +0 -96
  525. package/third-party/cargo/read-fonts@0.39.2/LICENSE-APACHE +0 -67
  526. package/third-party/cargo/read-fonts@0.39.2/LICENSE-MIT +0 -25
  527. package/third-party/cargo/renderdoc-sys@1.1.0/Cargo.toml +0 -39
  528. package/third-party/cargo/renderdoc-sys@1.1.0/LICENSE-APACHE +0 -201
  529. package/third-party/cargo/renderdoc-sys@1.1.0/LICENSE-MIT +0 -25
  530. package/third-party/cargo/resvg@0.47.0/Cargo.toml +0 -112
  531. package/third-party/cargo/resvg@0.47.0/LICENSE-APACHE +0 -176
  532. package/third-party/cargo/resvg@0.47.0/LICENSE-MIT +0 -25
  533. package/third-party/cargo/rgb@0.8.53/Cargo.toml +0 -110
  534. package/third-party/cargo/rgb@0.8.53/LICENSE +0 -21
  535. package/third-party/cargo/roxmltree@0.21.1/Cargo.toml +0 -75
  536. package/third-party/cargo/roxmltree@0.21.1/LICENSE-APACHE +0 -201
  537. package/third-party/cargo/roxmltree@0.21.1/LICENSE-MIT +0 -21
  538. package/third-party/cargo/rustc-hash@1.1.0/Cargo.toml +0 -25
  539. package/third-party/cargo/rustc-hash@1.1.0/LICENSE-APACHE +0 -201
  540. package/third-party/cargo/rustc-hash@1.1.0/LICENSE-MIT +0 -23
  541. package/third-party/cargo/rustc-hash@2.1.3/Cargo.toml +0 -50
  542. package/third-party/cargo/rustc-hash@2.1.3/LICENSE-APACHE +0 -176
  543. package/third-party/cargo/rustc-hash@2.1.3/LICENSE-MIT +0 -23
  544. package/third-party/cargo/rustix@1.1.4/COPYRIGHT +0 -29
  545. package/third-party/cargo/rustix@1.1.4/Cargo.toml +0 -292
  546. package/third-party/cargo/rustix@1.1.4/LICENSE-APACHE +0 -201
  547. package/third-party/cargo/rustix@1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception +0 -220
  548. package/third-party/cargo/rustix@1.1.4/LICENSE-MIT +0 -23
  549. package/third-party/cargo/rustversion@1.0.23/Cargo.toml +0 -69
  550. package/third-party/cargo/rustversion@1.0.23/LICENSE-APACHE +0 -176
  551. package/third-party/cargo/rustversion@1.0.23/LICENSE-MIT +0 -23
  552. package/third-party/cargo/rustybuzz@0.20.1/Cargo.toml +0 -110
  553. package/third-party/cargo/rustybuzz@0.20.1/LICENSE +0 -22
  554. package/third-party/cargo/scopeguard@1.2.0/Cargo.toml +0 -43
  555. package/third-party/cargo/scopeguard@1.2.0/LICENSE-APACHE +0 -201
  556. package/third-party/cargo/scopeguard@1.2.0/LICENSE-MIT +0 -25
  557. package/third-party/cargo/self_cell@1.2.2/Cargo.toml +0 -33
  558. package/third-party/cargo/self_cell@1.2.2/LICENSE-APACHE +0 -201
  559. package/third-party/cargo/self_cell@1.2.2/LICENSE-GPLv2 +0 -339
  560. package/third-party/cargo/self_cell@1.3.0/Cargo.toml +0 -33
  561. package/third-party/cargo/self_cell@1.3.0/LICENSE-APACHE +0 -201
  562. package/third-party/cargo/self_cell@1.3.0/LICENSE-GPLv2 +0 -339
  563. package/third-party/cargo/serde@1.0.228/Cargo.toml +0 -84
  564. package/third-party/cargo/serde@1.0.228/LICENSE-APACHE +0 -176
  565. package/third-party/cargo/serde@1.0.228/LICENSE-MIT +0 -23
  566. package/third-party/cargo/serde_core@1.0.228/Cargo.toml +0 -87
  567. package/third-party/cargo/serde_core@1.0.228/LICENSE-APACHE +0 -176
  568. package/third-party/cargo/serde_core@1.0.228/LICENSE-MIT +0 -23
  569. package/third-party/cargo/serde_derive@1.0.228/Cargo.toml +0 -87
  570. package/third-party/cargo/serde_derive@1.0.228/LICENSE-APACHE +0 -176
  571. package/third-party/cargo/serde_derive@1.0.228/LICENSE-MIT +0 -23
  572. package/third-party/cargo/serde_json@1.0.150/Cargo.toml +0 -162
  573. package/third-party/cargo/serde_json@1.0.150/LICENSE-APACHE +0 -176
  574. package/third-party/cargo/serde_json@1.0.150/LICENSE-MIT +0 -23
  575. package/third-party/cargo/simd-adler32@0.3.10/Cargo.toml +0 -68
  576. package/third-party/cargo/simd-adler32@0.3.10/LICENSE.md +0 -21
  577. package/third-party/cargo/simplecss@0.2.2/Cargo.toml +0 -146
  578. package/third-party/cargo/simplecss@0.2.2/LICENSE-APACHE +0 -201
  579. package/third-party/cargo/simplecss@0.2.2/LICENSE-MIT +0 -21
  580. package/third-party/cargo/siphasher@1.0.3/COPYING +0 -7
  581. package/third-party/cargo/siphasher@1.0.3/Cargo.toml +0 -64
  582. package/third-party/cargo/skrifa@0.40.0/Cargo.toml +0 -86
  583. package/third-party/cargo/skrifa@0.40.0/LICENSE-APACHE +0 -67
  584. package/third-party/cargo/skrifa@0.40.0/LICENSE-MIT +0 -25
  585. package/third-party/cargo/skrifa@0.42.1/Cargo.toml +0 -86
  586. package/third-party/cargo/skrifa@0.42.1/LICENSE-APACHE +0 -67
  587. package/third-party/cargo/skrifa@0.42.1/LICENSE-MIT +0 -25
  588. package/third-party/cargo/slab@0.4.12/Cargo.toml +0 -67
  589. package/third-party/cargo/slab@0.4.12/LICENSE +0 -25
  590. package/third-party/cargo/slotmap@1.1.1/Cargo.toml +0 -83
  591. package/third-party/cargo/slotmap@1.1.1/LICENSE +0 -19
  592. package/third-party/cargo/smallvec@1.15.2/Cargo.toml +0 -103
  593. package/third-party/cargo/smallvec@1.15.2/LICENSE-APACHE +0 -201
  594. package/third-party/cargo/smallvec@1.15.2/LICENSE-MIT +0 -25
  595. package/third-party/cargo/smol_str@0.3.6/Cargo.toml +0 -155
  596. package/third-party/cargo/smol_str@0.3.6/LICENSE-APACHE +0 -201
  597. package/third-party/cargo/smol_str@0.3.6/LICENSE-MIT +0 -23
  598. package/third-party/cargo/spirv@0.4.0+sdk-1.4.341.0/Cargo.toml +0 -56
  599. package/third-party/cargo/spirv@0.4.0+sdk-1.4.341.0/gfx-rs-rspirv-8afc3d0ac8e1-LICENSE +0 -202
  600. package/third-party/cargo/static_assertions@1.1.0/Cargo.toml +0 -39
  601. package/third-party/cargo/static_assertions@1.1.0/LICENSE-APACHE +0 -202
  602. package/third-party/cargo/static_assertions@1.1.0/LICENSE-MIT +0 -21
  603. package/third-party/cargo/strict-num@0.1.1/Cargo.toml +0 -35
  604. package/third-party/cargo/strict-num@0.1.1/LICENSE +0 -19
  605. package/third-party/cargo/svgtypes@0.16.1/Cargo.toml +0 -57
  606. package/third-party/cargo/svgtypes@0.16.1/LICENSE-APACHE +0 -201
  607. package/third-party/cargo/svgtypes@0.16.1/LICENSE-MIT +0 -20
  608. package/third-party/cargo/swash@0.2.9/Cargo.toml +0 -93
  609. package/third-party/cargo/swash@0.2.9/LICENSE-APACHE +0 -201
  610. package/third-party/cargo/swash@0.2.9/LICENSE-MIT +0 -25
  611. package/third-party/cargo/syn@2.0.118/Cargo.toml +0 -272
  612. package/third-party/cargo/syn@2.0.118/LICENSE-APACHE +0 -176
  613. package/third-party/cargo/syn@2.0.118/LICENSE-MIT +0 -23
  614. package/third-party/cargo/sys-locale@0.3.2/Cargo.toml +0 -73
  615. package/third-party/cargo/sys-locale@0.3.2/LICENSE-APACHE +0 -201
  616. package/third-party/cargo/sys-locale@0.3.2/LICENSE-MIT +0 -21
  617. package/third-party/cargo/taffy@0.12.1/Cargo.toml +0 -171
  618. package/third-party/cargo/taffy@0.12.1/DioxusLabs-taffy-1c9e37a0a1cf-LICENSE.md +0 -25
  619. package/third-party/cargo/tempfile@3.27.0/Cargo.toml +0 -105
  620. package/third-party/cargo/tempfile@3.27.0/LICENSE-APACHE +0 -201
  621. package/third-party/cargo/tempfile@3.27.0/LICENSE-MIT +0 -25
  622. package/third-party/cargo/termcolor@1.4.1/COPYING +0 -3
  623. package/third-party/cargo/termcolor@1.4.1/Cargo.toml +0 -40
  624. package/third-party/cargo/termcolor@1.4.1/LICENSE-MIT +0 -21
  625. package/third-party/cargo/termcolor@1.4.1/UNLICENSE +0 -24
  626. package/third-party/cargo/thiserror-impl@2.0.18/Cargo.toml +0 -52
  627. package/third-party/cargo/thiserror-impl@2.0.18/LICENSE-APACHE +0 -176
  628. package/third-party/cargo/thiserror-impl@2.0.18/LICENSE-MIT +0 -23
  629. package/third-party/cargo/thiserror@2.0.18/Cargo.toml +0 -119
  630. package/third-party/cargo/thiserror@2.0.18/LICENSE-APACHE +0 -176
  631. package/third-party/cargo/thiserror@2.0.18/LICENSE-MIT +0 -23
  632. package/third-party/cargo/tiny-skia-path@0.12.0/Cargo.toml +0 -58
  633. package/third-party/cargo/tiny-skia-path@0.12.0/LICENSE +0 -30
  634. package/third-party/cargo/tiny-skia@0.12.0/Cargo.toml +0 -116
  635. package/third-party/cargo/tiny-skia@0.12.0/LICENSE +0 -30
  636. package/third-party/cargo/tiny_http@0.12.0/Cargo.toml +0 -82
  637. package/third-party/cargo/tiny_http@0.12.0/LICENSE-APACHE +0 -201
  638. package/third-party/cargo/tiny_http@0.12.0/LICENSE-MIT +0 -25
  639. package/third-party/cargo/tinyvec@1.12.0/Cargo.toml +0 -184
  640. package/third-party/cargo/tinyvec@1.12.0/LICENSE-APACHE.md +0 -202
  641. package/third-party/cargo/tinyvec@1.12.0/LICENSE-MIT.md +0 -5
  642. package/third-party/cargo/tinyvec@1.12.0/LICENSE-ZLIB.md +0 -11
  643. package/third-party/cargo/tinyvec_macros@0.1.1/Cargo.toml +0 -19
  644. package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-APACHE.md +0 -202
  645. package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-MIT.md +0 -21
  646. package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-ZLIB.md +0 -20
  647. package/third-party/cargo/ttf-parser@0.25.1/Cargo.toml +0 -101
  648. package/third-party/cargo/ttf-parser@0.25.1/LICENSE-APACHE +0 -201
  649. package/third-party/cargo/ttf-parser@0.25.1/LICENSE-MIT +0 -20
  650. package/third-party/cargo/unicode-bidi-mirroring@0.4.0/Cargo.toml +0 -22
  651. package/third-party/cargo/unicode-bidi-mirroring@0.4.0/LICENSE-APACHE +0 -201
  652. package/third-party/cargo/unicode-bidi-mirroring@0.4.0/LICENSE-MIT +0 -21
  653. package/third-party/cargo/unicode-bidi@0.3.18/COPYRIGHT +0 -8
  654. package/third-party/cargo/unicode-bidi@0.3.18/Cargo.toml +0 -90
  655. package/third-party/cargo/unicode-bidi@0.3.18/LICENSE-APACHE +0 -201
  656. package/third-party/cargo/unicode-bidi@0.3.18/LICENSE-MIT +0 -25
  657. package/third-party/cargo/unicode-ccc@0.4.0/Cargo.toml +0 -22
  658. package/third-party/cargo/unicode-ccc@0.4.0/LICENSE-APACHE +0 -201
  659. package/third-party/cargo/unicode-ccc@0.4.0/LICENSE-MIT +0 -21
  660. package/third-party/cargo/unicode-ident@1.0.24/Cargo.toml +0 -84
  661. package/third-party/cargo/unicode-ident@1.0.24/LICENSE-APACHE +0 -176
  662. package/third-party/cargo/unicode-ident@1.0.24/LICENSE-MIT +0 -23
  663. package/third-party/cargo/unicode-ident@1.0.24/LICENSE-UNICODE +0 -39
  664. package/third-party/cargo/unicode-linebreak@0.1.5/Cargo.toml +0 -32
  665. package/third-party/cargo/unicode-linebreak@0.1.5/LICENSE +0 -201
  666. package/third-party/cargo/unicode-properties@0.1.4/COPYRIGHT +0 -7
  667. package/third-party/cargo/unicode-properties@0.1.4/Cargo.toml +0 -65
  668. package/third-party/cargo/unicode-properties@0.1.4/LICENSE-APACHE +0 -201
  669. package/third-party/cargo/unicode-properties@0.1.4/LICENSE-MIT +0 -25
  670. package/third-party/cargo/unicode-script@0.5.8/Cargo.toml +0 -63
  671. package/third-party/cargo/unicode-script@0.5.8/LICENSE-APACHE +0 -201
  672. package/third-party/cargo/unicode-script@0.5.8/LICENSE-MIT +0 -27
  673. package/third-party/cargo/unicode-segmentation@1.13.3/COPYRIGHT +0 -7
  674. package/third-party/cargo/unicode-segmentation@1.13.3/Cargo.toml +0 -91
  675. package/third-party/cargo/unicode-segmentation@1.13.3/LICENSE-APACHE +0 -201
  676. package/third-party/cargo/unicode-segmentation@1.13.3/LICENSE-MIT +0 -25
  677. package/third-party/cargo/unicode-vo@0.1.0/Cargo.toml +0 -22
  678. package/third-party/cargo/unicode-vo@0.1.0/LICENSE-APACHE +0 -201
  679. package/third-party/cargo/unicode-vo@0.1.0/LICENSE-MIT +0 -21
  680. package/third-party/cargo/unicode-width@0.2.2/COPYRIGHT +0 -7
  681. package/third-party/cargo/unicode-width@0.2.2/Cargo.toml +0 -77
  682. package/third-party/cargo/unicode-width@0.2.2/LICENSE-APACHE +0 -201
  683. package/third-party/cargo/unicode-width@0.2.2/LICENSE-MIT +0 -25
  684. package/third-party/cargo/unicode-xid@0.2.6/COPYRIGHT +0 -7
  685. package/third-party/cargo/unicode-xid@0.2.6/Cargo.toml +0 -65
  686. package/third-party/cargo/unicode-xid@0.2.6/LICENSE-APACHE +0 -201
  687. package/third-party/cargo/unicode-xid@0.2.6/LICENSE-MIT +0 -25
  688. package/third-party/cargo/urlencoding@2.1.3/Cargo.toml +0 -41
  689. package/third-party/cargo/urlencoding@2.1.3/LICENSE +0 -20
  690. package/third-party/cargo/usvg@0.47.0/Cargo.toml +0 -138
  691. package/third-party/cargo/usvg@0.47.0/LICENSE-APACHE +0 -176
  692. package/third-party/cargo/usvg@0.47.0/LICENSE-MIT +0 -25
  693. package/third-party/cargo/version_check@0.9.5/Cargo.toml +0 -38
  694. package/third-party/cargo/version_check@0.9.5/LICENSE-APACHE +0 -201
  695. package/third-party/cargo/version_check@0.9.5/LICENSE-MIT +0 -19
  696. package/third-party/cargo/wasm-bindgen-futures@0.4.76/Cargo.toml +0 -88
  697. package/third-party/cargo/wasm-bindgen-futures@0.4.76/LICENSE-APACHE +0 -201
  698. package/third-party/cargo/wasm-bindgen-futures@0.4.76/LICENSE-MIT +0 -25
  699. package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/Cargo.toml +0 -83
  700. package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/LICENSE-APACHE +0 -201
  701. package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/LICENSE-MIT +0 -25
  702. package/third-party/cargo/wasm-bindgen-macro@0.2.126/Cargo.toml +0 -73
  703. package/third-party/cargo/wasm-bindgen-macro@0.2.126/LICENSE-APACHE +0 -201
  704. package/third-party/cargo/wasm-bindgen-macro@0.2.126/LICENSE-MIT +0 -25
  705. package/third-party/cargo/wasm-bindgen-shared@0.2.126/Cargo.toml +0 -66
  706. package/third-party/cargo/wasm-bindgen-shared@0.2.126/LICENSE-APACHE +0 -201
  707. package/third-party/cargo/wasm-bindgen-shared@0.2.126/LICENSE-MIT +0 -25
  708. package/third-party/cargo/wasm-bindgen@0.2.126/Cargo.toml +0 -122
  709. package/third-party/cargo/wasm-bindgen@0.2.126/LICENSE-APACHE +0 -201
  710. package/third-party/cargo/wasm-bindgen@0.2.126/LICENSE-MIT +0 -25
  711. package/third-party/cargo/wayland-sys@0.31.11/Cargo.toml +0 -81
  712. package/third-party/cargo/wayland-sys@0.31.11/LICENSE.txt +0 -19
  713. package/third-party/cargo/web-sys@0.3.103/Cargo.toml +0 -2823
  714. package/third-party/cargo/web-sys@0.3.103/LICENSE-APACHE +0 -201
  715. package/third-party/cargo/web-sys@0.3.103/LICENSE-MIT +0 -25
  716. package/third-party/cargo/weezl@0.1.12/Cargo.toml +0 -120
  717. package/third-party/cargo/weezl@0.1.12/LICENSE-APACHE +0 -201
  718. package/third-party/cargo/weezl@0.1.12/LICENSE-MIT +0 -22
  719. package/third-party/cargo/wgpu-core-deps-apple@30.0.0/Cargo.toml +0 -53
  720. package/third-party/cargo/wgpu-core-deps-apple@30.0.0/LICENSE.APACHE +0 -176
  721. package/third-party/cargo/wgpu-core-deps-apple@30.0.0/LICENSE.MIT +0 -21
  722. package/third-party/cargo/wgpu-core@30.0.0/Cargo.toml +0 -237
  723. package/third-party/cargo/wgpu-core@30.0.0/LICENSE.APACHE +0 -176
  724. package/third-party/cargo/wgpu-core@30.0.0/LICENSE.MIT +0 -21
  725. package/third-party/cargo/wgpu-hal@30.0.0/Cargo.toml +0 -541
  726. package/third-party/cargo/wgpu-hal@30.0.0/LICENSE.APACHE +0 -176
  727. package/third-party/cargo/wgpu-hal@30.0.0/LICENSE.MIT +0 -21
  728. package/third-party/cargo/wgpu-naga-bridge@30.0.0/Cargo.toml +0 -46
  729. package/third-party/cargo/wgpu-naga-bridge@30.0.0/LICENSE.APACHE +0 -176
  730. package/third-party/cargo/wgpu-naga-bridge@30.0.0/LICENSE.MIT +0 -21
  731. package/third-party/cargo/wgpu-types@30.0.0/Cargo.toml +0 -161
  732. package/third-party/cargo/wgpu-types@30.0.0/LICENSE.APACHE +0 -176
  733. package/third-party/cargo/wgpu-types@30.0.0/LICENSE.MIT +0 -21
  734. package/third-party/cargo/wgpu@30.0.0/Cargo.toml +0 -278
  735. package/third-party/cargo/wgpu@30.0.0/LICENSE.APACHE +0 -176
  736. package/third-party/cargo/wgpu@30.0.0/LICENSE.MIT +0 -21
  737. package/third-party/cargo/xmlwriter@0.1.0/Cargo.toml +0 -23
  738. package/third-party/cargo/xmlwriter@0.1.0/LICENSE +0 -21
  739. package/third-party/cargo/yazi@0.2.1/Cargo.toml +0 -45
  740. package/third-party/cargo/yazi@0.2.1/LICENSE-APACHE +0 -201
  741. package/third-party/cargo/yazi@0.2.1/LICENSE-MIT +0 -19
  742. package/third-party/cargo/zeno@0.3.3/Cargo.toml +0 -65
  743. package/third-party/cargo/zeno@0.3.3/LICENSE-APACHE +0 -201
  744. package/third-party/cargo/zeno@0.3.3/LICENSE-MIT +0 -25
  745. package/third-party/cargo/zerocopy-derive@0.8.54/Cargo.toml +0 -209
  746. package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-APACHE +0 -202
  747. package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-BSD +0 -24
  748. package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-MIT +0 -26
  749. package/third-party/cargo/zerocopy@0.8.54/Cargo.toml +0 -419
  750. package/third-party/cargo/zerocopy@0.8.54/LICENSE-APACHE +0 -202
  751. package/third-party/cargo/zerocopy@0.8.54/LICENSE-BSD +0 -24
  752. package/third-party/cargo/zerocopy@0.8.54/LICENSE-MIT +0 -26
  753. package/third-party/cargo/zmij@1.0.23/Cargo.toml +0 -90
  754. package/third-party/cargo/zmij@1.0.23/LICENSE-MIT +0 -23
  755. package/third-party/cargo/zune-core@0.5.1/Cargo.toml +0 -47
  756. package/third-party/cargo/zune-core@0.5.1/LICENSE-APACHE +0 -201
  757. package/third-party/cargo/zune-core@0.5.1/LICENSE-MIT +0 -21
  758. package/third-party/cargo/zune-core@0.5.1/LICENSE-ZLIB +0 -19
  759. package/third-party/cargo/zune-jpeg@0.5.15/Cargo.toml +0 -67
  760. package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-APACHE +0 -201
  761. package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-MIT +0 -21
  762. package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-ZLIB +0 -19
  763. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/esbuild-0.27.4-LICENSE.md +0 -21
  764. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/go-LICENSE +0 -27
  765. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/golang-x-sys-c0bba94af5f8-LICENSE +0 -27
  766. package/third-party/npm/@esbuild__darwin-arm64@0.27.4/package.json +0 -20
  767. package/third-party/npm/@floating-ui__core@1.7.5/LICENSE +0 -20
  768. package/third-party/npm/@floating-ui__core@1.7.5/package.json +0 -63
  769. package/third-party/npm/@floating-ui__dom@1.7.6/LICENSE +0 -20
  770. package/third-party/npm/@floating-ui__dom@1.7.6/package.json +0 -71
  771. package/third-party/npm/@floating-ui__react-dom@2.1.8/LICENSE +0 -20
  772. package/third-party/npm/@floating-ui__react-dom@2.1.8/package.json +0 -76
  773. package/third-party/npm/@floating-ui__utils@0.2.11/LICENSE +0 -20
  774. package/third-party/npm/@floating-ui__utils@0.2.11/package.json +0 -70
  775. package/third-party/npm/@radix-ui__primitive@1.1.7/LICENSE +0 -21
  776. package/third-party/npm/@radix-ui__primitive@1.1.7/package.json +0 -79
  777. package/third-party/npm/@radix-ui__react-arrow@1.1.15/LICENSE +0 -21
  778. package/third-party/npm/@radix-ui__react-arrow@1.1.15/package.json +0 -68
  779. package/third-party/npm/@radix-ui__react-collection@1.1.15/LICENSE +0 -21
  780. package/third-party/npm/@radix-ui__react-collection@1.1.15/package.json +0 -71
  781. package/third-party/npm/@radix-ui__react-compose-refs@1.1.5/LICENSE +0 -21
  782. package/third-party/npm/@radix-ui__react-compose-refs@1.1.5/package.json +0 -60
  783. package/third-party/npm/@radix-ui__react-context-menu@2.3.7/LICENSE +0 -21
  784. package/third-party/npm/@radix-ui__react-context-menu@2.3.7/package.json +0 -73
  785. package/third-party/npm/@radix-ui__react-context@1.2.2/LICENSE +0 -21
  786. package/third-party/npm/@radix-ui__react-context@1.2.2/package.json +0 -60
  787. package/third-party/npm/@radix-ui__react-direction@1.1.4/LICENSE +0 -21
  788. package/third-party/npm/@radix-ui__react-direction@1.1.4/package.json +0 -60
  789. package/third-party/npm/@radix-ui__react-dismissable-layer@1.1.19/LICENSE +0 -21
  790. package/third-party/npm/@radix-ui__react-dismissable-layer@1.1.19/package.json +0 -74
  791. package/third-party/npm/@radix-ui__react-focus-guards@1.1.6/LICENSE +0 -21
  792. package/third-party/npm/@radix-ui__react-focus-guards@1.1.6/package.json +0 -60
  793. package/third-party/npm/@radix-ui__react-focus-scope@1.1.16/LICENSE +0 -21
  794. package/third-party/npm/@radix-ui__react-focus-scope@1.1.16/package.json +0 -71
  795. package/third-party/npm/@radix-ui__react-id@1.1.4/LICENSE +0 -21
  796. package/third-party/npm/@radix-ui__react-id@1.1.4/package.json +0 -63
  797. package/third-party/npm/@radix-ui__react-menu@2.1.24/LICENSE +0 -21
  798. package/third-party/npm/@radix-ui__react-menu@2.1.24/package.json +0 -86
  799. package/third-party/npm/@radix-ui__react-popper@1.3.7/LICENSE +0 -21
  800. package/third-party/npm/@radix-ui__react-popper@1.3.7/package.json +0 -78
  801. package/third-party/npm/@radix-ui__react-portal@1.1.17/LICENSE +0 -21
  802. package/third-party/npm/@radix-ui__react-portal@1.1.17/package.json +0 -69
  803. package/third-party/npm/@radix-ui__react-presence@1.1.10/LICENSE +0 -21
  804. package/third-party/npm/@radix-ui__react-presence@1.1.10/package.json +0 -68
  805. package/third-party/npm/@radix-ui__react-primitive@2.1.10/LICENSE +0 -21
  806. package/third-party/npm/@radix-ui__react-primitive@2.1.10/package.json +0 -68
  807. package/third-party/npm/@radix-ui__react-roving-focus@1.1.19/LICENSE +0 -21
  808. package/third-party/npm/@radix-ui__react-roving-focus@1.1.19/package.json +0 -78
  809. package/third-party/npm/@radix-ui__react-slot@1.3.3/LICENSE +0 -21
  810. package/third-party/npm/@radix-ui__react-slot@1.3.3/package.json +0 -63
  811. package/third-party/npm/@radix-ui__react-use-callback-ref@1.1.4/LICENSE +0 -21
  812. package/third-party/npm/@radix-ui__react-use-callback-ref@1.1.4/package.json +0 -60
  813. package/third-party/npm/@radix-ui__react-use-controllable-state@1.2.6/LICENSE +0 -21
  814. package/third-party/npm/@radix-ui__react-use-controllable-state@1.2.6/package.json +0 -65
  815. package/third-party/npm/@radix-ui__react-use-effect-event@0.0.5/LICENSE +0 -21
  816. package/third-party/npm/@radix-ui__react-use-effect-event@0.0.5/package.json +0 -65
  817. package/third-party/npm/@radix-ui__react-use-is-hydrated@0.1.3/LICENSE +0 -21
  818. package/third-party/npm/@radix-ui__react-use-is-hydrated@0.1.3/package.json +0 -61
  819. package/third-party/npm/@radix-ui__react-use-layout-effect@1.1.4/LICENSE +0 -21
  820. package/third-party/npm/@radix-ui__react-use-layout-effect@1.1.4/package.json +0 -60
  821. package/third-party/npm/@radix-ui__react-use-size@1.1.4/LICENSE +0 -21
  822. package/third-party/npm/@radix-ui__react-use-size@1.1.4/package.json +0 -63
  823. package/third-party/npm/aria-hidden@1.2.6/LICENSE +0 -21
  824. package/third-party/npm/aria-hidden@1.2.6/package.json +0 -71
  825. package/third-party/npm/detect-node-es@1.1.0/LICENSE +0 -21
  826. package/third-party/npm/detect-node-es@1.1.0/package.json +0 -29
  827. package/third-party/npm/esbuild@0.27.4/LICENSE.md +0 -21
  828. package/third-party/npm/esbuild@0.27.4/package.json +0 -49
  829. package/third-party/npm/get-nonce@1.0.1/LICENSE +0 -21
  830. package/third-party/npm/get-nonce@1.0.1/package.json +0 -68
  831. package/third-party/npm/mediabunny@1.52.2/LICENSE +0 -373
  832. package/third-party/npm/mediabunny@1.52.2/package.json +0 -136
  833. package/third-party/npm/mediabunny@1.52.2/source/LICENSE +0 -373
  834. package/third-party/npm/mediabunny@1.52.2/source/package.json +0 -136
  835. package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-demuxer.ts +0 -400
  836. package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-muxer.ts +0 -124
  837. package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-reader.ts +0 -85
  838. package/third-party/npm/mediabunny@1.52.2/source/src/aes.ts +0 -307
  839. package/third-party/npm/mediabunny@1.52.2/source/src/codec-data.ts +0 -3178
  840. package/third-party/npm/mediabunny@1.52.2/source/src/codec.ts +0 -1154
  841. package/third-party/npm/mediabunny@1.52.2/source/src/conversion.ts +0 -2042
  842. package/third-party/npm/mediabunny@1.52.2/source/src/custom-coder.ts +0 -212
  843. package/third-party/npm/mediabunny@1.52.2/source/src/decode.ts +0 -263
  844. package/third-party/npm/mediabunny@1.52.2/source/src/demuxer.ts +0 -43
  845. package/third-party/npm/mediabunny@1.52.2/source/src/encode.ts +0 -1412
  846. package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-demuxer.ts +0 -813
  847. package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-misc.ts +0 -165
  848. package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-muxer.ts +0 -350
  849. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-demuxer.ts +0 -995
  850. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-misc.ts +0 -81
  851. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-muxer.ts +0 -1529
  852. package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-segmented-input.ts +0 -772
  853. package/third-party/npm/mediabunny@1.52.2/source/src/id3.ts +0 -972
  854. package/third-party/npm/mediabunny@1.52.2/source/src/index.ts +0 -319
  855. package/third-party/npm/mediabunny@1.52.2/source/src/input-format.ts +0 -819
  856. package/third-party/npm/mediabunny@1.52.2/source/src/input-track.ts +0 -1141
  857. package/third-party/npm/mediabunny@1.52.2/source/src/input.ts +0 -571
  858. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-boxes.ts +0 -1911
  859. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-demuxer.ts +0 -3953
  860. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-misc.ts +0 -90
  861. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-muxer.ts +0 -1595
  862. package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-reader.ts +0 -91
  863. package/third-party/npm/mediabunny@1.52.2/source/src/logging.ts +0 -120
  864. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/ebml.ts +0 -770
  865. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-demuxer.ts +0 -2598
  866. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-misc.ts +0 -29
  867. package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-muxer.ts +0 -1360
  868. package/third-party/npm/mediabunny@1.52.2/source/src/media-sink.ts +0 -2589
  869. package/third-party/npm/mediabunny@1.52.2/source/src/media-source.ts +0 -3041
  870. package/third-party/npm/mediabunny@1.52.2/source/src/metadata.ts +0 -335
  871. package/third-party/npm/mediabunny@1.52.2/source/src/misc.ts +0 -1336
  872. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-demuxer.ts +0 -469
  873. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-muxer.ts +0 -169
  874. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-reader.ts +0 -61
  875. package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-writer.ts +0 -121
  876. package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-demuxer.ts +0 -2519
  877. package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-misc.ts +0 -32
  878. package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-muxer.ts +0 -842
  879. package/third-party/npm/mediabunny@1.52.2/source/src/muxer.ts +0 -80
  880. package/third-party/npm/mediabunny@1.52.2/source/src/node.ts +0 -11
  881. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-demuxer.ts +0 -1087
  882. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-misc.ts +0 -116
  883. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-muxer.ts +0 -527
  884. package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-reader.ts +0 -93
  885. package/third-party/npm/mediabunny@1.52.2/source/src/output-format.ts +0 -1425
  886. package/third-party/npm/mediabunny@1.52.2/source/src/output.ts +0 -940
  887. package/third-party/npm/mediabunny@1.52.2/source/src/packet.ts +0 -287
  888. package/third-party/npm/mediabunny@1.52.2/source/src/pcm.ts +0 -112
  889. package/third-party/npm/mediabunny@1.52.2/source/src/reader.ts +0 -391
  890. package/third-party/npm/mediabunny@1.52.2/source/src/resample.ts +0 -287
  891. package/third-party/npm/mediabunny@1.52.2/source/src/sample.ts +0 -3231
  892. package/third-party/npm/mediabunny@1.52.2/source/src/segmented-input.ts +0 -575
  893. package/third-party/npm/mediabunny@1.52.2/source/src/source.ts +0 -2588
  894. package/third-party/npm/mediabunny@1.52.2/source/src/subtitles.ts +0 -131
  895. package/third-party/npm/mediabunny@1.52.2/source/src/target.ts +0 -871
  896. package/third-party/npm/mediabunny@1.52.2/source/src/tsconfig.json +0 -16
  897. package/third-party/npm/mediabunny@1.52.2/source/src/wave/riff-writer.ts +0 -36
  898. package/third-party/npm/mediabunny@1.52.2/source/src/wave/wave-demuxer.ts +0 -583
  899. package/third-party/npm/mediabunny@1.52.2/source/src/wave/wave-muxer.ts +0 -375
  900. package/third-party/npm/mediabunny@1.52.2/source/src/writer.ts +0 -128
  901. package/third-party/npm/react-dom@19.3.0/LICENSE +0 -21
  902. package/third-party/npm/react-dom@19.3.0/package.json +0 -117
  903. package/third-party/npm/react-remove-scroll-bar@2.3.8/package.json +0 -85
  904. package/third-party/npm/react-remove-scroll-bar@2.3.8/react-remove-scroll-bar-LICENSE +0 -21
  905. package/third-party/npm/react-remove-scroll@2.7.2/LICENSE +0 -21
  906. package/third-party/npm/react-remove-scroll@2.7.2/package.json +0 -98
  907. package/third-party/npm/react-style-singleton@2.2.3/LICENSE +0 -21
  908. package/third-party/npm/react-style-singleton@2.2.3/package.json +0 -90
  909. package/third-party/npm/react@19.3.0/LICENSE +0 -21
  910. package/third-party/npm/react@19.3.0/package.json +0 -51
  911. package/third-party/npm/scheduler@0.28.0/LICENSE +0 -21
  912. package/third-party/npm/scheduler@0.28.0/package.json +0 -27
  913. package/third-party/npm/tslib@2.8.1/LICENSE.txt +0 -12
  914. package/third-party/npm/tslib@2.8.1/package.json +0 -47
  915. package/third-party/npm/typescript@5.9.3/LICENSE.txt +0 -55
  916. package/third-party/npm/typescript@5.9.3/ThirdPartyNoticeText.txt +0 -193
  917. package/third-party/npm/typescript@5.9.3/package.json +0 -120
  918. package/third-party/npm/use-callback-ref@1.3.3/LICENSE +0 -21
  919. package/third-party/npm/use-callback-ref@1.3.3/package.json +0 -85
  920. package/third-party/npm/use-sidecar@1.1.3/LICENSE +0 -21
  921. package/third-party/npm/use-sidecar@1.1.3/package.json +0 -93
  922. package/third-party/packages/render-engine/assets/OFL.txt +0 -93
  923. package/third-party/packages/studio/public/fonts/OFL.txt +0 -93
  924. package/third-party/packages/studio/vendor/osmo-icons/LICENSE +0 -21
  925. package/third-party/packages/studio/vendor/shadcn/LICENSE +0 -21
  926. package/third-party/repository-notices.md +0 -41
  927. package/third-party/rust-standard-library/COPYRIGHT-library.html +0 -5556
  928. package/third-party/rust-standard-library/licenses/Apache-2.0.txt +0 -73
  929. package/third-party/rust-standard-library/licenses/BSD-2-Clause.txt +0 -9
  930. package/third-party/rust-standard-library/licenses/CC-BY-SA-4.0.txt +0 -427
  931. package/third-party/rust-standard-library/licenses/GCC-exception-3.1.txt +0 -30
  932. package/third-party/rust-standard-library/licenses/GPL-2.0-only.txt +0 -133
  933. package/third-party/rust-standard-library/licenses/GPL-3.0-or-later.txt +0 -202
  934. package/third-party/rust-standard-library/licenses/ISC.txt +0 -7
  935. package/third-party/rust-standard-library/licenses/LLVM-exception.txt +0 -15
  936. package/third-party/rust-standard-library/licenses/MIT.txt +0 -9
  937. package/third-party/rust-standard-library/licenses/NCSA.txt +0 -29
  938. package/third-party/rust-standard-library/licenses/OFL-1.1.txt +0 -43
  939. package/third-party/rust-standard-library/licenses/Unicode-3.0.txt +0 -39
  940. package/third-party/rust-standard-library/version.txt +0 -1
  941. package/vendor/osmo-lite +0 -0
  942. package/vendor/osmo-scene-0.1.0.tgz +0 -0
  943. package/vendor/platform.json +0 -1
@@ -1,3041 +0,0 @@
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 { buildAacAudioSpecificConfig, parseAacAudioSpecificConfig } from '../shared/aac-misc';
10
- import {
11
- AUDIO_CODECS,
12
- AudioCodec,
13
- MediaCodec,
14
- parsePcmCodec,
15
- PCM_AUDIO_CODECS,
16
- PcmAudioCodec,
17
- SUBTITLE_CODECS,
18
- SubtitleCodec,
19
- VIDEO_CODECS,
20
- VideoCodec,
21
- } from './codec';
22
- import { OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from './output';
23
- import {
24
- assert,
25
- assertNever,
26
- binarySearchLessOrEqual,
27
- CallSerializer,
28
- clamp,
29
- clearIntervalUnthrottled,
30
- floorToDivisor,
31
- last,
32
- promiseWithResolvers,
33
- roundToDivisor,
34
- setInt24,
35
- setIntervalUnthrottled,
36
- setUint24,
37
- toUint8Array,
38
- UnthrottledTimerHandle,
39
- wait,
40
- } from './misc';
41
- import { Muxer } from './muxer';
42
- import { SubtitleParser } from './subtitles';
43
- import { toAlaw, toUlaw } from './pcm';
44
- import {
45
- CustomVideoEncoder,
46
- CustomAudioEncoder,
47
- customVideoEncoders,
48
- customAudioEncoders,
49
- } from './custom-coder';
50
- import { EncodedPacket, EncodedPacketSideData, PacketType } from './packet';
51
- import {
52
- AudioSample,
53
- audioSampleToInterleavedFormat,
54
- toInterleavedAudioFormat,
55
- VideoSample,
56
- VideoSamplePixelFormat,
57
- } from './sample';
58
- import {
59
- AudioEncodingConfig,
60
- buildAudioEncoderConfig,
61
- buildQuantizerEncodeOptions,
62
- buildVideoEncoderConfigs,
63
- resolveQuality,
64
- validateAudioEncodingConfig,
65
- validateVideoEncodingConfig,
66
- VideoEncoderConfigCandidate,
67
- VideoEncodingConfig,
68
- } from './encode';
69
- import { AudioResampler } from './resample';
70
- import { determineVideoPacketType } from './codec-data';
71
- import { Logging } from './logging';
72
-
73
- /**
74
- * Base class for media sources. Media sources are used to add media samples to an output file.
75
- * @group Media sources
76
- * @public
77
- */
78
- export abstract class MediaSource {
79
- /** @internal */
80
- abstract readonly _codec: MediaCodec;
81
- /** @internal */
82
- _connectedTrack: OutputTrack | null = null;
83
- /** @internal */
84
- _closingPromise: Promise<void> | null = null;
85
- /** @internal */
86
- _closed = false;
87
-
88
- /** @internal */
89
- _ensureValidAdd() {
90
- if (!this._connectedTrack) {
91
- throw new Error('Source is not connected to an output track.');
92
- }
93
-
94
- if (this._connectedTrack.output.state === 'canceled') {
95
- throw new Error('Output has been canceled.');
96
- }
97
-
98
- if (this._connectedTrack.output.state === 'finalizing' || this._connectedTrack.output.state === 'finalized') {
99
- throw new Error('Output has been finalized.');
100
- }
101
-
102
- if (this._connectedTrack.output.state === 'pending') {
103
- throw new Error('Output has not started.');
104
- }
105
-
106
- if (this._closed) {
107
- throw new Error('Source is closed.');
108
- }
109
- }
110
-
111
- /** @internal */
112
- async _start() {}
113
- /** @internal */
114
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
115
- async _flushAndClose(forceClose: boolean) {}
116
-
117
- /**
118
- * Closes this source. This prevents future samples from being added and signals to the output file that no further
119
- * samples will come in for this track. Calling `.close()` is optional but recommended after adding the
120
- * last sample - for improved performance and reduced memory usage.
121
- */
122
- close() {
123
- if (this._closingPromise) {
124
- return;
125
- }
126
-
127
- const connectedTrack = this._connectedTrack;
128
-
129
- if (!connectedTrack) {
130
- throw new Error('Cannot call close without connecting the source to an output track.');
131
- }
132
-
133
- if (connectedTrack.output.state === 'pending') {
134
- throw new Error('Cannot call close before output has been started.');
135
- }
136
-
137
- this._closingPromise = (async () => {
138
- await this._flushAndClose(false);
139
-
140
- this._closed = true;
141
-
142
- if (connectedTrack.output.state === 'finalizing' || connectedTrack.output.state === 'finalized') {
143
- return;
144
- }
145
-
146
- connectedTrack.output._muxer.onTrackClose(connectedTrack);
147
- })();
148
- }
149
-
150
- /** @internal */
151
- async _flushOrWaitForOngoingClose(forceClose: boolean) {
152
- return this._closingPromise ??= (async () => {
153
- await this._flushAndClose(forceClose);
154
- this._closed = true;
155
- })();
156
- }
157
- }
158
-
159
- /**
160
- * Base class for video sources - sources for video tracks.
161
- * @group Media sources
162
- * @public
163
- */
164
- export abstract class VideoSource extends MediaSource {
165
- /** @internal */
166
- override _connectedTrack: OutputVideoTrack | null = null;
167
- /** @internal */
168
- override readonly _codec: VideoCodec;
169
-
170
- /** Internal constructor. */
171
- constructor(codec: VideoCodec) {
172
- super();
173
-
174
- if (!VIDEO_CODECS.includes(codec)) {
175
- throw new TypeError(`Invalid video codec '${codec}'. Must be one of: ${VIDEO_CODECS.join(', ')}.`);
176
- }
177
-
178
- this._codec = codec;
179
- }
180
- }
181
-
182
- const maybeEnsureIsKeyPacket = (track: OutputVideoTrack, packet: EncodedPacket) => {
183
- if (track.metadata.hasOnlyKeyPackets && packet.type !== 'key') {
184
- throw new Error('Cannot add non-key packets to a hasOnlyKeyPackets video track.');
185
- }
186
- };
187
-
188
- /**
189
- * The most basic video source; can be used to directly pipe encoded packets into the output file.
190
- * @group Media sources
191
- * @public
192
- */
193
- export class EncodedVideoPacketSource extends VideoSource {
194
- /** Creates a new {@link EncodedVideoPacketSource} whose packets are encoded using `codec`. */
195
- constructor(codec: VideoCodec) {
196
- super(codec);
197
- }
198
-
199
- /**
200
- * Adds an encoded packet to the output video track. Packets must be added in *decode order*, while a packet's
201
- * timestamp must be its *presentation timestamp*. B-frames are handled automatically.
202
- *
203
- * @param meta - Additional metadata from the encoder. You should pass this for the first call, including a valid
204
- * decoder config.
205
- *
206
- * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
207
- * to respect writer and encoder backpressure.
208
- */
209
- add(packet: EncodedPacket, meta?: EncodedVideoChunkMetadata) {
210
- if (!(packet instanceof EncodedPacket)) {
211
- throw new TypeError('packet must be an EncodedPacket.');
212
- }
213
- if (packet.isMetadataOnly) {
214
- throw new TypeError('Metadata-only packets cannot be added.');
215
- }
216
- if (meta !== undefined && (!meta || typeof meta !== 'object')) {
217
- throw new TypeError('meta, when provided, must be an object.');
218
- }
219
-
220
- this._ensureValidAdd();
221
-
222
- maybeEnsureIsKeyPacket(this._connectedTrack!, packet);
223
- return this._connectedTrack!.output._muxer.addEncodedVideoPacket(this._connectedTrack!, packet, meta);
224
- }
225
- }
226
-
227
- class VideoEncoderWrapper {
228
- private ensureEncoderPromise: Promise<void> | null = null;
229
- private encoderInitialized = false;
230
- private encoder: VideoEncoder | null = null;
231
- private muxer: Muxer | null = null;
232
- private lastMultipleOfKeyFrameInterval = -1;
233
- private emittedEncoderPackets = 0;
234
-
235
- // Tracks the input dimensions of the first frame
236
- private codedWidth: number | null = null;
237
- private codedHeight: number | null = null;
238
- // Tracks the output dimensions of the first frame (used to lock dimensions for fill/contain/cover)
239
- private outputWidth: number | null = null;
240
- private outputHeight: number | null = null;
241
-
242
- // Frame rate normalization state
243
- private frameRateLastSample: VideoSample | null = null;
244
- private frameRateLastTimestamp: number | null = null;
245
- private frameRateLastEndTimestamp: number | null = null;
246
-
247
- // VideoEncoder converts everything to microseconds, so we need to do some bookkeeping to restore the original
248
- // timing information
249
- private preciseTimings: {
250
- microsecondTimestamp: number;
251
- timestamp: number;
252
- duration: number;
253
- timestampIsValid: boolean;
254
- durationIsValid: boolean;
255
- }[] = [];
256
-
257
- private customEncoder: CustomVideoEncoder | null = null;
258
- private customEncoderCallSerializer = new CallSerializer();
259
- private customEncoderQueueSize = 0;
260
-
261
- // Set when the encoder uses quantizer-based rate control; carries the quantizer value applied to each frame
262
- private defaultEncodeOptions: VideoEncoderEncodeOptions = {};
263
-
264
- // Alpha stuff
265
- private alphaEncoder: VideoEncoder | null = null;
266
- private splitter: ColorAlphaSplitter | null = null;
267
- private splitterCreationFailed = false;
268
- private alphaFrameQueue: (VideoFrame | null)[] = [];
269
-
270
- /**
271
- * Encoders typically throw their errors "out of band", meaning asynchronously in some other execution context.
272
- * However, we want to surface these errors to the user within the normal control flow, so they don't go uncaught.
273
- * So, we keep track of the encoder error and throw it as soon as we get the chance.
274
- */
275
- private error: unknown = null;
276
- private errorSet = false;
277
-
278
- private setError(error: unknown) {
279
- if (!this.errorSet) {
280
- this.error = error;
281
- this.errorSet = true;
282
- }
283
- }
284
-
285
- private lastMuxerPromise: Promise<void> = Promise.resolve();
286
- private closed = false;
287
-
288
- constructor(private source: VideoSource, private encodingConfig: VideoEncodingConfig) {}
289
-
290
- async add(videoSample: VideoSample, shouldClose: boolean, encodeOptions?: VideoEncoderEncodeOptions) {
291
- const originalSample = videoSample;
292
-
293
- try {
294
- this.checkForEncoderError();
295
- this.source._ensureValidAdd();
296
-
297
- const config = this.encodingConfig;
298
- const sizeChangeBehavior = config.sizeChangeBehavior ?? 'deny';
299
- let isSizeChange = false;
300
-
301
- // Ensure video sample size remains constant or handle the change
302
- if (this.codedWidth !== null && this.codedHeight !== null) {
303
- if (videoSample.codedWidth !== this.codedWidth || videoSample.codedHeight !== this.codedHeight) {
304
- isSizeChange = true;
305
- if (sizeChangeBehavior === 'deny') {
306
- throw new Error(
307
- `Video sample size must remain constant. Expected ${this.codedWidth}x${this.codedHeight},`
308
- + ` got ${videoSample.codedWidth}x${videoSample.codedHeight}. To allow the sample size to`
309
- + ` change over time, set \`sizeChangeBehavior\` to a value other than 'deny' in the`
310
- + ` encoding options.`,
311
- );
312
- }
313
- }
314
- } else {
315
- this.codedWidth = videoSample.codedWidth;
316
- this.codedHeight = videoSample.codedHeight;
317
- }
318
-
319
- // Determine if we need to apply transformations via canvas
320
- const hasTransformConfig = config.transform?.width !== undefined
321
- || config.transform?.height !== undefined
322
- || config.transform?.rotate !== undefined
323
- || config.transform?.crop !== undefined
324
- || config.transform?.force === true;
325
- const needsTransform = hasTransformConfig || (isSizeChange && sizeChangeBehavior !== 'passThrough');
326
-
327
- if (needsTransform) {
328
- let targetWidth = config.transform?.width;
329
- let targetHeight = config.transform?.height;
330
- let appliedFit: 'fill' | 'contain' | 'cover' = config.transform?.fit ?? 'fill';
331
-
332
- // If the size changed and behavior is fill/contain/cover, lock to the original output dimensions
333
- if (isSizeChange && sizeChangeBehavior !== 'passThrough') {
334
- assert(this.outputWidth);
335
- assert(this.outputHeight);
336
- assert(sizeChangeBehavior !== 'deny');
337
-
338
- targetWidth = this.outputWidth!;
339
- targetHeight = this.outputHeight!;
340
- appliedFit = sizeChangeBehavior;
341
- }
342
-
343
- const transformed = await videoSample.transform({
344
- width: targetWidth,
345
- height: targetHeight,
346
- roundDimensionsTo: 2,
347
- crop: config.transform?.crop,
348
- rotate: config.transform?.rotate,
349
- fit: appliedFit,
350
- alpha: config.alpha,
351
- });
352
-
353
- // Save the output dimensions of the first frame
354
- if (this.outputWidth === null || this.outputHeight === null) {
355
- this.outputWidth = transformed.displayWidth;
356
- this.outputHeight = transformed.displayHeight;
357
- }
358
-
359
- if (shouldClose) {
360
- videoSample.close();
361
- }
362
-
363
- videoSample = transformed;
364
- shouldClose = true;
365
- } else {
366
- // If no canvas is needed, we still need to record the output dimensions for the first frame
367
- if (this.outputWidth === null || this.outputHeight === null) {
368
- this.outputWidth = videoSample.codedWidth;
369
- this.outputHeight = videoSample.codedHeight;
370
- }
371
- }
372
-
373
- const frameRate = config.transform?.frameRate;
374
- if (frameRate !== undefined) {
375
- // Apply frame rate normalization
376
- const originalEndTimestamp = videoSample.timestamp + videoSample.duration;
377
- const alignedTimestamp = floorToDivisor(videoSample.timestamp, frameRate);
378
-
379
- if (this.frameRateLastSample !== null) {
380
- if (alignedTimestamp <= this.frameRateLastTimestamp!) {
381
- // Same frame rate slot, replace stored sample with the newer one
382
- this.frameRateLastSample.close();
383
- this.frameRateLastSample = videoSample.clone();
384
- this.frameRateLastEndTimestamp = originalEndTimestamp;
385
- return;
386
- } else {
387
- // Pad the gap by repeating the previous frame
388
- await this.padFrameRate(alignedTimestamp, encodeOptions);
389
- }
390
- }
391
-
392
- // Clone if the sample is still the user's, to avoid mutating externally-owned data
393
- if (videoSample === originalSample) {
394
- videoSample = videoSample.clone();
395
- shouldClose = true;
396
- }
397
-
398
- videoSample.setTimestamp(alignedTimestamp);
399
- videoSample.setDuration(1 / frameRate);
400
-
401
- this.frameRateLastSample?.close();
402
- this.frameRateLastSample = videoSample.clone();
403
- this.frameRateLastTimestamp = alignedTimestamp;
404
- this.frameRateLastEndTimestamp = originalEndTimestamp;
405
- }
406
-
407
- await this.processAndEncode(videoSample, encodeOptions);
408
- } finally {
409
- if (shouldClose) {
410
- videoSample.close();
411
- }
412
- }
413
- }
414
-
415
- /**
416
- * Runs the process function (if any) and encodes the resulting samples.
417
- */
418
- private async processAndEncode(
419
- videoSample: VideoSample,
420
- encodeOptions?: VideoEncoderEncodeOptions,
421
- ) {
422
- const config = this.encodingConfig;
423
- let samplesToEncode: VideoSample[];
424
-
425
- // Apply the user-defined process function, if any
426
- if (config.transform?.process) {
427
- let processed = config.transform.process(videoSample);
428
- if (processed instanceof Promise) {
429
- processed = await processed;
430
- }
431
-
432
- if (processed === null) {
433
- return;
434
- }
435
-
436
- if (!Array.isArray(processed)) {
437
- processed = [processed];
438
- }
439
-
440
- const mappedSamples: VideoSample[] = [];
441
-
442
- try {
443
- for (const x of processed) {
444
- if (x instanceof VideoSample) {
445
- mappedSamples.push(x);
446
- } else if (typeof VideoFrame !== 'undefined' && x instanceof VideoFrame) {
447
- mappedSamples.push(new VideoSample(x));
448
- } else {
449
- // Calling the VideoSample constructor here will automatically handle input validation for us
450
- // (it throws for any non-legal argument).
451
- mappedSamples.push(new VideoSample(x as CanvasImageSource, {
452
- timestamp: videoSample.timestamp,
453
- duration: videoSample.duration,
454
- }));
455
- }
456
- }
457
- } catch (error) {
458
- // One of the returned elements was invalid; close everything closable so no resource is leaked
459
- for (const sample of mappedSamples) {
460
- if (sample !== videoSample) {
461
- sample.close();
462
- }
463
- }
464
- for (const x of processed) {
465
- if (x instanceof VideoSample && x !== videoSample) {
466
- x.close();
467
- } else if (typeof VideoFrame !== 'undefined' && x instanceof VideoFrame) {
468
- x.close();
469
- }
470
- }
471
-
472
- throw error;
473
- }
474
-
475
- samplesToEncode = mappedSamples;
476
- } else {
477
- samplesToEncode = [videoSample];
478
- }
479
-
480
- try {
481
- for (const sampleToEncode of samplesToEncode) {
482
- if (!this.encoderInitialized) {
483
- if (!this.ensureEncoderPromise) {
484
- this.ensureEncoder(sampleToEncode);
485
- }
486
-
487
- // No, this "if" statement is not useless. Sometimes, the above call to
488
- // `ensureEncoder` might have synchronously completed and the encoder is
489
- // already initialized. In this case, we don't need to await the promise
490
- // anymore. This also fixes nasty async race condition bugs when multiple
491
- // code paths are calling this method: It's important that the call that
492
- // initialized the encoder go through this code first.
493
- if (!this.encoderInitialized) {
494
- await this.ensureEncoderPromise;
495
- }
496
- }
497
- assert(this.encoderInitialized);
498
-
499
- if (this.closed) {
500
- break;
501
- }
502
-
503
- const keyFrameInterval = this.encodingConfig.keyFrameInterval ?? 2;
504
- const multipleOfKeyFrameInterval = Math.floor(sampleToEncode.timestamp / keyFrameInterval);
505
-
506
- const mergedEncodeOptions = {
507
- ...this.defaultEncodeOptions,
508
- ...sampleToEncode.encodeOptions,
509
- ...encodeOptions,
510
- };
511
-
512
- const finalEncodeOptions = {
513
- ...mergedEncodeOptions,
514
- keyFrame: mergedEncodeOptions.keyFrame !== undefined
515
- ? mergedEncodeOptions.keyFrame
516
- // Ensure a key frame every keyFrameInterval seconds. It is important that all video tracks
517
- // follow the same "key frame" rhythm, because aligned key frames are required to start new
518
- // fragments in ISOBMFF or clusters in Matroska (or at least desirable).
519
- : keyFrameInterval === 0
520
- || multipleOfKeyFrameInterval !== this.lastMultipleOfKeyFrameInterval,
521
- };
522
- this.lastMultipleOfKeyFrameInterval = multipleOfKeyFrameInterval;
523
-
524
- this.encodingConfig.onEncodedSample?.(sampleToEncode);
525
-
526
- if (this.customEncoder) {
527
- this.customEncoderQueueSize++;
528
-
529
- // We clone the sample so it cannot be closed on us from the outside before it reaches the encoder
530
- const clonedSample = sampleToEncode.clone();
531
-
532
- const promise = this.customEncoderCallSerializer
533
- .call(() => this.customEncoder!.encode(clonedSample, finalEncodeOptions))
534
- .catch((error: unknown) => this.setError(error))
535
- .finally(() => {
536
- this.customEncoderQueueSize--;
537
- clonedSample.close();
538
- });
539
-
540
- if (this.customEncoderQueueSize >= 4) {
541
- await promise;
542
- }
543
- } else {
544
- assert(this.encoder);
545
-
546
- const videoFrame = sampleToEncode.toVideoFrame();
547
-
548
- const preciseTimingIndex = binarySearchLessOrEqual(
549
- this.preciseTimings,
550
- videoFrame.timestamp,
551
- x => x.microsecondTimestamp,
552
- );
553
- const existingEntry = preciseTimingIndex !== -1
554
- ? this.preciseTimings[preciseTimingIndex]
555
- : null;
556
- if (existingEntry && existingEntry.microsecondTimestamp === videoFrame.timestamp) {
557
- if (existingEntry.timestamp !== sampleToEncode.timestamp) {
558
- // Mapping isn't unique, can't use the timestamp
559
- existingEntry.timestampIsValid = false;
560
- }
561
- if (existingEntry.duration !== sampleToEncode.duration) {
562
- // Mapping isn't unique, can't use the duration
563
- existingEntry.durationIsValid = false;
564
- }
565
- } else {
566
- this.preciseTimings.splice(preciseTimingIndex + 1, 0, {
567
- microsecondTimestamp: videoFrame.timestamp,
568
- timestamp: sampleToEncode.timestamp,
569
- duration: sampleToEncode.duration,
570
- timestampIsValid: true,
571
- durationIsValid: true,
572
- });
573
-
574
- // Make sure it doesn't grow indefinitely
575
- if (this.preciseTimings.length > 128) {
576
- this.preciseTimings.shift();
577
- }
578
- }
579
-
580
- if (!this.alphaEncoder) {
581
- // No alpha encoder, simple case
582
- try {
583
- this.encoder.encode(videoFrame, finalEncodeOptions);
584
- } finally {
585
- videoFrame.close();
586
- }
587
- } else {
588
- // We're expected to encode alpha as well
589
- const frameDefinitelyHasNoAlpha = !!videoFrame.format && !videoFrame.format.includes('A');
590
-
591
- if (frameDefinitelyHasNoAlpha || this.splitterCreationFailed) {
592
- this.alphaFrameQueue.push(null);
593
- try {
594
- this.encoder.encode(videoFrame, finalEncodeOptions);
595
- } finally {
596
- videoFrame.close();
597
- }
598
- } else {
599
- if (!this.splitter) {
600
- this.splitter = new ColorAlphaSplitter();
601
- }
602
-
603
- // The splitter takes ownership, so no need to close the frames ourselves
604
- const { colorFrame, alphaFrame } = await this.splitter.split(videoFrame);
605
-
606
- this.alphaFrameQueue.push(alphaFrame);
607
- try {
608
- this.encoder.encode(colorFrame, finalEncodeOptions);
609
- } finally {
610
- colorFrame.close();
611
- }
612
- }
613
- }
614
-
615
- // We need to do this after sending the frame to the encoder as the frame otherwise might be closed
616
- if (this.encoder.encodeQueueSize >= 4) {
617
- await new Promise(resolve =>
618
- this.encoder!.addEventListener('dequeue', resolve, { once: true }),
619
- );
620
- }
621
- }
622
-
623
- await this.lastMuxerPromise; // Allow the writer to apply backpressure
624
- }
625
- } finally {
626
- for (const sample of samplesToEncode) {
627
- if (sample !== videoSample) {
628
- sample.close();
629
- }
630
- }
631
- }
632
- }
633
-
634
- /** Repeats the last frame rate sample to fill the gap up to the given timestamp. */
635
- private async padFrameRate(until: number, encodeOptions?: VideoEncoderEncodeOptions) {
636
- const frameRate = this.encodingConfig.transform!.frameRate!;
637
- assert(this.frameRateLastSample);
638
-
639
- const frameDifference = Math.round((until - this.frameRateLastTimestamp!) * frameRate);
640
-
641
- for (let i = 1; i < frameDifference; i++) {
642
- using sample = this.frameRateLastSample.clone();
643
- sample.setTimestamp(this.frameRateLastTimestamp! + i / frameRate);
644
- sample.setDuration(1 / frameRate);
645
- await this.processAndEncode(sample, encodeOptions);
646
- }
647
- }
648
-
649
- private ensureEncoder(videoSample: VideoSample) {
650
- this.ensureEncoderPromise = (async () => {
651
- // eslint-disable-next-line @typescript-eslint/no-deprecated
652
- const quality = resolveQuality(this.encodingConfig.quality, this.encodingConfig.bitrate);
653
- assert(quality !== undefined);
654
-
655
- const candidates = buildVideoEncoderConfigs({
656
- ...this.encodingConfig,
657
- quality,
658
- width: videoSample.codedWidth,
659
- height: videoSample.codedHeight,
660
- squarePixelWidth: videoSample.squarePixelWidth,
661
- squarePixelHeight: videoSample.squarePixelHeight,
662
- framerate: this.source._connectedTrack?.metadata.frameRate,
663
- });
664
-
665
- // Try the candidate configs in order of preference until we find one that is supported
666
- let selected: VideoEncoderConfigCandidate | null = null;
667
- let MatchingCustomEncoder: (typeof customVideoEncoders)[number] | undefined;
668
-
669
- for (const candidate of candidates) {
670
- const candidateConfig = candidate.config;
671
- this.encodingConfig.onEncoderConfig?.(candidateConfig);
672
-
673
- MatchingCustomEncoder = customVideoEncoders.find(x => x.supports(
674
- this.encodingConfig.codec,
675
- candidateConfig,
676
- ));
677
- if (MatchingCustomEncoder) {
678
- selected = candidate;
679
- break;
680
- }
681
-
682
- if (typeof VideoEncoder === 'undefined') {
683
- continue;
684
- }
685
-
686
- candidateConfig.alpha = 'discard'; // Since we handle alpha ourselves
687
-
688
- if (this.encodingConfig.alpha === 'keep') {
689
- // Encoding alpha requires using two parallel encoders, so we need to make sure they stay in sync
690
- // and that neither of them drops frames. Setting latencyMode to 'quality' achieves this, because
691
- // "User Agents MUST not drop frames to achieve the target bitrate and/or framerate."
692
- candidateConfig.latencyMode = 'quality';
693
- }
694
-
695
- const hasOddDimension = candidateConfig.width % 2 === 1 || candidateConfig.height % 2 === 1;
696
- if (
697
- hasOddDimension
698
- && (this.encodingConfig.codec === 'avc' || this.encodingConfig.codec === 'hevc')
699
- ) {
700
- // Throw a special error for this case as it gets hit often
701
- throw new Error(
702
- `The dimensions ${candidateConfig.width}x${candidateConfig.height} are not supported for codec`
703
- + ` '${this.encodingConfig.codec}'; both width and height must be even numbers. Make sure to`
704
- + ` round your dimensions to the nearest even number.`,
705
- );
706
- }
707
-
708
- const support = await VideoEncoder.isConfigSupported(candidateConfig);
709
- if (support.supported) {
710
- selected = candidate;
711
- break;
712
- }
713
- }
714
-
715
- if (!selected) {
716
- if (typeof VideoEncoder === 'undefined') {
717
- throw new Error('VideoEncoder is not supported by this browser.');
718
- }
719
-
720
- // The candidates only differ in their rate control, so we describe them as one config with a
721
- // slash-separated list of the attempted rate control methods
722
- const firstConfig = candidates[0]!.config;
723
- const rateControls = candidates.map(({ config, quantizer }) =>
724
- quantizer !== null ? `quantizer ${quantizer}` : `${config.bitrate} bps`,
725
- );
726
-
727
- throw new Error(
728
- `This specific encoder configuration (${firstConfig.codec}, ${rateControls.join(' / ')},`
729
- + ` ${firstConfig.width}x${firstConfig.height}, hardware acceleration:`
730
- + ` ${firstConfig.hardwareAcceleration ?? 'no-preference'}) is not supported by this browser.`
731
- + ` Consider using another codec or changing your video parameters.`,
732
- );
733
- }
734
-
735
- const encoderConfig = selected.config;
736
- if (selected.quantizer !== null) {
737
- // The chosen config uses quantizer-based rate control, so each frame must carry the quantizer value
738
- this.defaultEncodeOptions = buildQuantizerEncodeOptions(
739
- this.encodingConfig.codec,
740
- selected.quantizer,
741
- );
742
- }
743
-
744
- if (MatchingCustomEncoder) {
745
- // @ts-expect-error "Can't create instance of abstract class 🤓"
746
- this.customEncoder = new MatchingCustomEncoder() as CustomVideoEncoder;
747
- // @ts-expect-error It's technically readonly
748
- this.customEncoder.codec = this.encodingConfig.codec;
749
- // @ts-expect-error It's technically readonly
750
- this.customEncoder.config = encoderConfig;
751
- // @ts-expect-error It's technically readonly
752
- this.customEncoder.onPacket = (packet, meta) => {
753
- if (!(packet instanceof EncodedPacket)) {
754
- throw new TypeError('The first argument passed to onPacket must be an EncodedPacket.');
755
- }
756
- if (meta !== undefined && (!meta || typeof meta !== 'object')) {
757
- throw new TypeError('The second argument passed to onPacket must be an object or undefined.');
758
- }
759
-
760
- maybeEnsureIsKeyPacket(this.source._connectedTrack!, packet);
761
-
762
- this.encodingConfig.onEncodedPacket?.(packet, meta);
763
- this.lastMuxerPromise
764
- = this.muxer!.addEncodedVideoPacket(this.source._connectedTrack!, packet, meta)
765
- .catch((error) => {
766
- this.setError(error);
767
- });
768
- };
769
- // @ts-expect-error It's technically readonly
770
- this.customEncoder.onError = (error) => {
771
- this.setError(error);
772
- };
773
-
774
- await this.customEncoder.init();
775
- } else {
776
- /** Queue of color chunks waiting for their alpha counterpart. */
777
- const colorChunkQueue: {
778
- chunk: EncodedVideoChunk;
779
- meta: EncodedVideoChunkMetadata | undefined;
780
- }[] = [];
781
- /** Each value is the number of encoded alpha chunks at which a null alpha chunk should be added. */
782
- const nullAlphaChunkQueue: number[] = [];
783
- let encodedAlphaChunkCount = 0;
784
- let alphaEncoderQueue = 0;
785
-
786
- const addPacket = (
787
- colorChunk: EncodedVideoChunk,
788
- alphaChunk: EncodedVideoChunk | null,
789
- meta: EncodedVideoChunkMetadata | undefined,
790
- ) => {
791
- const sideData: EncodedPacketSideData = {};
792
-
793
- if (alphaChunk) {
794
- const alphaData = new Uint8Array(alphaChunk.byteLength);
795
- alphaChunk.copyTo(alphaData);
796
-
797
- sideData.alpha = alphaData;
798
- }
799
-
800
- let packet = EncodedPacket.fromEncodedChunk(colorChunk, sideData);
801
-
802
- // See if there's a relevant timing entry to refine the packet's timing data
803
- const preciseTimingIndex = binarySearchLessOrEqual(
804
- this.preciseTimings,
805
- colorChunk.timestamp,
806
- x => x.microsecondTimestamp,
807
- );
808
- const entry = preciseTimingIndex !== -1
809
- ? this.preciseTimings[preciseTimingIndex]
810
- : null;
811
-
812
- let actualType: PacketType | null = null;
813
- if (this.emittedEncoderPackets === 0 && packet.type === 'delta' && meta?.decoderConfig) {
814
- // https://github.com/Vanilagy/mediabunny/issues/365
815
- // We expect the first packet to be a key packet. If it's not, let's actually verify that it's
816
- // not by getting the actual type.
817
- actualType = determineVideoPacketType(
818
- this.encodingConfig.codec,
819
- meta.decoderConfig,
820
- packet.data,
821
- );
822
- }
823
-
824
- // Define the packet
825
- if ((entry && entry.microsecondTimestamp === colorChunk.timestamp) || actualType !== null) {
826
- packet = packet.clone({
827
- timestamp: entry?.timestampIsValid ? entry.timestamp : undefined,
828
- duration: entry?.durationIsValid ? entry.duration : undefined,
829
- type: actualType ?? undefined,
830
- });
831
- }
832
-
833
- maybeEnsureIsKeyPacket(this.source._connectedTrack!, packet);
834
-
835
- this.encodingConfig.onEncodedPacket?.(packet, meta);
836
- this.lastMuxerPromise
837
- = this.muxer!.addEncodedVideoPacket(this.source._connectedTrack!, packet, meta)
838
- .catch((error) => {
839
- this.setError(error);
840
- });
841
-
842
- this.emittedEncoderPackets++;
843
- };
844
-
845
- const stack = new Error('Encoding error').stack;
846
-
847
- this.encoder = new VideoEncoder({
848
- output: (chunk, meta) => {
849
- if (!this.alphaEncoder) {
850
- // We're done
851
- addPacket(chunk, null, meta);
852
- return;
853
- }
854
-
855
- const alphaFrame = this.alphaFrameQueue.shift();
856
- assert(alphaFrame !== undefined);
857
-
858
- if (alphaFrame) {
859
- this.alphaEncoder.encode(alphaFrame, {
860
- ...this.defaultEncodeOptions,
861
- // Crucial: The alpha frame is forced to be a key frame whenever the color frame
862
- // also is. Without this, playback can glitch and even crash in some browsers.
863
- // This is the reason why the two encoders are wired in series and not in parallel.
864
- keyFrame: chunk.type === 'key',
865
- });
866
- alphaEncoderQueue++;
867
- alphaFrame.close();
868
- colorChunkQueue.push({ chunk, meta });
869
- } else {
870
- // There was no alpha component for this frame
871
- if (alphaEncoderQueue === 0) {
872
- // No pending alpha encodes either, so we're done
873
- addPacket(chunk, null, meta);
874
- } else {
875
- // There are still alpha encodes pending, so we can't add the packet immediately since
876
- // we'd end up with out-of-order packets. Instead, let's queue a null alpha chunk to be
877
- // added in the future, after the current encoder workload has completed:
878
- nullAlphaChunkQueue.push(encodedAlphaChunkCount + alphaEncoderQueue);
879
- colorChunkQueue.push({ chunk, meta });
880
- }
881
- }
882
- },
883
- error: (error) => {
884
- error.stack = stack; // Provide a more useful stack trace, the default one sucks
885
- this.setError(error);
886
- },
887
- });
888
- this.encoder.configure(encoderConfig);
889
-
890
- if (this.encodingConfig.alpha === 'keep') {
891
- const stack = new Error('Encoding error').stack;
892
-
893
- // We need to encode alpha as well, which we do with a separate encoder
894
- this.alphaEncoder = new VideoEncoder({
895
- // We ignore the alpha chunk's metadata
896
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
897
- output: (chunk, meta) => {
898
- alphaEncoderQueue--;
899
-
900
- // There has to be a color chunk because the encoders are wired in series
901
- const colorChunk = colorChunkQueue.shift();
902
- assert(colorChunk !== undefined);
903
-
904
- addPacket(colorChunk.chunk, chunk, colorChunk.meta);
905
-
906
- // See if there are any null alpha chunks queued up
907
- encodedAlphaChunkCount++;
908
- while (
909
- nullAlphaChunkQueue.length > 0
910
- && nullAlphaChunkQueue[0] === encodedAlphaChunkCount
911
- ) {
912
- nullAlphaChunkQueue.shift();
913
- const colorChunk = colorChunkQueue.shift();
914
- assert(colorChunk !== undefined);
915
-
916
- addPacket(colorChunk.chunk, null, colorChunk.meta);
917
- }
918
- },
919
- error: (error) => {
920
- error.stack = stack; // Provide a more useful stack trace
921
- this.setError(error);
922
- },
923
- });
924
- this.alphaEncoder.configure(encoderConfig);
925
- }
926
- }
927
-
928
- assert(this.source._connectedTrack);
929
- this.muxer = this.source._connectedTrack.output._muxer;
930
-
931
- this.encoderInitialized = true;
932
- })();
933
- }
934
-
935
- async flushAndClose(forceClose: boolean) {
936
- try {
937
- if (!forceClose) {
938
- this.checkForEncoderError();
939
-
940
- // Final frame rate padding: fill remaining frames up to the last sample's original end timestamp
941
- if (this.frameRateLastSample) {
942
- const frameRate = this.encodingConfig.transform!.frameRate!;
943
- const alignedEnd = floorToDivisor(this.frameRateLastEndTimestamp!, frameRate);
944
- await this.padFrameRate(alignedEnd);
945
- }
946
- }
947
-
948
- this.closed = true;
949
-
950
- if (!forceClose) {
951
- if (this.customEncoder) {
952
- void this.customEncoderCallSerializer.call(() => this.customEncoder!.flush());
953
- } else if (this.encoder) {
954
- // These are wired in series, therefore they must also be flushed in series
955
- await this.encoder.flush();
956
- await this.alphaEncoder?.flush();
957
-
958
- // Workaround for https://issues.chromium.org/issues/529852980 to give it time for errors to
959
- // surface
960
- await wait(25);
961
- }
962
- }
963
- } finally {
964
- // This cleanup must also run when padding or flushing threw (e.g. due to an encoder error), otherwise
965
- // samples, frames and encoders would be left dangling
966
- this.closed = true;
967
-
968
- this.frameRateLastSample?.close();
969
- this.frameRateLastSample = null;
970
-
971
- if (this.customEncoder) {
972
- await this.customEncoderCallSerializer.call(() => this.customEncoder!.close())
973
- .catch((error: unknown) => this.setError(error));
974
- } else if (this.encoder) {
975
- if (this.encoder.state !== 'closed') {
976
- this.encoder.close();
977
- }
978
- if (this.alphaEncoder && this.alphaEncoder.state !== 'closed') {
979
- this.alphaEncoder.close();
980
- }
981
-
982
- this.alphaFrameQueue.forEach(x => x?.close());
983
- this.alphaFrameQueue.length = 0;
984
-
985
- this.splitter?.close();
986
- }
987
- }
988
-
989
- if (!forceClose) {
990
- this.checkForEncoderError();
991
- }
992
- }
993
-
994
- getQueueSize() {
995
- if (this.customEncoder) {
996
- return this.customEncoderQueueSize;
997
- } else {
998
- // Because the color and alpha encoders are wired in series, there's no need to also include the alpha
999
- // encoder's queue size here
1000
- return this.encoder?.encodeQueueSize ?? 0;
1001
- }
1002
- }
1003
-
1004
- checkForEncoderError() {
1005
- if (this.errorSet) {
1006
- throw this.error;
1007
- }
1008
- }
1009
- }
1010
-
1011
- let splitterWorkerUrl: string | null = null;
1012
-
1013
- /** Utility class for splitting a composite frame into separate color and alpha parts on the CPU in a worker. */
1014
- export class ColorAlphaSplitter {
1015
- private worker: Worker | null = null;
1016
- private pendingRequests = new Map<
1017
- number,
1018
- ReturnType<typeof promiseWithResolvers<{ colorFrame: VideoFrame; alphaFrame: VideoFrame }>>
1019
- >();
1020
-
1021
- private nextRequestId = 0;
1022
-
1023
- split(sourceFrame: VideoFrame): Promise<{ colorFrame: VideoFrame; alphaFrame: VideoFrame }> {
1024
- if (!this.worker) {
1025
- if (!splitterWorkerUrl) {
1026
- const blob = new Blob(
1027
- [`(${colorAlphaSplitterWorkerCode.toString()})()`],
1028
- { type: 'application/javascript' },
1029
- );
1030
- splitterWorkerUrl = URL.createObjectURL(blob);
1031
- }
1032
-
1033
- this.worker = new Worker(splitterWorkerUrl);
1034
-
1035
- this.worker.addEventListener('message', (event: MessageEvent<ColorAlphaSplitterWorkerResponse>) => {
1036
- const data = event.data;
1037
- const pending = this.pendingRequests.get(data.id);
1038
- if (!pending) {
1039
- return;
1040
- }
1041
- this.pendingRequests.delete(data.id);
1042
-
1043
- if ('error' in data) {
1044
- pending.reject(new Error(data.error));
1045
- } else {
1046
- pending.resolve({ colorFrame: data.colorFrame, alphaFrame: data.alphaFrame });
1047
- }
1048
- });
1049
-
1050
- this.worker.addEventListener('error', (event) => {
1051
- const error = new Error(event.message || 'Color/alpha splitter worker error.');
1052
- for (const pending of this.pendingRequests.values()) {
1053
- pending.reject(error);
1054
- }
1055
- this.pendingRequests.clear();
1056
- });
1057
- }
1058
-
1059
- const id = this.nextRequestId++;
1060
- const pending = promiseWithResolvers<{ colorFrame: VideoFrame; alphaFrame: VideoFrame }>();
1061
- this.pendingRequests.set(id, pending);
1062
- this.worker.postMessage({ id, sourceFrame }, { transfer: [sourceFrame] });
1063
-
1064
- return pending.promise;
1065
- }
1066
-
1067
- close() {
1068
- this.worker?.terminate();
1069
- this.worker = null;
1070
-
1071
- const error = new Error('Color/alpha splitter closed.');
1072
- for (const pending of this.pendingRequests.values()) {
1073
- pending.reject(error);
1074
- }
1075
- this.pendingRequests.clear();
1076
- }
1077
- }
1078
-
1079
- type ColorAlphaSplitterWorkerRequest = {
1080
- id: number;
1081
- sourceFrame: VideoFrame;
1082
- };
1083
-
1084
- type ColorAlphaSplitterWorkerResponse =
1085
- | { id: number; colorFrame: VideoFrame; alphaFrame: VideoFrame }
1086
- | { id: number; error: string };
1087
-
1088
- const colorAlphaSplitterWorkerCode = () => {
1089
- // Reused across frames as long as the size matches, since consecutive frames usually share dimensions.
1090
- let cpuSourceBuffer: Uint8Array | null = null;
1091
-
1092
- // Serialize execution internally so concurrent requests don't race on the shared cpuSourceBuffer.
1093
- let chain: Promise<void> = Promise.resolve();
1094
- self.addEventListener('message', (event: MessageEvent<ColorAlphaSplitterWorkerRequest>) => {
1095
- const { id, sourceFrame } = event.data;
1096
- chain = chain.then(async () => {
1097
- try {
1098
- const { colorFrame, alphaFrame } = await split(sourceFrame);
1099
- self.postMessage({ id, colorFrame, alphaFrame }, { transfer: [colorFrame, alphaFrame] });
1100
- } catch (error) {
1101
- self.postMessage({ id, error: (error as Error).message });
1102
- } finally {
1103
- sourceFrame.close();
1104
- }
1105
- });
1106
- });
1107
-
1108
- const split = async (sourceFrame: VideoFrame) => {
1109
- const format = sourceFrame.format as VideoSamplePixelFormat | null;
1110
- if (!format) {
1111
- throw new Error('CPU color/alpha splitting requires a known VideoFrame format.');
1112
- }
1113
-
1114
- const sourceSize = sourceFrame.allocationSize();
1115
-
1116
- if (!cpuSourceBuffer || cpuSourceBuffer.byteLength !== sourceSize) {
1117
- cpuSourceBuffer = new Uint8Array(sourceSize);
1118
- }
1119
- await sourceFrame.copyTo(cpuSourceBuffer);
1120
-
1121
- if (format === 'RGBA' || format === 'BGRA') {
1122
- return splitInterleavedRgba(cpuSourceBuffer, format, sourceFrame);
1123
- } else if (
1124
- format === 'I420A' || format === 'I420AP10' || format === 'I420AP12'
1125
- || format === 'I422A' || format === 'I422AP10' || format === 'I422AP12'
1126
- || format === 'I444A' || format === 'I444AP10' || format === 'I444AP12'
1127
- ) {
1128
- return splitPlanarYuvA(cpuSourceBuffer, format, sourceFrame);
1129
- }
1130
-
1131
- throw new Error(`CPU color/alpha splitting does not support format '${format}'.`);
1132
- };
1133
-
1134
- const splitInterleavedRgba = (
1135
- source: Uint8Array,
1136
- format: 'RGBA' | 'BGRA',
1137
- sourceFrame: VideoFrame,
1138
- ) => {
1139
- const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;
1140
- const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;
1141
-
1142
- const pixelCount = width * height;
1143
- const chromaW = Math.ceil(width / 2);
1144
- const chromaH = Math.ceil(height / 2);
1145
- const alphaSize = pixelCount + chromaW * chromaH * 2;
1146
-
1147
- // Encode alpha as I420: Y = source A bytes, UV = 128
1148
- const alphaBuffer = new Uint8Array(alphaSize);
1149
- for (let i = 0, j = 3; i < pixelCount; i++, j += 4) {
1150
- alphaBuffer[i] = source[j]!;
1151
- }
1152
- alphaBuffer.fill(128, pixelCount);
1153
-
1154
- // Hand the source buffer straight to VideoFrame as RGBX/BGRX so the A bytes are ignored
1155
- const colorFrame = new VideoFrame(source, {
1156
- format: format === 'RGBA' ? 'RGBX' : 'BGRX',
1157
- codedWidth: width,
1158
- codedHeight: height,
1159
- timestamp: sourceFrame.timestamp,
1160
- duration: sourceFrame.duration ?? undefined,
1161
- // No transfer!
1162
- });
1163
- const alphaInit = {
1164
- format: 'I420' as const,
1165
- codedWidth: width,
1166
- codedHeight: height,
1167
- timestamp: sourceFrame.timestamp,
1168
- duration: sourceFrame.duration ?? undefined,
1169
- transfer: [alphaBuffer.buffer],
1170
- };
1171
- const alphaFrame = new VideoFrame(alphaBuffer, alphaInit);
1172
- return { colorFrame, alphaFrame };
1173
- };
1174
-
1175
- const splitPlanarYuvA = (
1176
- source: Uint8Array,
1177
- format:
1178
- | 'I420A' | 'I420AP10' | 'I420AP12'
1179
- | 'I422A' | 'I422AP10' | 'I422AP12'
1180
- | 'I444A' | 'I444AP10' | 'I444AP12',
1181
- sourceFrame: VideoFrame,
1182
- ) => {
1183
- const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;
1184
- const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;
1185
-
1186
- const is10 = format.includes('P10');
1187
- const is12 = format.includes('P12');
1188
- const bytesPerSample = (is10 || is12) ? 2 : 1;
1189
-
1190
- let chromaW: number;
1191
- let chromaH: number;
1192
- if (format.startsWith('I420')) {
1193
- chromaW = Math.ceil(width / 2);
1194
- chromaH = Math.ceil(height / 2);
1195
- } else if (format.startsWith('I422')) {
1196
- chromaW = Math.ceil(width / 2);
1197
- chromaH = height;
1198
- } else {
1199
- chromaW = width;
1200
- chromaH = height;
1201
- }
1202
-
1203
- const ySamples = width * height;
1204
- const uvSamples = chromaW * chromaH;
1205
- const yBytes = ySamples * bytesPerSample;
1206
- const uvBytes = uvSamples * bytesPerSample;
1207
- const aBytes = ySamples * bytesPerSample;
1208
-
1209
- const colorBytes = yBytes + uvBytes * 2;
1210
- const colorFormat = format.replace('A', '') as VideoPixelFormat;
1211
-
1212
- const alphaChromaW = Math.ceil(width / 2);
1213
- const alphaChromaH = Math.ceil(height / 2);
1214
- const alphaUvSamples = alphaChromaW * alphaChromaH;
1215
- const alphaUvBytes = alphaUvSamples * bytesPerSample;
1216
- const alphaSize = aBytes + 2 * alphaUvBytes;
1217
- const alphaBuffer = new Uint8Array(alphaSize);
1218
-
1219
- const aPlaneStart = colorBytes;
1220
- alphaBuffer.set(source.subarray(aPlaneStart, aPlaneStart + aBytes), 0);
1221
-
1222
- // Fill UV planes with the neutral chroma value
1223
- const uvOffset = aBytes;
1224
- const neutralChroma = is10 ? 512 : (is12 ? 2048 : 128);
1225
-
1226
- if (bytesPerSample === 1) {
1227
- alphaBuffer.fill(neutralChroma, uvOffset);
1228
- } else {
1229
- const uvView = new Uint16Array(alphaBuffer.buffer, uvOffset, 2 * alphaUvSamples);
1230
- uvView.fill(neutralChroma);
1231
- }
1232
-
1233
- const alphaFormat = (is10 ? 'I420P10' : (is12 ? 'I420P12' : 'I420')) as VideoPixelFormat;
1234
-
1235
- // Color frame is simply a prefix of the combined bytes
1236
- const colorFrame = new VideoFrame(source.subarray(0, colorBytes), {
1237
- format: colorFormat,
1238
- codedWidth: width,
1239
- codedHeight: height,
1240
- timestamp: sourceFrame.timestamp,
1241
- duration: sourceFrame.duration ?? undefined,
1242
- });
1243
- const alphaInit = {
1244
- format: alphaFormat,
1245
- codedWidth: width,
1246
- codedHeight: height,
1247
- timestamp: sourceFrame.timestamp,
1248
- duration: sourceFrame.duration ?? undefined,
1249
- transfer: [alphaBuffer.buffer],
1250
- };
1251
- const alphaFrame = new VideoFrame(alphaBuffer, alphaInit);
1252
- return { colorFrame, alphaFrame };
1253
- };
1254
- };
1255
-
1256
- /**
1257
- * This source can be used to add raw, unencoded video samples (frames) to an output video track. These frames will
1258
- * automatically be encoded and then piped into the output.
1259
- * @group Media sources
1260
- * @public
1261
- */
1262
- export class VideoSampleSource extends VideoSource {
1263
- /** @internal */
1264
- private _encoder: VideoEncoderWrapper;
1265
-
1266
- /**
1267
- * Creates a new {@link VideoSampleSource} whose samples are encoded according to the specified
1268
- * {@link VideoEncodingConfig}.
1269
- */
1270
- constructor(encodingConfig: VideoEncodingConfig) {
1271
- validateVideoEncodingConfig(encodingConfig);
1272
-
1273
- super(encodingConfig.codec);
1274
- this._encoder = new VideoEncoderWrapper(this, encodingConfig);
1275
- }
1276
-
1277
- /**
1278
- * Encodes a video sample (frame) and then adds it to the output.
1279
- *
1280
- * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
1281
- * to respect writer and encoder backpressure.
1282
- */
1283
- add(videoSample: VideoSample, encodeOptions?: VideoEncoderEncodeOptions) {
1284
- if (!(videoSample instanceof VideoSample)) {
1285
- throw new TypeError('videoSample must be a VideoSample.');
1286
- }
1287
-
1288
- return this._encoder.add(videoSample, false, encodeOptions);
1289
- }
1290
-
1291
- /** @internal */
1292
- override _flushAndClose(forceClose: boolean) {
1293
- return this._encoder.flushAndClose(forceClose);
1294
- }
1295
- }
1296
-
1297
- /**
1298
- * This source can be used to add video frames to the output track from a fixed canvas element. Since canvases are often
1299
- * used for rendering, this source provides a convenient wrapper around {@link VideoSampleSource}.
1300
- * @group Media sources
1301
- * @public
1302
- */
1303
- export class CanvasSource extends VideoSource {
1304
- /** @internal */
1305
- private _encoder: VideoEncoderWrapper;
1306
- /** @internal */
1307
- private _canvas: HTMLCanvasElement | OffscreenCanvas;
1308
-
1309
- /**
1310
- * Creates a new {@link CanvasSource} from a canvas element or `OffscreenCanvas` whose samples are encoded
1311
- * according to the specified {@link VideoEncodingConfig}.
1312
- */
1313
- constructor(canvas: HTMLCanvasElement | OffscreenCanvas, encodingConfig: VideoEncodingConfig) {
1314
- if (
1315
- !(typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement)
1316
- && !(typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas)
1317
- ) {
1318
- throw new TypeError('canvas must be an HTMLCanvasElement or OffscreenCanvas.');
1319
- }
1320
- validateVideoEncodingConfig(encodingConfig);
1321
-
1322
- super(encodingConfig.codec);
1323
- this._encoder = new VideoEncoderWrapper(this, encodingConfig);
1324
- this._canvas = canvas;
1325
- }
1326
-
1327
- /**
1328
- * Captures the current canvas state as a video sample (frame), encodes it and adds it to the output.
1329
- *
1330
- * @param timestamp - The timestamp of the sample, in seconds.
1331
- * @param duration - The duration of the sample, in seconds.
1332
- *
1333
- * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
1334
- * to respect writer and encoder backpressure.
1335
- */
1336
- add(timestamp: number, duration = 0, encodeOptions?: VideoEncoderEncodeOptions) {
1337
- if (!Number.isFinite(timestamp) || timestamp < 0) {
1338
- throw new TypeError('timestamp must be a non-negative number.');
1339
- }
1340
- if (!Number.isFinite(duration) || duration < 0) {
1341
- throw new TypeError('duration must be a non-negative number.');
1342
- }
1343
-
1344
- const sample = new VideoSample(this._canvas, { timestamp, duration });
1345
- return this._encoder.add(sample, true, encodeOptions);
1346
- }
1347
-
1348
- /** @internal */
1349
- override _flushAndClose(forceClose: boolean) {
1350
- return this._encoder.flushAndClose(forceClose);
1351
- }
1352
- }
1353
-
1354
- /**
1355
- * Options for {@link MediaStreamVideoTrackSource}.
1356
- * @group Media sources
1357
- * @public
1358
- */
1359
- export type MediaStreamVideoTrackSourceOptions = {
1360
- /**
1361
- * The frame rate at which the underlying video track is sampled. Defaults to the frame rate specified in the
1362
- * track's [`MediaTrackSettings`](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings). Set to
1363
- * `null` to only add a frame whenever the underlying track pushes one - this minimizes frame count but can
1364
- * lead to wildly irregular FPS.
1365
- */
1366
- frameRate?: number | null;
1367
- /**
1368
- * Controls the basis (zero point) for video frame timestamps.
1369
- *
1370
- * When set to `'synced-zero'`, timestamps will be relative to the first chunk of media from a `MediaStreamTrack`
1371
- * added to the {@link Output}.
1372
- *
1373
- * When set to `'zero'`, timestamps will be relative to the first video frame emitted by this source.
1374
- *
1375
- * When set to `'unix'`, timestamps will be relative to the Unix epoch, so clearly associated with a distinct point
1376
- * in time. Here, pausing via {@link MediaStreamVideoTrackSource.pause} will also create gaps in timestamps. Be sure
1377
- * to pair this mode with {@link BaseTrackMetadata.isRelativeToUnixEpoch}.
1378
- *
1379
- * Defaults to `'synced-zero'`.
1380
- */
1381
- timestampBase?: 'synced-zero' | 'zero' | 'unix';
1382
- };
1383
-
1384
- /**
1385
- * Video source that encodes the frames of a
1386
- * [`MediaStreamVideoTrack`](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) and pipes them into the
1387
- * output. This is useful for capturing live or real-time data such as webcams or screen captures. Frames will
1388
- * automatically start being captured once the connected {@link Output} is started, and will keep being captured until
1389
- * the {@link Output} is finalized or this source is closed.
1390
- * @group Media sources
1391
- * @public
1392
- */
1393
- export class MediaStreamVideoTrackSource extends VideoSource {
1394
- /** @internal */
1395
- private _options: MediaStreamVideoTrackSourceOptions;
1396
- /** @internal */
1397
- private _encoder: VideoEncoderWrapper;
1398
- /** @internal */
1399
- private _abortController: AbortController | null = null;
1400
- /** @internal */
1401
- private _track: MediaStreamVideoTrack;
1402
- /** @internal */
1403
- private _workerTrackId: number | null = null;
1404
- /** @internal */
1405
- private _workerListener: ((event: MessageEvent) => void) | null = null;
1406
- /** @internal */
1407
- private _promiseWithResolvers = promiseWithResolvers();
1408
- /** @internal */
1409
- private _errorPromiseAccessed = false;
1410
- /** @internal */
1411
- private _paused = false;
1412
- /** @internal */
1413
- private _lastVideoFrame: VideoFrame | null = null;
1414
- /** @internal */
1415
- private _timerHandle: UnthrottledTimerHandle | null = null;
1416
- /** @internal */
1417
- private _videoElement: HTMLVideoElement | null = null;
1418
-
1419
- /** A promise that rejects upon any error within this source. This promise never resolves. */
1420
- get errorPromise() {
1421
- this._errorPromiseAccessed = true;
1422
- return this._promiseWithResolvers.promise;
1423
- }
1424
-
1425
- /** Whether this source is currently paused as a result of calling `.pause()`. */
1426
- get paused() {
1427
- return this._paused;
1428
- }
1429
-
1430
- /**
1431
- * Creates a new {@link MediaStreamVideoTrackSource} from a
1432
- * [`MediaStreamVideoTrack`](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack), which will pull
1433
- * video samples from the stream in real time and encode them according to {@link VideoEncodingConfig}.
1434
- */
1435
- constructor(
1436
- track: MediaStreamVideoTrack,
1437
- encodingConfig: VideoEncodingConfig,
1438
- options: MediaStreamVideoTrackSourceOptions = {},
1439
- ) {
1440
- if (!(track instanceof MediaStreamTrack) || track.kind !== 'video') {
1441
- throw new TypeError('track must be a video MediaStreamTrack.');
1442
- }
1443
- validateVideoEncodingConfig(encodingConfig);
1444
- if (typeof options !== 'object' || !options) {
1445
- throw new TypeError('options must be an object.');
1446
- }
1447
- if (options.frameRate != null && (typeof options.frameRate !== 'number' || options.frameRate <= 0)) {
1448
- throw new TypeError('options.frameRate, when provided, must be either a positive number or null.');
1449
- }
1450
- if (
1451
- options.timestampBase !== undefined
1452
- && options.timestampBase !== 'synced-zero'
1453
- && options.timestampBase !== 'zero'
1454
- && options.timestampBase !== 'unix'
1455
- ) {
1456
- throw new TypeError(
1457
- 'options.timestampBase, when provided, must be one of \'synced-zero\', \'zero\', or \'unix\'.',
1458
- );
1459
- }
1460
-
1461
- encodingConfig = {
1462
- ...encodingConfig,
1463
- latencyMode: 'realtime',
1464
- };
1465
-
1466
- super(encodingConfig.codec);
1467
-
1468
- this._options = options;
1469
- this._encoder = new VideoEncoderWrapper(this, encodingConfig);
1470
- this._track = track;
1471
- }
1472
-
1473
- /** @internal */
1474
- override async _start() {
1475
- if (!this._errorPromiseAccessed) {
1476
- Logging._warn(
1477
- 'Make sure not to ignore the `errorPromise` field on MediaStreamVideoTrackSource, so that any internal'
1478
- + ' errors get bubbled up properly.',
1479
- );
1480
- }
1481
-
1482
- const frameRate = this._options.frameRate !== undefined
1483
- ? this._options.frameRate
1484
- : (this._track.getSettings().frameRate ?? null);
1485
-
1486
- this._abortController = new AbortController();
1487
-
1488
- let firstVideoFrameTimestamp: number | null = null;
1489
- let lastFrameTime: number | null = null;
1490
- let frameCount = 0;
1491
- let errored = false;
1492
- let lastSampleTimestamp: number | null = null;
1493
- let timestampOffset = 0;
1494
-
1495
- const tick = () => {
1496
- assert(frameRate !== null);
1497
-
1498
- if (!this._lastVideoFrame) {
1499
- return;
1500
- }
1501
-
1502
- assert(lastFrameTime !== null);
1503
- assert(firstVideoFrameTimestamp !== null);
1504
-
1505
- const now = performance.now();
1506
-
1507
- // Add as many frames as warranted by the elapsed time.
1508
- // > instead of >= intentionally because tick() is called before the _lastVideoFrame is changed
1509
- while (now - lastFrameTime > 1000 / frameRate) {
1510
- lastFrameTime += 1000 / frameRate;
1511
- const timestamp = firstVideoFrameTimestamp + frameCount / frameRate;
1512
-
1513
- const frame = new VideoFrame(this._videoElement ?? this._lastVideoFrame, {
1514
- timestamp: 1e6 * timestamp,
1515
- duration: 1e6 / frameRate,
1516
- });
1517
-
1518
- addVideoFrame(frame, now);
1519
- }
1520
- };
1521
-
1522
- if (frameRate !== null) {
1523
- this._timerHandle = setIntervalUnthrottled(tick, 4); // Run it at 250 Hz
1524
- }
1525
-
1526
- const onVideoFrame = (videoFrame: VideoFrame) => {
1527
- if (frameRate === null) {
1528
- addVideoFrame(videoFrame);
1529
- } else {
1530
- const now = performance.now();
1531
-
1532
- if (!this._lastVideoFrame) {
1533
- addVideoFrame(videoFrame.clone(), now);
1534
- lastFrameTime = now;
1535
- this._lastVideoFrame = videoFrame;
1536
- } else {
1537
- tick();
1538
- this._lastVideoFrame?.close();
1539
- this._lastVideoFrame = videoFrame;
1540
- }
1541
- }
1542
- };
1543
-
1544
- const addVideoFrame = (videoFrame: VideoFrame, now = performance.now()) => {
1545
- if (errored) {
1546
- videoFrame.close();
1547
- return;
1548
- }
1549
-
1550
- frameCount++;
1551
- const currentTimestamp = videoFrame.timestamp / 1e6;
1552
-
1553
- if (this._paused) {
1554
- const frameSeen = firstVideoFrameTimestamp !== null;
1555
- if (frameSeen) {
1556
- if (lastSampleTimestamp !== null && this._options.timestampBase !== 'unix') {
1557
- // In addition to dropping this frame, let's also keep track of the time we have lost due to the
1558
- // pause. Doing it like this instead of simply keeping track of the paused time is better since
1559
- // it retains the frame rate of the underlying source.
1560
- const timeDelta = currentTimestamp - lastSampleTimestamp;
1561
- timestampOffset -= timeDelta;
1562
- }
1563
- lastSampleTimestamp = currentTimestamp;
1564
- }
1565
-
1566
- videoFrame.close();
1567
- return;
1568
- }
1569
-
1570
- if (firstVideoFrameTimestamp === null) {
1571
- firstVideoFrameTimestamp = currentTimestamp;
1572
-
1573
- let target: number;
1574
- const timestampBase = this._options.timestampBase ?? 'synced-zero';
1575
- if (timestampBase === 'unix') {
1576
- target = Date.now() / 1000;
1577
- } else if (timestampBase === 'zero') {
1578
- target = 0;
1579
- } else {
1580
- const output = this._connectedTrack!.output;
1581
- if (output._firstMediaStreamTimestamp === null) {
1582
- output._firstMediaStreamTimestamp = now / 1000;
1583
- target = 0;
1584
- } else {
1585
- target = now / 1000 - output._firstMediaStreamTimestamp;
1586
- }
1587
- }
1588
-
1589
- timestampOffset = target - firstVideoFrameTimestamp;
1590
- }
1591
-
1592
- lastSampleTimestamp = currentTimestamp;
1593
-
1594
- if (this._encoder.getQueueSize() >= 8) {
1595
- // Drop frames if the encoder is overloaded
1596
- videoFrame.close();
1597
- return;
1598
- }
1599
-
1600
- const sample = new VideoSample(videoFrame, {
1601
- timestamp: currentTimestamp + timestampOffset,
1602
- });
1603
-
1604
- void this._encoder.add(sample, true)
1605
- .catch((error) => {
1606
- errored = true;
1607
-
1608
- this._abortController?.abort();
1609
- this._promiseWithResolvers.reject(error);
1610
-
1611
- if (this._workerTrackId !== null) {
1612
- // Tell the worker to stop the track
1613
- sendMessageToMediaStreamTrackProcessorWorker({
1614
- type: 'stopTrack',
1615
- trackId: this._workerTrackId,
1616
- });
1617
- }
1618
- });
1619
- };
1620
-
1621
- if (typeof MediaStreamTrackProcessor !== 'undefined') {
1622
- // We can do it here directly, perfect
1623
- const processor = new MediaStreamTrackProcessor({ track: this._track });
1624
- const consumer = new WritableStream<VideoFrame>({ write: onVideoFrame });
1625
-
1626
- processor.readable.pipeTo(consumer, {
1627
- signal: this._abortController.signal,
1628
- }).catch((error) => {
1629
- // Handle AbortError silently
1630
- if (error instanceof DOMException && error.name === 'AbortError') return;
1631
-
1632
- this._promiseWithResolvers.reject(error);
1633
- });
1634
- } else {
1635
- // It might still be supported in a worker, so let's check that
1636
- const supportedInWorker = await mediaStreamTrackProcessorIsSupportedInWorker();
1637
-
1638
- if (supportedInWorker) {
1639
- this._workerTrackId = nextMediaStreamTrackProcessorWorkerId++;
1640
-
1641
- sendMessageToMediaStreamTrackProcessorWorker({
1642
- type: 'videoTrack',
1643
- trackId: this._workerTrackId,
1644
- track: this._track,
1645
- });
1646
-
1647
- this._workerListener = (event: MessageEvent) => {
1648
- const message = event.data as MediaStreamTrackProcessorWorkerMessage;
1649
-
1650
- if (message.type === 'videoFrame' && message.trackId === this._workerTrackId) {
1651
- onVideoFrame(message.videoFrame);
1652
- } else if (message.type === 'error' && message.trackId === this._workerTrackId) {
1653
- this._promiseWithResolvers.reject(message.error);
1654
- }
1655
- };
1656
-
1657
- mediaStreamTrackProcessorWorker!.addEventListener('message', this._workerListener);
1658
- } else if (frameRate !== null) {
1659
- // No MediaStreamTrackProcessor support at all (e.g. Firefox), but we have a frame rate, so we can
1660
- // manually sample from a hidden <video> element instead.
1661
- const video = document.createElement('video');
1662
- video.style.position = 'fixed';
1663
- video.style.left = '-10000px';
1664
- video.style.top = '-10000px';
1665
- video.style.width = '1px';
1666
- video.style.height = '1px';
1667
- video.style.opacity = '0';
1668
- video.style.pointerEvents = 'none';
1669
- video.muted = true;
1670
- video.srcObject = new MediaStream([this._track]);
1671
- document.body.appendChild(video);
1672
- this._videoElement = video;
1673
-
1674
- video.addEventListener('loadeddata', () => {
1675
- if (errored || !this._videoElement) {
1676
- return;
1677
- }
1678
-
1679
- // This will be the first frame
1680
- const frame = new VideoFrame(video, {
1681
- timestamp: 1000 * performance.now(),
1682
- });
1683
- onVideoFrame(frame);
1684
- frame.close();
1685
- }, { once: true });
1686
-
1687
- void video.play().catch((error) => {
1688
- errored = true;
1689
- this._promiseWithResolvers.reject(error);
1690
- });
1691
- } else {
1692
- throw new Error(
1693
- 'When no explicit frame rate is set, MediaStreamTrackProcessor is required; but it\'s not supported'
1694
- + ' by this browser.',
1695
- );
1696
- }
1697
- }
1698
- }
1699
-
1700
- /**
1701
- * Pauses the capture of video frames - any video frames emitted by the underlying media stream will be ignored
1702
- * while paused. This does *not* close the underlying `MediaStreamVideoTrack`, it just ignores its output.
1703
- */
1704
- pause() {
1705
- this._paused = true;
1706
- }
1707
-
1708
- /** Resumes the capture of video frames after being paused. */
1709
- resume() {
1710
- this._paused = false;
1711
- }
1712
-
1713
- /** @internal */
1714
- override async _flushAndClose(forceClose: boolean) {
1715
- if (this._abortController) {
1716
- this._abortController.abort();
1717
- this._abortController = null;
1718
- }
1719
-
1720
- if (this._timerHandle) {
1721
- clearIntervalUnthrottled(this._timerHandle);
1722
- }
1723
- this._lastVideoFrame?.close();
1724
-
1725
- if (this._videoElement) {
1726
- this._videoElement.srcObject = null;
1727
- this._videoElement.remove();
1728
- this._videoElement = null;
1729
- }
1730
-
1731
- if (this._workerTrackId !== null) {
1732
- assert(this._workerListener);
1733
-
1734
- sendMessageToMediaStreamTrackProcessorWorker({
1735
- type: 'stopTrack',
1736
- trackId: this._workerTrackId,
1737
- });
1738
-
1739
- // Wait for the worker to stop the track
1740
- await new Promise<void>((resolve) => {
1741
- const listener = (event: MessageEvent) => {
1742
- const message = event.data as MediaStreamTrackProcessorWorkerMessage;
1743
-
1744
- if (message.type === 'trackStopped' && message.trackId === this._workerTrackId) {
1745
- assert(this._workerListener);
1746
- mediaStreamTrackProcessorWorker!.removeEventListener('message', this._workerListener);
1747
- mediaStreamTrackProcessorWorker!.removeEventListener('message', listener);
1748
-
1749
- resolve();
1750
- }
1751
- };
1752
-
1753
- mediaStreamTrackProcessorWorker!.addEventListener('message', listener);
1754
- });
1755
- }
1756
-
1757
- await this._encoder.flushAndClose(forceClose);
1758
- }
1759
- }
1760
-
1761
- /**
1762
- * Base class for audio sources - sources for audio tracks.
1763
- * @group Media sources
1764
- * @public
1765
- */
1766
- export abstract class AudioSource extends MediaSource {
1767
- /** @internal */
1768
- override _connectedTrack: OutputAudioTrack | null = null;
1769
- /** @internal */
1770
- override readonly _codec: AudioCodec;
1771
-
1772
- /** Internal constructor. */
1773
- constructor(codec: AudioCodec) {
1774
- super();
1775
-
1776
- if (!AUDIO_CODECS.includes(codec)) {
1777
- throw new TypeError(`Invalid audio codec '${codec}'. Must be one of: ${AUDIO_CODECS.join(', ')}.`);
1778
- }
1779
-
1780
- this._codec = codec;
1781
- }
1782
- }
1783
-
1784
- /**
1785
- * The most basic audio source; can be used to directly pipe encoded packets into the output file.
1786
- * @group Media sources
1787
- * @public
1788
- */
1789
- export class EncodedAudioPacketSource extends AudioSource {
1790
- /** Creates a new {@link EncodedAudioPacketSource} whose packets are encoded using `codec`. */
1791
- constructor(codec: AudioCodec) {
1792
- super(codec);
1793
- }
1794
-
1795
- /**
1796
- * Adds an encoded packet to the output audio track. Packets must be added in *decode order*.
1797
- *
1798
- * @param meta - Additional metadata from the encoder. You should pass this for the first call, including a valid
1799
- * decoder config.
1800
- *
1801
- * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
1802
- * to respect writer and encoder backpressure.
1803
- */
1804
- add(packet: EncodedPacket, meta?: EncodedAudioChunkMetadata) {
1805
- if (!(packet instanceof EncodedPacket)) {
1806
- throw new TypeError('packet must be an EncodedPacket.');
1807
- }
1808
- if (packet.isMetadataOnly) {
1809
- throw new TypeError('Metadata-only packets cannot be added.');
1810
- }
1811
- if (meta !== undefined && (!meta || typeof meta !== 'object')) {
1812
- throw new TypeError('meta, when provided, must be an object.');
1813
- }
1814
-
1815
- this._ensureValidAdd();
1816
- return this._connectedTrack!.output._muxer.addEncodedAudioPacket(this._connectedTrack!, packet, meta);
1817
- }
1818
- }
1819
-
1820
- class AudioEncoderWrapper {
1821
- private ensureEncoderPromise: Promise<void> | null = null;
1822
- private encoderInitialized = false;
1823
- private encoder: AudioEncoder | null = null;
1824
- private muxer: Muxer | null = null;
1825
- private lastNumberOfChannels: number | null = null;
1826
- private lastSampleRate: number | null = null;
1827
-
1828
- private isPcmEncoder = false;
1829
- private outputSampleSize: number | null = null;
1830
- private writeOutputValue: ((view: DataView, byteOffset: number, value: number) => void) | null = null;
1831
-
1832
- private customEncoder: CustomAudioEncoder | null = null;
1833
- private customEncoderCallSerializer = new CallSerializer();
1834
- private customEncoderQueueSize = 0;
1835
-
1836
- private lastEndSampleIndex: number | null = null;
1837
-
1838
- private resampler: AudioResampler | null = null;
1839
-
1840
- /**
1841
- * Encoders typically throw their errors "out of band", meaning asynchronously in some other execution context.
1842
- * However, we want to surface these errors to the user within the normal control flow, so they don't go uncaught.
1843
- * So, we keep track of the encoder error and throw it as soon as we get the chance.
1844
- */
1845
- private error: unknown = null;
1846
- private errorSet = false;
1847
-
1848
- private setError(error: unknown) {
1849
- if (!this.errorSet) {
1850
- this.error = error;
1851
- this.errorSet = true;
1852
- }
1853
- }
1854
-
1855
- private lastMuxerPromise: Promise<void> = Promise.resolve();
1856
- private closed = false;
1857
-
1858
- constructor(private source: AudioSource, private encodingConfig: AudioEncodingConfig) {}
1859
-
1860
- async add(audioSample: AudioSample, shouldClose: boolean) {
1861
- try {
1862
- this.checkForEncoderError();
1863
- this.source._ensureValidAdd();
1864
-
1865
- // Ensure audio parameters remain constant
1866
- if (this.lastNumberOfChannels !== null && this.lastSampleRate !== null) {
1867
- if (
1868
- audioSample.numberOfChannels !== this.lastNumberOfChannels
1869
- || audioSample.sampleRate !== this.lastSampleRate
1870
- ) {
1871
- throw new Error(
1872
- `Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at`
1873
- + ` ${this.lastSampleRate} Hz, got ${audioSample.numberOfChannels} channels at`
1874
- + ` ${audioSample.sampleRate} Hz.`,
1875
- );
1876
- }
1877
- } else {
1878
- this.lastNumberOfChannels = audioSample.numberOfChannels;
1879
- this.lastSampleRate = audioSample.sampleRate;
1880
- }
1881
-
1882
- const config = this.encodingConfig;
1883
- const needsResample = config.transform?.numberOfChannels !== undefined
1884
- || config.transform?.sampleRate !== undefined;
1885
-
1886
- if (needsResample) {
1887
- if (!this.resampler) {
1888
- // Initialize the resampler on first sample
1889
- this.resampler = new AudioResampler({
1890
- targetNumberOfChannels: config.transform!.numberOfChannels
1891
- ?? audioSample.numberOfChannels,
1892
- targetSampleRate: config.transform!.sampleRate
1893
- ?? audioSample.sampleRate,
1894
- onSample: async (sample) => {
1895
- await this.processAndEncode(sample, true);
1896
- },
1897
- });
1898
- }
1899
-
1900
- await this.resampler.add(audioSample);
1901
- } else {
1902
- await this.processAndEncode(audioSample, shouldClose);
1903
- }
1904
- } finally {
1905
- if (shouldClose) {
1906
- audioSample.close();
1907
- }
1908
- }
1909
- }
1910
-
1911
- /**
1912
- * Runs the process function (if any) and encodes the resulting samples.
1913
- */
1914
- private async processAndEncode(audioSample: AudioSample, shouldClose: boolean) {
1915
- const config = this.encodingConfig;
1916
-
1917
- if (
1918
- config.transform?.sampleFormat !== undefined
1919
- && toInterleavedAudioFormat(audioSample.format) !== config.transform.sampleFormat
1920
- ) {
1921
- // Do a sample format conversion
1922
- const newSample = audioSampleToInterleavedFormat(audioSample, config.transform.sampleFormat);
1923
-
1924
- if (shouldClose) {
1925
- audioSample.close();
1926
- }
1927
-
1928
- audioSample = newSample;
1929
- shouldClose = true;
1930
- }
1931
-
1932
- if (config.transform?.process) {
1933
- try {
1934
- let processed = config.transform.process(audioSample);
1935
- if (processed instanceof Promise) {
1936
- processed = await processed;
1937
- }
1938
-
1939
- if (processed === null) {
1940
- return;
1941
- }
1942
-
1943
- if (!Array.isArray(processed)) {
1944
- processed = [processed];
1945
- }
1946
-
1947
- try {
1948
- for (const sample of processed) {
1949
- if (!(sample instanceof AudioSample)) {
1950
- throw new TypeError(
1951
- 'The audio process function must return an AudioSample, null, or an array of'
1952
- + ' AudioSamples.',
1953
- );
1954
- }
1955
- }
1956
-
1957
- for (const sample of processed) {
1958
- await this.encodeSample(sample, true);
1959
- }
1960
- } finally {
1961
- // encodeSample closes the samples it was passed; this additionally covers the samples never
1962
- // reached because an earlier one threw (closing is idempotent)
1963
- for (const sample of processed) {
1964
- if (sample instanceof AudioSample) {
1965
- sample.close();
1966
- }
1967
- }
1968
- }
1969
- } finally {
1970
- if (shouldClose) {
1971
- audioSample.close();
1972
- }
1973
- }
1974
- } else {
1975
- await this.encodeSample(audioSample, shouldClose);
1976
- }
1977
- }
1978
-
1979
- /**
1980
- * Encodes a single audio sample, handling encoder init, gap padding, and backpressure.
1981
- */
1982
- private async encodeSample(audioSample: AudioSample, shouldClose: boolean) {
1983
- try {
1984
- if (!this.encoderInitialized) {
1985
- if (!this.ensureEncoderPromise) {
1986
- this.ensureEncoder(audioSample);
1987
- }
1988
-
1989
- // No, this "if" statement is not useless. Sometimes, the above call to `ensureEncoder` might have
1990
- // synchronously completed and the encoder is already initialized. In this case, we don't need to await
1991
- // the promise anymore. This also fixes nasty async race condition bugs when multiple code paths are
1992
- // calling this method: It's important that the call that initialized the encoder go through this
1993
- // code first.
1994
- if (!this.encoderInitialized) {
1995
- await this.ensureEncoderPromise;
1996
- }
1997
- }
1998
- assert(this.encoderInitialized);
1999
-
2000
- if (this.closed) {
2001
- return;
2002
- }
2003
-
2004
- // Handle padding of gaps with silence to avoid audio drift over time, like in
2005
- // https://github.com/Vanilagy/mediabunny/issues/176
2006
- // TODO An open question is how encoders deal with the first AudioData having a non-zero timestamp, and with
2007
- // AudioDatas that have an overlapping timestamp range.
2008
- {
2009
- const startSampleIndex = Math.round(
2010
- audioSample.timestamp * audioSample.sampleRate,
2011
- );
2012
- const endSampleIndex = Math.round(
2013
- (audioSample.timestamp + audioSample.duration) * audioSample.sampleRate,
2014
- );
2015
-
2016
- if (this.lastEndSampleIndex === null) {
2017
- this.lastEndSampleIndex = endSampleIndex;
2018
- } else {
2019
- const sampleDiff = startSampleIndex - this.lastEndSampleIndex;
2020
-
2021
- if (sampleDiff >= 64) {
2022
- // The gap is big enough, let's add a correction sample
2023
- const fillSample = new AudioSample({
2024
- data: new Float32Array(sampleDiff * audioSample.numberOfChannels),
2025
- format: 'f32-planar',
2026
- sampleRate: audioSample.sampleRate,
2027
- numberOfChannels: audioSample.numberOfChannels,
2028
- numberOfFrames: sampleDiff,
2029
- timestamp: this.lastEndSampleIndex / audioSample.sampleRate,
2030
- });
2031
-
2032
- await this.encodeSample(fillSample, true); // Recursive call
2033
- }
2034
-
2035
- this.lastEndSampleIndex += audioSample.numberOfFrames;
2036
- }
2037
- }
2038
-
2039
- this.encodingConfig.onEncodedSample?.(audioSample);
2040
-
2041
- if (this.customEncoder) {
2042
- this.customEncoderQueueSize++;
2043
-
2044
- // We clone the sample so it cannot be closed on us from the outside before it reaches the encoder
2045
- const clonedSample = audioSample.clone();
2046
-
2047
- const promise = this.customEncoderCallSerializer
2048
- .call(() => this.customEncoder!.encode(clonedSample))
2049
- .catch((error: unknown) => this.setError(error))
2050
- .finally(() => {
2051
- this.customEncoderQueueSize--;
2052
- clonedSample.close();
2053
- });
2054
-
2055
- if (this.customEncoderQueueSize >= 4) {
2056
- await promise;
2057
- }
2058
-
2059
- await this.lastMuxerPromise; // Allow the writer to apply backpressure
2060
- } else if (this.isPcmEncoder) {
2061
- await this.doPcmEncoding(audioSample, shouldClose);
2062
- } else {
2063
- assert(this.encoder);
2064
- const audioData = audioSample.toAudioData();
2065
- this.encoder.encode(audioData);
2066
- audioData.close();
2067
-
2068
- if (shouldClose) {
2069
- audioSample.close();
2070
- }
2071
-
2072
- if (this.encoder.encodeQueueSize >= 4) {
2073
- await new Promise(resolve => this.encoder!.addEventListener('dequeue', resolve, { once: true }));
2074
- }
2075
-
2076
- await this.lastMuxerPromise; // Allow the writer to apply backpressure
2077
- }
2078
- } finally {
2079
- if (shouldClose) {
2080
- audioSample.close();
2081
- }
2082
- }
2083
- }
2084
-
2085
- private async doPcmEncoding(audioSample: AudioSample, shouldClose: boolean) {
2086
- assert(this.outputSampleSize);
2087
- assert(this.writeOutputValue);
2088
-
2089
- // Need to extract data from the audio data before we close it
2090
- const { numberOfChannels, numberOfFrames, sampleRate, timestamp } = audioSample;
2091
-
2092
- const CHUNK_SIZE = 2048;
2093
- const outputs: {
2094
- frameCount: number;
2095
- view: DataView;
2096
- }[] = [];
2097
-
2098
- // Prepare all of the output buffers, each being bounded by CHUNK_SIZE so we don't generate huge packets
2099
- for (let frame = 0; frame < numberOfFrames; frame += CHUNK_SIZE) {
2100
- const frameCount = Math.min(CHUNK_SIZE, audioSample.numberOfFrames - frame);
2101
- const outputSize = frameCount * numberOfChannels * this.outputSampleSize;
2102
- const outputBuffer = new ArrayBuffer(outputSize);
2103
- const outputView = new DataView(outputBuffer);
2104
-
2105
- outputs.push({ frameCount, view: outputView });
2106
- }
2107
-
2108
- const allocationSize = audioSample.allocationSize(({ planeIndex: 0, format: 'f32-planar' }));
2109
- const floats = new Float32Array(allocationSize / Float32Array.BYTES_PER_ELEMENT);
2110
-
2111
- for (let i = 0; i < numberOfChannels; i++) {
2112
- audioSample.copyTo(floats, { planeIndex: i, format: 'f32-planar' });
2113
-
2114
- for (let j = 0; j < outputs.length; j++) {
2115
- const { frameCount, view } = outputs[j]!;
2116
-
2117
- for (let k = 0; k < frameCount; k++) {
2118
- this.writeOutputValue(
2119
- view,
2120
- (k * numberOfChannels + i) * this.outputSampleSize,
2121
- floats[j * CHUNK_SIZE + k]!,
2122
- );
2123
- }
2124
- }
2125
- }
2126
-
2127
- if (shouldClose) {
2128
- audioSample.close();
2129
- }
2130
-
2131
- const meta: EncodedAudioChunkMetadata = {
2132
- decoderConfig: {
2133
- codec: this.encodingConfig.codec,
2134
- numberOfChannels,
2135
- sampleRate,
2136
- },
2137
- };
2138
-
2139
- for (let i = 0; i < outputs.length; i++) {
2140
- const { frameCount, view } = outputs[i]!;
2141
- const outputBuffer = view.buffer;
2142
- const startFrame = i * CHUNK_SIZE;
2143
-
2144
- const packet = new EncodedPacket(
2145
- new Uint8Array(outputBuffer),
2146
- 'key',
2147
- timestamp + startFrame / sampleRate,
2148
- frameCount / sampleRate,
2149
- );
2150
-
2151
- this.encodingConfig.onEncodedPacket?.(packet, meta);
2152
- await this.muxer!.addEncodedAudioPacket(this.source._connectedTrack!, packet, meta); // With backpressure
2153
- }
2154
- }
2155
-
2156
- private ensureEncoder(audioSample: AudioSample) {
2157
- this.ensureEncoderPromise = (async () => {
2158
- const { numberOfChannels, sampleRate } = audioSample;
2159
-
2160
- // eslint-disable-next-line @typescript-eslint/no-deprecated
2161
- const quality = resolveQuality(this.encodingConfig.quality, this.encodingConfig.bitrate);
2162
-
2163
- const encoderConfig = buildAudioEncoderConfig({
2164
- numberOfChannels,
2165
- sampleRate,
2166
- ...this.encodingConfig,
2167
- quality,
2168
- });
2169
- this.encodingConfig.onEncoderConfig?.(encoderConfig);
2170
-
2171
- const MatchingCustomEncoder = customAudioEncoders.find(x => x.supports(
2172
- this.encodingConfig.codec,
2173
- encoderConfig,
2174
- ));
2175
-
2176
- if (MatchingCustomEncoder) {
2177
- // @ts-expect-error "Can't create instance of abstract class 🤓"
2178
- this.customEncoder = new MatchingCustomEncoder() as CustomAudioEncoder;
2179
- // @ts-expect-error It's technically readonly
2180
- this.customEncoder.codec = this.encodingConfig.codec;
2181
- // @ts-expect-error It's technically readonly
2182
- this.customEncoder.config = encoderConfig;
2183
- // @ts-expect-error It's technically readonly
2184
- this.customEncoder.onPacket = (packet, meta) => {
2185
- if (!(packet instanceof EncodedPacket)) {
2186
- throw new TypeError('The first argument passed to onPacket must be an EncodedPacket.');
2187
- }
2188
- if (meta !== undefined && (!meta || typeof meta !== 'object')) {
2189
- throw new TypeError('The second argument passed to onPacket must be an object or undefined.');
2190
- }
2191
-
2192
- this.encodingConfig.onEncodedPacket?.(packet, meta);
2193
- this.lastMuxerPromise
2194
- = this.muxer!.addEncodedAudioPacket(this.source._connectedTrack!, packet, meta)
2195
- .catch((error) => {
2196
- this.setError(error);
2197
- });
2198
- };
2199
- // @ts-expect-error It's technically readonly
2200
- this.customEncoder.onError = (error) => {
2201
- this.setError(error);
2202
- };
2203
-
2204
- await this.customEncoder.init();
2205
- } else if ((PCM_AUDIO_CODECS as readonly string[]).includes(this.encodingConfig.codec)) {
2206
- this.initPcmEncoder();
2207
- } else {
2208
- if (typeof AudioEncoder === 'undefined') {
2209
- throw new Error('AudioEncoder is not supported by this browser.');
2210
- }
2211
-
2212
- const support = await AudioEncoder.isConfigSupported(encoderConfig);
2213
- if (!support.supported) {
2214
- throw new Error(
2215
- `This specific encoder configuration (${encoderConfig.codec}, ${encoderConfig.bitrate} bps,`
2216
- + ` ${encoderConfig.numberOfChannels} channels, ${encoderConfig.sampleRate} Hz) is not`
2217
- + ` supported by this browser. Consider using another codec or changing your audio parameters.`,
2218
- );
2219
- }
2220
-
2221
- const stack = new Error('Encoding error').stack;
2222
-
2223
- this.encoder = new AudioEncoder({
2224
- output: (chunk, meta) => {
2225
- // WebKit emits an invalid description for AAC (https://bugs.webkit.org/show_bug.cgi?id=302253),
2226
- // which we try to detect here. If detected, we'll provide our own description instead, derived
2227
- // from the codec string and audio parameters.
2228
- if (this.encodingConfig.codec === 'aac' && meta?.decoderConfig) {
2229
- let needsDescriptionOverwrite = false;
2230
- if (!meta.decoderConfig.description || meta.decoderConfig.description.byteLength < 2) {
2231
- needsDescriptionOverwrite = true;
2232
- } else {
2233
- const audioSpecificConfig = parseAacAudioSpecificConfig(
2234
- toUint8Array(meta.decoderConfig.description),
2235
- );
2236
-
2237
- needsDescriptionOverwrite = audioSpecificConfig.objectType === 0;
2238
- }
2239
-
2240
- if (needsDescriptionOverwrite) {
2241
- const objectType = Number(last(encoderConfig.codec.split('.')));
2242
-
2243
- meta.decoderConfig.description = buildAacAudioSpecificConfig({
2244
- objectType,
2245
- numberOfChannels: meta.decoderConfig.numberOfChannels,
2246
- sampleRate: meta.decoderConfig.sampleRate,
2247
- });
2248
- }
2249
- }
2250
-
2251
- let packet = EncodedPacket.fromEncodedChunk(chunk);
2252
-
2253
- // Snap the timing information to the sample rate to recover information lost in microsecond
2254
- // conversion
2255
- packet = packet.clone({
2256
- timestamp: roundToDivisor(packet.timestamp, encoderConfig.sampleRate),
2257
- duration: chunk.duration != null
2258
- ? roundToDivisor(packet.duration, encoderConfig.sampleRate)
2259
- : undefined,
2260
- });
2261
-
2262
- this.encodingConfig.onEncodedPacket?.(packet, meta);
2263
- this.lastMuxerPromise
2264
- = this.muxer!.addEncodedAudioPacket(this.source._connectedTrack!, packet, meta)
2265
- .catch((error) => {
2266
- this.setError(error);
2267
- });
2268
- },
2269
- error: (error) => {
2270
- error.stack = stack; // Provide a more useful stack trace
2271
- this.setError(error);
2272
- },
2273
- });
2274
- this.encoder.configure(encoderConfig);
2275
- }
2276
-
2277
- assert(this.source._connectedTrack);
2278
- this.muxer = this.source._connectedTrack.output._muxer;
2279
-
2280
- this.encoderInitialized = true;
2281
- })();
2282
- }
2283
-
2284
- private initPcmEncoder() {
2285
- this.isPcmEncoder = true;
2286
-
2287
- const codec = this.encodingConfig.codec as PcmAudioCodec;
2288
- const { dataType, sampleSize, littleEndian } = parsePcmCodec(codec);
2289
-
2290
- this.outputSampleSize = sampleSize;
2291
-
2292
- // All these functions receive a float sample as input and map it into the desired format
2293
-
2294
- switch (sampleSize) {
2295
- case 1: {
2296
- if (dataType === 'unsigned') {
2297
- this.writeOutputValue = (view, byteOffset, value) =>
2298
- view.setUint8(byteOffset, clamp((value + 1) * 127.5, 0, 255));
2299
- } else if (dataType === 'signed') {
2300
- this.writeOutputValue = (view, byteOffset, value) => {
2301
- view.setInt8(byteOffset, clamp(Math.round(value * 128), -128, 127));
2302
- };
2303
- } else if (dataType === 'ulaw') {
2304
- this.writeOutputValue = (view, byteOffset, value) => {
2305
- const int16 = clamp(Math.floor(value * 32767), -32768, 32767);
2306
- view.setUint8(byteOffset, toUlaw(int16));
2307
- };
2308
- } else if (dataType === 'alaw') {
2309
- this.writeOutputValue = (view, byteOffset, value) => {
2310
- const int16 = clamp(Math.floor(value * 32767), -32768, 32767);
2311
- view.setUint8(byteOffset, toAlaw(int16));
2312
- };
2313
- } else {
2314
- assert(false);
2315
- }
2316
- }; break;
2317
- case 2: {
2318
- if (dataType === 'unsigned') {
2319
- this.writeOutputValue = (view, byteOffset, value) =>
2320
- view.setUint16(byteOffset, clamp((value + 1) * 32767.5, 0, 65535), littleEndian);
2321
- } else if (dataType === 'signed') {
2322
- this.writeOutputValue = (view, byteOffset, value) =>
2323
- view.setInt16(byteOffset, clamp(Math.round(value * 32767), -32768, 32767), littleEndian);
2324
- } else {
2325
- assert(false);
2326
- }
2327
- }; break;
2328
- case 3: {
2329
- if (dataType === 'unsigned') {
2330
- this.writeOutputValue = (view, byteOffset, value) =>
2331
- setUint24(view, byteOffset, clamp((value + 1) * 8388607.5, 0, 16777215), littleEndian);
2332
- } else if (dataType === 'signed') {
2333
- this.writeOutputValue = (view, byteOffset, value) =>
2334
- setInt24(
2335
- view,
2336
- byteOffset,
2337
- clamp(Math.round(value * 8388607), -8388608, 8388607),
2338
- littleEndian,
2339
- );
2340
- } else {
2341
- assert(false);
2342
- }
2343
- }; break;
2344
- case 4: {
2345
- if (dataType === 'unsigned') {
2346
- this.writeOutputValue = (view, byteOffset, value) =>
2347
- view.setUint32(byteOffset, clamp((value + 1) * 2147483647.5, 0, 4294967295), littleEndian);
2348
- } else if (dataType === 'signed') {
2349
- this.writeOutputValue = (view, byteOffset, value) =>
2350
- view.setInt32(
2351
- byteOffset,
2352
- clamp(Math.round(value * 2147483647), -2147483648, 2147483647),
2353
- littleEndian,
2354
- );
2355
- } else if (dataType === 'float') {
2356
- this.writeOutputValue = (view, byteOffset, value) =>
2357
- view.setFloat32(byteOffset, value, littleEndian);
2358
- } else {
2359
- assert(false);
2360
- }
2361
- }; break;
2362
- case 8: {
2363
- if (dataType === 'float') {
2364
- this.writeOutputValue = (view, byteOffset, value) =>
2365
- view.setFloat64(byteOffset, value, littleEndian);
2366
- } else {
2367
- assert(false);
2368
- }
2369
- }; break;
2370
- default: {
2371
- assertNever(sampleSize);
2372
- assert(false);
2373
- };
2374
- }
2375
- }
2376
-
2377
- async flushAndClose(forceClose: boolean) {
2378
- try {
2379
- if (!forceClose) {
2380
- this.checkForEncoderError();
2381
-
2382
- // Finalize the resampler to flush any buffered audio
2383
- if (this.resampler) {
2384
- await this.resampler.finalize();
2385
- }
2386
- }
2387
-
2388
- this.closed = true;
2389
-
2390
- if (!forceClose) {
2391
- if (this.customEncoder) {
2392
- void this.customEncoderCallSerializer.call(() => this.customEncoder!.flush());
2393
- } else if (this.encoder) {
2394
- await this.encoder.flush();
2395
- }
2396
- }
2397
- } finally {
2398
- // This cleanup must also run when flushing threw (e.g. due to an encoder error), otherwise the encoder
2399
- // would be left dangling
2400
- this.closed = true;
2401
- this.resampler = null;
2402
-
2403
- if (this.customEncoder) {
2404
- await this.customEncoderCallSerializer.call(() => this.customEncoder!.close())
2405
- .catch((error: unknown) => this.setError(error));
2406
- } else if (this.encoder && this.encoder.state !== 'closed') {
2407
- this.encoder.close();
2408
- }
2409
- }
2410
-
2411
- if (!forceClose) {
2412
- this.checkForEncoderError();
2413
- }
2414
- }
2415
-
2416
- getQueueSize() {
2417
- if (this.customEncoder) {
2418
- return this.customEncoderQueueSize;
2419
- } else if (this.isPcmEncoder) {
2420
- return 0;
2421
- } else {
2422
- return this.encoder?.encodeQueueSize ?? 0;
2423
- }
2424
- }
2425
-
2426
- checkForEncoderError() {
2427
- if (this.errorSet) {
2428
- throw this.error;
2429
- }
2430
- }
2431
- }
2432
-
2433
- /**
2434
- * This source can be used to add raw, unencoded audio samples to an output audio track. These samples will
2435
- * automatically be encoded and then piped into the output.
2436
- * @group Media sources
2437
- * @public
2438
- */
2439
- export class AudioSampleSource extends AudioSource {
2440
- /** @internal */
2441
- private _encoder: AudioEncoderWrapper;
2442
-
2443
- /**
2444
- * Creates a new {@link AudioSampleSource} whose samples are encoded according to the specified
2445
- * {@link AudioEncodingConfig}.
2446
- */
2447
- constructor(encodingConfig: AudioEncodingConfig) {
2448
- validateAudioEncodingConfig(encodingConfig);
2449
-
2450
- super(encodingConfig.codec);
2451
- this._encoder = new AudioEncoderWrapper(this, encodingConfig);
2452
- }
2453
-
2454
- /**
2455
- * Encodes an audio sample and then adds it to the output.
2456
- *
2457
- * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
2458
- * to respect writer and encoder backpressure.
2459
- */
2460
- add(audioSample: AudioSample) {
2461
- if (!(audioSample instanceof AudioSample)) {
2462
- throw new TypeError('audioSample must be an AudioSample.');
2463
- }
2464
-
2465
- return this._encoder.add(audioSample, false);
2466
- }
2467
-
2468
- /** @internal */
2469
- override _flushAndClose(forceClose: boolean) {
2470
- return this._encoder.flushAndClose(forceClose);
2471
- }
2472
- }
2473
-
2474
- /**
2475
- * This source can be used to add audio data from an AudioBuffer to the output track. This is useful when working with
2476
- * the Web Audio API.
2477
- * @group Media sources
2478
- * @public
2479
- */
2480
- export class AudioBufferSource extends AudioSource {
2481
- /** @internal */
2482
- private _encoder: AudioEncoderWrapper;
2483
- /** @internal */
2484
- private _accumulatedTime = 0;
2485
-
2486
- /**
2487
- * Creates a new {@link AudioBufferSource} whose `AudioBuffer` instances are encoded according to the specified
2488
- * {@link AudioEncodingConfig}.
2489
- */
2490
- constructor(encodingConfig: AudioEncodingConfig) {
2491
- validateAudioEncodingConfig(encodingConfig);
2492
-
2493
- super(encodingConfig.codec);
2494
- this._encoder = new AudioEncoderWrapper(this, encodingConfig);
2495
- }
2496
-
2497
- /**
2498
- * Converts an AudioBuffer to audio samples, encodes them and adds them to the output. The first AudioBuffer will
2499
- * be played at timestamp 0, and any subsequent AudioBuffer will have a timestamp equal to the total duration of
2500
- * all previous AudioBuffers.
2501
- *
2502
- * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
2503
- * to respect writer and encoder backpressure.
2504
- */
2505
- async add(audioBuffer: AudioBuffer) {
2506
- if (!(audioBuffer instanceof AudioBuffer)) {
2507
- throw new TypeError('audioBuffer must be an AudioBuffer.');
2508
- }
2509
-
2510
- const iterator = AudioSample._fromAudioBuffer(audioBuffer, this._accumulatedTime);
2511
- this._accumulatedTime += audioBuffer.duration;
2512
-
2513
- for (const audioSample of iterator) {
2514
- await this._encoder.add(audioSample, true);
2515
- }
2516
- }
2517
-
2518
- /** @internal */
2519
- override _flushAndClose(forceClose: boolean) {
2520
- return this._encoder.flushAndClose(forceClose);
2521
- }
2522
- }
2523
-
2524
- /**
2525
- * Options for {@link MediaStreamAudioTrackSource}.
2526
- * @group Media sources
2527
- * @public
2528
- */
2529
- export type MediaStreamAudioTrackSourceOptions = {
2530
- /**
2531
- * Controls the basis (zero point) for audio sample timestamps.
2532
- *
2533
- * When set to `'synced-zero'`, timestamps will be relative to the first chunk of media from a `MediaStreamTrack`
2534
- * added to the {@link Output}.
2535
- *
2536
- * When set to `'zero'`, timestamps will be relative to the first audio sample emitted by this source.
2537
- *
2538
- * When set to `'unix'`, timestamps will be relative to the Unix epoch, so clearly associated with a distinct point
2539
- * in time. Here, pausing via {@link MediaStreamAudioTrackSource.pause} will also create gaps in timestamps. Be sure
2540
- * to pair this mode with {@link BaseTrackMetadata.isRelativeToUnixEpoch}.
2541
- *
2542
- * Defaults to `'synced-zero'`.
2543
- */
2544
- timestampBase?: 'synced-zero' | 'zero' | 'unix';
2545
- };
2546
-
2547
- /**
2548
- * Audio source that encodes the data of a
2549
- * [`MediaStreamAudioTrack`](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) and pipes it into the
2550
- * output. This is useful for capturing live or real-time audio such as microphones or audio from other media elements.
2551
- * Audio will automatically start being captured once the connected {@link Output} is started, and will keep being
2552
- * captured until the {@link Output} is finalized or this source is closed.
2553
- * @group Media sources
2554
- * @public
2555
- */
2556
- export class MediaStreamAudioTrackSource extends AudioSource {
2557
- /** @internal */
2558
- private _options: MediaStreamAudioTrackSourceOptions;
2559
- /** @internal */
2560
- private _encoder: AudioEncoderWrapper;
2561
- /** @internal */
2562
- private _abortController: AbortController | null = null;
2563
- /** @internal */
2564
- private _track: MediaStreamAudioTrack;
2565
- /** @internal */
2566
- private _audioContext: AudioContext | null = null;
2567
- /** @internal */
2568
- // eslint-disable-next-line @typescript-eslint/no-deprecated
2569
- private _scriptProcessorNode: ScriptProcessorNode | null = null; // Deprecated but goated
2570
- /** @internal */
2571
- private _promiseWithResolvers = promiseWithResolvers();
2572
- /** @internal */
2573
- private _errorPromiseAccessed = false;
2574
- /** @internal */
2575
- private _paused = false;
2576
-
2577
- /** A promise that rejects upon any error within this source. This promise never resolves. */
2578
- get errorPromise() {
2579
- this._errorPromiseAccessed = true;
2580
- return this._promiseWithResolvers.promise;
2581
- }
2582
-
2583
- /** Whether this source is currently paused as a result of calling `.pause()`. */
2584
- get paused() {
2585
- return this._paused;
2586
- }
2587
-
2588
- /**
2589
- * Creates a new {@link MediaStreamAudioTrackSource} from a `MediaStreamAudioTrack`, which will pull audio samples
2590
- * from the stream in real time and encode them according to {@link AudioEncodingConfig}.
2591
- */
2592
- constructor(
2593
- track: MediaStreamAudioTrack,
2594
- encodingConfig: AudioEncodingConfig,
2595
- options: MediaStreamAudioTrackSourceOptions = {},
2596
- ) {
2597
- if (!(track instanceof MediaStreamTrack) || track.kind !== 'audio') {
2598
- throw new TypeError('track must be an audio MediaStreamTrack.');
2599
- }
2600
- validateAudioEncodingConfig(encodingConfig);
2601
- if (typeof options !== 'object' || !options) {
2602
- throw new TypeError('options must be an object.');
2603
- }
2604
- if (
2605
- options.timestampBase !== undefined
2606
- && options.timestampBase !== 'synced-zero'
2607
- && options.timestampBase !== 'zero'
2608
- && options.timestampBase !== 'unix'
2609
- ) {
2610
- throw new TypeError(
2611
- 'options.timestampBase, when provided, must be one of \'synced-zero\', \'zero\', or \'unix\'.',
2612
- );
2613
- }
2614
-
2615
- super(encodingConfig.codec);
2616
- this._options = options;
2617
- this._encoder = new AudioEncoderWrapper(this, encodingConfig);
2618
- this._track = track;
2619
- }
2620
-
2621
- /** @internal */
2622
- override async _start() {
2623
- if (!this._errorPromiseAccessed) {
2624
- Logging._warn(
2625
- 'Make sure not to ignore the `errorPromise` field on MediaStreamAudioTrackSource, so that any internal'
2626
- + ' errors get bubbled up properly.',
2627
- );
2628
- }
2629
-
2630
- this._abortController = new AbortController();
2631
-
2632
- let firstAudioDataTimestamp: number | null = null;
2633
- let errored = false;
2634
- let lastSampleTimestamp: number | null = null;
2635
- let timestampOffset = 0;
2636
-
2637
- const onAudioSample = (audioSample: AudioSample) => {
2638
- if (errored) {
2639
- audioSample.close();
2640
- return;
2641
- }
2642
-
2643
- const currentTimestamp = audioSample.timestamp;
2644
-
2645
- if (this._paused) {
2646
- const dataSeen = firstAudioDataTimestamp !== null;
2647
- if (dataSeen) {
2648
- if (lastSampleTimestamp !== null && this._options.timestampBase !== 'unix') {
2649
- // In addition to dropping this sample, let's also keep track of the time we have lost due to
2650
- // the pause. Doing it like this instead of simply keeping track of the paused time is better
2651
- // since it retains the sample rate of the underlying source.
2652
- const timeDelta = currentTimestamp - lastSampleTimestamp;
2653
- timestampOffset -= timeDelta;
2654
- }
2655
- lastSampleTimestamp = currentTimestamp;
2656
- }
2657
-
2658
- audioSample.close();
2659
- return;
2660
- }
2661
-
2662
- if (firstAudioDataTimestamp === null) {
2663
- firstAudioDataTimestamp = audioSample.timestamp;
2664
-
2665
- let target: number;
2666
- const timestampBase = this._options.timestampBase ?? 'synced-zero';
2667
- if (timestampBase === 'unix') {
2668
- target = Date.now() / 1000;
2669
- } else if (timestampBase === 'zero') {
2670
- target = 0;
2671
- } else {
2672
- const output = this._connectedTrack!.output;
2673
- if (output._firstMediaStreamTimestamp === null) {
2674
- output._firstMediaStreamTimestamp = performance.now() / 1000;
2675
- target = 0;
2676
- } else {
2677
- target = performance.now() / 1000 - output._firstMediaStreamTimestamp;
2678
- }
2679
- }
2680
-
2681
- timestampOffset = target - firstAudioDataTimestamp;
2682
- }
2683
-
2684
- lastSampleTimestamp = currentTimestamp;
2685
-
2686
- if (this._encoder.getQueueSize() >= 8) {
2687
- // Drop data if the encoder is overloaded
2688
- audioSample.close();
2689
- return;
2690
- }
2691
-
2692
- audioSample.setTimestamp(currentTimestamp + timestampOffset);
2693
-
2694
- void this._encoder.add(audioSample, true)
2695
- .catch((error) => {
2696
- errored = true;
2697
-
2698
- this._abortController?.abort();
2699
- this._promiseWithResolvers.reject(error);
2700
- void this._audioContext?.suspend();
2701
- });
2702
- };
2703
-
2704
- if (typeof MediaStreamTrackProcessor !== 'undefined') {
2705
- // Great, MediaStreamTrackProcessor is supported, this is the preferred way of doing things
2706
- const processor = new MediaStreamTrackProcessor({ track: this._track });
2707
- const consumer = new WritableStream<AudioData>({
2708
- write: audioData => onAudioSample(new AudioSample(audioData)),
2709
- });
2710
-
2711
- processor.readable.pipeTo(consumer, {
2712
- signal: this._abortController.signal,
2713
- }).catch((error) => {
2714
- // Handle AbortError silently
2715
- if (error instanceof DOMException && error.name === 'AbortError') return;
2716
-
2717
- this._promiseWithResolvers.reject(error);
2718
- });
2719
- } else {
2720
- // Let's fall back to an AudioContext approach
2721
-
2722
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
2723
- const AudioContext = window.AudioContext || (window as any).webkitAudioContext;
2724
-
2725
- this._audioContext = new AudioContext({ sampleRate: this._track.getSettings().sampleRate });
2726
- const sourceNode = this._audioContext.createMediaStreamSource(new MediaStream([this._track]));
2727
- // eslint-disable-next-line @typescript-eslint/no-deprecated
2728
- this._scriptProcessorNode = this._audioContext.createScriptProcessor(4096);
2729
-
2730
- if (this._audioContext.state === 'suspended') {
2731
- await this._audioContext.resume();
2732
- }
2733
-
2734
- sourceNode.connect(this._scriptProcessorNode);
2735
- this._scriptProcessorNode.connect(this._audioContext.destination);
2736
-
2737
- let totalDuration = 0;
2738
-
2739
- // eslint-disable-next-line @typescript-eslint/no-deprecated
2740
- this._scriptProcessorNode.onaudioprocess = (event) => {
2741
- // eslint-disable-next-line @typescript-eslint/no-deprecated
2742
- const iterator = AudioSample._fromAudioBuffer(event.inputBuffer, totalDuration);
2743
- // eslint-disable-next-line @typescript-eslint/no-deprecated
2744
- totalDuration += event.inputBuffer.duration;
2745
-
2746
- for (const audioSample of iterator) {
2747
- onAudioSample(audioSample);
2748
- }
2749
- };
2750
- }
2751
- }
2752
-
2753
- /**
2754
- * Pauses the capture of audio data - any audio data emitted by the underlying media stream will be ignored
2755
- * while paused. This does *not* close the underlying `MediaStreamAudioTrack`, it just ignores its output.
2756
- */
2757
- pause() {
2758
- this._paused = true;
2759
- }
2760
-
2761
- /** Resumes the capture of audio data after being paused. */
2762
- resume() {
2763
- this._paused = false;
2764
- }
2765
-
2766
- /** @internal */
2767
- override async _flushAndClose(forceClose: boolean) {
2768
- if (this._abortController) {
2769
- this._abortController.abort();
2770
- this._abortController = null;
2771
- }
2772
-
2773
- if (this._audioContext) {
2774
- assert(this._scriptProcessorNode);
2775
-
2776
- this._scriptProcessorNode.disconnect();
2777
- await this._audioContext.suspend();
2778
- }
2779
-
2780
- await this._encoder.flushAndClose(forceClose);
2781
- }
2782
- }
2783
-
2784
- // === MEDIA STREAM TRACK PROCESSOR WORKER ===
2785
-
2786
- type MediaStreamTrackProcessorWorkerMessage = {
2787
- type: 'support';
2788
- supported: boolean;
2789
- } | {
2790
- type: 'videoFrame';
2791
- trackId: number;
2792
- videoFrame: VideoFrame;
2793
- } | {
2794
- type: 'trackStopped';
2795
- trackId: number;
2796
- } | {
2797
- type: 'error';
2798
- trackId: number;
2799
- error: unknown;
2800
- };
2801
-
2802
- type MediaStreamTrackProcessorControllerMessage = {
2803
- type: 'videoTrack';
2804
- trackId: number;
2805
- track: MediaStreamVideoTrack;
2806
- } | {
2807
- type: 'stopTrack';
2808
- trackId: number;
2809
- };
2810
-
2811
- const mediaStreamTrackProcessorWorkerCode = () => {
2812
- const sendMessage = (message: MediaStreamTrackProcessorWorkerMessage, transfer?: Transferable[]) => {
2813
- if (transfer) {
2814
- self.postMessage(message, { transfer });
2815
- } else {
2816
- self.postMessage(message);
2817
- }
2818
- };
2819
-
2820
- // Immediately send a message to the main thread, letting them know of the support
2821
- sendMessage({
2822
- type: 'support',
2823
- supported: typeof MediaStreamTrackProcessor !== 'undefined',
2824
- });
2825
-
2826
- const abortControllers = new Map<number, AbortController>();
2827
- const activeTracks = new Map<number, MediaStreamVideoTrack>();
2828
-
2829
- self.addEventListener('message', (event) => {
2830
- const message = event.data as MediaStreamTrackProcessorControllerMessage;
2831
-
2832
- switch (message.type) {
2833
- case 'videoTrack': {
2834
- activeTracks.set(message.trackId, message.track);
2835
-
2836
- const processor = new MediaStreamTrackProcessor({ track: message.track });
2837
- const consumer = new WritableStream<VideoFrame>({
2838
- write: (videoFrame) => {
2839
- if (!activeTracks.has(message.trackId)) {
2840
- videoFrame.close();
2841
- return;
2842
- }
2843
-
2844
- // Send it to the main thread
2845
- sendMessage({
2846
- type: 'videoFrame',
2847
- trackId: message.trackId,
2848
- videoFrame,
2849
- }, [videoFrame]);
2850
- },
2851
- });
2852
-
2853
- const abortController = new AbortController();
2854
- abortControllers.set(message.trackId, abortController);
2855
-
2856
- processor.readable.pipeTo(consumer, {
2857
- signal: abortController.signal,
2858
- }).catch((error: unknown) => {
2859
- // Handle AbortError silently
2860
- if (error instanceof DOMException && error.name === 'AbortError') return;
2861
-
2862
- sendMessage({
2863
- type: 'error',
2864
- trackId: message.trackId,
2865
- error,
2866
- });
2867
- });
2868
- }; break;
2869
-
2870
- case 'stopTrack': {
2871
- const abortController = abortControllers.get(message.trackId);
2872
- if (abortController) {
2873
- abortController.abort();
2874
- abortControllers.delete(message.trackId);
2875
- }
2876
-
2877
- const track = activeTracks.get(message.trackId);
2878
- track?.stop();
2879
- activeTracks.delete(message.trackId);
2880
-
2881
- sendMessage({
2882
- type: 'trackStopped',
2883
- trackId: message.trackId,
2884
- });
2885
- }; break;
2886
-
2887
- default: assertNever(message);
2888
- }
2889
- });
2890
- };
2891
-
2892
- let nextMediaStreamTrackProcessorWorkerId = 0;
2893
- let mediaStreamTrackProcessorWorker: Worker | null = null;
2894
-
2895
- const initMediaStreamTrackProcessorWorker = () => {
2896
- const blob = new Blob(
2897
- [`(${mediaStreamTrackProcessorWorkerCode.toString()})()`],
2898
- { type: 'application/javascript' },
2899
- );
2900
- const url = URL.createObjectURL(blob);
2901
-
2902
- mediaStreamTrackProcessorWorker = new Worker(url);
2903
- };
2904
-
2905
- let mediaStreamTrackProcessorIsSupportedInWorkerCache: boolean | null = null;
2906
- const mediaStreamTrackProcessorIsSupportedInWorker = async () => {
2907
- if (mediaStreamTrackProcessorIsSupportedInWorkerCache !== null) {
2908
- return mediaStreamTrackProcessorIsSupportedInWorkerCache;
2909
- }
2910
-
2911
- if (!mediaStreamTrackProcessorWorker) {
2912
- initMediaStreamTrackProcessorWorker();
2913
- }
2914
-
2915
- return new Promise<boolean>((resolve) => {
2916
- assert(mediaStreamTrackProcessorWorker);
2917
-
2918
- const listener = (event: MessageEvent) => {
2919
- const message = event.data as MediaStreamTrackProcessorWorkerMessage;
2920
-
2921
- if (message.type === 'support') {
2922
- mediaStreamTrackProcessorIsSupportedInWorkerCache = message.supported;
2923
- mediaStreamTrackProcessorWorker!.removeEventListener('message', listener);
2924
-
2925
- resolve(message.supported);
2926
- }
2927
- };
2928
-
2929
- mediaStreamTrackProcessorWorker.addEventListener('message', listener);
2930
- });
2931
- };
2932
-
2933
- const sendMessageToMediaStreamTrackProcessorWorker = (
2934
- message: MediaStreamTrackProcessorControllerMessage,
2935
- transfer?: Transferable[],
2936
- ) => {
2937
- assert(mediaStreamTrackProcessorWorker);
2938
-
2939
- if (transfer) {
2940
- mediaStreamTrackProcessorWorker.postMessage(message, transfer);
2941
- } else {
2942
- mediaStreamTrackProcessorWorker.postMessage(message);
2943
- }
2944
- };
2945
-
2946
- /**
2947
- * Base class for subtitle sources - sources for subtitle tracks.
2948
- * @group Media sources
2949
- * @public
2950
- */
2951
- export abstract class SubtitleSource extends MediaSource {
2952
- /** @internal */
2953
- override _connectedTrack: OutputSubtitleTrack | null = null;
2954
- /** @internal */
2955
- override readonly _codec: SubtitleCodec;
2956
-
2957
- /** Internal constructor. */
2958
- constructor(codec: SubtitleCodec) {
2959
- super();
2960
-
2961
- if (!SUBTITLE_CODECS.includes(codec)) {
2962
- throw new TypeError(`Invalid subtitle codec '${codec}'. Must be one of: ${SUBTITLE_CODECS.join(', ')}.`);
2963
- }
2964
-
2965
- this._codec = codec;
2966
- }
2967
- }
2968
-
2969
- /**
2970
- * This source can be used to add subtitles from a subtitle text file.
2971
- * @group Media sources
2972
- * @public
2973
- */
2974
- export class TextSubtitleSource extends SubtitleSource {
2975
- /** @internal */
2976
- private _parser: SubtitleParser;
2977
- /** @internal */
2978
- private _error: unknown = null;
2979
- /** @internal */
2980
- private _errorSet = false;
2981
- /** @internal */
2982
- private _lastMuxerPromise: Promise<void> = Promise.resolve();
2983
-
2984
- /** Creates a new {@link TextSubtitleSource} where added text chunks are in the specified `codec`. */
2985
- constructor(codec: SubtitleCodec) {
2986
- super(codec);
2987
-
2988
- this._parser = new SubtitleParser({
2989
- codec,
2990
- output: (cue, metadata) => {
2991
- this._lastMuxerPromise
2992
- = this._connectedTrack!.output._muxer.addSubtitleCue(this._connectedTrack!, cue, metadata)
2993
- .catch((error) => {
2994
- this._setError(error);
2995
- });
2996
- },
2997
- });
2998
- }
2999
-
3000
- /**
3001
- * Parses the subtitle text according to the specified codec and adds it to the output track. You don't have to
3002
- * add the entire subtitle file at once here; you can provide it in chunks.
3003
- *
3004
- * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
3005
- * to respect writer and encoder backpressure.
3006
- */
3007
- add(text: string) {
3008
- if (typeof text !== 'string') {
3009
- throw new TypeError('text must be a string.');
3010
- }
3011
-
3012
- this._checkForError();
3013
-
3014
- this._ensureValidAdd();
3015
- this._parser.parse(text);
3016
-
3017
- return this._lastMuxerPromise; // Allow the writer to apply backpressure
3018
- }
3019
-
3020
- /** @internal */
3021
- private _setError(error: unknown) {
3022
- if (!this._errorSet) {
3023
- this._error = error;
3024
- this._errorSet = true;
3025
- }
3026
- }
3027
-
3028
- /** @internal */
3029
- _checkForError() {
3030
- if (this._errorSet) {
3031
- throw this._error;
3032
- }
3033
- }
3034
-
3035
- /** @internal */
3036
- override async _flushAndClose(forceClose: boolean) {
3037
- if (!forceClose) {
3038
- this._checkForError();
3039
- }
3040
- }
3041
- }