@osmo.inc/cli 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEPLOY.md +87 -0
- package/README.md +243 -0
- package/RELEASE.md +164 -0
- package/SETUP.md +60 -0
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/bin/osmo.mjs +12 -0
- package/dist/osmo.js +7817 -0
- package/node_modules/@esbuild/darwin-arm64/README.md +3 -0
- package/node_modules/@esbuild/darwin-arm64/bin/esbuild +0 -0
- package/node_modules/@esbuild/darwin-arm64/package.json +20 -0
- package/node_modules/esbuild/LICENSE.md +21 -0
- package/node_modules/esbuild/README.md +3 -0
- package/node_modules/esbuild/bin/esbuild +223 -0
- package/node_modules/esbuild/install.js +289 -0
- package/node_modules/esbuild/lib/main.d.ts +716 -0
- package/node_modules/esbuild/lib/main.js +2532 -0
- package/node_modules/esbuild/package.json +49 -0
- package/node_modules/fflate/LICENSE +21 -0
- package/node_modules/fflate/esm/index.mjs +2710 -0
- package/node_modules/fflate/package.json +128 -0
- package/node_modules/typescript/LICENSE.txt +55 -0
- package/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
- package/node_modules/typescript/lib/lib.d.ts +22 -0
- package/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
- package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
- package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
- package/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
- package/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
- package/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
- package/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
- package/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
- package/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
- package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
- package/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
- package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
- package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
- package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
- package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
- package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
- package/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
- package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
- package/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
- package/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
- package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
- package/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
- package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
- package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
- package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
- package/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
- package/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
- package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
- package/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
- package/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
- package/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
- package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
- package/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
- package/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
- package/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
- package/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
- package/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
- package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
- package/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
- package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
- package/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
- package/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
- package/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
- package/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
- package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
- package/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
- package/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
- package/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
- package/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
- package/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
- package/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
- package/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
- package/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
- package/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
- package/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
- package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
- package/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
- package/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
- package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
- package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
- package/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
- package/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
- package/node_modules/typescript/lib/lib.es6.d.ts +23 -0
- package/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
- package/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
- package/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
- package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
- package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
- package/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
- package/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
- package/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
- package/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
- package/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
- package/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
- package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
- package/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
- package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
- package/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
- package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
- package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
- package/node_modules/typescript/lib/typescript.js +382 -0
- package/node_modules/typescript/package.json +57 -0
- package/package.json +54 -0
- package/release.json +136 -0
- package/runtime/apps/native/tools/compile.mjs +217 -0
- package/runtime/packages/scene/scripts/validate-strict.mjs +246 -0
- package/runtime/packages/scene/src/clip.d.mts +14 -0
- package/runtime/packages/scene/src/clip.mjs +15 -0
- package/runtime/packages/scene/src/color-value.d.mts +5 -0
- package/runtime/packages/scene/src/color-value.mjs +19 -0
- package/runtime/packages/scene/src/color.ts +1 -0
- package/runtime/packages/scene/src/effects.ts +213 -0
- package/runtime/packages/scene/src/elements.ts +491 -0
- package/runtime/packages/scene/src/evaluate.ts +919 -0
- package/runtime/packages/scene/src/index.ts +69 -0
- package/runtime/packages/scene/src/jsx-runtime.ts +73 -0
- package/runtime/packages/scene/src/knob-schema.ts +115 -0
- package/runtime/packages/scene/src/knobs.ts +118 -0
- package/runtime/packages/scene/src/mask.d.mts +6 -0
- package/runtime/packages/scene/src/mask.mjs +17 -0
- package/runtime/packages/scene/src/metadata.d.mts +23 -0
- package/runtime/packages/scene/src/metadata.mjs +44 -0
- package/runtime/packages/scene/src/motion-sampling.d.mts +2 -0
- package/runtime/packages/scene/src/motion-sampling.mjs +50 -0
- package/runtime/packages/scene/src/motion.ts +121 -0
- package/runtime/packages/scene/src/shader.ts +57 -0
- package/src/resource-examples/example-image/landscape.svg +7 -0
- package/src/resource-examples/example-image/resource.json +8 -0
- package/src/resource-examples/example-model/gem.glb +0 -0
- package/src/resource-examples/example-model/preview.svg +1 -0
- package/src/resource-examples/example-model/resource.json +7 -0
- package/src/resource-examples/example-scene/resource.json +1 -0
- package/src/resource-examples/example-scene/scene.tsx +8 -0
- package/src/resource-examples/example-shader/effect.wgsl +7 -0
- package/src/resource-examples/example-shader/preview.svg +1 -0
- package/src/resource-examples/example-shader/resource.json +27 -0
- package/src/resource-examples/example-shader/tint.wgsl +7 -0
- package/src/resource-examples/example-video/ambient.mp4 +0 -0
- package/src/resource-examples/example-video/resource.json +9 -0
- package/src/starter/scene.tsx +20 -0
- package/studio/index.html +2 -0
- package/studio/public/check-circle-filled.svg +3 -0
- package/studio/public/comment-cursor/active-outer.svg +3 -0
- package/studio/public/comment-cursor/default.svg +6 -0
- package/studio/public/osmo-studio-logo.svg +12 -0
- package/studio/public/pencil-simple.svg +5 -0
- package/studio/src/active-outer-TVYUEURE.svg +3 -0
- package/studio/src/app.css +4 -0
- package/studio/src/app.js +705 -0
- package/studio/src/default-6CTRH4V5.svg +6 -0
- package/studio/src/engine_bg.wasm +0 -0
- package/studio/src/inter-cyrillic-ext-wght-normal-IYF56FF6.woff2 +0 -0
- package/studio/src/inter-cyrillic-wght-normal-JEOLYBOO.woff2 +0 -0
- package/studio/src/inter-greek-ext-wght-normal-EOVOK2B5.woff2 +0 -0
- package/studio/src/inter-greek-wght-normal-IRE366VL.woff2 +0 -0
- package/studio/src/inter-latin-ext-wght-normal-HA22NDSG.woff2 +0 -0
- package/studio/src/inter-latin-wght-normal-NRMW37G5.woff2 +0 -0
- package/studio/src/inter-vietnamese-wght-normal-CE5GGD3W.woff2 +0 -0
- package/studio/src/osmo-studio-logo-D5R2V77B.svg +12 -0
- package/third-party/cargo/adler2@2.0.1/Cargo.toml +91 -0
- package/third-party/cargo/adler2@2.0.1/LICENSE-0BSD +12 -0
- package/third-party/cargo/adler2@2.0.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/adler2@2.0.1/LICENSE-MIT +23 -0
- package/third-party/cargo/allocator-api2@0.2.21/Cargo.toml +48 -0
- package/third-party/cargo/allocator-api2@0.2.21/LICENSE-APACHE +176 -0
- package/third-party/cargo/allocator-api2@0.2.21/LICENSE-MIT +23 -0
- package/third-party/cargo/arrayref@0.3.9/Cargo.toml +23 -0
- package/third-party/cargo/arrayref@0.3.9/LICENSE +26 -0
- package/third-party/cargo/arrayvec@0.7.8/Cargo.toml +112 -0
- package/third-party/cargo/arrayvec@0.7.8/LICENSE-APACHE +201 -0
- package/third-party/cargo/arrayvec@0.7.8/LICENSE-MIT +25 -0
- package/third-party/cargo/ascii@1.1.0/Cargo.toml +41 -0
- package/third-party/cargo/ascii@1.1.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/ascii@1.1.0/LICENSE-MIT +22 -0
- package/third-party/cargo/ash@0.38.0+1.3.281/Cargo.toml +66 -0
- package/third-party/cargo/ash@0.38.0+1.3.281/LICENSE-APACHE +67 -0
- package/third-party/cargo/ash@0.38.0+1.3.281/LICENSE-MIT +25 -0
- package/third-party/cargo/autocfg@1.5.1/Cargo.toml +75 -0
- package/third-party/cargo/autocfg@1.5.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/autocfg@1.5.1/LICENSE-MIT +25 -0
- package/third-party/cargo/base64@0.13.1/Cargo.toml +53 -0
- package/third-party/cargo/base64@0.13.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/base64@0.13.1/LICENSE-MIT +21 -0
- package/third-party/cargo/base64@0.22.1/Cargo.toml +85 -0
- package/third-party/cargo/base64@0.22.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/base64@0.22.1/LICENSE-MIT +21 -0
- package/third-party/cargo/bit-set@0.10.0/Cargo.toml +108 -0
- package/third-party/cargo/bit-set@0.10.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/bit-set@0.10.0/LICENSE-MIT +25 -0
- package/third-party/cargo/bit-vec@0.9.1/Cargo.toml +94 -0
- package/third-party/cargo/bit-vec@0.9.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/bit-vec@0.9.1/LICENSE-MIT +25 -0
- package/third-party/cargo/bitflags@2.13.0/Cargo.toml +120 -0
- package/third-party/cargo/bitflags@2.13.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/bitflags@2.13.0/LICENSE-MIT +25 -0
- package/third-party/cargo/block2@0.6.2/Cargo.toml +115 -0
- package/third-party/cargo/block2@0.6.2/MIT-terms.txt +22 -0
- package/third-party/cargo/block2@0.6.2/madsmtm-objc2-b4167b582b2f-LICENSE.md +34 -0
- package/third-party/cargo/bumpalo@3.20.3/Cargo.toml +99 -0
- package/third-party/cargo/bumpalo@3.20.3/LICENSE-APACHE +201 -0
- package/third-party/cargo/bumpalo@3.20.3/LICENSE-MIT +25 -0
- package/third-party/cargo/bytemuck@1.25.1/Cargo.toml +153 -0
- package/third-party/cargo/bytemuck@1.25.1/LICENSE-APACHE +61 -0
- package/third-party/cargo/bytemuck@1.25.1/LICENSE-MIT +9 -0
- package/third-party/cargo/bytemuck@1.25.1/LICENSE-ZLIB +11 -0
- package/third-party/cargo/bytemuck_derive@1.11.0/Cargo.toml +60 -0
- package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-APACHE +61 -0
- package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-MIT +9 -0
- package/third-party/cargo/bytemuck_derive@1.11.0/LICENSE-ZLIB +11 -0
- package/third-party/cargo/byteorder-lite@0.1.0/Cargo.toml +57 -0
- package/third-party/cargo/byteorder-lite@0.1.0/LICENSE-MIT +21 -0
- package/third-party/cargo/byteorder-lite@0.1.0/UNLICENSE +24 -0
- package/third-party/cargo/byteorder@1.5.0/COPYING +3 -0
- package/third-party/cargo/byteorder@1.5.0/Cargo.toml +54 -0
- package/third-party/cargo/byteorder@1.5.0/LICENSE-MIT +21 -0
- package/third-party/cargo/byteorder@1.5.0/UNLICENSE +24 -0
- package/third-party/cargo/cfg-if@1.0.4/Cargo.toml +47 -0
- package/third-party/cargo/cfg-if@1.0.4/LICENSE-APACHE +201 -0
- package/third-party/cargo/cfg-if@1.0.4/LICENSE-MIT +25 -0
- package/third-party/cargo/cfg_aliases@0.2.1/Cargo.toml +43 -0
- package/third-party/cargo/cfg_aliases@0.2.1/LICENSE +9 -0
- package/third-party/cargo/chunked_transfer@1.5.0/Cargo.toml +27 -0
- package/third-party/cargo/chunked_transfer@1.5.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/chunked_transfer@1.5.0/LICENSE-MIT +22 -0
- package/third-party/cargo/codespan-reporting@0.13.1/Cargo.toml +115 -0
- package/third-party/cargo/codespan-reporting@0.13.1/LICENSE +201 -0
- package/third-party/cargo/color_quant@1.1.0/Cargo.toml +20 -0
- package/third-party/cargo/color_quant@1.1.0/LICENSE +21 -0
- package/third-party/cargo/console_error_panic_hook@0.1.7/Cargo.toml +27 -0
- package/third-party/cargo/console_error_panic_hook@0.1.7/LICENSE-APACHE +201 -0
- package/third-party/cargo/console_error_panic_hook@0.1.7/LICENSE-MIT +25 -0
- package/third-party/cargo/core_maths@0.1.1/Cargo.toml +37 -0
- package/third-party/cargo/core_maths@0.1.1/LICENSE +21 -0
- package/third-party/cargo/cosmic-text@0.19.0/Cargo.toml +214 -0
- package/third-party/cargo/cosmic-text@0.19.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/cosmic-text@0.19.0/LICENSE-MIT +21 -0
- package/third-party/cargo/crc32fast@1.5.0/Cargo.toml +68 -0
- package/third-party/cargo/crc32fast@1.5.0/LICENSE-APACHE +202 -0
- package/third-party/cargo/crc32fast@1.5.0/LICENSE-MIT +21 -0
- package/third-party/cargo/data-url@0.3.2/Cargo.toml +63 -0
- package/third-party/cargo/data-url@0.3.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/data-url@0.3.2/LICENSE-MIT +25 -0
- package/third-party/cargo/dispatch2@0.3.1/Cargo.toml +122 -0
- package/third-party/cargo/dispatch2@0.3.1/MIT-terms.txt +22 -0
- package/third-party/cargo/dispatch2@0.3.1/madsmtm-objc2-8852b424193c-LICENSE.md +34 -0
- package/third-party/cargo/dlib@0.5.3/Cargo.toml +43 -0
- package/third-party/cargo/dlib@0.5.3/LICENSE.txt +19 -0
- package/third-party/cargo/document-features@0.2.12/Cargo.toml +51 -0
- package/third-party/cargo/document-features@0.2.12/LICENSE-APACHE +73 -0
- package/third-party/cargo/document-features@0.2.12/LICENSE-MIT +19 -0
- package/third-party/cargo/equivalent@1.0.2/Cargo.toml +37 -0
- package/third-party/cargo/equivalent@1.0.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/equivalent@1.0.2/LICENSE-MIT +25 -0
- package/third-party/cargo/errno@0.3.14/Cargo.toml +67 -0
- package/third-party/cargo/errno@0.3.14/LICENSE-APACHE +201 -0
- package/third-party/cargo/errno@0.3.14/LICENSE-MIT +25 -0
- package/third-party/cargo/etagere@0.3.0/Cargo.toml +57 -0
- package/third-party/cargo/etagere@0.3.0/LICENSE-APACHE +13 -0
- package/third-party/cargo/etagere@0.3.0/LICENSE-MIT +20 -0
- package/third-party/cargo/euclid@0.22.14/COPYRIGHT +5 -0
- package/third-party/cargo/euclid@0.22.14/Cargo.toml +66 -0
- package/third-party/cargo/euclid@0.22.14/LICENSE-APACHE +201 -0
- package/third-party/cargo/euclid@0.22.14/LICENSE-MIT +25 -0
- package/third-party/cargo/fastrand@2.5.0/Cargo.toml +89 -0
- package/third-party/cargo/fastrand@2.5.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/fastrand@2.5.0/LICENSE-MIT +23 -0
- package/third-party/cargo/fdeflate@0.3.7/Cargo.toml +55 -0
- package/third-party/cargo/fdeflate@0.3.7/LICENSE-APACHE +177 -0
- package/third-party/cargo/fdeflate@0.3.7/LICENSE-MIT +25 -0
- package/third-party/cargo/flate2@1.1.9/Cargo.toml +274 -0
- package/third-party/cargo/flate2@1.1.9/LICENSE-APACHE +201 -0
- package/third-party/cargo/flate2@1.1.9/LICENSE-MIT +25 -0
- package/third-party/cargo/float-cmp@0.9.0/Cargo.toml +39 -0
- package/third-party/cargo/float-cmp@0.9.0/LICENSE +19 -0
- package/third-party/cargo/float_next_after@1.0.0/Cargo.toml +43 -0
- package/third-party/cargo/float_next_after@1.0.0/LICENSE +23 -0
- package/third-party/cargo/foldhash@0.2.0/Cargo.toml +80 -0
- package/third-party/cargo/foldhash@0.2.0/LICENSE +19 -0
- package/third-party/cargo/font-types@0.11.3/Cargo.toml +55 -0
- package/third-party/cargo/font-types@0.11.3/LICENSE-APACHE +67 -0
- package/third-party/cargo/font-types@0.11.3/LICENSE-MIT +25 -0
- package/third-party/cargo/fontdb@0.23.0/Cargo.toml +83 -0
- package/third-party/cargo/fontdb@0.23.0/LICENSE +21 -0
- package/third-party/cargo/futures-core@0.3.32/Cargo.toml +66 -0
- package/third-party/cargo/futures-core@0.3.32/LICENSE-APACHE +202 -0
- package/third-party/cargo/futures-core@0.3.32/LICENSE-MIT +26 -0
- package/third-party/cargo/futures-task@0.3.32/Cargo.toml +58 -0
- package/third-party/cargo/futures-task@0.3.32/LICENSE-APACHE +202 -0
- package/third-party/cargo/futures-task@0.3.32/LICENSE-MIT +26 -0
- package/third-party/cargo/futures-util@0.3.32/Cargo.toml +181 -0
- package/third-party/cargo/futures-util@0.3.32/LICENSE-APACHE +202 -0
- package/third-party/cargo/futures-util@0.3.32/LICENSE-MIT +26 -0
- package/third-party/cargo/getrandom@0.4.3/Cargo.toml +165 -0
- package/third-party/cargo/getrandom@0.4.3/LICENSE-APACHE +201 -0
- package/third-party/cargo/getrandom@0.4.3/LICENSE-MIT +26 -0
- package/third-party/cargo/gif@0.14.2/Cargo.toml +84 -0
- package/third-party/cargo/gif@0.14.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/gif@0.14.2/LICENSE-MIT +21 -0
- package/third-party/cargo/glam@0.33.2/Cargo.toml +354 -0
- package/third-party/cargo/glam@0.33.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/glam@0.33.2/LICENSE-MIT +23 -0
- package/third-party/cargo/glow@0.17.0/Cargo.toml +122 -0
- package/third-party/cargo/glow@0.17.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/glow@0.17.0/LICENSE-MIT +23 -0
- package/third-party/cargo/glow@0.17.0/LICENSE-ZLIB +15 -0
- package/third-party/cargo/gltf-derive@1.4.1/Cargo.toml +34 -0
- package/third-party/cargo/gltf-derive@1.4.1/LICENSE-APACHE +202 -0
- package/third-party/cargo/gltf-derive@1.4.1/LICENSE-MIT +26 -0
- package/third-party/cargo/gltf-json@1.4.1/Cargo.toml +50 -0
- package/third-party/cargo/gltf-json@1.4.1/LICENSE-APACHE +202 -0
- package/third-party/cargo/gltf-json@1.4.1/LICENSE-MIT +26 -0
- package/third-party/cargo/gltf@1.4.1/Cargo.toml +122 -0
- package/third-party/cargo/gltf@1.4.1/LICENSE-APACHE +202 -0
- package/third-party/cargo/gltf@1.4.1/LICENSE-MIT +26 -0
- package/third-party/cargo/glyphon@0.12.0/Cargo.toml +35 -0
- package/third-party/cargo/glyphon@0.12.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/glyphon@0.12.0/LICENSE-MIT +23 -0
- package/third-party/cargo/glyphon@0.12.0/LICENSE-ZLIB +15 -0
- package/third-party/cargo/gpu-allocator@0.28.0/Cargo.toml +180 -0
- package/third-party/cargo/gpu-allocator@0.28.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/gpu-allocator@0.28.0/LICENSE-MIT +7 -0
- package/third-party/cargo/grid@1.0.1/Cargo.toml +77 -0
- package/third-party/cargo/grid@1.0.1/LICENSE +21 -0
- package/third-party/cargo/half@2.7.1/Cargo.toml +137 -0
- package/third-party/cargo/half@2.7.1/LICENSE-APACHE +176 -0
- package/third-party/cargo/half@2.7.1/LICENSE-MIT +19 -0
- package/third-party/cargo/harfrust@0.5.2/Cargo.toml +115 -0
- package/third-party/cargo/harfrust@0.5.2/harfbuzz-harfrust-efdae3142ab7-LICENSE +22 -0
- package/third-party/cargo/hashbrown@0.16.1/Cargo.toml +180 -0
- package/third-party/cargo/hashbrown@0.16.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/hashbrown@0.16.1/LICENSE-MIT +25 -0
- package/third-party/cargo/hashbrown@0.17.1/Cargo.toml +201 -0
- package/third-party/cargo/hashbrown@0.17.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/hashbrown@0.17.1/LICENSE-MIT +25 -0
- package/third-party/cargo/httpdate@1.0.3/Cargo.toml +35 -0
- package/third-party/cargo/httpdate@1.0.3/LICENSE-APACHE +201 -0
- package/third-party/cargo/httpdate@1.0.3/LICENSE-MIT +19 -0
- package/third-party/cargo/image-webp@0.2.4/Cargo.toml +63 -0
- package/third-party/cargo/image-webp@0.2.4/LICENSE-APACHE +177 -0
- package/third-party/cargo/image-webp@0.2.4/LICENSE-MIT +25 -0
- package/third-party/cargo/image@0.25.10/Cargo.toml +248 -0
- package/third-party/cargo/image@0.25.10/LICENSE-APACHE +177 -0
- package/third-party/cargo/image@0.25.10/LICENSE-MIT +25 -0
- package/third-party/cargo/imagesize@0.14.0/Cargo.toml +104 -0
- package/third-party/cargo/imagesize@0.14.0/LICENSE +21 -0
- package/third-party/cargo/indexmap@2.14.0/Cargo.toml +167 -0
- package/third-party/cargo/indexmap@2.14.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/indexmap@2.14.0/LICENSE-MIT +25 -0
- package/third-party/cargo/inflections@1.1.1/Cargo.toml +21 -0
- package/third-party/cargo/inflections@1.1.1/LICENSE +20 -0
- package/third-party/cargo/itoa@1.0.18/Cargo.toml +66 -0
- package/third-party/cargo/itoa@1.0.18/LICENSE-APACHE +176 -0
- package/third-party/cargo/itoa@1.0.18/LICENSE-MIT +23 -0
- package/third-party/cargo/js-sys@0.3.103/Cargo.toml +98 -0
- package/third-party/cargo/js-sys@0.3.103/LICENSE-APACHE +201 -0
- package/third-party/cargo/js-sys@0.3.103/LICENSE-MIT +25 -0
- package/third-party/cargo/khronos-egl@6.0.0/Cargo.toml +82 -0
- package/third-party/cargo/khronos-egl@6.0.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/khronos-egl@6.0.0/LICENSE-MIT +23 -0
- package/third-party/cargo/kurbo@0.13.1/Cargo.toml +230 -0
- package/third-party/cargo/kurbo@0.13.1/LICENSE-APACHE +202 -0
- package/third-party/cargo/kurbo@0.13.1/LICENSE-MIT +25 -0
- package/third-party/cargo/lazy_static@1.5.0/Cargo.toml +46 -0
- package/third-party/cargo/lazy_static@1.5.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/lazy_static@1.5.0/LICENSE-MIT +25 -0
- package/third-party/cargo/libc@0.2.186/Cargo.toml +229 -0
- package/third-party/cargo/libc@0.2.186/LICENSE-APACHE +176 -0
- package/third-party/cargo/libc@0.2.186/LICENSE-MIT +25 -0
- package/third-party/cargo/libloading@0.8.9/Cargo.toml +90 -0
- package/third-party/cargo/libloading@0.8.9/LICENSE +12 -0
- package/third-party/cargo/libm@0.2.16/Cargo.toml +61 -0
- package/third-party/cargo/libm@0.2.16/LICENSE.txt +258 -0
- package/third-party/cargo/linebender_resource_handle@0.1.1/Cargo.toml +107 -0
- package/third-party/cargo/linebender_resource_handle@0.1.1/LICENSE-APACHE +176 -0
- package/third-party/cargo/linebender_resource_handle@0.1.1/LICENSE-MIT +19 -0
- package/third-party/cargo/litrs@1.0.0/Cargo.toml +63 -0
- package/third-party/cargo/litrs@1.0.0/LICENSE-APACHE +176 -0
- package/third-party/cargo/litrs@1.0.0/LICENSE-MIT +25 -0
- package/third-party/cargo/lock_api@0.4.14/Cargo.toml +68 -0
- package/third-party/cargo/lock_api@0.4.14/LICENSE-APACHE +201 -0
- package/third-party/cargo/lock_api@0.4.14/LICENSE-MIT +25 -0
- package/third-party/cargo/log@0.4.33/Cargo.toml +152 -0
- package/third-party/cargo/log@0.4.33/LICENSE-APACHE +201 -0
- package/third-party/cargo/log@0.4.33/LICENSE-MIT +25 -0
- package/third-party/cargo/lru@0.16.4/Cargo.toml +47 -0
- package/third-party/cargo/lru@0.16.4/LICENSE +21 -0
- package/third-party/cargo/lyon_geom@1.0.19/Cargo.toml +69 -0
- package/third-party/cargo/lyon_geom@1.0.19/nical-lyon-56219c95c658-LICENSE-APACHE +13 -0
- package/third-party/cargo/lyon_geom@1.0.19/nical-lyon-56219c95c658-LICENSE-MIT +20 -0
- package/third-party/cargo/lyon_path@1.0.19/Cargo.toml +58 -0
- package/third-party/cargo/lyon_path@1.0.19/nical-lyon-56219c95c658-LICENSE-APACHE +13 -0
- package/third-party/cargo/lyon_path@1.0.19/nical-lyon-56219c95c658-LICENSE-MIT +20 -0
- package/third-party/cargo/lyon_tessellation@1.0.20/Cargo.toml +71 -0
- package/third-party/cargo/lyon_tessellation@1.0.20/nical-lyon-7fa75ea8eea3-LICENSE-APACHE +13 -0
- package/third-party/cargo/lyon_tessellation@1.0.20/nical-lyon-7fa75ea8eea3-LICENSE-MIT +20 -0
- package/third-party/cargo/memchr@2.8.3/COPYING +3 -0
- package/third-party/cargo/memchr@2.8.3/Cargo.toml +89 -0
- package/third-party/cargo/memchr@2.8.3/LICENSE-MIT +21 -0
- package/third-party/cargo/memchr@2.8.3/UNLICENSE +24 -0
- package/third-party/cargo/memmap2@0.9.11/Cargo.toml +59 -0
- package/third-party/cargo/memmap2@0.9.11/LICENSE-APACHE +201 -0
- package/third-party/cargo/memmap2@0.9.11/LICENSE-MIT +26 -0
- package/third-party/cargo/miniz_oxide@0.8.9/Cargo.toml +91 -0
- package/third-party/cargo/miniz_oxide@0.8.9/LICENSE +25 -0
- package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-APACHE.md +177 -0
- package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-MIT.md +24 -0
- package/third-party/cargo/miniz_oxide@0.8.9/LICENSE-ZLIB.md +14 -0
- package/third-party/cargo/moxcms@0.8.1/Cargo.toml +113 -0
- package/third-party/cargo/moxcms@0.8.1/LICENSE-APACHE.md +201 -0
- package/third-party/cargo/moxcms@0.8.1/LICENSE.md +26 -0
- package/third-party/cargo/naga-types@30.0.0/Cargo.toml +94 -0
- package/third-party/cargo/naga-types@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/naga-types@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/naga@30.0.0/Cargo.toml +246 -0
- package/third-party/cargo/naga@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/naga@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/num-traits@0.2.19/Cargo.toml +54 -0
- package/third-party/cargo/num-traits@0.2.19/LICENSE-APACHE +201 -0
- package/third-party/cargo/num-traits@0.2.19/LICENSE-MIT +25 -0
- package/third-party/cargo/objc2-core-foundation@0.3.2/Cargo.toml +221 -0
- package/third-party/cargo/objc2-core-foundation@0.3.2/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2-core-foundation@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
- package/third-party/cargo/objc2-core-graphics@0.3.2/Cargo.toml +331 -0
- package/third-party/cargo/objc2-core-graphics@0.3.2/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2-core-graphics@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
- package/third-party/cargo/objc2-encode@4.1.0/Cargo.toml +81 -0
- package/third-party/cargo/objc2-encode@4.1.0/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2-encode@4.1.0/madsmtm-objc2-8d214f547736-LICENSE.md +34 -0
- package/third-party/cargo/objc2-foundation@0.3.2/Cargo.toml +529 -0
- package/third-party/cargo/objc2-foundation@0.3.2/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2-foundation@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
- package/third-party/cargo/objc2-io-surface@0.3.2/Cargo.toml +139 -0
- package/third-party/cargo/objc2-io-surface@0.3.2/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2-io-surface@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
- package/third-party/cargo/objc2-metal@0.3.2/Cargo.toml +601 -0
- package/third-party/cargo/objc2-metal@0.3.2/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2-metal@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
- package/third-party/cargo/objc2-quartz-core@0.3.2/Cargo.toml +302 -0
- package/third-party/cargo/objc2-quartz-core@0.3.2/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2-quartz-core@0.3.2/madsmtm-objc2-7b1abfd750a2-LICENSE.md +34 -0
- package/third-party/cargo/objc2@0.6.4/Cargo.toml +232 -0
- package/third-party/cargo/objc2@0.6.4/MIT-terms.txt +22 -0
- package/third-party/cargo/objc2@0.6.4/madsmtm-objc2-8852b424193c-LICENSE.md +34 -0
- package/third-party/cargo/once_cell@1.21.4/Cargo.toml +119 -0
- package/third-party/cargo/once_cell@1.21.4/LICENSE-APACHE +201 -0
- package/third-party/cargo/once_cell@1.21.4/LICENSE-MIT +23 -0
- package/third-party/cargo/ordered-float@5.3.0/Cargo.toml +162 -0
- package/third-party/cargo/ordered-float@5.3.0/LICENSE-MIT +25 -0
- package/third-party/cargo/parking_lot@0.12.5/Cargo.toml +87 -0
- package/third-party/cargo/parking_lot@0.12.5/LICENSE-APACHE +201 -0
- package/third-party/cargo/parking_lot@0.12.5/LICENSE-MIT +25 -0
- package/third-party/cargo/parking_lot_core@0.9.12/Cargo.toml +72 -0
- package/third-party/cargo/parking_lot_core@0.9.12/LICENSE-APACHE +201 -0
- package/third-party/cargo/parking_lot_core@0.9.12/LICENSE-MIT +25 -0
- package/third-party/cargo/pico-args@0.5.0/Cargo.toml +32 -0
- package/third-party/cargo/pico-args@0.5.0/LICENSE +20 -0
- package/third-party/cargo/pin-project-lite@0.2.17/Cargo.toml +198 -0
- package/third-party/cargo/pin-project-lite@0.2.17/LICENSE-APACHE +177 -0
- package/third-party/cargo/pin-project-lite@0.2.17/LICENSE-MIT +23 -0
- package/third-party/cargo/pkg-config@0.3.33/Cargo.toml +43 -0
- package/third-party/cargo/pkg-config@0.3.33/LICENSE-APACHE +201 -0
- package/third-party/cargo/pkg-config@0.3.33/LICENSE-MIT +25 -0
- package/third-party/cargo/png@0.18.1/Cargo.toml +130 -0
- package/third-party/cargo/png@0.18.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/png@0.18.1/LICENSE-MIT +25 -0
- package/third-party/cargo/pollster@0.4.0/Cargo.toml +53 -0
- package/third-party/cargo/pollster@0.4.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/pollster@0.4.0/LICENSE-MIT +25 -0
- package/third-party/cargo/polycool@0.4.0/Cargo.toml +83 -0
- package/third-party/cargo/polycool@0.4.0/LICENSE-APACHE +202 -0
- package/third-party/cargo/polycool@0.4.0/LICENSE-MIT +25 -0
- package/third-party/cargo/pp-rs@0.2.1/Cargo.toml +23 -0
- package/third-party/cargo/pp-rs@0.2.1/LICENSE +29 -0
- package/third-party/cargo/presser@0.3.1/Cargo.toml +58 -0
- package/third-party/cargo/presser@0.3.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/presser@0.3.1/LICENSE-MIT +25 -0
- package/third-party/cargo/proc-macro2@1.0.106/Cargo.toml +105 -0
- package/third-party/cargo/proc-macro2@1.0.106/LICENSE-APACHE +176 -0
- package/third-party/cargo/proc-macro2@1.0.106/LICENSE-MIT +23 -0
- package/third-party/cargo/profiling@1.0.18/Cargo.toml +96 -0
- package/third-party/cargo/profiling@1.0.18/aclysma-profiling-8271551172eb-LICENSE-APACHE +201 -0
- package/third-party/cargo/profiling@1.0.18/aclysma-profiling-8271551172eb-LICENSE-MIT +21 -0
- package/third-party/cargo/pxfm@0.1.30/Cargo.toml +53 -0
- package/third-party/cargo/pxfm@0.1.30/LICENSE-APACHE.md +201 -0
- package/third-party/cargo/pxfm@0.1.30/LICENSE.md +26 -0
- package/third-party/cargo/quick-error@2.0.1/Cargo.toml +24 -0
- package/third-party/cargo/quick-error@2.0.1/LICENSE-APACHE +202 -0
- package/third-party/cargo/quick-error@2.0.1/LICENSE-MIT +19 -0
- package/third-party/cargo/quote@1.0.46/Cargo.toml +70 -0
- package/third-party/cargo/quote@1.0.46/LICENSE-APACHE +176 -0
- package/third-party/cargo/quote@1.0.46/LICENSE-MIT +23 -0
- package/third-party/cargo/rangemap@1.7.1/Cargo.toml +91 -0
- package/third-party/cargo/rangemap@1.7.1/LICENSE-APACHE +191 -0
- package/third-party/cargo/rangemap@1.7.1/LICENSE-MIT +7 -0
- package/third-party/cargo/raw-window-handle@0.6.2/Cargo.toml +56 -0
- package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-APACHE.md +177 -0
- package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-MIT.md +21 -0
- package/third-party/cargo/raw-window-handle@0.6.2/LICENSE-ZLIB.md +11 -0
- package/third-party/cargo/raw-window-metal@1.1.0/Cargo.toml +111 -0
- package/third-party/cargo/raw-window-metal@1.1.0/LICENSE-APACHE +202 -0
- package/third-party/cargo/raw-window-metal@1.1.0/LICENSE-MIT +17 -0
- package/third-party/cargo/read-fonts@0.37.0/Cargo.toml +95 -0
- package/third-party/cargo/read-fonts@0.37.0/LICENSE-APACHE +67 -0
- package/third-party/cargo/read-fonts@0.37.0/LICENSE-MIT +25 -0
- package/third-party/cargo/read-fonts@0.39.2/Cargo.toml +96 -0
- package/third-party/cargo/read-fonts@0.39.2/LICENSE-APACHE +67 -0
- package/third-party/cargo/read-fonts@0.39.2/LICENSE-MIT +25 -0
- package/third-party/cargo/renderdoc-sys@1.1.0/Cargo.toml +39 -0
- package/third-party/cargo/renderdoc-sys@1.1.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/renderdoc-sys@1.1.0/LICENSE-MIT +25 -0
- package/third-party/cargo/resvg@0.47.0/Cargo.toml +112 -0
- package/third-party/cargo/resvg@0.47.0/LICENSE-APACHE +176 -0
- package/third-party/cargo/resvg@0.47.0/LICENSE-MIT +25 -0
- package/third-party/cargo/rgb@0.8.53/Cargo.toml +110 -0
- package/third-party/cargo/rgb@0.8.53/LICENSE +21 -0
- package/third-party/cargo/roxmltree@0.21.1/Cargo.toml +75 -0
- package/third-party/cargo/roxmltree@0.21.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/roxmltree@0.21.1/LICENSE-MIT +21 -0
- package/third-party/cargo/rustc-hash@1.1.0/Cargo.toml +25 -0
- package/third-party/cargo/rustc-hash@1.1.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/rustc-hash@1.1.0/LICENSE-MIT +23 -0
- package/third-party/cargo/rustc-hash@2.1.3/Cargo.toml +50 -0
- package/third-party/cargo/rustc-hash@2.1.3/LICENSE-APACHE +176 -0
- package/third-party/cargo/rustc-hash@2.1.3/LICENSE-MIT +23 -0
- package/third-party/cargo/rustix@1.1.4/COPYRIGHT +29 -0
- package/third-party/cargo/rustix@1.1.4/Cargo.toml +292 -0
- package/third-party/cargo/rustix@1.1.4/LICENSE-APACHE +201 -0
- package/third-party/cargo/rustix@1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
- package/third-party/cargo/rustix@1.1.4/LICENSE-MIT +23 -0
- package/third-party/cargo/rustversion@1.0.23/Cargo.toml +69 -0
- package/third-party/cargo/rustversion@1.0.23/LICENSE-APACHE +176 -0
- package/third-party/cargo/rustversion@1.0.23/LICENSE-MIT +23 -0
- package/third-party/cargo/rustybuzz@0.20.1/Cargo.toml +110 -0
- package/third-party/cargo/rustybuzz@0.20.1/LICENSE +22 -0
- package/third-party/cargo/scopeguard@1.2.0/Cargo.toml +43 -0
- package/third-party/cargo/scopeguard@1.2.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/scopeguard@1.2.0/LICENSE-MIT +25 -0
- package/third-party/cargo/self_cell@1.2.2/Cargo.toml +33 -0
- package/third-party/cargo/self_cell@1.2.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/self_cell@1.2.2/LICENSE-GPLv2 +339 -0
- package/third-party/cargo/self_cell@1.3.0/Cargo.toml +33 -0
- package/third-party/cargo/self_cell@1.3.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/self_cell@1.3.0/LICENSE-GPLv2 +339 -0
- package/third-party/cargo/serde@1.0.228/Cargo.toml +84 -0
- package/third-party/cargo/serde@1.0.228/LICENSE-APACHE +176 -0
- package/third-party/cargo/serde@1.0.228/LICENSE-MIT +23 -0
- package/third-party/cargo/serde_core@1.0.228/Cargo.toml +87 -0
- package/third-party/cargo/serde_core@1.0.228/LICENSE-APACHE +176 -0
- package/third-party/cargo/serde_core@1.0.228/LICENSE-MIT +23 -0
- package/third-party/cargo/serde_derive@1.0.228/Cargo.toml +87 -0
- package/third-party/cargo/serde_derive@1.0.228/LICENSE-APACHE +176 -0
- package/third-party/cargo/serde_derive@1.0.228/LICENSE-MIT +23 -0
- package/third-party/cargo/serde_json@1.0.150/Cargo.toml +162 -0
- package/third-party/cargo/serde_json@1.0.150/LICENSE-APACHE +176 -0
- package/third-party/cargo/serde_json@1.0.150/LICENSE-MIT +23 -0
- package/third-party/cargo/simd-adler32@0.3.10/Cargo.toml +68 -0
- package/third-party/cargo/simd-adler32@0.3.10/LICENSE.md +21 -0
- package/third-party/cargo/simplecss@0.2.2/Cargo.toml +146 -0
- package/third-party/cargo/simplecss@0.2.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/simplecss@0.2.2/LICENSE-MIT +21 -0
- package/third-party/cargo/siphasher@1.0.3/COPYING +7 -0
- package/third-party/cargo/siphasher@1.0.3/Cargo.toml +64 -0
- package/third-party/cargo/skrifa@0.40.0/Cargo.toml +86 -0
- package/third-party/cargo/skrifa@0.40.0/LICENSE-APACHE +67 -0
- package/third-party/cargo/skrifa@0.40.0/LICENSE-MIT +25 -0
- package/third-party/cargo/skrifa@0.42.1/Cargo.toml +86 -0
- package/third-party/cargo/skrifa@0.42.1/LICENSE-APACHE +67 -0
- package/third-party/cargo/skrifa@0.42.1/LICENSE-MIT +25 -0
- package/third-party/cargo/slab@0.4.12/Cargo.toml +67 -0
- package/third-party/cargo/slab@0.4.12/LICENSE +25 -0
- package/third-party/cargo/slotmap@1.1.1/Cargo.toml +83 -0
- package/third-party/cargo/slotmap@1.1.1/LICENSE +19 -0
- package/third-party/cargo/smallvec@1.15.2/Cargo.toml +103 -0
- package/third-party/cargo/smallvec@1.15.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/smallvec@1.15.2/LICENSE-MIT +25 -0
- package/third-party/cargo/smol_str@0.3.6/Cargo.toml +155 -0
- package/third-party/cargo/smol_str@0.3.6/LICENSE-APACHE +201 -0
- package/third-party/cargo/smol_str@0.3.6/LICENSE-MIT +23 -0
- package/third-party/cargo/spirv@0.4.0+sdk-1.4.341.0/Cargo.toml +56 -0
- package/third-party/cargo/spirv@0.4.0+sdk-1.4.341.0/gfx-rs-rspirv-8afc3d0ac8e1-LICENSE +202 -0
- package/third-party/cargo/static_assertions@1.1.0/Cargo.toml +39 -0
- package/third-party/cargo/static_assertions@1.1.0/LICENSE-APACHE +202 -0
- package/third-party/cargo/static_assertions@1.1.0/LICENSE-MIT +21 -0
- package/third-party/cargo/strict-num@0.1.1/Cargo.toml +35 -0
- package/third-party/cargo/strict-num@0.1.1/LICENSE +19 -0
- package/third-party/cargo/svgtypes@0.16.1/Cargo.toml +57 -0
- package/third-party/cargo/svgtypes@0.16.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/svgtypes@0.16.1/LICENSE-MIT +20 -0
- package/third-party/cargo/swash@0.2.9/Cargo.toml +93 -0
- package/third-party/cargo/swash@0.2.9/LICENSE-APACHE +201 -0
- package/third-party/cargo/swash@0.2.9/LICENSE-MIT +25 -0
- package/third-party/cargo/syn@2.0.118/Cargo.toml +272 -0
- package/third-party/cargo/syn@2.0.118/LICENSE-APACHE +176 -0
- package/third-party/cargo/syn@2.0.118/LICENSE-MIT +23 -0
- package/third-party/cargo/sys-locale@0.3.2/Cargo.toml +73 -0
- package/third-party/cargo/sys-locale@0.3.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/sys-locale@0.3.2/LICENSE-MIT +21 -0
- package/third-party/cargo/taffy@0.12.1/Cargo.toml +171 -0
- package/third-party/cargo/taffy@0.12.1/DioxusLabs-taffy-1c9e37a0a1cf-LICENSE.md +25 -0
- package/third-party/cargo/tempfile@3.27.0/Cargo.toml +105 -0
- package/third-party/cargo/tempfile@3.27.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/tempfile@3.27.0/LICENSE-MIT +25 -0
- package/third-party/cargo/termcolor@1.4.1/COPYING +3 -0
- package/third-party/cargo/termcolor@1.4.1/Cargo.toml +40 -0
- package/third-party/cargo/termcolor@1.4.1/LICENSE-MIT +21 -0
- package/third-party/cargo/termcolor@1.4.1/UNLICENSE +24 -0
- package/third-party/cargo/thiserror-impl@2.0.18/Cargo.toml +52 -0
- package/third-party/cargo/thiserror-impl@2.0.18/LICENSE-APACHE +176 -0
- package/third-party/cargo/thiserror-impl@2.0.18/LICENSE-MIT +23 -0
- package/third-party/cargo/thiserror@2.0.18/Cargo.toml +119 -0
- package/third-party/cargo/thiserror@2.0.18/LICENSE-APACHE +176 -0
- package/third-party/cargo/thiserror@2.0.18/LICENSE-MIT +23 -0
- package/third-party/cargo/tiny-skia-path@0.12.0/Cargo.toml +58 -0
- package/third-party/cargo/tiny-skia-path@0.12.0/LICENSE +30 -0
- package/third-party/cargo/tiny-skia@0.12.0/Cargo.toml +116 -0
- package/third-party/cargo/tiny-skia@0.12.0/LICENSE +30 -0
- package/third-party/cargo/tiny_http@0.12.0/Cargo.toml +82 -0
- package/third-party/cargo/tiny_http@0.12.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/tiny_http@0.12.0/LICENSE-MIT +25 -0
- package/third-party/cargo/tinyvec@1.12.0/Cargo.toml +184 -0
- package/third-party/cargo/tinyvec@1.12.0/LICENSE-APACHE.md +202 -0
- package/third-party/cargo/tinyvec@1.12.0/LICENSE-MIT.md +5 -0
- package/third-party/cargo/tinyvec@1.12.0/LICENSE-ZLIB.md +11 -0
- package/third-party/cargo/tinyvec_macros@0.1.1/Cargo.toml +19 -0
- package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-APACHE.md +202 -0
- package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-MIT.md +21 -0
- package/third-party/cargo/tinyvec_macros@0.1.1/LICENSE-ZLIB.md +20 -0
- package/third-party/cargo/ttf-parser@0.25.1/Cargo.toml +101 -0
- package/third-party/cargo/ttf-parser@0.25.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/ttf-parser@0.25.1/LICENSE-MIT +20 -0
- package/third-party/cargo/unicode-bidi-mirroring@0.4.0/Cargo.toml +22 -0
- package/third-party/cargo/unicode-bidi-mirroring@0.4.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-bidi-mirroring@0.4.0/LICENSE-MIT +21 -0
- package/third-party/cargo/unicode-bidi@0.3.18/COPYRIGHT +8 -0
- package/third-party/cargo/unicode-bidi@0.3.18/Cargo.toml +90 -0
- package/third-party/cargo/unicode-bidi@0.3.18/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-bidi@0.3.18/LICENSE-MIT +25 -0
- package/third-party/cargo/unicode-ccc@0.4.0/Cargo.toml +22 -0
- package/third-party/cargo/unicode-ccc@0.4.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-ccc@0.4.0/LICENSE-MIT +21 -0
- package/third-party/cargo/unicode-ident@1.0.24/Cargo.toml +84 -0
- package/third-party/cargo/unicode-ident@1.0.24/LICENSE-APACHE +176 -0
- package/third-party/cargo/unicode-ident@1.0.24/LICENSE-MIT +23 -0
- package/third-party/cargo/unicode-ident@1.0.24/LICENSE-UNICODE +39 -0
- package/third-party/cargo/unicode-linebreak@0.1.5/Cargo.toml +32 -0
- package/third-party/cargo/unicode-linebreak@0.1.5/LICENSE +201 -0
- package/third-party/cargo/unicode-properties@0.1.4/COPYRIGHT +7 -0
- package/third-party/cargo/unicode-properties@0.1.4/Cargo.toml +65 -0
- package/third-party/cargo/unicode-properties@0.1.4/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-properties@0.1.4/LICENSE-MIT +25 -0
- package/third-party/cargo/unicode-script@0.5.8/Cargo.toml +63 -0
- package/third-party/cargo/unicode-script@0.5.8/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-script@0.5.8/LICENSE-MIT +27 -0
- package/third-party/cargo/unicode-segmentation@1.13.3/COPYRIGHT +7 -0
- package/third-party/cargo/unicode-segmentation@1.13.3/Cargo.toml +91 -0
- package/third-party/cargo/unicode-segmentation@1.13.3/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-segmentation@1.13.3/LICENSE-MIT +25 -0
- package/third-party/cargo/unicode-vo@0.1.0/Cargo.toml +22 -0
- package/third-party/cargo/unicode-vo@0.1.0/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-vo@0.1.0/LICENSE-MIT +21 -0
- package/third-party/cargo/unicode-width@0.2.2/COPYRIGHT +7 -0
- package/third-party/cargo/unicode-width@0.2.2/Cargo.toml +77 -0
- package/third-party/cargo/unicode-width@0.2.2/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-width@0.2.2/LICENSE-MIT +25 -0
- package/third-party/cargo/unicode-xid@0.2.6/COPYRIGHT +7 -0
- package/third-party/cargo/unicode-xid@0.2.6/Cargo.toml +65 -0
- package/third-party/cargo/unicode-xid@0.2.6/LICENSE-APACHE +201 -0
- package/third-party/cargo/unicode-xid@0.2.6/LICENSE-MIT +25 -0
- package/third-party/cargo/urlencoding@2.1.3/Cargo.toml +41 -0
- package/third-party/cargo/urlencoding@2.1.3/LICENSE +20 -0
- package/third-party/cargo/usvg@0.47.0/Cargo.toml +138 -0
- package/third-party/cargo/usvg@0.47.0/LICENSE-APACHE +176 -0
- package/third-party/cargo/usvg@0.47.0/LICENSE-MIT +25 -0
- package/third-party/cargo/version_check@0.9.5/Cargo.toml +38 -0
- package/third-party/cargo/version_check@0.9.5/LICENSE-APACHE +201 -0
- package/third-party/cargo/version_check@0.9.5/LICENSE-MIT +19 -0
- package/third-party/cargo/wasm-bindgen-futures@0.4.76/Cargo.toml +88 -0
- package/third-party/cargo/wasm-bindgen-futures@0.4.76/LICENSE-APACHE +201 -0
- package/third-party/cargo/wasm-bindgen-futures@0.4.76/LICENSE-MIT +25 -0
- package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/Cargo.toml +83 -0
- package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/LICENSE-APACHE +201 -0
- package/third-party/cargo/wasm-bindgen-macro-support@0.2.126/LICENSE-MIT +25 -0
- package/third-party/cargo/wasm-bindgen-macro@0.2.126/Cargo.toml +73 -0
- package/third-party/cargo/wasm-bindgen-macro@0.2.126/LICENSE-APACHE +201 -0
- package/third-party/cargo/wasm-bindgen-macro@0.2.126/LICENSE-MIT +25 -0
- package/third-party/cargo/wasm-bindgen-shared@0.2.126/Cargo.toml +66 -0
- package/third-party/cargo/wasm-bindgen-shared@0.2.126/LICENSE-APACHE +201 -0
- package/third-party/cargo/wasm-bindgen-shared@0.2.126/LICENSE-MIT +25 -0
- package/third-party/cargo/wasm-bindgen@0.2.126/Cargo.toml +122 -0
- package/third-party/cargo/wasm-bindgen@0.2.126/LICENSE-APACHE +201 -0
- package/third-party/cargo/wasm-bindgen@0.2.126/LICENSE-MIT +25 -0
- package/third-party/cargo/wayland-sys@0.31.11/Cargo.toml +81 -0
- package/third-party/cargo/wayland-sys@0.31.11/LICENSE.txt +19 -0
- package/third-party/cargo/web-sys@0.3.103/Cargo.toml +2823 -0
- package/third-party/cargo/web-sys@0.3.103/LICENSE-APACHE +201 -0
- package/third-party/cargo/web-sys@0.3.103/LICENSE-MIT +25 -0
- package/third-party/cargo/weezl@0.1.12/Cargo.toml +120 -0
- package/third-party/cargo/weezl@0.1.12/LICENSE-APACHE +201 -0
- package/third-party/cargo/weezl@0.1.12/LICENSE-MIT +22 -0
- package/third-party/cargo/wgpu-core-deps-apple@30.0.0/Cargo.toml +53 -0
- package/third-party/cargo/wgpu-core-deps-apple@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/wgpu-core-deps-apple@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/wgpu-core@30.0.0/Cargo.toml +237 -0
- package/third-party/cargo/wgpu-core@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/wgpu-core@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/wgpu-hal@30.0.0/Cargo.toml +541 -0
- package/third-party/cargo/wgpu-hal@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/wgpu-hal@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/wgpu-naga-bridge@30.0.0/Cargo.toml +46 -0
- package/third-party/cargo/wgpu-naga-bridge@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/wgpu-naga-bridge@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/wgpu-types@30.0.0/Cargo.toml +161 -0
- package/third-party/cargo/wgpu-types@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/wgpu-types@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/wgpu@30.0.0/Cargo.toml +278 -0
- package/third-party/cargo/wgpu@30.0.0/LICENSE.APACHE +176 -0
- package/third-party/cargo/wgpu@30.0.0/LICENSE.MIT +21 -0
- package/third-party/cargo/xmlwriter@0.1.0/Cargo.toml +23 -0
- package/third-party/cargo/xmlwriter@0.1.0/LICENSE +21 -0
- package/third-party/cargo/yazi@0.2.1/Cargo.toml +45 -0
- package/third-party/cargo/yazi@0.2.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/yazi@0.2.1/LICENSE-MIT +19 -0
- package/third-party/cargo/zeno@0.3.3/Cargo.toml +65 -0
- package/third-party/cargo/zeno@0.3.3/LICENSE-APACHE +201 -0
- package/third-party/cargo/zeno@0.3.3/LICENSE-MIT +25 -0
- package/third-party/cargo/zerocopy-derive@0.8.54/Cargo.toml +209 -0
- package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-APACHE +202 -0
- package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-BSD +24 -0
- package/third-party/cargo/zerocopy-derive@0.8.54/LICENSE-MIT +26 -0
- package/third-party/cargo/zerocopy@0.8.54/Cargo.toml +419 -0
- package/third-party/cargo/zerocopy@0.8.54/LICENSE-APACHE +202 -0
- package/third-party/cargo/zerocopy@0.8.54/LICENSE-BSD +24 -0
- package/third-party/cargo/zerocopy@0.8.54/LICENSE-MIT +26 -0
- package/third-party/cargo/zmij@1.0.23/Cargo.toml +90 -0
- package/third-party/cargo/zmij@1.0.23/LICENSE-MIT +23 -0
- package/third-party/cargo/zune-core@0.5.1/Cargo.toml +47 -0
- package/third-party/cargo/zune-core@0.5.1/LICENSE-APACHE +201 -0
- package/third-party/cargo/zune-core@0.5.1/LICENSE-MIT +21 -0
- package/third-party/cargo/zune-core@0.5.1/LICENSE-ZLIB +19 -0
- package/third-party/cargo/zune-jpeg@0.5.15/Cargo.toml +67 -0
- package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-APACHE +201 -0
- package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-MIT +21 -0
- package/third-party/cargo/zune-jpeg@0.5.15/LICENSE-ZLIB +19 -0
- package/third-party/inventory.json +3614 -0
- package/third-party/npm/@esbuild__darwin-arm64@0.27.4/esbuild-0.27.4-LICENSE.md +21 -0
- package/third-party/npm/@esbuild__darwin-arm64@0.27.4/go-LICENSE +27 -0
- package/third-party/npm/@esbuild__darwin-arm64@0.27.4/golang-x-sys-c0bba94af5f8-LICENSE +27 -0
- package/third-party/npm/@esbuild__darwin-arm64@0.27.4/package.json +20 -0
- package/third-party/npm/@floating-ui__core@1.7.5/LICENSE +20 -0
- package/third-party/npm/@floating-ui__core@1.7.5/package.json +63 -0
- package/third-party/npm/@floating-ui__dom@1.7.6/LICENSE +20 -0
- package/third-party/npm/@floating-ui__dom@1.7.6/package.json +71 -0
- package/third-party/npm/@floating-ui__react-dom@2.1.8/LICENSE +20 -0
- package/third-party/npm/@floating-ui__react-dom@2.1.8/package.json +76 -0
- package/third-party/npm/@floating-ui__utils@0.2.11/LICENSE +20 -0
- package/third-party/npm/@floating-ui__utils@0.2.11/package.json +70 -0
- package/third-party/npm/@radix-ui__primitive@1.1.7/LICENSE +21 -0
- package/third-party/npm/@radix-ui__primitive@1.1.7/package.json +79 -0
- package/third-party/npm/@radix-ui__react-arrow@1.1.15/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-arrow@1.1.15/package.json +68 -0
- package/third-party/npm/@radix-ui__react-collection@1.1.15/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-collection@1.1.15/package.json +71 -0
- package/third-party/npm/@radix-ui__react-compose-refs@1.1.5/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-compose-refs@1.1.5/package.json +60 -0
- package/third-party/npm/@radix-ui__react-context-menu@2.3.7/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-context-menu@2.3.7/package.json +73 -0
- package/third-party/npm/@radix-ui__react-context@1.2.2/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-context@1.2.2/package.json +60 -0
- package/third-party/npm/@radix-ui__react-direction@1.1.4/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-direction@1.1.4/package.json +60 -0
- package/third-party/npm/@radix-ui__react-dismissable-layer@1.1.19/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-dismissable-layer@1.1.19/package.json +74 -0
- package/third-party/npm/@radix-ui__react-focus-guards@1.1.6/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-focus-guards@1.1.6/package.json +60 -0
- package/third-party/npm/@radix-ui__react-focus-scope@1.1.16/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-focus-scope@1.1.16/package.json +71 -0
- package/third-party/npm/@radix-ui__react-id@1.1.4/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-id@1.1.4/package.json +63 -0
- package/third-party/npm/@radix-ui__react-menu@2.1.24/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-menu@2.1.24/package.json +86 -0
- package/third-party/npm/@radix-ui__react-popper@1.3.7/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-popper@1.3.7/package.json +78 -0
- package/third-party/npm/@radix-ui__react-portal@1.1.17/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-portal@1.1.17/package.json +69 -0
- package/third-party/npm/@radix-ui__react-presence@1.1.10/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-presence@1.1.10/package.json +68 -0
- package/third-party/npm/@radix-ui__react-primitive@2.1.10/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-primitive@2.1.10/package.json +68 -0
- package/third-party/npm/@radix-ui__react-roving-focus@1.1.19/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-roving-focus@1.1.19/package.json +78 -0
- package/third-party/npm/@radix-ui__react-slot@1.3.3/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-slot@1.3.3/package.json +63 -0
- package/third-party/npm/@radix-ui__react-use-callback-ref@1.1.4/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-use-callback-ref@1.1.4/package.json +60 -0
- package/third-party/npm/@radix-ui__react-use-controllable-state@1.2.6/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-use-controllable-state@1.2.6/package.json +65 -0
- package/third-party/npm/@radix-ui__react-use-effect-event@0.0.5/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-use-effect-event@0.0.5/package.json +65 -0
- package/third-party/npm/@radix-ui__react-use-is-hydrated@0.1.3/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-use-is-hydrated@0.1.3/package.json +61 -0
- package/third-party/npm/@radix-ui__react-use-layout-effect@1.1.4/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-use-layout-effect@1.1.4/package.json +60 -0
- package/third-party/npm/@radix-ui__react-use-size@1.1.4/LICENSE +21 -0
- package/third-party/npm/@radix-ui__react-use-size@1.1.4/package.json +63 -0
- package/third-party/npm/aria-hidden@1.2.6/LICENSE +21 -0
- package/third-party/npm/aria-hidden@1.2.6/package.json +71 -0
- package/third-party/npm/detect-node-es@1.1.0/LICENSE +21 -0
- package/third-party/npm/detect-node-es@1.1.0/package.json +29 -0
- package/third-party/npm/esbuild@0.27.4/LICENSE.md +21 -0
- package/third-party/npm/esbuild@0.27.4/package.json +49 -0
- package/third-party/npm/fflate@0.8.3/LICENSE +21 -0
- package/third-party/npm/fflate@0.8.3/package.json +128 -0
- package/third-party/npm/get-nonce@1.0.1/LICENSE +21 -0
- package/third-party/npm/get-nonce@1.0.1/package.json +68 -0
- package/third-party/npm/mediabunny@1.52.2/LICENSE +373 -0
- package/third-party/npm/mediabunny@1.52.2/package.json +136 -0
- package/third-party/npm/mediabunny@1.52.2/source/LICENSE +373 -0
- package/third-party/npm/mediabunny@1.52.2/source/package.json +136 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-demuxer.ts +400 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-muxer.ts +124 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/adts/adts-reader.ts +85 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/aes.ts +307 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/codec-data.ts +3178 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/codec.ts +1154 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/conversion.ts +2042 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/custom-coder.ts +212 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/decode.ts +263 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/demuxer.ts +43 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/encode.ts +1412 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-demuxer.ts +813 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-misc.ts +165 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/flac/flac-muxer.ts +350 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-demuxer.ts +995 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-misc.ts +81 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-muxer.ts +1529 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/hls/hls-segmented-input.ts +772 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/id3.ts +972 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/index.ts +319 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/input-format.ts +819 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/input-track.ts +1141 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/input.ts +571 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-boxes.ts +1911 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-demuxer.ts +3953 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-misc.ts +90 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-muxer.ts +1595 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/isobmff/isobmff-reader.ts +91 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/logging.ts +120 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/matroska/ebml.ts +770 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-demuxer.ts +2598 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-misc.ts +29 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/matroska/matroska-muxer.ts +1360 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/media-sink.ts +2589 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/media-source.ts +3041 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/metadata.ts +335 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/misc.ts +1336 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-demuxer.ts +469 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-muxer.ts +169 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-reader.ts +61 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/mp3/mp3-writer.ts +121 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-demuxer.ts +2519 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-misc.ts +32 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/mpeg-ts/mpeg-ts-muxer.ts +842 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/muxer.ts +80 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/node.ts +11 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-demuxer.ts +1087 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-misc.ts +116 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-muxer.ts +527 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/ogg/ogg-reader.ts +93 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/output-format.ts +1425 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/output.ts +940 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/packet.ts +287 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/pcm.ts +112 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/reader.ts +391 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/resample.ts +287 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/sample.ts +3231 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/segmented-input.ts +575 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/source.ts +2588 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/subtitles.ts +131 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/target.ts +871 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/tsconfig.json +16 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/wave/riff-writer.ts +36 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/wave/wave-demuxer.ts +583 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/wave/wave-muxer.ts +375 -0
- package/third-party/npm/mediabunny@1.52.2/source/src/writer.ts +128 -0
- package/third-party/npm/react-dom@19.3.0/LICENSE +21 -0
- package/third-party/npm/react-dom@19.3.0/package.json +117 -0
- package/third-party/npm/react-remove-scroll-bar@2.3.8/package.json +85 -0
- package/third-party/npm/react-remove-scroll-bar@2.3.8/react-remove-scroll-bar-LICENSE +21 -0
- package/third-party/npm/react-remove-scroll@2.7.2/LICENSE +21 -0
- package/third-party/npm/react-remove-scroll@2.7.2/package.json +98 -0
- package/third-party/npm/react-style-singleton@2.2.3/LICENSE +21 -0
- package/third-party/npm/react-style-singleton@2.2.3/package.json +90 -0
- package/third-party/npm/react@19.3.0/LICENSE +21 -0
- package/third-party/npm/react@19.3.0/package.json +51 -0
- package/third-party/npm/scheduler@0.28.0/LICENSE +21 -0
- package/third-party/npm/scheduler@0.28.0/package.json +27 -0
- package/third-party/npm/tslib@2.8.1/LICENSE.txt +12 -0
- package/third-party/npm/tslib@2.8.1/package.json +47 -0
- package/third-party/npm/typescript@5.9.3/LICENSE.txt +55 -0
- package/third-party/npm/typescript@5.9.3/ThirdPartyNoticeText.txt +193 -0
- package/third-party/npm/typescript@5.9.3/package.json +120 -0
- package/third-party/npm/use-callback-ref@1.3.3/LICENSE +21 -0
- package/third-party/npm/use-callback-ref@1.3.3/package.json +85 -0
- package/third-party/npm/use-sidecar@1.1.3/LICENSE +21 -0
- package/third-party/npm/use-sidecar@1.1.3/package.json +93 -0
- package/third-party/packages/render-engine/assets/OFL.txt +93 -0
- package/third-party/packages/studio/public/fonts/OFL.txt +93 -0
- package/third-party/packages/studio/vendor/osmo-icons/LICENSE +21 -0
- package/third-party/packages/studio/vendor/shadcn/LICENSE +21 -0
- package/third-party/repository-notices.md +41 -0
- package/third-party/rust-standard-library/COPYRIGHT-library.html +5556 -0
- package/third-party/rust-standard-library/licenses/Apache-2.0.txt +73 -0
- package/third-party/rust-standard-library/licenses/BSD-2-Clause.txt +9 -0
- package/third-party/rust-standard-library/licenses/CC-BY-SA-4.0.txt +427 -0
- package/third-party/rust-standard-library/licenses/GCC-exception-3.1.txt +30 -0
- package/third-party/rust-standard-library/licenses/GPL-2.0-only.txt +133 -0
- package/third-party/rust-standard-library/licenses/GPL-3.0-or-later.txt +202 -0
- package/third-party/rust-standard-library/licenses/ISC.txt +7 -0
- package/third-party/rust-standard-library/licenses/LLVM-exception.txt +15 -0
- package/third-party/rust-standard-library/licenses/MIT.txt +9 -0
- package/third-party/rust-standard-library/licenses/NCSA.txt +29 -0
- package/third-party/rust-standard-library/licenses/OFL-1.1.txt +43 -0
- package/third-party/rust-standard-library/licenses/Unicode-3.0.txt +39 -0
- package/third-party/rust-standard-library/version.txt +1 -0
- package/vendor/osmo-lite +0 -0
- package/vendor/osmo-scene-0.1.0.tgz +0 -0
- package/vendor/platform.json +1 -0
|
@@ -0,0 +1,2042 @@
|
|
|
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 {
|
|
10
|
+
AUDIO_CODECS,
|
|
11
|
+
AudioCodec,
|
|
12
|
+
MediaCodec,
|
|
13
|
+
NON_PCM_AUDIO_CODECS,
|
|
14
|
+
VIDEO_CODECS,
|
|
15
|
+
VideoCodec,
|
|
16
|
+
} from './codec';
|
|
17
|
+
import {
|
|
18
|
+
AudioEncodingConfig,
|
|
19
|
+
getEncodableAudioCodecs,
|
|
20
|
+
getFirstEncodableVideoCodec,
|
|
21
|
+
Quality,
|
|
22
|
+
resolveQuality,
|
|
23
|
+
VideoEncodingConfig,
|
|
24
|
+
} from './encode';
|
|
25
|
+
import { Input } from './input';
|
|
26
|
+
import { InputAudioTrack, InputTrack, InputVideoTrack } from './input-track';
|
|
27
|
+
import { Logging } from './logging';
|
|
28
|
+
import {
|
|
29
|
+
AudioSampleSink,
|
|
30
|
+
EncodedPacketSink,
|
|
31
|
+
VideoSampleSink,
|
|
32
|
+
} from './media-sink';
|
|
33
|
+
import {
|
|
34
|
+
AudioSource,
|
|
35
|
+
EncodedVideoPacketSource,
|
|
36
|
+
EncodedAudioPacketSource,
|
|
37
|
+
VideoSource,
|
|
38
|
+
VideoSampleSource,
|
|
39
|
+
AudioSampleSource,
|
|
40
|
+
} from './media-source';
|
|
41
|
+
import {
|
|
42
|
+
assert,
|
|
43
|
+
assertNever,
|
|
44
|
+
ceilToMultipleOfTwo,
|
|
45
|
+
clamp,
|
|
46
|
+
isIso639Dash2LanguageCode,
|
|
47
|
+
MaybePromise,
|
|
48
|
+
normalizeRotation,
|
|
49
|
+
promiseWithResolvers,
|
|
50
|
+
Rotation,
|
|
51
|
+
} from './misc';
|
|
52
|
+
import { Output, OutputTrackGroup } from './output';
|
|
53
|
+
import { Mp4OutputFormat } from './output-format';
|
|
54
|
+
import {
|
|
55
|
+
AudioSample,
|
|
56
|
+
clampCropRectangle,
|
|
57
|
+
CropRectangle,
|
|
58
|
+
getBytesPerSample,
|
|
59
|
+
validateCropRectangle,
|
|
60
|
+
VideoSample,
|
|
61
|
+
VideoSampleResource,
|
|
62
|
+
} from './sample';
|
|
63
|
+
import { MetadataTags, validateMetadataTags } from './metadata';
|
|
64
|
+
import { NullTarget } from './target';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The options for media file conversion.
|
|
68
|
+
* @group Conversion
|
|
69
|
+
* @public
|
|
70
|
+
*/
|
|
71
|
+
export type ConversionOptions = {
|
|
72
|
+
/** The input file. */
|
|
73
|
+
input: Input;
|
|
74
|
+
/** The output file. */
|
|
75
|
+
output: Output;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Defines which input tracks are used for conversion. Defaults to `'all'` unless the input is an HLS input, in
|
|
79
|
+
* which case it defaults to `'primary'`.
|
|
80
|
+
*
|
|
81
|
+
* - `'all'`: All input tracks are eligible for conversion.
|
|
82
|
+
* - `'primary'`: Only the primary video and audio track from the input are eligible for conversion.
|
|
83
|
+
*/
|
|
84
|
+
tracks?: 'all' | 'primary';
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Video-specific options. When passing an object, the same options are applied to all video tracks. When passing a
|
|
88
|
+
* function, it will be invoked for each video track and is expected to return or resolve to the options
|
|
89
|
+
* for that specific track. The function is passed an instance of {@link InputVideoTrack} as well as a number `n`,
|
|
90
|
+
* which is the 1-based index of the track in the list of all video tracks. Using `n` is deprecated, prefer the
|
|
91
|
+
* identical `track.number` instead.
|
|
92
|
+
*
|
|
93
|
+
* When passing an array of a function that returns an array, one output track per array element will be created,
|
|
94
|
+
* allowing for "fan-out". Useful for creating multiple variants from a single track, for example with different
|
|
95
|
+
* resolutions.
|
|
96
|
+
*/
|
|
97
|
+
video?: ConversionVideoOptions
|
|
98
|
+
| ConversionVideoOptions[]
|
|
99
|
+
| ((track: InputVideoTrack, n: number) => MaybePromise<
|
|
100
|
+
ConversionVideoOptions | ConversionVideoOptions[] | undefined
|
|
101
|
+
>);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Audio-specific options. When passing an object, the same options are applied to all audio tracks. When passing a
|
|
105
|
+
* function, it will be invoked for each audio track and is expected to return or resolve to the options
|
|
106
|
+
* for that specific track. The function is passed an instance of {@link InputAudioTrack} as well as a number `n`,
|
|
107
|
+
* which is the 1-based index of the track in the list of all audio tracks. Using `n` is deprecated, prefer the
|
|
108
|
+
* identical `track.number` instead.
|
|
109
|
+
*
|
|
110
|
+
* When passing an array of a function that returns an array, one output track per array element will be created,
|
|
111
|
+
* allowing for "fan-out". Useful for creating multiple variants from a single track, for example with different
|
|
112
|
+
* bitrates.
|
|
113
|
+
*/
|
|
114
|
+
audio?: ConversionAudioOptions
|
|
115
|
+
| ConversionAudioOptions[]
|
|
116
|
+
| ((track: InputAudioTrack, n: number) => MaybePromise<
|
|
117
|
+
ConversionAudioOptions | ConversionAudioOptions[] | undefined
|
|
118
|
+
>);
|
|
119
|
+
|
|
120
|
+
/** Options to trim the input file. */
|
|
121
|
+
trim?: {
|
|
122
|
+
/**
|
|
123
|
+
* The time in the input file in seconds at which the output file should start. Must be less than `end`.
|
|
124
|
+
* When omitted, defaults to the earliest start timestamp of the non-discarded tracks, or to 0, whichever
|
|
125
|
+
* is higher.
|
|
126
|
+
*/
|
|
127
|
+
start?: number;
|
|
128
|
+
/**
|
|
129
|
+
* The time in the input file in seconds at which the output file should end. Must be greater than `start`.
|
|
130
|
+
* Defaults to the duration of the input when omitted.
|
|
131
|
+
*/
|
|
132
|
+
end?: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* An object or a callback that returns or resolves to an object containing the descriptive metadata tags that
|
|
137
|
+
* should be written to the output file. If a function is passed, it will be passed the tags of the input file as
|
|
138
|
+
* its first argument, allowing you to modify, augment or extend them.
|
|
139
|
+
*
|
|
140
|
+
* If no function is set, the input's metadata tags will be copied to the output.
|
|
141
|
+
*/
|
|
142
|
+
tags?: MetadataTags | ((inputTags: MetadataTags) => MaybePromise<MetadataTags>);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Whether to show potential console warnings about discarded tracks after calling `Conversion.init()`, defaults to
|
|
146
|
+
* `true`. Set this to `false` if you're properly handling the `discardedTracks` and `isValid` fields already and
|
|
147
|
+
* want to keep the console output clean.
|
|
148
|
+
*/
|
|
149
|
+
showWarnings?: boolean;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Whether this conversion is composable, defaults to `false`. A non-composable conversion takes full ownership of
|
|
153
|
+
* the output: it requires a fresh output and controls its entire lifecycle, meaning it starts it, writes its
|
|
154
|
+
* metadata tags, and finalizes it.
|
|
155
|
+
*
|
|
156
|
+
* A composable conversion only adds tracks to the output and drives their media data; starting and finalizing
|
|
157
|
+
* the output is an outside responsibility. This is useful when only some output tracks should be driven by a
|
|
158
|
+
* conversion, and other are to be driven manually. Additionally, it can be used to have multiple conversions target
|
|
159
|
+
* the same output.
|
|
160
|
+
*/
|
|
161
|
+
composable?: boolean;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Video-specific options.
|
|
166
|
+
* @group Conversion
|
|
167
|
+
* @public
|
|
168
|
+
*/
|
|
169
|
+
export type ConversionVideoOptions = {
|
|
170
|
+
/** If `true`, all video tracks will be discarded and will not be present in the output. */
|
|
171
|
+
discard?: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* The desired width of the output video in pixels, defaulting to the video's natural display width. If height
|
|
174
|
+
* is not set, it will be deduced automatically based on aspect ratio.
|
|
175
|
+
*/
|
|
176
|
+
width?: number;
|
|
177
|
+
/**
|
|
178
|
+
* The desired height of the output video in pixels, defaulting to the video's natural display height. If width
|
|
179
|
+
* is not set, it will be deduced automatically based on aspect ratio.
|
|
180
|
+
*/
|
|
181
|
+
height?: number;
|
|
182
|
+
/**
|
|
183
|
+
* The fitting algorithm in case both width and height are set, or if the input video changes its size over time.
|
|
184
|
+
*
|
|
185
|
+
* - `'fill'` will stretch the image to fill the entire box, potentially altering aspect ratio.
|
|
186
|
+
* - `'contain'` will contain the entire image within the box while preserving aspect ratio. This may lead to
|
|
187
|
+
* letterboxing.
|
|
188
|
+
* - `'cover'` will scale the image until the entire box is filled, while preserving aspect ratio.
|
|
189
|
+
*/
|
|
190
|
+
fit?: 'fill' | 'contain' | 'cover';
|
|
191
|
+
/**
|
|
192
|
+
* The angle in degrees to rotate the input video by, clockwise. Rotation is applied before cropping and resizing.
|
|
193
|
+
* This rotation is _in addition to_ the natural rotation of the input video as specified in input file's metadata.
|
|
194
|
+
*/
|
|
195
|
+
rotate?: Rotation;
|
|
196
|
+
/**
|
|
197
|
+
* Defaults to `true`. When enabled, Mediabunny will use the rotation metadata in the output file to perform video
|
|
198
|
+
* rotation whenever possible. Set this field to `false` if you want to ensure the output file does not make use of
|
|
199
|
+
* rotation metadata and that any rotation is baked into the video frames directly.
|
|
200
|
+
*/
|
|
201
|
+
allowRotationMetadata?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Specifies the rectangular region of the input video to crop to. The crop region will automatically be clamped to
|
|
204
|
+
* the dimensions of the input video track. Cropping is performed after rotation but before resizing.
|
|
205
|
+
*/
|
|
206
|
+
crop?: CropRectangle;
|
|
207
|
+
/**
|
|
208
|
+
* The desired frame rate of the output video, in hertz. If not specified, the original input frame rate will
|
|
209
|
+
* be used (which may be variable).
|
|
210
|
+
*/
|
|
211
|
+
frameRate?: number;
|
|
212
|
+
/** The desired output video codec. */
|
|
213
|
+
codec?: VideoCodec;
|
|
214
|
+
/** The desired quality of the output video. */
|
|
215
|
+
quality?: Quality;
|
|
216
|
+
/**
|
|
217
|
+
* The desired bitrate of the output video.
|
|
218
|
+
* @deprecated Use `quality` instead.
|
|
219
|
+
*/
|
|
220
|
+
bitrate?: number | Quality;
|
|
221
|
+
/**
|
|
222
|
+
* Whether to discard or keep the transparency information of the input video. The default is `'discard'`. Note that
|
|
223
|
+
* for `'keep'` to produce a transparent video, you must use an output config that supports it, such as WebM with
|
|
224
|
+
* VP9.
|
|
225
|
+
*/
|
|
226
|
+
alpha?: 'discard' | 'keep';
|
|
227
|
+
/**
|
|
228
|
+
* The interval, in seconds, of how often frames are encoded as a key frame. The default is 5 seconds. Frequent key
|
|
229
|
+
* frames improve seeking behavior but increase file size. When using multiple video tracks, you should give them
|
|
230
|
+
* all the same key frame interval.
|
|
231
|
+
*
|
|
232
|
+
* Setting this fields forces a transcode.
|
|
233
|
+
*/
|
|
234
|
+
keyFrameInterval?: number;
|
|
235
|
+
/**
|
|
236
|
+
* A hint that configures the hardware acceleration method used when transcoding. This is best left on
|
|
237
|
+
* `'no-preference'`, the default.
|
|
238
|
+
*/
|
|
239
|
+
hardwareAcceleration?: 'no-preference' | 'prefer-hardware' | 'prefer-software';
|
|
240
|
+
/** When `true`, video will always be re-encoded instead of directly copying over the encoded samples. */
|
|
241
|
+
forceTranscode?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Allows for custom user-defined processing of video frames, e.g. for applying overlays, color transformations, or
|
|
244
|
+
* timestamp modifications. Will be called for each input video sample after transformations and frame rate
|
|
245
|
+
* corrections.
|
|
246
|
+
*
|
|
247
|
+
* Must return a {@link VideoSample}, a {@link VideoSampleResource} or a `CanvasImageSource`, an array of them, or
|
|
248
|
+
* `null` for dropping the frame. When non-timestamped data is returned, the timestamp and duration from the source
|
|
249
|
+
* sample will be used. Rotation metadata of the returned sample will be ignored.
|
|
250
|
+
*
|
|
251
|
+
* This function can also be used to manually resize frames. When doing so, you should signal the post-process
|
|
252
|
+
* dimensions using the `processedWidth` and `processedHeight` fields, which enables the encoder to better know what
|
|
253
|
+
* to expect. If these fields aren't set, Mediabunny will assume you won't perform any resizing.
|
|
254
|
+
*/
|
|
255
|
+
process?: (sample: VideoSample) => MaybePromise<
|
|
256
|
+
CanvasImageSource | VideoSample | VideoSampleResource
|
|
257
|
+
| (CanvasImageSource | VideoSample | VideoSampleResource)[] | null
|
|
258
|
+
>;
|
|
259
|
+
/**
|
|
260
|
+
* An optional hint specifying the width of video samples returned by the `process` function, for better
|
|
261
|
+
* encoder configuration.
|
|
262
|
+
*/
|
|
263
|
+
processedWidth?: number;
|
|
264
|
+
/**
|
|
265
|
+
* An optional hint specifying the height of video samples returned by the `process` function, for better
|
|
266
|
+
* encoder configuration.
|
|
267
|
+
*/
|
|
268
|
+
processedHeight?: number;
|
|
269
|
+
/**
|
|
270
|
+
* Defines the group(s) the output track is a part of. For more, see {@link BaseTrackMetadata.group}.
|
|
271
|
+
*
|
|
272
|
+
* If left blank, tracks will internally be assigned to groups such that the output track pairability graph exactly
|
|
273
|
+
* matches the input track pairability graph.
|
|
274
|
+
*/
|
|
275
|
+
group?: OutputTrackGroup | OutputTrackGroup[];
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Audio-specific options.
|
|
280
|
+
* @group Conversion
|
|
281
|
+
* @public
|
|
282
|
+
*/
|
|
283
|
+
export type ConversionAudioOptions = {
|
|
284
|
+
/** If `true`, all audio tracks will be discarded and will not be present in the output. */
|
|
285
|
+
discard?: boolean;
|
|
286
|
+
/** The desired channel count of the output audio. */
|
|
287
|
+
numberOfChannels?: number;
|
|
288
|
+
/** The desired sample rate of the output audio, in hertz. */
|
|
289
|
+
sampleRate?: number;
|
|
290
|
+
/**
|
|
291
|
+
* The desired sample format (and therefore bit depth) of the audio samples before they are passed to the encoder.
|
|
292
|
+
* Can be used to control bit depth with certain output codecs such as FLAC.
|
|
293
|
+
*
|
|
294
|
+
* Setting this field forces audio transcoding.
|
|
295
|
+
*/
|
|
296
|
+
sampleFormat?: 'u8' | 's16' | 's32' | 'f32';
|
|
297
|
+
/** The desired output audio codec. */
|
|
298
|
+
codec?: AudioCodec;
|
|
299
|
+
/** The desired quality of the output audio. */
|
|
300
|
+
quality?: Quality;
|
|
301
|
+
/**
|
|
302
|
+
* The desired bitrate of the output audio.
|
|
303
|
+
* @deprecated Use `quality` instead.
|
|
304
|
+
*/
|
|
305
|
+
bitrate?: number | Quality;
|
|
306
|
+
/** When `true`, audio will always be re-encoded instead of directly copying over the encoded samples. */
|
|
307
|
+
forceTranscode?: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* Allows for custom user-defined processing of audio samples, e.g. for applying audio effects, transformations, or
|
|
310
|
+
* timestamp modifications. Will be called for each input audio sample after remixing and resampling.
|
|
311
|
+
*
|
|
312
|
+
* Must return an {@link AudioSample}, an array of them, or `null` for dropping the sample.
|
|
313
|
+
*
|
|
314
|
+
* This function can also be used to manually perform remixing or resampling. When doing so, you should signal the
|
|
315
|
+
* post-process parameters using the `processedNumberOfChannels` and `processedSampleRate` fields, which enables the
|
|
316
|
+
* encoder to better know what to expect. If these fields aren't set, Mediabunny will assume you won't perform
|
|
317
|
+
* remixing or resampling.
|
|
318
|
+
*/
|
|
319
|
+
process?: (sample: AudioSample) => MaybePromise<
|
|
320
|
+
AudioSample | AudioSample[] | null
|
|
321
|
+
>;
|
|
322
|
+
/**
|
|
323
|
+
* An optional hint specifying the channel count of audio samples returned by the `process` function, for better
|
|
324
|
+
* encoder configuration.
|
|
325
|
+
*/
|
|
326
|
+
processedNumberOfChannels?: number;
|
|
327
|
+
/**
|
|
328
|
+
* An optional hint specifying the sample rate of audio samples returned by the `process` function, for better
|
|
329
|
+
* encoder configuration.
|
|
330
|
+
*/
|
|
331
|
+
processedSampleRate?: number;
|
|
332
|
+
/**
|
|
333
|
+
* Defines the group(s) the output track is a part of. For more, see {@link BaseTrackMetadata.group}.
|
|
334
|
+
*
|
|
335
|
+
* If left blank, tracks will internally be assigned to groups such that the output track pairability graph exactly
|
|
336
|
+
* matches the input track pairability graph.
|
|
337
|
+
*/
|
|
338
|
+
group?: OutputTrackGroup | OutputTrackGroup[];
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const validateVideoOptions = (videoOptions: ConversionVideoOptions) => {
|
|
342
|
+
if (!videoOptions || typeof videoOptions !== 'object') {
|
|
343
|
+
throw new TypeError('options.video, when provided, must be an object.');
|
|
344
|
+
}
|
|
345
|
+
if (videoOptions?.discard !== undefined && typeof videoOptions.discard !== 'boolean') {
|
|
346
|
+
throw new TypeError('options.video.discard, when provided, must be a boolean.');
|
|
347
|
+
}
|
|
348
|
+
if (videoOptions?.forceTranscode !== undefined && typeof videoOptions.forceTranscode !== 'boolean') {
|
|
349
|
+
throw new TypeError('options.video.forceTranscode, when provided, must be a boolean.');
|
|
350
|
+
}
|
|
351
|
+
if (videoOptions?.codec !== undefined && !VIDEO_CODECS.includes(videoOptions.codec)) {
|
|
352
|
+
throw new TypeError(
|
|
353
|
+
`options.video.codec, when provided, must be one of: ${VIDEO_CODECS.join(', ')}.`,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
357
|
+
const bitrate = videoOptions?.bitrate;
|
|
358
|
+
if (videoOptions?.quality !== undefined && !(videoOptions.quality instanceof Quality)) {
|
|
359
|
+
throw new TypeError('options.video.quality, when provided, must be a Quality.');
|
|
360
|
+
}
|
|
361
|
+
if (videoOptions?.quality !== undefined && bitrate !== undefined) {
|
|
362
|
+
throw new TypeError('options.video.quality and options.video.bitrate cannot both be provided.');
|
|
363
|
+
}
|
|
364
|
+
if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {
|
|
365
|
+
throw new TypeError('options.video.bitrate, when provided, must be a positive integer or a quality.');
|
|
366
|
+
}
|
|
367
|
+
if (
|
|
368
|
+
videoOptions?.width !== undefined
|
|
369
|
+
&& (!Number.isInteger(videoOptions.width) || videoOptions.width <= 0)
|
|
370
|
+
) {
|
|
371
|
+
throw new TypeError('options.video.width, when provided, must be a positive integer.');
|
|
372
|
+
}
|
|
373
|
+
if (
|
|
374
|
+
videoOptions?.height !== undefined
|
|
375
|
+
&& (!Number.isInteger(videoOptions.height) || videoOptions.height <= 0)
|
|
376
|
+
) {
|
|
377
|
+
throw new TypeError('options.video.height, when provided, must be a positive integer.');
|
|
378
|
+
}
|
|
379
|
+
if (videoOptions?.fit !== undefined && !['fill', 'contain', 'cover'].includes(videoOptions.fit)) {
|
|
380
|
+
throw new TypeError('options.video.fit, when provided, must be one of \'fill\', \'contain\', or \'cover\'.');
|
|
381
|
+
}
|
|
382
|
+
if (
|
|
383
|
+
videoOptions?.width !== undefined
|
|
384
|
+
&& videoOptions.height !== undefined
|
|
385
|
+
&& videoOptions.fit === undefined
|
|
386
|
+
) {
|
|
387
|
+
throw new TypeError(
|
|
388
|
+
'When both options.video.width and options.video.height are provided, options.video.fit must also be'
|
|
389
|
+
+ ' provided.',
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
if (videoOptions?.rotate !== undefined && ![0, 90, 180, 270].includes(videoOptions.rotate)) {
|
|
393
|
+
throw new TypeError('options.video.rotate, when provided, must be 0, 90, 180 or 270.');
|
|
394
|
+
}
|
|
395
|
+
if (videoOptions?.allowRotationMetadata !== undefined && typeof videoOptions.allowRotationMetadata !== 'boolean') {
|
|
396
|
+
throw new TypeError('options.video.allowRotationMetadata, when provided, must be a boolean.');
|
|
397
|
+
}
|
|
398
|
+
if (videoOptions?.crop !== undefined) {
|
|
399
|
+
validateCropRectangle(videoOptions.crop, 'options.video.');
|
|
400
|
+
}
|
|
401
|
+
if (
|
|
402
|
+
videoOptions?.frameRate !== undefined
|
|
403
|
+
&& (!Number.isFinite(videoOptions.frameRate) || videoOptions.frameRate <= 0)
|
|
404
|
+
) {
|
|
405
|
+
throw new TypeError('options.video.frameRate, when provided, must be a finite positive number.');
|
|
406
|
+
}
|
|
407
|
+
if (videoOptions?.alpha !== undefined && !['discard', 'keep'].includes(videoOptions.alpha)) {
|
|
408
|
+
throw new TypeError('options.video.alpha, when provided, must be either \'discard\' or \'keep\'.');
|
|
409
|
+
}
|
|
410
|
+
if (
|
|
411
|
+
videoOptions?.keyFrameInterval !== undefined
|
|
412
|
+
&& (!Number.isFinite(videoOptions.keyFrameInterval) || videoOptions.keyFrameInterval < 0)
|
|
413
|
+
) {
|
|
414
|
+
throw new TypeError('options.video.keyFrameInterval, when provided, must be a non-negative number.');
|
|
415
|
+
}
|
|
416
|
+
if (videoOptions?.process !== undefined && typeof videoOptions.process !== 'function') {
|
|
417
|
+
throw new TypeError('options.video.process, when provided, must be a function.');
|
|
418
|
+
}
|
|
419
|
+
if (
|
|
420
|
+
videoOptions?.processedWidth !== undefined
|
|
421
|
+
&& (!Number.isInteger(videoOptions.processedWidth) || videoOptions.processedWidth <= 0)
|
|
422
|
+
) {
|
|
423
|
+
throw new TypeError('options.video.processedWidth, when provided, must be a positive integer.');
|
|
424
|
+
}
|
|
425
|
+
if (
|
|
426
|
+
videoOptions?.processedHeight !== undefined
|
|
427
|
+
&& (!Number.isInteger(videoOptions.processedHeight) || videoOptions.processedHeight <= 0)
|
|
428
|
+
) {
|
|
429
|
+
throw new TypeError('options.video.processedHeight, when provided, must be a positive integer.');
|
|
430
|
+
}
|
|
431
|
+
if (
|
|
432
|
+
videoOptions?.hardwareAcceleration !== undefined
|
|
433
|
+
&& !['no-preference', 'prefer-hardware', 'prefer-software'].includes(videoOptions.hardwareAcceleration)
|
|
434
|
+
) {
|
|
435
|
+
throw new TypeError(
|
|
436
|
+
'options.video.hardwareAcceleration, when provided, must be \'no-preference\', \'prefer-hardware\' or'
|
|
437
|
+
+ ' \'prefer-software\'.',
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
if (
|
|
441
|
+
videoOptions?.group !== undefined
|
|
442
|
+
&& !(
|
|
443
|
+
videoOptions.group instanceof OutputTrackGroup
|
|
444
|
+
|| (Array.isArray(videoOptions.group) && videoOptions.group.every(x => x instanceof OutputTrackGroup))
|
|
445
|
+
)
|
|
446
|
+
) {
|
|
447
|
+
throw new TypeError(
|
|
448
|
+
'options.video.group, when provided, must be an OutputTrackGroup or an array of OutputTrackGroups.',
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const validateAudioOptions = (audioOptions: ConversionAudioOptions) => {
|
|
454
|
+
if (!audioOptions || typeof audioOptions !== 'object') {
|
|
455
|
+
throw new TypeError('options.audio, when provided, must be an object.');
|
|
456
|
+
}
|
|
457
|
+
if (audioOptions?.discard !== undefined && typeof audioOptions.discard !== 'boolean') {
|
|
458
|
+
throw new TypeError('options.audio.discard, when provided, must be a boolean.');
|
|
459
|
+
}
|
|
460
|
+
if (audioOptions?.forceTranscode !== undefined && typeof audioOptions.forceTranscode !== 'boolean') {
|
|
461
|
+
throw new TypeError('options.audio.forceTranscode, when provided, must be a boolean.');
|
|
462
|
+
}
|
|
463
|
+
if (audioOptions?.codec !== undefined && !AUDIO_CODECS.includes(audioOptions.codec)) {
|
|
464
|
+
throw new TypeError(
|
|
465
|
+
`options.audio.codec, when provided, must be one of: ${AUDIO_CODECS.join(', ')}.`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
469
|
+
const bitrate = audioOptions?.bitrate;
|
|
470
|
+
if (audioOptions?.quality !== undefined && !(audioOptions.quality instanceof Quality)) {
|
|
471
|
+
throw new TypeError('options.audio.quality, when provided, must be a Quality.');
|
|
472
|
+
}
|
|
473
|
+
if (audioOptions?.quality !== undefined && bitrate !== undefined) {
|
|
474
|
+
throw new TypeError('options.audio.quality and options.audio.bitrate cannot both be provided.');
|
|
475
|
+
}
|
|
476
|
+
if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {
|
|
477
|
+
throw new TypeError('options.audio.bitrate, when provided, must be a positive integer or a quality.');
|
|
478
|
+
}
|
|
479
|
+
if (
|
|
480
|
+
audioOptions?.numberOfChannels !== undefined
|
|
481
|
+
&& (!Number.isInteger(audioOptions.numberOfChannels) || audioOptions.numberOfChannels <= 0)
|
|
482
|
+
) {
|
|
483
|
+
throw new TypeError('options.audio.numberOfChannels, when provided, must be a positive integer.');
|
|
484
|
+
}
|
|
485
|
+
if (
|
|
486
|
+
audioOptions?.sampleRate !== undefined
|
|
487
|
+
&& (!Number.isInteger(audioOptions.sampleRate) || audioOptions.sampleRate <= 0)
|
|
488
|
+
) {
|
|
489
|
+
throw new TypeError('options.audio.sampleRate, when provided, must be a positive integer.');
|
|
490
|
+
}
|
|
491
|
+
if (
|
|
492
|
+
audioOptions?.sampleFormat !== undefined
|
|
493
|
+
&& !['u8', 's16', 's32', 'f32'].includes(audioOptions.sampleFormat)
|
|
494
|
+
) {
|
|
495
|
+
throw new TypeError('options.audio.sampleFormat, when provided, must be one of: u8, s16, s32, f32.');
|
|
496
|
+
}
|
|
497
|
+
if (audioOptions?.process !== undefined && typeof audioOptions.process !== 'function') {
|
|
498
|
+
throw new TypeError('options.audio.process, when provided, must be a function.');
|
|
499
|
+
}
|
|
500
|
+
if (
|
|
501
|
+
audioOptions?.processedNumberOfChannels !== undefined
|
|
502
|
+
&& (!Number.isInteger(audioOptions.processedNumberOfChannels) || audioOptions.processedNumberOfChannels <= 0)
|
|
503
|
+
) {
|
|
504
|
+
throw new TypeError('options.audio.processedNumberOfChannels, when provided, must be a positive integer.');
|
|
505
|
+
}
|
|
506
|
+
if (
|
|
507
|
+
audioOptions?.processedSampleRate !== undefined
|
|
508
|
+
&& (!Number.isInteger(audioOptions.processedSampleRate) || audioOptions.processedSampleRate <= 0)
|
|
509
|
+
) {
|
|
510
|
+
throw new TypeError('options.audio.processedSampleRate, when provided, must be a positive integer.');
|
|
511
|
+
}
|
|
512
|
+
if (
|
|
513
|
+
audioOptions?.group !== undefined
|
|
514
|
+
&& !(
|
|
515
|
+
audioOptions.group instanceof OutputTrackGroup
|
|
516
|
+
|| (Array.isArray(audioOptions.group) && audioOptions.group.every(x => x instanceof OutputTrackGroup))
|
|
517
|
+
)
|
|
518
|
+
) {
|
|
519
|
+
throw new TypeError(
|
|
520
|
+
'options.audio.group, when provided, must be an OutputTrackGroup or an array of OutputTrackGroups.',
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const FALLBACK_NUMBER_OF_CHANNELS = 2;
|
|
526
|
+
const FALLBACK_SAMPLE_RATE = 48000;
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* An input track that was discarded (excluded) from a {@link Conversion} alongside the discard reason.
|
|
530
|
+
* @group Conversion
|
|
531
|
+
* @public
|
|
532
|
+
*/
|
|
533
|
+
export type DiscardedTrack = {
|
|
534
|
+
/** The track that was discarded. */
|
|
535
|
+
track: InputTrack;
|
|
536
|
+
/**
|
|
537
|
+
* The reason for discarding the track.
|
|
538
|
+
*
|
|
539
|
+
* - `'discarded_by_user'`: You discarded this track by setting `discard: true`.
|
|
540
|
+
* - `'max_track_count_reached'`: The output had no more room for another track.
|
|
541
|
+
* - `'max_track_count_of_type_reached'`: The output had no more room for another track of this type, or the output
|
|
542
|
+
* doesn't support this track type at all.
|
|
543
|
+
* - `'unknown_source_codec'`: We don't know the codec of the input track and therefore don't know what to do
|
|
544
|
+
* with it.
|
|
545
|
+
* - `'undecodable_source_codec'`: The input track's codec is known, but we are unable to decode it.
|
|
546
|
+
* - `'no_encodable_target_codec'`: We can't find a codec that we are able to encode and that can be contained
|
|
547
|
+
* within the output format. This reason can be hit if the environment doesn't support the necessary encoders, or if
|
|
548
|
+
* you requested a codec that cannot be contained within the output format.
|
|
549
|
+
*/
|
|
550
|
+
reason:
|
|
551
|
+
| 'discarded_by_user'
|
|
552
|
+
| 'max_track_count_reached'
|
|
553
|
+
| 'max_track_count_of_type_reached'
|
|
554
|
+
| 'unknown_source_codec'
|
|
555
|
+
| 'undecodable_source_codec'
|
|
556
|
+
| 'no_encodable_target_codec';
|
|
557
|
+
/** The options that were provided for this track, or `{}` if none were provided. */
|
|
558
|
+
trackOptions: ConversionVideoOptions | ConversionAudioOptions;
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Options for controlling a single call to {@link Conversion.execute}.
|
|
563
|
+
* @group Conversion
|
|
564
|
+
* @public
|
|
565
|
+
*/
|
|
566
|
+
export type ConversionExecuteOptions = {
|
|
567
|
+
/**
|
|
568
|
+
* The timestamp in seconds, in the output's timescale, until which the conversion should advance. Defaults to
|
|
569
|
+
* `Infinity`, meaning the conversion runs until the end.
|
|
570
|
+
*
|
|
571
|
+
* This field is especially useful for composable conversions, as it allows you to advance the conversion in
|
|
572
|
+
* lockstep with other media data sources.
|
|
573
|
+
*/
|
|
574
|
+
until?: number;
|
|
575
|
+
/**
|
|
576
|
+
* A signal that, when triggered, pauses the conversion as soon as possible.
|
|
577
|
+
*/
|
|
578
|
+
pauseSignal?: AbortSignal;
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
type TrackPump = {
|
|
582
|
+
done: boolean;
|
|
583
|
+
resolvers: ReturnType<typeof promiseWithResolvers<void>>;
|
|
584
|
+
wake: (() => void) | null;
|
|
585
|
+
start: () => void;
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Represents a media file conversion process, used to convert one media file into another. In addition to conversion,
|
|
590
|
+
* this class can be used to resize and rotate video, resample audio, drop tracks, or trim to a specific time range.
|
|
591
|
+
* @group Conversion
|
|
592
|
+
* @public
|
|
593
|
+
*/
|
|
594
|
+
export class Conversion {
|
|
595
|
+
/** The input file. */
|
|
596
|
+
readonly input: Input;
|
|
597
|
+
/** The output file. */
|
|
598
|
+
readonly output: Output;
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* The current state of the conversion.
|
|
602
|
+
*
|
|
603
|
+
* - `'idle'`: The conversion is not currently executing and isn't done; `execute` can be called.
|
|
604
|
+
* - `'executing'`: A call to `execute` is currently running.
|
|
605
|
+
* - `'canceled'`: The conversion has been canceled and can no longer be executed.
|
|
606
|
+
* - `'done'`: The conversion has run to completion. Subsequent calls to `execute` do nothing.
|
|
607
|
+
*/
|
|
608
|
+
state: 'idle' | 'executing' | 'canceled' | 'done' = 'idle';
|
|
609
|
+
|
|
610
|
+
/** @internal */
|
|
611
|
+
_options: ConversionOptions;
|
|
612
|
+
/** @internal */
|
|
613
|
+
_startTimestamp!: number;
|
|
614
|
+
/** @internal */
|
|
615
|
+
_endTimestamp!: number;
|
|
616
|
+
|
|
617
|
+
/** @internal */
|
|
618
|
+
_nextOutputTrackId = 0;
|
|
619
|
+
/** @internal */
|
|
620
|
+
_outputTrackIds: number[] = [];
|
|
621
|
+
/** @internal */
|
|
622
|
+
_outputOwnTrackGroups: (OutputTrackGroup | null)[] = [];
|
|
623
|
+
|
|
624
|
+
/** @internal */
|
|
625
|
+
_trackPumps: TrackPump[] = [];
|
|
626
|
+
/** @internal */
|
|
627
|
+
_composable = false;
|
|
628
|
+
|
|
629
|
+
/** @internal */
|
|
630
|
+
_executed = false;
|
|
631
|
+
/** @internal */
|
|
632
|
+
_executionUntil = Infinity;
|
|
633
|
+
/** @internal */
|
|
634
|
+
_pauseRequested = false;
|
|
635
|
+
|
|
636
|
+
/** @internal */
|
|
637
|
+
_synchronizer = new TrackSynchronizer(this);
|
|
638
|
+
|
|
639
|
+
/** @internal */
|
|
640
|
+
_totalDuration: number | null = null;
|
|
641
|
+
/** @internal */
|
|
642
|
+
_maxTimestamps = new Map<number, number>(); // Track ID -> timestamp
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* A callback that is fired whenever the conversion progresses. Gets passed as first argument a number between
|
|
646
|
+
* 0 and 1, indicating the completion of the conversion. Note that a progress of 1 doesn't necessarily mean the
|
|
647
|
+
* conversion is complete; the conversion is complete once `execute()` resolves.
|
|
648
|
+
*
|
|
649
|
+
* As second argument, this callback receives the input time in seconds that has been processed.
|
|
650
|
+
*
|
|
651
|
+
* In order for progress to be computed, this property must be set before `execute` is called.
|
|
652
|
+
*/
|
|
653
|
+
onProgress?: (progress: number, processedTime: number) => unknown = undefined;
|
|
654
|
+
/** @internal */
|
|
655
|
+
_computeProgress = false;
|
|
656
|
+
/** @internal */
|
|
657
|
+
_lastProgress = 0;
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Whether this conversion, as it has been configured, is valid and can be executed. If this field is `false`, check
|
|
661
|
+
* the `discardedTracks` field for reasons. Composable conversions are always valid, even if they utilize
|
|
662
|
+
* zero tracks.
|
|
663
|
+
*
|
|
664
|
+
* Note: a conversion having discarded tracks does not automatically mean it is invalid; if the remaining, utilized
|
|
665
|
+
* tracks make for a valid output file, the conversion is still allowed.
|
|
666
|
+
*/
|
|
667
|
+
isValid = false;
|
|
668
|
+
/**
|
|
669
|
+
* The list of tracks that are included in the output file. When fan-out is used, the same track appears in this
|
|
670
|
+
* array multiple times.
|
|
671
|
+
*/
|
|
672
|
+
readonly utilizedTracks: InputTrack[] = [];
|
|
673
|
+
/** The list of tracks from the input file that have been discarded, alongside the discard reason. */
|
|
674
|
+
readonly discardedTracks: DiscardedTrack[] = [];
|
|
675
|
+
|
|
676
|
+
/** Initializes a new conversion process without starting the conversion. */
|
|
677
|
+
static async init(options: ConversionOptions) {
|
|
678
|
+
const conversion = new Conversion(options);
|
|
679
|
+
await conversion._init();
|
|
680
|
+
|
|
681
|
+
return conversion;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** Creates a new Conversion instance (duh). */
|
|
685
|
+
private constructor(options: ConversionOptions) {
|
|
686
|
+
if (!options || typeof options !== 'object') {
|
|
687
|
+
throw new TypeError('options must be an object.');
|
|
688
|
+
}
|
|
689
|
+
if (!(options.input instanceof Input)) {
|
|
690
|
+
throw new TypeError('options.input must be an Input.');
|
|
691
|
+
}
|
|
692
|
+
if (!(options.output instanceof Output)) {
|
|
693
|
+
throw new TypeError('options.output must be an Output.');
|
|
694
|
+
}
|
|
695
|
+
if (
|
|
696
|
+
options.tracks !== undefined
|
|
697
|
+
&& options.tracks !== 'all'
|
|
698
|
+
&& options.tracks !== 'primary'
|
|
699
|
+
) {
|
|
700
|
+
throw new TypeError(
|
|
701
|
+
'options.tracks, when provided, must be either \'all\' or \'primary\'.',
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
if (options.composable !== undefined && typeof options.composable !== 'boolean') {
|
|
705
|
+
throw new TypeError('options.composable, when provided, must be a boolean.');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const composable = options.composable ?? false;
|
|
709
|
+
if (!composable) {
|
|
710
|
+
if (
|
|
711
|
+
options.output.tracks.length > 0
|
|
712
|
+
|| Object.keys(options.output._metadataTags).length > 0
|
|
713
|
+
|| options.output.state !== 'pending'
|
|
714
|
+
) {
|
|
715
|
+
throw new TypeError('options.output must be fresh: no tracks or metadata tags added and not started.');
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
if (options.tags !== undefined) {
|
|
719
|
+
throw new TypeError(
|
|
720
|
+
'options.tags cannot be set by a composable conversion; set metadata directly on the output'
|
|
721
|
+
+ ' instead.',
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (options.output.state !== 'pending') {
|
|
726
|
+
throw new TypeError('options.output must not have been started yet.');
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (options.video !== undefined && typeof options.video !== 'function') {
|
|
731
|
+
if (Array.isArray(options.video)) {
|
|
732
|
+
for (const obj of options.video) {
|
|
733
|
+
validateVideoOptions(obj);
|
|
734
|
+
}
|
|
735
|
+
} else {
|
|
736
|
+
validateVideoOptions(options.video);
|
|
737
|
+
}
|
|
738
|
+
} else {
|
|
739
|
+
// We'll validate the return value later
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (options.audio !== undefined && typeof options.audio !== 'function') {
|
|
743
|
+
if (Array.isArray(options.audio)) {
|
|
744
|
+
for (const obj of options.audio) {
|
|
745
|
+
validateAudioOptions(obj);
|
|
746
|
+
}
|
|
747
|
+
} else {
|
|
748
|
+
validateAudioOptions(options.audio);
|
|
749
|
+
}
|
|
750
|
+
} else {
|
|
751
|
+
// We'll validate the return value later
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (options.trim !== undefined && (!options.trim || typeof options.trim !== 'object')) {
|
|
755
|
+
throw new TypeError('options.trim, when provided, must be an object.');
|
|
756
|
+
}
|
|
757
|
+
if (options.trim?.start !== undefined && (!Number.isFinite(options.trim.start))) {
|
|
758
|
+
throw new TypeError('options.trim.start, when provided, must be a finite number.');
|
|
759
|
+
}
|
|
760
|
+
if (options.trim?.end !== undefined && (!Number.isFinite(options.trim.end))) {
|
|
761
|
+
throw new TypeError('options.trim.end, when provided, must be a finite number.');
|
|
762
|
+
}
|
|
763
|
+
if (
|
|
764
|
+
options.trim?.start !== undefined
|
|
765
|
+
&& options.trim.end !== undefined
|
|
766
|
+
&& options.trim.start >= options.trim.end) {
|
|
767
|
+
throw new TypeError('options.trim.start must be less than options.trim.end.');
|
|
768
|
+
}
|
|
769
|
+
if (
|
|
770
|
+
options.tags !== undefined
|
|
771
|
+
&& (typeof options.tags !== 'object' || !options.tags)
|
|
772
|
+
&& typeof options.tags !== 'function'
|
|
773
|
+
) {
|
|
774
|
+
throw new TypeError('options.tags, when provided, must be an object or a function.');
|
|
775
|
+
}
|
|
776
|
+
if (typeof options.tags === 'object') {
|
|
777
|
+
validateMetadataTags(options.tags);
|
|
778
|
+
}
|
|
779
|
+
if (options.showWarnings !== undefined && typeof options.showWarnings !== 'boolean') {
|
|
780
|
+
throw new TypeError('options.showWarnings, when provided, must be a boolean.');
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
this._options = options;
|
|
784
|
+
this._composable = composable;
|
|
785
|
+
this.input = options.input;
|
|
786
|
+
this.output = options.output;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** @internal */
|
|
790
|
+
async _init() {
|
|
791
|
+
const inputFormat = await this.input.getFormat();
|
|
792
|
+
|
|
793
|
+
let tracks: InputTrack[];
|
|
794
|
+
|
|
795
|
+
let trackMode = this._options.tracks;
|
|
796
|
+
if (trackMode === undefined) {
|
|
797
|
+
// HACK to keep bundle size low, temp for now
|
|
798
|
+
const defaultTrackMode = inputFormat.name.includes('(HLS)')
|
|
799
|
+
? 'primary'
|
|
800
|
+
: 'all';
|
|
801
|
+
|
|
802
|
+
trackMode = defaultTrackMode;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
if (trackMode === 'all') {
|
|
806
|
+
tracks = await this.input.getTracks();
|
|
807
|
+
} else if (trackMode === 'primary') {
|
|
808
|
+
const primaryVideoTrack = await this.input.getPrimaryVideoTrack();
|
|
809
|
+
const primaryAudioTrack = await this.input.getPrimaryAudioTrack();
|
|
810
|
+
|
|
811
|
+
tracks = [primaryVideoTrack, primaryAudioTrack].filter(x => x !== null);
|
|
812
|
+
} else {
|
|
813
|
+
assertNever(trackMode);
|
|
814
|
+
assert(false);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const outputTrackCounts = this.output.format.getSupportedTrackCounts();
|
|
818
|
+
|
|
819
|
+
// Input track counters
|
|
820
|
+
let nVideo = 1;
|
|
821
|
+
let nAudio = 1;
|
|
822
|
+
|
|
823
|
+
// All tracks that aren't discarded by the user
|
|
824
|
+
const filteredTracks: InputTrack[] = [];
|
|
825
|
+
const filteredTrackOptions: (ConversionVideoOptions | ConversionAudioOptions)[][] = [];
|
|
826
|
+
|
|
827
|
+
for (const track of tracks) {
|
|
828
|
+
let trackOptions: (ConversionVideoOptions | ConversionAudioOptions)[];
|
|
829
|
+
|
|
830
|
+
if (track.isVideoTrack()) {
|
|
831
|
+
if (this._options.video) {
|
|
832
|
+
if (typeof this._options.video === 'function') {
|
|
833
|
+
const returnedTrackOptions = await this._options.video(track, nVideo) ?? {};
|
|
834
|
+
if (Array.isArray(returnedTrackOptions)) {
|
|
835
|
+
for (const obj of returnedTrackOptions) {
|
|
836
|
+
validateVideoOptions(obj);
|
|
837
|
+
}
|
|
838
|
+
} else {
|
|
839
|
+
validateVideoOptions(returnedTrackOptions);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
trackOptions = Array.isArray(returnedTrackOptions)
|
|
843
|
+
? returnedTrackOptions
|
|
844
|
+
: [returnedTrackOptions];
|
|
845
|
+
|
|
846
|
+
nVideo++;
|
|
847
|
+
} else {
|
|
848
|
+
// Already validated
|
|
849
|
+
trackOptions = Array.isArray(this._options.video)
|
|
850
|
+
? this._options.video
|
|
851
|
+
: [this._options.video];
|
|
852
|
+
}
|
|
853
|
+
} else {
|
|
854
|
+
trackOptions = [{}];
|
|
855
|
+
}
|
|
856
|
+
} else if (track.isAudioTrack()) {
|
|
857
|
+
if (this._options.audio) {
|
|
858
|
+
if (typeof this._options.audio === 'function') {
|
|
859
|
+
const returnedTrackOptions = await this._options.audio(track, nAudio) ?? {};
|
|
860
|
+
if (Array.isArray(returnedTrackOptions)) {
|
|
861
|
+
for (const obj of returnedTrackOptions) {
|
|
862
|
+
validateAudioOptions(obj);
|
|
863
|
+
}
|
|
864
|
+
} else {
|
|
865
|
+
validateAudioOptions(returnedTrackOptions);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
trackOptions = Array.isArray(returnedTrackOptions)
|
|
869
|
+
? returnedTrackOptions
|
|
870
|
+
: [returnedTrackOptions];
|
|
871
|
+
|
|
872
|
+
nAudio++;
|
|
873
|
+
} else {
|
|
874
|
+
// Already validated
|
|
875
|
+
trackOptions = Array.isArray(this._options.audio)
|
|
876
|
+
? this._options.audio
|
|
877
|
+
: [this._options.audio];
|
|
878
|
+
}
|
|
879
|
+
} else {
|
|
880
|
+
trackOptions = [{}];
|
|
881
|
+
}
|
|
882
|
+
} else {
|
|
883
|
+
assert(false);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const discardOptions = trackOptions.filter(x => x.discard);
|
|
887
|
+
for (const discardOption of discardOptions) {
|
|
888
|
+
this.discardedTracks.push({
|
|
889
|
+
track,
|
|
890
|
+
reason: 'discarded_by_user',
|
|
891
|
+
trackOptions: discardOption,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (trackOptions.length === discardOptions.length) {
|
|
896
|
+
if (trackOptions.length === 0) {
|
|
897
|
+
this.discardedTracks.push({
|
|
898
|
+
track,
|
|
899
|
+
reason: 'discarded_by_user',
|
|
900
|
+
trackOptions: {},
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const nonDiscardOptions = trackOptions.filter(x => !x.discard);
|
|
908
|
+
filteredTracks.push(track);
|
|
909
|
+
filteredTrackOptions.push(nonDiscardOptions);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
if (this._options.trim?.start !== undefined) {
|
|
913
|
+
this._startTimestamp = this._options.trim.start;
|
|
914
|
+
} else {
|
|
915
|
+
// Compute the start timestamp from the set of filtered tracks. Technically these can still be narrowed
|
|
916
|
+
// down later due to discarded tracks, but we need to fix the start timestamp now due to track processing
|
|
917
|
+
// depending on it.
|
|
918
|
+
this._startTimestamp = Math.max(
|
|
919
|
+
await this.input.getFirstTimestamp(filteredTracks),
|
|
920
|
+
// Samples can also have negative timestamps, but the meaning typically is "don't present me", so let's
|
|
921
|
+
// cut those out by default.
|
|
922
|
+
0,
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
this._endTimestamp = Math.max(this._options.trim?.end ?? Infinity, this._startTimestamp);
|
|
927
|
+
|
|
928
|
+
// Run these sequentially so that output tracks have a deterministic order
|
|
929
|
+
for (let i = 0; i < filteredTracks.length; i++) {
|
|
930
|
+
const track = filteredTracks[i]!;
|
|
931
|
+
const options = filteredTrackOptions[i]!;
|
|
932
|
+
|
|
933
|
+
for (const option of options) {
|
|
934
|
+
if (this.output.tracks.length === outputTrackCounts.total.max) {
|
|
935
|
+
this.discardedTracks.push({
|
|
936
|
+
track,
|
|
937
|
+
reason: 'max_track_count_reached',
|
|
938
|
+
trackOptions: option,
|
|
939
|
+
});
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const addedCountOfType = this.output.tracks.reduce(
|
|
944
|
+
(count, t) => count + (t.type === track.type ? 1 : 0),
|
|
945
|
+
0,
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
if (addedCountOfType === outputTrackCounts[track.type].max) {
|
|
949
|
+
this.discardedTracks.push({
|
|
950
|
+
track,
|
|
951
|
+
reason: 'max_track_count_of_type_reached',
|
|
952
|
+
trackOptions: option,
|
|
953
|
+
});
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
const outputTrackId = this._nextOutputTrackId++;
|
|
958
|
+
|
|
959
|
+
if (track.isVideoTrack()) {
|
|
960
|
+
await this._processVideoTrack(track, option as ConversionVideoOptions, outputTrackId);
|
|
961
|
+
} else if (track.isAudioTrack()) {
|
|
962
|
+
await this._processAudioTrack(track, option as ConversionAudioOptions, outputTrackId);
|
|
963
|
+
} else {
|
|
964
|
+
assert(false);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// When no track groups are set by the user, then the output track pairability should be *identical* to the
|
|
970
|
+
// input's. We do the naive algorithm to achieve this: assign each track to its own group, and pair groups with
|
|
971
|
+
// each other based on input track pairability.
|
|
972
|
+
for (let i = 0; i < this.utilizedTracks.length - 1; i++) {
|
|
973
|
+
for (let j = i + 1; j < this.utilizedTracks.length; j++) {
|
|
974
|
+
const trackA = this.utilizedTracks[i]!;
|
|
975
|
+
const trackB = this.utilizedTracks[j]!;
|
|
976
|
+
const ownGroupA = this._outputOwnTrackGroups[i];
|
|
977
|
+
const ownGroupB = this._outputOwnTrackGroups[j];
|
|
978
|
+
|
|
979
|
+
assert(ownGroupA !== undefined);
|
|
980
|
+
assert(ownGroupB !== undefined);
|
|
981
|
+
|
|
982
|
+
if (ownGroupA && ownGroupB && trackA.canBePairedWith(trackB)) {
|
|
983
|
+
ownGroupA.pairWith(ownGroupB);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Now, let's deal with metadata tags. A composable conversion does not touch the output's metadata tags; that
|
|
989
|
+
// remains the responsibility of whoever owns the output.
|
|
990
|
+
|
|
991
|
+
if (!this._composable) {
|
|
992
|
+
const inputTags = await this.input.getMetadataTags();
|
|
993
|
+
let outputTags: MetadataTags;
|
|
994
|
+
|
|
995
|
+
if (this._options.tags) {
|
|
996
|
+
const result = typeof this._options.tags === 'function'
|
|
997
|
+
? await this._options.tags(inputTags)
|
|
998
|
+
: this._options.tags;
|
|
999
|
+
validateMetadataTags(result);
|
|
1000
|
+
|
|
1001
|
+
outputTags = result;
|
|
1002
|
+
} else {
|
|
1003
|
+
outputTags = inputTags;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// Somewhat dirty but pragmatic
|
|
1007
|
+
const inputAndOutputFormatMatch = inputFormat.mimeType === this.output.format.mimeType;
|
|
1008
|
+
const rawTagsAreUnchanged = inputTags.raw === outputTags.raw;
|
|
1009
|
+
|
|
1010
|
+
if (inputTags.raw && rawTagsAreUnchanged && !inputAndOutputFormatMatch) {
|
|
1011
|
+
// If the input and output formats aren't the same, copying over raw metadata tags makes no sense and
|
|
1012
|
+
// only results in junk tags, so let's cut them out.
|
|
1013
|
+
delete outputTags.raw;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
this.output.setMetadataTags(outputTags);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// Let's check if the conversion can actually be executed
|
|
1020
|
+
if (!this._composable) {
|
|
1021
|
+
this.isValid = this.output.hasEnoughTracks();
|
|
1022
|
+
} else {
|
|
1023
|
+
// Checking Output start validity is not up to us. We consider even zero-track conversions to be valid
|
|
1024
|
+
this.isValid = true;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (this._options.showWarnings ?? true) {
|
|
1028
|
+
const warnElements: unknown[] = [];
|
|
1029
|
+
|
|
1030
|
+
const unintentionallyDiscardedTracks = this.discardedTracks.filter(x => x.reason !== 'discarded_by_user');
|
|
1031
|
+
if (unintentionallyDiscardedTracks.length > 0) {
|
|
1032
|
+
// Let's give the user a notice/warning about discarded tracks so they aren't confused
|
|
1033
|
+
warnElements.push(
|
|
1034
|
+
'Some tracks had to be discarded from the conversion:', unintentionallyDiscardedTracks,
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
if (!this.isValid) {
|
|
1039
|
+
if (warnElements.length > 0) {
|
|
1040
|
+
warnElements.push('\n\n');
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
warnElements.push(this._getInvalidityExplanation().join(''));
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
if (warnElements.length > 0) {
|
|
1047
|
+
Logging._warn(...warnElements);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/** @internal */
|
|
1053
|
+
_getInvalidityExplanation() {
|
|
1054
|
+
const elements: string[] = [];
|
|
1055
|
+
|
|
1056
|
+
if (this.discardedTracks.length === 0) {
|
|
1057
|
+
elements.push(
|
|
1058
|
+
'Due to missing tracks, this conversion cannot be executed.',
|
|
1059
|
+
);
|
|
1060
|
+
} else {
|
|
1061
|
+
const encodabilityIsTheProblem = this.discardedTracks.every(x =>
|
|
1062
|
+
x.reason === 'discarded_by_user' || x.reason === 'no_encodable_target_codec',
|
|
1063
|
+
) && this.discardedTracks.some(x => x.reason === 'no_encodable_target_codec');
|
|
1064
|
+
|
|
1065
|
+
elements.push(
|
|
1066
|
+
'Due to discarded tracks, this conversion cannot be executed.',
|
|
1067
|
+
);
|
|
1068
|
+
|
|
1069
|
+
if (encodabilityIsTheProblem) {
|
|
1070
|
+
const codecs = this.discardedTracks.flatMap((x) => {
|
|
1071
|
+
if (x.reason === 'discarded_by_user') return [];
|
|
1072
|
+
|
|
1073
|
+
let supportedCodecs: MediaCodec[];
|
|
1074
|
+
if (x.track.type === 'video') {
|
|
1075
|
+
supportedCodecs = this.output.format.getSupportedVideoCodecs();
|
|
1076
|
+
} else if (x.track.type === 'audio') {
|
|
1077
|
+
supportedCodecs = this.output.format.getSupportedAudioCodecs();
|
|
1078
|
+
} else {
|
|
1079
|
+
supportedCodecs = this.output.format.getSupportedSubtitleCodecs();
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// If the user requested a specific codec, only that codec was ever attempted
|
|
1083
|
+
return supportedCodecs.filter(codec => !x.trackOptions.codec || codec === x.trackOptions.codec);
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
const uniqueCodecs = [...new Set(codecs)];
|
|
1087
|
+
|
|
1088
|
+
if (uniqueCodecs.length === 1) {
|
|
1089
|
+
elements.push(
|
|
1090
|
+
`\nTracks were discarded because your environment is not able to encode '${uniqueCodecs[0]}'`
|
|
1091
|
+
+ ' with the provided parameters.',
|
|
1092
|
+
);
|
|
1093
|
+
} else {
|
|
1094
|
+
elements.push(
|
|
1095
|
+
'\nTracks were discarded because your environment is not able to encode any of the codecs'
|
|
1096
|
+
+ ` ${uniqueCodecs.map(x => `'${x}'`).join(', ')} with the provided parameters.`,
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
if (uniqueCodecs.includes('mp3')) {
|
|
1101
|
+
elements.push(
|
|
1102
|
+
`\nThe @mediabunny/mp3-encoder extension package provides support for encoding MP3.`,
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (uniqueCodecs.includes('aac')) {
|
|
1107
|
+
elements.push(
|
|
1108
|
+
'\nThe @mediabunny/aac-encoder extension package provides support for encoding AAC.',
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if (uniqueCodecs.includes('ac3') || uniqueCodecs.includes('eac3')) {
|
|
1113
|
+
elements.push(
|
|
1114
|
+
'\nThe @mediabunny/ac3 extension package provides support'
|
|
1115
|
+
+ ' for encoding and decoding AC-3/E-AC-3.',
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (uniqueCodecs.includes('flac')) {
|
|
1120
|
+
elements.push(
|
|
1121
|
+
'\nThe @mediabunny/flac-encoder extension package provides support for encoding FLAC.',
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
} else {
|
|
1125
|
+
elements.push('\nCheck the discardedTracks field for more info.');
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
return elements;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* Executes the conversion process and resolves when the conversion is complete. When
|
|
1134
|
+
* {@link ConversionExecuteOptions.until} is provided, the conversion will be suspended once that output timestamp
|
|
1135
|
+
* is reached and can be resumed with another call to `execute`. An ongoing execution may also be suspended via
|
|
1136
|
+
* {@link ConversionExecuteOptions.pauseSignal}.
|
|
1137
|
+
*
|
|
1138
|
+
* Execution will throw if `isValid` is `false`.
|
|
1139
|
+
*/
|
|
1140
|
+
async execute(options: ConversionExecuteOptions = {}) {
|
|
1141
|
+
if (!options || typeof options !== 'object') {
|
|
1142
|
+
throw new TypeError('options must be an object.');
|
|
1143
|
+
}
|
|
1144
|
+
if (options.until !== undefined && (typeof options.until !== 'number' || Number.isNaN(options.until))) {
|
|
1145
|
+
throw new TypeError('options.until, when provided, must be a number.');
|
|
1146
|
+
}
|
|
1147
|
+
if (options.pauseSignal !== undefined && !(options.pauseSignal instanceof AbortSignal)) {
|
|
1148
|
+
throw new TypeError('options.pauseSignal, when provided, must be an AbortSignal.');
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (!this.isValid) {
|
|
1152
|
+
throw new Error(
|
|
1153
|
+
'Cannot execute this conversion because its output configuration is invalid. Make sure to always check'
|
|
1154
|
+
+ ' the isValid field before executing a conversion.\n'
|
|
1155
|
+
+ this._getInvalidityExplanation().join(''),
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
if (this.state === 'executing') {
|
|
1160
|
+
throw new Error('Cannot call execute() while a previous call to execute() is still running.');
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
if (this.state === 'canceled') {
|
|
1164
|
+
throw new ConversionCanceledError();
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
if (this.state === 'done') {
|
|
1168
|
+
// The conversion already ran to completion, nothing left to do
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (this._composable && this.output.state === 'pending') {
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
'A composable conversion requires the output to be started. Call start() on the output before executing'
|
|
1175
|
+
+ ' the conversion.',
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
this.state = 'executing';
|
|
1180
|
+
this._executionUntil = options.until ?? Infinity;
|
|
1181
|
+
this._pauseRequested = options.pauseSignal?.aborted ?? false;
|
|
1182
|
+
|
|
1183
|
+
const onPause = () => {
|
|
1184
|
+
if (this.state !== 'executing') {
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
this._pauseRequested = true;
|
|
1189
|
+
|
|
1190
|
+
// Release any pumps stuck in the synchronizer so they can reach their next checkpoint and suspend
|
|
1191
|
+
this._synchronizer.resolveAll();
|
|
1192
|
+
};
|
|
1193
|
+
options.pauseSignal?.addEventListener('abort', onPause);
|
|
1194
|
+
|
|
1195
|
+
for (const pump of this._trackPumps) {
|
|
1196
|
+
if (!pump.done) {
|
|
1197
|
+
pump.resolvers = promiseWithResolvers();
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (!this._executed) {
|
|
1202
|
+
this._executed = true;
|
|
1203
|
+
|
|
1204
|
+
for (const id of this._outputTrackIds) {
|
|
1205
|
+
this._synchronizer.declareTrack(id);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if (this.onProgress) {
|
|
1209
|
+
// Compute duration using only the utilized tracks
|
|
1210
|
+
const uniqueUtilizedTracks = new Set(this.utilizedTracks);
|
|
1211
|
+
const durationPromises = [...uniqueUtilizedTracks].map(async (track) => {
|
|
1212
|
+
if (await track.isLive()) {
|
|
1213
|
+
return Infinity; // Upper bound (assuming no universe heat death)
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
return (await track.getDurationFromMetadata()) ?? (await track.computeDuration());
|
|
1217
|
+
});
|
|
1218
|
+
const duration = Math.max(0, ...await Promise.all(durationPromises));
|
|
1219
|
+
|
|
1220
|
+
this._computeProgress = true;
|
|
1221
|
+
this._totalDuration = Math.min(
|
|
1222
|
+
duration - this._startTimestamp,
|
|
1223
|
+
this._endTimestamp - this._startTimestamp,
|
|
1224
|
+
);
|
|
1225
|
+
|
|
1226
|
+
for (const id of this._outputTrackIds) {
|
|
1227
|
+
this._maxTimestamps.set(id, 0);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
this.onProgress?.(0, 0);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if (!this._composable) {
|
|
1234
|
+
await this.output.start();
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
for (const pump of this._trackPumps) {
|
|
1238
|
+
pump.start();
|
|
1239
|
+
}
|
|
1240
|
+
} else {
|
|
1241
|
+
// Wake all suspended track pumps
|
|
1242
|
+
for (const pump of this._trackPumps) {
|
|
1243
|
+
pump.wake?.();
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
try {
|
|
1248
|
+
await Promise.all(this._trackPumps.map(x => x.resolvers.promise));
|
|
1249
|
+
} catch (error) {
|
|
1250
|
+
if ((this.state as Conversion['state']) !== 'canceled') {
|
|
1251
|
+
// Make sure to cancel to stop other encoding processes and clean up resources
|
|
1252
|
+
void this.cancel();
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
throw error;
|
|
1256
|
+
} finally {
|
|
1257
|
+
options.pauseSignal?.removeEventListener('abort', onPause);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
if ((this.state as Conversion['state']) === 'canceled') {
|
|
1261
|
+
throw new ConversionCanceledError();
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
const isDone = this._trackPumps.every(x => x.done);
|
|
1265
|
+
this.state = isDone ? 'done' : 'idle';
|
|
1266
|
+
|
|
1267
|
+
if (isDone) {
|
|
1268
|
+
if (!this._composable) {
|
|
1269
|
+
await this.output.finalize();
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
if (this._computeProgress) {
|
|
1273
|
+
const minTimestamp = Math.min(...this._maxTimestamps.values());
|
|
1274
|
+
this.onProgress?.(1, minTimestamp);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* Cancels the conversion process, causing any ongoing `execute` call to throw a `ConversionCanceledError`.
|
|
1281
|
+
* Does nothing if the conversion is already complete.
|
|
1282
|
+
*/
|
|
1283
|
+
async cancel() {
|
|
1284
|
+
if (this.state === 'done') {
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
if (this.state === 'canceled') {
|
|
1289
|
+
Logging._warn('Conversion already canceled.');
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
this.state = 'canceled';
|
|
1294
|
+
|
|
1295
|
+
// Wake all suspended track pumps so they can wind down
|
|
1296
|
+
for (const pump of this._trackPumps) {
|
|
1297
|
+
pump.wake?.();
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
this._synchronizer.resolveAll();
|
|
1301
|
+
|
|
1302
|
+
if (!this._composable) {
|
|
1303
|
+
await this.output.cancel();
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/** @internal */
|
|
1308
|
+
async _processVideoTrack(track: InputVideoTrack, trackOptions: ConversionVideoOptions, outputTrackId: number) {
|
|
1309
|
+
const sourceCodec = await track.getCodec();
|
|
1310
|
+
if (!sourceCodec) {
|
|
1311
|
+
this.discardedTracks.push({
|
|
1312
|
+
track,
|
|
1313
|
+
reason: 'unknown_source_codec',
|
|
1314
|
+
trackOptions,
|
|
1315
|
+
});
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
let videoSource: VideoSource;
|
|
1320
|
+
|
|
1321
|
+
const innateRotation = await track.getRotation();
|
|
1322
|
+
const totalRotation = normalizeRotation(innateRotation + (trackOptions.rotate ?? 0));
|
|
1323
|
+
let outputTrackRotation = totalRotation;
|
|
1324
|
+
const canUseRotationMetadata = this.output.format.supportsVideoRotationMetadata
|
|
1325
|
+
&& (trackOptions.allowRotationMetadata ?? true);
|
|
1326
|
+
|
|
1327
|
+
const squarePixelWidth = await track.getSquarePixelWidth();
|
|
1328
|
+
const squarePixelHeight = await track.getSquarePixelHeight();
|
|
1329
|
+
const [rotatedWidth, rotatedHeight] = totalRotation % 180 === 0
|
|
1330
|
+
? [squarePixelWidth, squarePixelHeight]
|
|
1331
|
+
: [squarePixelHeight, squarePixelWidth];
|
|
1332
|
+
|
|
1333
|
+
let crop = trackOptions.crop;
|
|
1334
|
+
if (crop) {
|
|
1335
|
+
crop = clampCropRectangle(crop, rotatedWidth, rotatedHeight);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
const [originalWidth, originalHeight] = crop
|
|
1339
|
+
? [crop.width, crop.height]
|
|
1340
|
+
: [rotatedWidth, rotatedHeight];
|
|
1341
|
+
|
|
1342
|
+
let width = originalWidth;
|
|
1343
|
+
let height = originalHeight;
|
|
1344
|
+
const aspectRatio = width / height;
|
|
1345
|
+
|
|
1346
|
+
// A lot of video encoders require that the dimensions be multiples of 2
|
|
1347
|
+
if (trackOptions.width !== undefined && trackOptions.height === undefined) {
|
|
1348
|
+
width = ceilToMultipleOfTwo(trackOptions.width);
|
|
1349
|
+
height = ceilToMultipleOfTwo(Math.round(width / aspectRatio));
|
|
1350
|
+
} else if (trackOptions.width === undefined && trackOptions.height !== undefined) {
|
|
1351
|
+
height = ceilToMultipleOfTwo(trackOptions.height);
|
|
1352
|
+
width = ceilToMultipleOfTwo(Math.round(height * aspectRatio));
|
|
1353
|
+
} else if (trackOptions.width !== undefined && trackOptions.height !== undefined) {
|
|
1354
|
+
width = ceilToMultipleOfTwo(trackOptions.width);
|
|
1355
|
+
height = ceilToMultipleOfTwo(trackOptions.height);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
const firstTimestamp = await track.getFirstTimestamp();
|
|
1359
|
+
let videoCodecs = this.output.format.getSupportedVideoCodecs();
|
|
1360
|
+
|
|
1361
|
+
const needsTranscode = !!trackOptions.forceTranscode
|
|
1362
|
+
|| firstTimestamp < this._startTimestamp
|
|
1363
|
+
|| !!trackOptions.frameRate
|
|
1364
|
+
|| trackOptions.keyFrameInterval !== undefined
|
|
1365
|
+
|| trackOptions.process !== undefined
|
|
1366
|
+
|| trackOptions.quality !== undefined
|
|
1367
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1368
|
+
|| trackOptions.bitrate !== undefined
|
|
1369
|
+
|| !videoCodecs.includes(sourceCodec)
|
|
1370
|
+
|| (trackOptions.codec && trackOptions.codec !== sourceCodec)
|
|
1371
|
+
|| width !== originalWidth
|
|
1372
|
+
|| height !== originalHeight
|
|
1373
|
+
// TODO This is suboptimal: Forcing a rerender when both rotation and process are set is not
|
|
1374
|
+
// performance-optimal, but right now there's no other way because we can't change the track rotation
|
|
1375
|
+
// metadata after the output has already started. Should be possible with API changes in v2, though!
|
|
1376
|
+
|| (totalRotation !== 0 && !canUseRotationMetadata)
|
|
1377
|
+
|| !!crop;
|
|
1378
|
+
|
|
1379
|
+
const alpha = trackOptions.alpha ?? 'discard';
|
|
1380
|
+
|
|
1381
|
+
if (!needsTranscode) {
|
|
1382
|
+
// Fast path, we can simply copy over the encoded packets
|
|
1383
|
+
|
|
1384
|
+
const source = new EncodedVideoPacketSource(sourceCodec);
|
|
1385
|
+
videoSource = source;
|
|
1386
|
+
|
|
1387
|
+
this._registerTrackPump(async (pump) => {
|
|
1388
|
+
const sink = new EncodedPacketSink(track);
|
|
1389
|
+
const decoderConfig = await track.getDecoderConfig();
|
|
1390
|
+
const meta: EncodedVideoChunkMetadata = { decoderConfig: decoderConfig ?? undefined };
|
|
1391
|
+
|
|
1392
|
+
for await (const packet of sink.packets(undefined, undefined, { verifyKeyPackets: true })) {
|
|
1393
|
+
if (this.state === 'canceled') {
|
|
1394
|
+
break;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
if (packet.timestamp >= this._endTimestamp) {
|
|
1398
|
+
break;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
const modifiedPacket = packet.clone({
|
|
1402
|
+
timestamp: packet.timestamp - this._startTimestamp,
|
|
1403
|
+
sideData: alpha === 'discard'
|
|
1404
|
+
? {} // Remove alpha side data
|
|
1405
|
+
: packet.sideData,
|
|
1406
|
+
});
|
|
1407
|
+
assert(modifiedPacket.timestamp >= 0);
|
|
1408
|
+
|
|
1409
|
+
this._reportProgress(outputTrackId, modifiedPacket.timestamp + modifiedPacket.duration);
|
|
1410
|
+
await source.add(modifiedPacket, meta);
|
|
1411
|
+
|
|
1412
|
+
if (this._synchronizer.shouldWait(outputTrackId, modifiedPacket.timestamp)) {
|
|
1413
|
+
await this._synchronizer.wait(modifiedPacket.timestamp);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
await this._checkpoint(pump, modifiedPacket.timestamp);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
source.close();
|
|
1420
|
+
this._synchronizer.closeTrack(outputTrackId);
|
|
1421
|
+
});
|
|
1422
|
+
} else {
|
|
1423
|
+
// We need to decode & reencode the video
|
|
1424
|
+
|
|
1425
|
+
const canDecode = await track.canDecode();
|
|
1426
|
+
if (!canDecode) {
|
|
1427
|
+
this.discardedTracks.push({
|
|
1428
|
+
track,
|
|
1429
|
+
reason: 'undecodable_source_codec',
|
|
1430
|
+
trackOptions,
|
|
1431
|
+
});
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
if (trackOptions.codec) {
|
|
1436
|
+
videoCodecs = videoCodecs.filter(codec => codec === trackOptions.codec);
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1440
|
+
const quality = resolveQuality(trackOptions.quality, trackOptions.bitrate)
|
|
1441
|
+
?? new Quality('high');
|
|
1442
|
+
|
|
1443
|
+
const encodableCodec = await getFirstEncodableVideoCodec(videoCodecs, {
|
|
1444
|
+
width: trackOptions.process && trackOptions.processedWidth
|
|
1445
|
+
? trackOptions.processedWidth
|
|
1446
|
+
: width,
|
|
1447
|
+
height: trackOptions.process && trackOptions.processedHeight
|
|
1448
|
+
? trackOptions.processedHeight
|
|
1449
|
+
: height,
|
|
1450
|
+
quality,
|
|
1451
|
+
});
|
|
1452
|
+
if (!encodableCodec) {
|
|
1453
|
+
this.discardedTracks.push({
|
|
1454
|
+
track,
|
|
1455
|
+
reason: 'no_encodable_target_codec',
|
|
1456
|
+
trackOptions,
|
|
1457
|
+
});
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
const encodingConfig: VideoEncodingConfig = {
|
|
1462
|
+
codec: encodableCodec,
|
|
1463
|
+
quality,
|
|
1464
|
+
keyFrameInterval: trackOptions.keyFrameInterval,
|
|
1465
|
+
sizeChangeBehavior: trackOptions.fit ?? 'passThrough',
|
|
1466
|
+
alpha,
|
|
1467
|
+
hardwareAcceleration: trackOptions.hardwareAcceleration,
|
|
1468
|
+
transform: {},
|
|
1469
|
+
};
|
|
1470
|
+
assert(encodingConfig.transform);
|
|
1471
|
+
|
|
1472
|
+
let needsRerender = width !== originalWidth
|
|
1473
|
+
|| height !== originalHeight
|
|
1474
|
+
|| (totalRotation !== 0 && (!canUseRotationMetadata || trackOptions.process !== undefined))
|
|
1475
|
+
|| !!crop
|
|
1476
|
+
// Don't expect encoders to reliably handle non-square pixels:
|
|
1477
|
+
|| squarePixelWidth !== await track.getCodedWidth()
|
|
1478
|
+
|| squarePixelHeight !== await track.getCodedHeight();
|
|
1479
|
+
|
|
1480
|
+
if (!needsRerender) {
|
|
1481
|
+
// If we're directly passing decoded samples back to the encoder, sometimes the encoder may error due
|
|
1482
|
+
// to lack of support of certain video frame formats, like when HDR is at play. To check for this, we
|
|
1483
|
+
// first try to pass a single frame to the encoder to see how it behaves. If it throws, we then fall
|
|
1484
|
+
// back to the rerender path.
|
|
1485
|
+
//
|
|
1486
|
+
// Creating a new temporary Output is sort of hacky, but due to a lack of an isolated encoder API right
|
|
1487
|
+
// now, this is the simplest way. Will refactor in the future! TODO
|
|
1488
|
+
|
|
1489
|
+
const tempOutput = new Output({
|
|
1490
|
+
format: new Mp4OutputFormat(), // Supports all video codecs
|
|
1491
|
+
target: new NullTarget(),
|
|
1492
|
+
});
|
|
1493
|
+
|
|
1494
|
+
const tempSource = new VideoSampleSource(encodingConfig);
|
|
1495
|
+
tempOutput.addVideoTrack(tempSource);
|
|
1496
|
+
|
|
1497
|
+
await tempOutput.start();
|
|
1498
|
+
|
|
1499
|
+
const sink = new VideoSampleSink(track);
|
|
1500
|
+
using firstSample = await sink.getSample(firstTimestamp); // Let's just use the first sample
|
|
1501
|
+
|
|
1502
|
+
if (firstSample) {
|
|
1503
|
+
try {
|
|
1504
|
+
await tempSource.add(firstSample);
|
|
1505
|
+
firstSample.close();
|
|
1506
|
+
await tempOutput.finalize();
|
|
1507
|
+
} catch (error) {
|
|
1508
|
+
Logging._warn(
|
|
1509
|
+
'An error occurred when probing encoder support. Falling back to rerender path.', error,
|
|
1510
|
+
);
|
|
1511
|
+
void tempOutput.cancel();
|
|
1512
|
+
|
|
1513
|
+
needsRerender = true;
|
|
1514
|
+
encodingConfig.transform.force = true;
|
|
1515
|
+
}
|
|
1516
|
+
} else {
|
|
1517
|
+
await tempOutput.cancel();
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
if (trackOptions.frameRate) {
|
|
1522
|
+
encodingConfig.transform.frameRate = trackOptions.frameRate;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (trackOptions.process) {
|
|
1526
|
+
encodingConfig.transform.process = trackOptions.process;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
if (needsRerender) {
|
|
1530
|
+
outputTrackRotation = 0; // Since the rotation is baked into the output
|
|
1531
|
+
|
|
1532
|
+
encodingConfig.transform.width = width;
|
|
1533
|
+
encodingConfig.transform.height = height;
|
|
1534
|
+
encodingConfig.transform.fit = trackOptions.fit ?? 'fill';
|
|
1535
|
+
encodingConfig.transform.rotate = normalizeRotation(totalRotation - innateRotation);
|
|
1536
|
+
encodingConfig.transform.crop = crop;
|
|
1537
|
+
encodingConfig.transform.alpha = alpha;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// We need to do this because `process` can emit new timestamps
|
|
1541
|
+
let lastSampleTimestamp: number | null = null;
|
|
1542
|
+
encodingConfig.onEncodedSample = (sample) => {
|
|
1543
|
+
lastSampleTimestamp = sample.timestamp;
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
const source = new VideoSampleSource(encodingConfig);
|
|
1547
|
+
videoSource = source;
|
|
1548
|
+
|
|
1549
|
+
this._registerTrackPump(async (pump) => {
|
|
1550
|
+
const sink = new VideoSampleSink(track);
|
|
1551
|
+
|
|
1552
|
+
for await (using sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
|
1553
|
+
if (this.state === 'canceled') {
|
|
1554
|
+
break;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
const adjustedSampleTimestamp = Math.max(sample.timestamp - this._startTimestamp, 0);
|
|
1558
|
+
sample.setTimestamp(adjustedSampleTimestamp);
|
|
1559
|
+
|
|
1560
|
+
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
1561
|
+
await source.add(sample);
|
|
1562
|
+
sample.close();
|
|
1563
|
+
|
|
1564
|
+
if (lastSampleTimestamp !== null) {
|
|
1565
|
+
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
|
1566
|
+
await this._synchronizer.wait(lastSampleTimestamp);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
await this._checkpoint(pump, lastSampleTimestamp);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
source.close();
|
|
1574
|
+
this._synchronizer.closeTrack(outputTrackId);
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
let ownGroup: OutputTrackGroup | null = null;
|
|
1579
|
+
if (!trackOptions.group && !this._composable) {
|
|
1580
|
+
// Create per-track groups to replicate the input's pairability graph. Don't do this for composable
|
|
1581
|
+
// conversions.
|
|
1582
|
+
ownGroup = new OutputTrackGroup();
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
const videoTrackLanguageCode = await track.getLanguageCode();
|
|
1586
|
+
this.output.addVideoTrack(videoSource, {
|
|
1587
|
+
frameRate: trackOptions.frameRate,
|
|
1588
|
+
// TODO: This condition can be removed when all demuxers properly homogenize to BCP47 in v2
|
|
1589
|
+
languageCode: isIso639Dash2LanguageCode(videoTrackLanguageCode) ? videoTrackLanguageCode : undefined,
|
|
1590
|
+
name: await track.getName() ?? undefined,
|
|
1591
|
+
disposition: await track.getDisposition(),
|
|
1592
|
+
rotation: outputTrackRotation,
|
|
1593
|
+
group: ownGroup ?? trackOptions.group,
|
|
1594
|
+
});
|
|
1595
|
+
|
|
1596
|
+
this.utilizedTracks.push(track);
|
|
1597
|
+
this._outputTrackIds.push(outputTrackId);
|
|
1598
|
+
this._outputOwnTrackGroups.push(ownGroup);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/** @internal */
|
|
1602
|
+
async _processAudioTrack(track: InputAudioTrack, trackOptions: ConversionAudioOptions, outputTrackId: number) {
|
|
1603
|
+
const sourceCodec = await track.getCodec();
|
|
1604
|
+
if (!sourceCodec) {
|
|
1605
|
+
this.discardedTracks.push({
|
|
1606
|
+
track,
|
|
1607
|
+
reason: 'unknown_source_codec',
|
|
1608
|
+
trackOptions,
|
|
1609
|
+
});
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
let audioSource: AudioSource;
|
|
1614
|
+
|
|
1615
|
+
const originalNumberOfChannels = await track.getNumberOfChannels();
|
|
1616
|
+
const originalSampleRate = await track.getSampleRate();
|
|
1617
|
+
|
|
1618
|
+
const firstTimestamp = await track.getFirstTimestamp();
|
|
1619
|
+
|
|
1620
|
+
let numberOfChannels = trackOptions.numberOfChannels ?? originalNumberOfChannels;
|
|
1621
|
+
let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
|
|
1622
|
+
|
|
1623
|
+
const needsTrimming = firstTimestamp < this._startTimestamp;
|
|
1624
|
+
let needsPadding = firstTimestamp > this._startTimestamp && !this.output.format.supportsTimestampedMediaData;
|
|
1625
|
+
|
|
1626
|
+
let audioCodecs = this.output.format.getSupportedAudioCodecs();
|
|
1627
|
+
if (
|
|
1628
|
+
!trackOptions.forceTranscode
|
|
1629
|
+
&& !trackOptions.quality
|
|
1630
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1631
|
+
&& !trackOptions.bitrate
|
|
1632
|
+
&& numberOfChannels === originalNumberOfChannels
|
|
1633
|
+
&& sampleRate === originalSampleRate
|
|
1634
|
+
&& !needsTrimming
|
|
1635
|
+
&& !needsPadding
|
|
1636
|
+
&& audioCodecs.includes(sourceCodec)
|
|
1637
|
+
&& (!trackOptions.codec || trackOptions.codec === sourceCodec)
|
|
1638
|
+
&& !trackOptions.process
|
|
1639
|
+
&& trackOptions.sampleFormat === undefined
|
|
1640
|
+
) {
|
|
1641
|
+
// Fast path, we can simply copy over the encoded packets
|
|
1642
|
+
|
|
1643
|
+
const source = new EncodedAudioPacketSource(sourceCodec);
|
|
1644
|
+
audioSource = source;
|
|
1645
|
+
|
|
1646
|
+
this._registerTrackPump(async (pump) => {
|
|
1647
|
+
const sink = new EncodedPacketSink(track);
|
|
1648
|
+
const decoderConfig = await track.getDecoderConfig();
|
|
1649
|
+
const meta: EncodedAudioChunkMetadata = { decoderConfig: decoderConfig ?? undefined };
|
|
1650
|
+
|
|
1651
|
+
for await (const packet of sink.packets()) {
|
|
1652
|
+
if (this.state === 'canceled') {
|
|
1653
|
+
break;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
if (packet.timestamp >= this._endTimestamp) {
|
|
1657
|
+
break;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
const modifiedPacket = packet.clone({
|
|
1661
|
+
timestamp: packet.timestamp - this._startTimestamp,
|
|
1662
|
+
});
|
|
1663
|
+
assert(modifiedPacket.timestamp >= 0);
|
|
1664
|
+
|
|
1665
|
+
this._reportProgress(outputTrackId, modifiedPacket.timestamp + modifiedPacket.duration);
|
|
1666
|
+
await source.add(modifiedPacket, meta);
|
|
1667
|
+
|
|
1668
|
+
if (this._synchronizer.shouldWait(outputTrackId, modifiedPacket.timestamp)) {
|
|
1669
|
+
await this._synchronizer.wait(modifiedPacket.timestamp);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
await this._checkpoint(pump, modifiedPacket.timestamp);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
source.close();
|
|
1676
|
+
this._synchronizer.closeTrack(outputTrackId);
|
|
1677
|
+
});
|
|
1678
|
+
} else {
|
|
1679
|
+
// We need to decode & reencode the audio
|
|
1680
|
+
|
|
1681
|
+
const canDecode = await track.canDecode();
|
|
1682
|
+
if (!canDecode) {
|
|
1683
|
+
this.discardedTracks.push({
|
|
1684
|
+
track,
|
|
1685
|
+
reason: 'undecodable_source_codec',
|
|
1686
|
+
trackOptions,
|
|
1687
|
+
});
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
let codecOfChoice: AudioCodec | null = null;
|
|
1692
|
+
|
|
1693
|
+
if (trackOptions.codec) {
|
|
1694
|
+
audioCodecs = audioCodecs.filter(codec => codec === trackOptions.codec);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
1698
|
+
const quality = resolveQuality(trackOptions.quality, trackOptions.bitrate)
|
|
1699
|
+
?? new Quality('high');
|
|
1700
|
+
|
|
1701
|
+
const encodableCodecs = await getEncodableAudioCodecs(audioCodecs, {
|
|
1702
|
+
numberOfChannels: trackOptions.process && trackOptions.processedNumberOfChannels
|
|
1703
|
+
? trackOptions.processedNumberOfChannels
|
|
1704
|
+
: numberOfChannels,
|
|
1705
|
+
sampleRate: trackOptions.process && trackOptions.processedSampleRate
|
|
1706
|
+
? trackOptions.processedSampleRate
|
|
1707
|
+
: sampleRate,
|
|
1708
|
+
quality,
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
if (
|
|
1712
|
+
!encodableCodecs.some(codec => (NON_PCM_AUDIO_CODECS as readonly string[]).includes(codec))
|
|
1713
|
+
&& audioCodecs.some(codec => (NON_PCM_AUDIO_CODECS as readonly string[]).includes(codec))
|
|
1714
|
+
&& (numberOfChannels !== FALLBACK_NUMBER_OF_CHANNELS || sampleRate !== FALLBACK_SAMPLE_RATE)
|
|
1715
|
+
) {
|
|
1716
|
+
// We could not find a compatible non-PCM codec despite the container supporting them. This can be
|
|
1717
|
+
// caused by strange channel count or sample rate configurations. Therefore, let's try again but with
|
|
1718
|
+
// fallback parameters.
|
|
1719
|
+
|
|
1720
|
+
const encodableCodecsWithDefaultParams = await getEncodableAudioCodecs(audioCodecs, {
|
|
1721
|
+
numberOfChannels: FALLBACK_NUMBER_OF_CHANNELS,
|
|
1722
|
+
sampleRate: FALLBACK_SAMPLE_RATE,
|
|
1723
|
+
quality,
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
const nonPcmCodec = encodableCodecsWithDefaultParams
|
|
1727
|
+
.find(codec => (NON_PCM_AUDIO_CODECS as readonly string[]).includes(codec));
|
|
1728
|
+
if (nonPcmCodec) {
|
|
1729
|
+
// We are able to encode using a non-PCM codec, but it'll require resampling
|
|
1730
|
+
codecOfChoice = nonPcmCodec;
|
|
1731
|
+
numberOfChannels = FALLBACK_NUMBER_OF_CHANNELS;
|
|
1732
|
+
sampleRate = FALLBACK_SAMPLE_RATE;
|
|
1733
|
+
}
|
|
1734
|
+
} else {
|
|
1735
|
+
codecOfChoice = encodableCodecs[0] ?? null;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
if (codecOfChoice === null) {
|
|
1739
|
+
this.discardedTracks.push({
|
|
1740
|
+
track,
|
|
1741
|
+
reason: 'no_encodable_target_codec',
|
|
1742
|
+
trackOptions,
|
|
1743
|
+
});
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
const encodingConfig: AudioEncodingConfig = {
|
|
1748
|
+
codec: codecOfChoice,
|
|
1749
|
+
quality,
|
|
1750
|
+
transform: {
|
|
1751
|
+
sampleFormat: trackOptions.sampleFormat,
|
|
1752
|
+
process: trackOptions.process,
|
|
1753
|
+
},
|
|
1754
|
+
};
|
|
1755
|
+
assert(encodingConfig.transform);
|
|
1756
|
+
|
|
1757
|
+
if (numberOfChannels !== originalNumberOfChannels) {
|
|
1758
|
+
encodingConfig.transform.numberOfChannels = numberOfChannels;
|
|
1759
|
+
}
|
|
1760
|
+
if (sampleRate !== originalSampleRate) {
|
|
1761
|
+
encodingConfig.transform.sampleRate = sampleRate;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
let lastSampleTimestamp: number | null = null;
|
|
1765
|
+
encodingConfig.onEncodedSample = (sample) => {
|
|
1766
|
+
lastSampleTimestamp = sample.timestamp;
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
const source = new AudioSampleSource(encodingConfig);
|
|
1770
|
+
audioSource = source;
|
|
1771
|
+
|
|
1772
|
+
this._registerTrackPump(async (pump) => {
|
|
1773
|
+
const sink = new AudioSampleSink(track);
|
|
1774
|
+
for await (using sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
|
1775
|
+
if (this.state === 'canceled') {
|
|
1776
|
+
break;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
if (needsPadding) {
|
|
1780
|
+
// Add one padding sample at the beginning
|
|
1781
|
+
const paddingLength = firstTimestamp - this._startTimestamp;
|
|
1782
|
+
const paddingLengthSamples = Math.round(paddingLength * originalSampleRate);
|
|
1783
|
+
|
|
1784
|
+
const bytesPerSample = getBytesPerSample(sample.format);
|
|
1785
|
+
const data = new Uint8Array(bytesPerSample * paddingLengthSamples * originalNumberOfChannels);
|
|
1786
|
+
if (sample.format === 'u8' || sample.format === 'u8-planar') {
|
|
1787
|
+
data.fill(2 ** 7); // Fill it with the silent value
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
using silentSample = new AudioSample({
|
|
1791
|
+
data,
|
|
1792
|
+
// Use the same format the decoder is spitting out. This avoids feeding changing sample
|
|
1793
|
+
// formats to the audio encoder.
|
|
1794
|
+
format: sample.format,
|
|
1795
|
+
numberOfChannels: originalNumberOfChannels,
|
|
1796
|
+
sampleRate: originalSampleRate,
|
|
1797
|
+
timestamp: 0,
|
|
1798
|
+
});
|
|
1799
|
+
await this._registerAudioSample(
|
|
1800
|
+
pump, silentSample, source, outputTrackId, () => lastSampleTimestamp,
|
|
1801
|
+
);
|
|
1802
|
+
|
|
1803
|
+
needsPadding = false;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
let startFrame = 0;
|
|
1807
|
+
let endFrame = sample.numberOfFrames;
|
|
1808
|
+
|
|
1809
|
+
if (sample.timestamp < this._startTimestamp) {
|
|
1810
|
+
startFrame = Math.round((this._startTimestamp - sample.timestamp) * sample.sampleRate);
|
|
1811
|
+
}
|
|
1812
|
+
if (sample.timestamp + sample.duration > this._endTimestamp) {
|
|
1813
|
+
endFrame = Math.round((this._endTimestamp - sample.timestamp) * sample.sampleRate);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// Can't assign to "using" identifiers so we gotta do this
|
|
1817
|
+
let finalSampleLet: AudioSample;
|
|
1818
|
+
if (startFrame > 0 || endFrame < sample.numberOfFrames) {
|
|
1819
|
+
// Trim the sample if it sticks out of the trim region on either end
|
|
1820
|
+
const trimmedSample = sample.trim(startFrame, endFrame);
|
|
1821
|
+
sample.close();
|
|
1822
|
+
finalSampleLet = trimmedSample;
|
|
1823
|
+
|
|
1824
|
+
if (trimmedSample.numberOfFrames === 0) {
|
|
1825
|
+
trimmedSample.close();
|
|
1826
|
+
continue;
|
|
1827
|
+
}
|
|
1828
|
+
} else {
|
|
1829
|
+
finalSampleLet = sample;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
using finalSample = finalSampleLet;
|
|
1833
|
+
|
|
1834
|
+
// Offset the timestamp as needed
|
|
1835
|
+
finalSample.setTimestamp(finalSample.timestamp - this._startTimestamp);
|
|
1836
|
+
|
|
1837
|
+
await this._registerAudioSample(
|
|
1838
|
+
pump, finalSample, source, outputTrackId, () => lastSampleTimestamp,
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
source.close();
|
|
1843
|
+
this._synchronizer.closeTrack(outputTrackId);
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
let ownGroup: OutputTrackGroup | null = null;
|
|
1848
|
+
if (!trackOptions.group && !this._composable) {
|
|
1849
|
+
// Create per-track groups to replicate the input's pairability graph. Don't do this for composable
|
|
1850
|
+
// conversions.
|
|
1851
|
+
ownGroup = new OutputTrackGroup();
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
const audioTrackLanguageCode = await track.getLanguageCode();
|
|
1855
|
+
this.output.addAudioTrack(audioSource, {
|
|
1856
|
+
// TODO: This condition can be removed when all demuxers properly homogenize to BCP47 in v2
|
|
1857
|
+
languageCode: isIso639Dash2LanguageCode(audioTrackLanguageCode) ? audioTrackLanguageCode : undefined,
|
|
1858
|
+
name: await track.getName() ?? undefined,
|
|
1859
|
+
disposition: await track.getDisposition(),
|
|
1860
|
+
group: ownGroup ?? trackOptions.group,
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
this.utilizedTracks.push(track);
|
|
1864
|
+
this._outputTrackIds.push(outputTrackId);
|
|
1865
|
+
this._outputOwnTrackGroups.push(ownGroup);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/** @internal */
|
|
1869
|
+
async _registerAudioSample(
|
|
1870
|
+
pump: TrackPump,
|
|
1871
|
+
sample: AudioSample,
|
|
1872
|
+
source: AudioSampleSource,
|
|
1873
|
+
outputTrackId: number,
|
|
1874
|
+
getLastSampleTimestamp: () => number | null,
|
|
1875
|
+
) {
|
|
1876
|
+
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
1877
|
+
|
|
1878
|
+
await source.add(sample);
|
|
1879
|
+
sample.close();
|
|
1880
|
+
|
|
1881
|
+
const lastSampleTimestamp = getLastSampleTimestamp();
|
|
1882
|
+
if (lastSampleTimestamp !== null) {
|
|
1883
|
+
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
|
1884
|
+
await this._synchronizer.wait(lastSampleTimestamp);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
await this._checkpoint(pump, lastSampleTimestamp);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
/** @internal */
|
|
1892
|
+
_registerTrackPump(fn: (pump: TrackPump) => Promise<void>) {
|
|
1893
|
+
const pump: TrackPump = {
|
|
1894
|
+
done: false,
|
|
1895
|
+
resolvers: promiseWithResolvers(),
|
|
1896
|
+
wake: null,
|
|
1897
|
+
start: () => {
|
|
1898
|
+
void fn(pump).then(() => {
|
|
1899
|
+
pump.done = true;
|
|
1900
|
+
pump.resolvers.resolve();
|
|
1901
|
+
}, (error) => {
|
|
1902
|
+
pump.resolvers.reject(error);
|
|
1903
|
+
});
|
|
1904
|
+
},
|
|
1905
|
+
};
|
|
1906
|
+
|
|
1907
|
+
this._trackPumps.push(pump);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
/** @internal */
|
|
1911
|
+
async _checkpoint(pump: TrackPump, timestamp: number) {
|
|
1912
|
+
while (this.state !== 'canceled' && (timestamp >= this._executionUntil || this._pauseRequested)) {
|
|
1913
|
+
// We've reached the target; signal it and suspend until the next execution wakes us up
|
|
1914
|
+
pump.resolvers.resolve();
|
|
1915
|
+
|
|
1916
|
+
const { promise, resolve } = promiseWithResolvers();
|
|
1917
|
+
pump.wake = resolve;
|
|
1918
|
+
await promise;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
/** @internal */
|
|
1923
|
+
_reportProgress(trackId: number, endTimestamp: number) {
|
|
1924
|
+
if (!this._computeProgress) {
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
assert(this._totalDuration !== null);
|
|
1928
|
+
|
|
1929
|
+
this._maxTimestamps.set(
|
|
1930
|
+
trackId,
|
|
1931
|
+
Math.max(endTimestamp, this._maxTimestamps.get(trackId)!),
|
|
1932
|
+
);
|
|
1933
|
+
|
|
1934
|
+
const minTimestamp = Math.min(...this._maxTimestamps.values());
|
|
1935
|
+
const newProgress = clamp(minTimestamp / this._totalDuration, 0, 1);
|
|
1936
|
+
|
|
1937
|
+
if (newProgress !== this._lastProgress) {
|
|
1938
|
+
this._lastProgress = newProgress;
|
|
1939
|
+
this.onProgress?.(newProgress, minTimestamp);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* Thrown when a conversion couldn't complete due to being canceled.
|
|
1946
|
+
* @group Conversion
|
|
1947
|
+
* @public
|
|
1948
|
+
*/
|
|
1949
|
+
export class ConversionCanceledError extends Error {
|
|
1950
|
+
/** Creates a new {@link ConversionCanceledError}. */
|
|
1951
|
+
constructor(message = 'Conversion has been canceled.') {
|
|
1952
|
+
super(message);
|
|
1953
|
+
this.name = 'ConversionCanceledError';
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
const MAX_TIMESTAMP_GAP = 1; // in seconds
|
|
1958
|
+
|
|
1959
|
+
/**
|
|
1960
|
+
* Utility class for synchronizing multiple track packet consumers with one another. We don't want one consumer to get
|
|
1961
|
+
* too out-of-sync with the others, as that may lead to a large number of packets that need to be internally buffered
|
|
1962
|
+
* before they can be written. Therefore, we use this class to slow down a consumer if it is too far ahead of the
|
|
1963
|
+
* slowest consumer.
|
|
1964
|
+
*/
|
|
1965
|
+
class TrackSynchronizer {
|
|
1966
|
+
conversion: Conversion;
|
|
1967
|
+
maxTimestamps = new Map<number, number>(); // Track ID -> timestamp
|
|
1968
|
+
resolvers: {
|
|
1969
|
+
timestamp: number;
|
|
1970
|
+
resolve: () => void;
|
|
1971
|
+
}[] = [];
|
|
1972
|
+
|
|
1973
|
+
constructor(conversion: Conversion) {
|
|
1974
|
+
this.conversion = conversion;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
declareTrack(trackId: number) {
|
|
1978
|
+
this.maxTimestamps.set(trackId, 0);
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
shouldWait(trackId: number, timestamp: number) {
|
|
1982
|
+
const currentValue = this.maxTimestamps.get(trackId);
|
|
1983
|
+
assert(currentValue !== undefined);
|
|
1984
|
+
|
|
1985
|
+
this.maxTimestamps.set(trackId, Math.max(timestamp, currentValue));
|
|
1986
|
+
|
|
1987
|
+
const newMin = this.computeMinAndMaybeResolve();
|
|
1988
|
+
if (
|
|
1989
|
+
this.conversion.state === 'canceled'
|
|
1990
|
+
|| this.conversion._pauseRequested
|
|
1991
|
+
|| timestamp >= this.conversion._executionUntil
|
|
1992
|
+
) {
|
|
1993
|
+
// No point in throttling consumers that are about to suspend or wind down anyway
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
return timestamp - newMin > MAX_TIMESTAMP_GAP; // Should wait if it is too far ahead of the slowest consumer
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
wait(timestamp: number) {
|
|
2001
|
+
const { promise, resolve } = promiseWithResolvers();
|
|
2002
|
+
|
|
2003
|
+
this.resolvers.push({
|
|
2004
|
+
timestamp,
|
|
2005
|
+
resolve,
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
return promise;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
closeTrack(trackId: number) {
|
|
2012
|
+
this.maxTimestamps.delete(trackId);
|
|
2013
|
+
this.computeMinAndMaybeResolve();
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
resolveAll() {
|
|
2017
|
+
for (const entry of this.resolvers) {
|
|
2018
|
+
entry.resolve();
|
|
2019
|
+
}
|
|
2020
|
+
this.resolvers.length = 0;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
computeMinAndMaybeResolve() {
|
|
2024
|
+
let newMin = Infinity;
|
|
2025
|
+
for (const [, timestamp] of this.maxTimestamps) {
|
|
2026
|
+
newMin = Math.min(newMin, timestamp);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
for (let i = 0; i < this.resolvers.length; i++) {
|
|
2030
|
+
const entry = this.resolvers[i]!;
|
|
2031
|
+
|
|
2032
|
+
if (entry.timestamp - newMin < MAX_TIMESTAMP_GAP) {
|
|
2033
|
+
// The gap has gotten small enough again, the consumer can continue again
|
|
2034
|
+
entry.resolve();
|
|
2035
|
+
this.resolvers.splice(i, 1);
|
|
2036
|
+
i--;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
return newMin;
|
|
2041
|
+
}
|
|
2042
|
+
}
|