mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-04 22:33:49 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad167c77e7 | ||
|
|
8b9a1acf16 | ||
|
|
0da1107858 | ||
|
|
00599e556e | ||
|
|
e1c6ab1866 | ||
|
|
bc533c71f7 | ||
|
|
879fd75547 |
+14
-6
@@ -91,13 +91,16 @@
|
||||
//const startTime = await primaryTrack.getFirstTimestamp();
|
||||
//console.log(startTime)
|
||||
|
||||
let ctx = null;
|
||||
const canvas = new OffscreenCanvas(1280, 720);
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
//let ctx = null;
|
||||
let conversion = await Mediabunny.Conversion.init({
|
||||
input,
|
||||
output,
|
||||
audio: {
|
||||
codec: 'aac',
|
||||
forceTranscode: true,
|
||||
//codec: 'aac',
|
||||
//forceTranscode: true,
|
||||
//forceTranscode: true,
|
||||
//sampleFormat: 's16',
|
||||
},
|
||||
@@ -126,7 +129,10 @@
|
||||
},
|
||||
*/
|
||||
video: {
|
||||
discard: true,
|
||||
process: (sample) => {
|
||||
sample.draw(ctx, 0, 0, 1280, 720);
|
||||
return new Mediabunny.VideoSample(canvas, { timestamp: sample.timestamp, duration: sample.duration });
|
||||
},
|
||||
},
|
||||
tags: {} ?? {
|
||||
title: 'Bigggy',
|
||||
@@ -146,8 +152,10 @@
|
||||
}
|
||||
},
|
||||
trim: {
|
||||
start: 300.14984567374756 - 100,
|
||||
end: 310.1548298151939 - 100,
|
||||
start: -2,
|
||||
end: 10,
|
||||
//start: 300.14984567374756 - 100,
|
||||
//end: 310.1548298151939 - 100,
|
||||
//end: 10,
|
||||
//start: startTime,
|
||||
//end: startTime + 2,
|
||||
|
||||
@@ -12,7 +12,7 @@ Mediabunny exposes HLS playlists as if they were a single giant input file. Like
|
||||
|
||||
## HLS inputs
|
||||
|
||||
HLS playlists (master & media) are read through the same `Input` interface as all other media files in Mediabunny. HLS must read multiple files, meaning any [`PathedSource`](../api/PathedSource) is required:
|
||||
HLS playlists (master & media) are read through the same `Input` interface as all other media files in Mediabunny. HLS must read multiple files, meaning any [`PathedSource`](../api/PathedSource) (such as `UrlSource`) is required:
|
||||
```ts
|
||||
import { Input, UrlSource, HLS_FORMATS } from 'mediabunny';
|
||||
|
||||
|
||||
Generated
+120
-62
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mediabunny",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mediabunny",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"license": "MPL-2.0",
|
||||
"workspaces": [
|
||||
".",
|
||||
@@ -382,6 +382,7 @@
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
|
||||
"integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1113,6 +1114,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fidm/asn1/-/asn1-1.0.4.tgz",
|
||||
"integrity": "sha512-esd1jyNvRb2HVaQGq2Gg8Z0kbQPXzV9Tq5Z14KNIov6KfFD6PTaRIO8UpcsYiTNzOqJpmyzWgVTrUwFV3UF4TQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
@@ -1122,6 +1124,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fidm/x509/-/x509-1.2.1.tgz",
|
||||
"integrity": "sha512-nwc2iesjyc9hkuzcrMCBXQRn653XuAUKorfWM8PZyJawiy1QzLj4vahwzaI25+pfpwOLvMzbJ0uKpWLDNmo16w==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@fidm/asn1": "^1.0.4",
|
||||
"tweetnacl": "^1.0.1"
|
||||
@@ -1439,7 +1442,8 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
||||
"integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@mediabunny/aac-encoder": {
|
||||
"resolved": "packages/aac-encoder",
|
||||
@@ -1569,13 +1573,15 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@minhducsun2002/leb128/-/leb128-1.0.0.tgz",
|
||||
"integrity": "sha512-eFrYUPDVHeuwWHluTG1kwNQUEUcFjVKYwPkU8z9DR1JH3AW7JtJsG9cRVGmwz809kKtGfwGJj58juCZxEvnI/g==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.9.7",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz",
|
||||
"integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.8.0"
|
||||
},
|
||||
@@ -1591,6 +1597,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
@@ -1603,6 +1610,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.7.0.tgz",
|
||||
"integrity": "sha512-hew63shtzzvBcSHbhm+cyAmKe6AIfinT9hzEqSPjDC6opTTMKmTkQ0gHuN2KsWlvqiKw1S/fS94fhag/FJkioQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-schema": "^2.7.0",
|
||||
"@peculiar/asn1-x509": "^2.7.0",
|
||||
@@ -1616,6 +1624,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.7.0.tgz",
|
||||
"integrity": "sha512-VVsAyGqErT9D1SY4aEqozThXMVI+ssVRiv2DDeYuvpBKLIgZ3hYs3Ay3u/VSoKq6ESFi9cf6rf3IOOzfwh7oMA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-schema": "^2.7.0",
|
||||
"@peculiar/asn1-x509": "^2.7.0",
|
||||
@@ -1628,6 +1637,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.7.0.tgz",
|
||||
"integrity": "sha512-n7KEs/Q/wrB415cxy4fHOBhegp4NdJ15fkJPwcB/3/8iNBQC2L/N7SChJPKDJPZGYH0jD4Tg4/0vnHmwghnbKw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-schema": "^2.7.0",
|
||||
"@peculiar/asn1-x509": "^2.7.0",
|
||||
@@ -1640,6 +1650,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.7.0.tgz",
|
||||
"integrity": "sha512-V/nrlQVmhg7lYAsM7E13UDL5erAwFv6kCIVFqNaMIHSVi7dngcT839JkRTkQBqznMG98l2XjxYk74ZztAohZzA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-cms": "^2.7.0",
|
||||
"@peculiar/asn1-pkcs8": "^2.7.0",
|
||||
@@ -1654,6 +1665,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.7.0.tgz",
|
||||
"integrity": "sha512-9GTl1nE8Mx1kTZ+7QyYatDyKsm34QcWRBFkY1iPvWC3X4Dona5s/tlLiQsx5WzVdZqiMBZNYT0buyw4/vbhnjw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-schema": "^2.7.0",
|
||||
"@peculiar/asn1-x509": "^2.7.0",
|
||||
@@ -1666,6 +1678,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.7.0.tgz",
|
||||
"integrity": "sha512-Bh7m+OuIaSEllPQcSd9OSp93F4ROWH7sbITWV8MI+8dwsjE5111/87VxiWVvYFKyww3vp39geLv9ENqhwWHcew==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-cms": "^2.7.0",
|
||||
"@peculiar/asn1-pfx": "^2.7.0",
|
||||
@@ -1682,6 +1695,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.7.0.tgz",
|
||||
"integrity": "sha512-/qvENQrXyTZURjMqSeofHul0JJt2sNSzSwk36pl2olkHbaioMQgrASDZAlHXl0xUlnVbHj0uGgOrBMTb5x2aJQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-schema": "^2.7.0",
|
||||
"@peculiar/asn1-x509": "^2.7.0",
|
||||
@@ -1694,6 +1708,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.7.0.tgz",
|
||||
"integrity": "sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/utils": "^2.0.2",
|
||||
"asn1js": "^3.0.6",
|
||||
@@ -1705,6 +1720,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.7.0.tgz",
|
||||
"integrity": "sha512-mUn9RRrkGDnG4ALfunDmzyRW5dg+sWCj/pfnCCqEHYbkGxEpvUt6iVJv8Yw1cyp6SWZ26ZE5oSmI5SqEaen15g==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-schema": "^2.7.0",
|
||||
"@peculiar/utils": "^2.0.2",
|
||||
@@ -1717,6 +1733,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.7.0.tgz",
|
||||
"integrity": "sha512-NS8e7SOgXipkzUPLF/sce7ukpMpWjhxYsH0n6Y+bHYo4TTxOb95Zv7hqwSuL212mj5YxovjdOKQOgH1As3E94w==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-schema": "^2.7.0",
|
||||
"@peculiar/asn1-x509": "^2.7.0",
|
||||
@@ -1729,6 +1746,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz",
|
||||
"integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
@@ -1738,6 +1756,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz",
|
||||
"integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@peculiar/asn1-cms": "^2.6.0",
|
||||
"@peculiar/asn1-csr": "^2.6.0",
|
||||
@@ -2210,9 +2229,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-darwin-arm64": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-darwin-arm64/-/node-av-darwin-arm64-5.2.4.tgz",
|
||||
"integrity": "sha512-uQh6jWXDeXtjuaGOXrBx2rszPDcQbRa4W1YFrmlQG+ffOo+IJyczGjhTWfgzebRksB9tMAe9ViztU39THWZAkw==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-darwin-arm64/-/node-av-darwin-arm64-6.0.0.tgz",
|
||||
"integrity": "sha512-OAXfBGnOk8ddPhoZmqcq8K04m1oLgGsiKnZ9p7oPI8jVsZaI+v7NXr5lum4bVakIh2TOhnD6EMdZl8v0KHgJLA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2226,9 +2245,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-darwin-x64": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-darwin-x64/-/node-av-darwin-x64-5.2.4.tgz",
|
||||
"integrity": "sha512-S9ptQF7JhvGSbvZH9jVeDAPXyRklA7rySKiFzi4UeLTMJslgFTSWFgY2nJAtHrP4wRdkAPnAVjExqd8U7jRZgg==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-darwin-x64/-/node-av-darwin-x64-6.0.0.tgz",
|
||||
"integrity": "sha512-9SMQkfzEGJgz9GedF43bPIqTp7HbeN/UWwPtAq9tR02mZ4vDGYlzvMvQXG4yx2JEiogcYyg8hDzoLud4zkqKww==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2242,9 +2261,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-linux-arm64": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-linux-arm64/-/node-av-linux-arm64-5.2.4.tgz",
|
||||
"integrity": "sha512-XlQAWIcqFxVItr98VShaxzx57Cfl0y+RPCPciLOHXuEmfeKjON4o/xdbChHg02hHFrVAeSCfaHWQmHNgB9m6eQ==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-linux-arm64/-/node-av-linux-arm64-6.0.0.tgz",
|
||||
"integrity": "sha512-pZsX2PPLxL/KioqZoofbm6B4aFLRGpvAB6JSNvCZIiU5eW2QSBZ68H9gC/IZ5tknH6+mX7ZT5vrJP0B0tSfvyw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2258,9 +2277,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-linux-x64": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-linux-x64/-/node-av-linux-x64-5.2.4.tgz",
|
||||
"integrity": "sha512-+2LdgZ7irDik++0mO6Bo1odYa2NZvze6xiy4B8jTbcf3F8k46qQEKkPjBwtV0qQu5fn3M+fUc/wk5coHdfxjxA==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-linux-x64/-/node-av-linux-x64-6.0.0.tgz",
|
||||
"integrity": "sha512-tvaH/0o+r2z4qEnHVyeUr6tIAzykjZcjssy93WEO+d2iRKO5araE702JEVBidJNTq2c/hd7p9Ml2KVLqere1ug==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2274,9 +2293,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-win32-arm64-mingw": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-arm64-mingw/-/node-av-win32-arm64-mingw-5.2.4.tgz",
|
||||
"integrity": "sha512-uVNgRQ44uSdtMcR9O6R+nrBou5BWmroTFnGPI3PX8pHR8oe1Nim5zXtWK0qGTTqOiTPNsK5XnSshM3c5/n0/Fg==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-arm64-mingw/-/node-av-win32-arm64-mingw-6.0.0.tgz",
|
||||
"integrity": "sha512-mE2vSv984b8wrthQedup0ofeyZ8PTj/iwiXpYS1rmZmbmd2XMJ+Q8+oHKSu3+DruErOvURhWw+VflTGUlZtnBQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2290,9 +2309,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-win32-arm64-msvc": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-arm64-msvc/-/node-av-win32-arm64-msvc-5.2.4.tgz",
|
||||
"integrity": "sha512-Gi+Op8j028WHbdl2jR7MnyHqR2J1TfkEogLPludb3jx54Yi+MO1zvouICFUmtSbxd9JC7CAeoU8++CZAMqcEvA==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-arm64-msvc/-/node-av-win32-arm64-msvc-6.0.0.tgz",
|
||||
"integrity": "sha512-bm9CbvhAneEeF4/k1GNXaFlM050cWDSqZ+hha88uETV4kijolQBc3UseQTFy9QvqhD8FftvMSR7C/TM/fBTimA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2306,9 +2325,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-win32-x64-mingw": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-x64-mingw/-/node-av-win32-x64-mingw-5.2.4.tgz",
|
||||
"integrity": "sha512-gClAhvV/aJa681tuTlG6dTqqMpnTM5k6QVhnWolBgkbpZXMi0qABzoUBTYpuwxnOEd6j2mBBAVwqx6kut55Ukw==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-x64-mingw/-/node-av-win32-x64-mingw-6.0.0.tgz",
|
||||
"integrity": "sha512-4IaoS2eA2mogjcDkOhJD3vVqGXX420/fb3ZCmgBD0G9HMW+mk7qKWq25b+0V2lg6pT9d8eq1DAEMtQ6UhKwODQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2322,9 +2341,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@seydx/node-av-win32-x64-msvc": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-x64-msvc/-/node-av-win32-x64-msvc-5.2.4.tgz",
|
||||
"integrity": "sha512-01s5ij6nudrtZ7ojiAOAgzuUrImvuQYrAE5o6uesoxYqOha6j7yRu5e4qCXRQCDKrh2FBzyO1cGDc7Ht8Ly8wQ==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@seydx/node-av-win32-x64-msvc/-/node-av-win32-x64-msvc-6.0.0.tgz",
|
||||
"integrity": "sha512-fHtjHYrAxjeCjPzHojPCnimh0p+UoiG3Tn0Av/mLSJ8RJcCqryz0iH/zdj6bSgtXHEz5LkHzLoZIVI0BMqY+fw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2429,6 +2448,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@shinyoshiaki/binary-data/-/binary-data-0.6.1.tgz",
|
||||
"integrity": "sha512-7HDb/fQAop2bCmvDIzU5+69i+UJaFgIVp99h1VzK1mpg1JwSODOkjbqD7ilTYnqlnadF8C4XjpwpepxDsGY6+w==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"generate-function": "^2.3.1",
|
||||
"is-plain-object": "^2.0.3"
|
||||
@@ -2440,7 +2460,8 @@
|
||||
"node_modules/@shinyoshiaki/jspack": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@shinyoshiaki/jspack/-/jspack-0.0.6.tgz",
|
||||
"integrity": "sha512-SdsNhLjQh4onBlyPrn4ia1Pdx5bXT88G/LIEpOYAjx2u4xeY/m/HB5yHqlkJB1uQR3Zw4R3hBWLj46STRAN0rg=="
|
||||
"integrity": "sha512-SdsNhLjQh4onBlyPrn4ia1Pdx5bXT88G/LIEpOYAjx2u4xeY/m/HB5yHqlkJB1uQR3Zw4R3hBWLj46STRAN0rg==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@stylistic/eslint-plugin": {
|
||||
"version": "4.2.0",
|
||||
@@ -4173,7 +4194,8 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz",
|
||||
"integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.4",
|
||||
@@ -4357,6 +4379,7 @@
|
||||
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz",
|
||||
"integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"pvtsutils": "^1.3.6",
|
||||
"pvutils": "^1.1.5",
|
||||
@@ -4501,6 +4524,7 @@
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -4563,6 +4587,7 @@
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -4587,6 +4612,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
|
||||
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
@@ -5534,6 +5560,7 @@
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
||||
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
},
|
||||
@@ -5555,6 +5582,7 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -5694,6 +5722,7 @@
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
|
||||
"integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@leichtgewicht/ip-codec": "^2.0.1"
|
||||
},
|
||||
@@ -6395,7 +6424,8 @@
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/fast-fifo": {
|
||||
"version": "1.3.2",
|
||||
@@ -6737,6 +6767,7 @@
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
||||
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-property": "^1.0.2"
|
||||
}
|
||||
@@ -7093,6 +7124,7 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -7182,7 +7214,8 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-1.1.0.tgz",
|
||||
"integrity": "sha512-94smTCQOvigN4d/2R/YDjz8YVG0Sufvv2aAh8P5m42gwhCsDAJqnbNOrxJsrADuAFAA69Q/ptGzxvNcNuIJcvw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
@@ -7197,7 +7230,8 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
||||
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "10.0.1",
|
||||
@@ -7284,6 +7318,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"isobject": "^3.0.1"
|
||||
},
|
||||
@@ -7295,7 +7330,8 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
@@ -7340,6 +7376,7 @@
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -7943,7 +7980,8 @@
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
@@ -8903,7 +8941,8 @@
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/mp4box/-/mp4box-0.5.4.tgz",
|
||||
"integrity": "sha512-GcCH0fySxBurJtvr0dfhz0IxHZjc1RP+F+I8xw+LIwkU1a+7HJx8NCDiww1I5u4Hz6g4eR1JlGADEGJ9r4lSfA==",
|
||||
"license": "BSD-3-Clause"
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
@@ -8918,13 +8957,15 @@
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/multicast-dns": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
|
||||
"integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"dns-packet": "^5.2.2",
|
||||
"thunky": "^1.0.2"
|
||||
@@ -8969,24 +9010,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-av": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/node-av/-/node-av-5.2.4.tgz",
|
||||
"integrity": "sha512-L5r+6k+YGvH5MZX8o55JHQbbeRZ+iFieAb1bhKtpa8hrqBEGuSwdkXRwpE4vd414JqVJsq/EQq6s+3qKeviQTg==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-av/-/node-av-6.0.0.tgz",
|
||||
"integrity": "sha512-raMugNMsLlJpmby4vdMyjpNHEw94WYDOpHw4z3ClxowyvWKx880rGbwDdERhS6DudyoLV0DNT+A5h3HBL0xF6Q==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unzipper": "^0.12.3",
|
||||
"werift": "^0.23.0"
|
||||
"unzipper": "^0.12.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@seydx/node-av-darwin-arm64": "^5.2.4",
|
||||
"@seydx/node-av-darwin-x64": "^5.2.4",
|
||||
"@seydx/node-av-linux-arm64": "^5.2.4",
|
||||
"@seydx/node-av-linux-x64": "^5.2.4",
|
||||
"@seydx/node-av-win32-arm64-mingw": "^5.2.4",
|
||||
"@seydx/node-av-win32-arm64-msvc": "^5.2.4",
|
||||
"@seydx/node-av-win32-x64-mingw": "^5.2.4",
|
||||
"@seydx/node-av-win32-x64-msvc": "^5.2.4"
|
||||
"@seydx/node-av-darwin-arm64": "^6.0.0",
|
||||
"@seydx/node-av-darwin-x64": "^6.0.0",
|
||||
"@seydx/node-av-linux-arm64": "^6.0.0",
|
||||
"@seydx/node-av-linux-x64": "^6.0.0",
|
||||
"@seydx/node-av-win32-arm64-mingw": "^6.0.0",
|
||||
"@seydx/node-av-win32-arm64-msvc": "^6.0.0",
|
||||
"@seydx/node-av-win32-x64-mingw": "^6.0.0",
|
||||
"@seydx/node-av-win32-x64-msvc": "^6.0.0",
|
||||
"werift": "^0.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
@@ -9109,6 +9150,7 @@
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -9689,6 +9731,7 @@
|
||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz",
|
||||
"integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.1"
|
||||
}
|
||||
@@ -9698,6 +9741,7 @@
|
||||
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz",
|
||||
"integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
@@ -9801,7 +9845,8 @@
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
|
||||
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/regex": {
|
||||
"version": "6.0.1",
|
||||
@@ -10066,7 +10111,8 @@
|
||||
"node_modules/rx.mini": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/rx.mini/-/rx.mini-1.4.0.tgz",
|
||||
"integrity": "sha512-8w5cSc1mwNja7fl465DXOkVvIOkpvh2GW4jo31nAIvX4WTXCsRnKJGUfiDBzWtYRInEcHAUYIZfzusjIrea8gA=="
|
||||
"integrity": "sha512-8w5cSc1mwNja7fl465DXOkVvIOkpvh2GW4jo31nAIvX4WTXCsRnKJGUfiDBzWtYRInEcHAUYIZfzusjIrea8gA==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/safaridriver": {
|
||||
"version": "1.0.0",
|
||||
@@ -10736,7 +10782,8 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "2.9.0",
|
||||
@@ -10868,7 +10915,8 @@
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.19.4",
|
||||
@@ -10895,6 +10943,7 @@
|
||||
"resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz",
|
||||
"integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
@@ -10906,13 +10955,15 @@
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
|
||||
"license": "Unlicense"
|
||||
"license": "Unlicense",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
@@ -12409,6 +12460,7 @@
|
||||
"resolved": "https://registry.npmjs.org/werift/-/werift-0.23.0.tgz",
|
||||
"integrity": "sha512-/WcIN5DHFG9Ri4anGOmIkp8gxBGFMWSIB/m4sfZ5CWlLfD3iMhiaAUuTBuc+KV3SY9NDmvmLtiN2uaM7k3lVzw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@fidm/x509": "^1.2.1",
|
||||
"@minhducsun2002/leb128": "^1.0.0",
|
||||
@@ -12440,6 +12492,7 @@
|
||||
"resolved": "https://registry.npmjs.org/werift-common/-/werift-common-0.0.3.tgz",
|
||||
"integrity": "sha512-ma3E4BqKTyZVLhrdfTVs2T1tg9seeUtKMRn5e64LwgrogWa62+3LAUoLBUSl1yPWhgSkXId7GmcHuWDen9IJeQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@shinyoshiaki/jspack": "^0.0.6",
|
||||
"debug": "^4.4.0"
|
||||
@@ -12453,6 +12506,7 @@
|
||||
"resolved": "https://registry.npmjs.org/werift-dtls/-/werift-dtls-0.5.7.tgz",
|
||||
"integrity": "sha512-z2fjbP7fFUFmu/Ky4bCKXzdgPTtmSY1DYi0TUf3GG2zJT4jMQ3TQmGY8y7BSSNGetvL4h3pRZ5un0EcSOWpPog==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@fidm/x509": "^1.2.1",
|
||||
"@noble/curves": "^1.3.0",
|
||||
@@ -12472,6 +12526,7 @@
|
||||
"resolved": "https://registry.npmjs.org/werift-ice/-/werift-ice-0.2.2.tgz",
|
||||
"integrity": "sha512-td52pHp+JmFnUn5jfDr/SSNO0dMCbknhuPdN1tFp9cfRj5jaktN63qnAdUuZC20QCC3ETWdsOthcm+RalHpFCQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@shinyoshiaki/jspack": "^0.0.6",
|
||||
"buffer-crc32": "^1.0.0",
|
||||
@@ -12489,6 +12544,7 @@
|
||||
"resolved": "https://registry.npmjs.org/werift-rtp/-/werift-rtp-0.8.8.tgz",
|
||||
"integrity": "sha512-GiYMSdvCyScQaw5bnEsraSoHUVZpjfokJAiLV4R1FsiB06t6XiebPYPpkqB9nYNNKiA8Z/cYWsym7wISq1sYSQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@minhducsun2002/leb128": "^1.0.0",
|
||||
"@shinyoshiaki/jspack": "^0.0.6",
|
||||
@@ -12505,6 +12561,7 @@
|
||||
"resolved": "https://registry.npmjs.org/werift-sctp/-/werift-sctp-0.0.11.tgz",
|
||||
"integrity": "sha512-7109yuI5U7NTEHjqjn0A8VeynytkgVaxM6lRr1Ziv0D8bPcaB8A7U/P88M7WaCpWDoELHoXiRUjQycMWStIgjQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@shinyoshiaki/jspack": "^0.0.6"
|
||||
},
|
||||
@@ -12517,6 +12574,7 @@
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
@@ -12806,7 +12864,7 @@
|
||||
},
|
||||
"packages/aac-encoder": {
|
||||
"name": "@mediabunny/aac-encoder",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -12821,7 +12879,7 @@
|
||||
},
|
||||
"packages/ac3": {
|
||||
"name": "@mediabunny/ac3",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -12836,7 +12894,7 @@
|
||||
},
|
||||
"packages/flac-encoder": {
|
||||
"name": "@mediabunny/flac-encoder",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -12851,7 +12909,7 @@
|
||||
},
|
||||
"packages/mp3-encoder": {
|
||||
"name": "@mediabunny/mp3-encoder",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -12866,10 +12924,10 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@mediabunny/server",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"node-av": "^5.2.4"
|
||||
"node-av": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mediabunny",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/aac-encoder",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"description": "AAC encoder extension for Mediabunny, based on FFmpeg.",
|
||||
"main": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
||||
"module": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/ac3",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"description": "AC-3 and E-AC-3 (Dolby Digital) decoder and encoder extension for Mediabunny, based on FFmpeg.",
|
||||
"main": "./dist/bundles/mediabunny-ac3.mjs",
|
||||
"module": "./dist/bundles/mediabunny-ac3.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/flac-encoder",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"description": "FLAC encoder extension for Mediabunny, based on libFLAC.",
|
||||
"main": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
||||
"module": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/mp3-encoder",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"description": "MP3 encoder extension for Mediabunny, based on LAME.",
|
||||
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||
|
||||
@@ -47,11 +47,11 @@ import { registerMediabunnyServer } from '@mediabunny/server';
|
||||
import * as NodeAv from 'node-av';
|
||||
|
||||
registerMediabunnyServer({
|
||||
// Use a specific hardware rendering device:
|
||||
hardwareContext: NodeAv.HardwareContext.create(
|
||||
NodeAv.AV_HWDEVICE_TYPE_VAAPI,
|
||||
'/dev/dri/renderD128',
|
||||
),
|
||||
// Use a specific hardware rendering device:
|
||||
hardwareContext: NodeAv.HardwareContext.create(
|
||||
NodeAv.AV_HWDEVICE_TYPE_VAAPI,
|
||||
'/dev/dri/renderD128',
|
||||
),
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/server",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.46.0",
|
||||
"version": "1.47.0",
|
||||
"description": "Adds full video and audio decoder and encoder support to Mediabunny for use in server-side environments (Node, Bun, Deno). Based on NodeAV.",
|
||||
"main": "./dist/bundles/mediabunny-server.cjs",
|
||||
"module": "./dist/bundles/mediabunny-server.mjs",
|
||||
@@ -34,7 +34,7 @@
|
||||
"url": "https://github.com/sponsors/Vanilagy"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-av": "^5.2.4"
|
||||
"node-av": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"mediabunny": "^1.45.0"
|
||||
|
||||
+108
-215
@@ -14,6 +14,7 @@ import {
|
||||
VideoCodec,
|
||||
} from './codec';
|
||||
import {
|
||||
AudioEncodingConfig,
|
||||
getEncodableAudioCodecs,
|
||||
getFirstEncodableVideoCodec,
|
||||
Quality,
|
||||
@@ -50,17 +51,14 @@ import { Output, OutputTrackGroup, TrackType } from './output';
|
||||
import { Mp4OutputFormat } from './output-format';
|
||||
import {
|
||||
AudioSample,
|
||||
audioSampleToInterleavedFormat,
|
||||
clampCropRectangle,
|
||||
CropRectangle,
|
||||
toInterleavedAudioFormat,
|
||||
validateCropRectangle,
|
||||
VideoSample,
|
||||
VideoSampleResource,
|
||||
} from './sample';
|
||||
import { MetadataTags, validateMetadataTags } from './metadata';
|
||||
import { NullTarget } from './target';
|
||||
import { AudioResampler } from './resample';
|
||||
|
||||
/**
|
||||
* The options for media file conversion.
|
||||
@@ -1342,6 +1340,10 @@ export class Conversion {
|
||||
encodingConfig.transform.frameRate = trackOptions.frameRate;
|
||||
}
|
||||
|
||||
if (trackOptions.process) {
|
||||
encodingConfig.transform.process = trackOptions.process;
|
||||
}
|
||||
|
||||
if (needsRerender) {
|
||||
outputTrackRotation = 0; // Since the rotation is baked into the output
|
||||
|
||||
@@ -1353,6 +1355,12 @@ export class Conversion {
|
||||
encodingConfig.transform.alpha = alpha;
|
||||
}
|
||||
|
||||
// We need to do this because `process` can emit new timestamps
|
||||
let lastSampleTimestamp: number | null = null;
|
||||
encodingConfig.onEncodedSample = (sample) => {
|
||||
lastSampleTimestamp = sample.timestamp;
|
||||
};
|
||||
|
||||
const source = new VideoSampleSource(encodingConfig);
|
||||
videoSource = source;
|
||||
|
||||
@@ -1370,7 +1378,14 @@ export class Conversion {
|
||||
const adjustedSampleTimestamp = Math.max(sample.timestamp - this._startTimestamp, 0);
|
||||
sample.setTimestamp(adjustedSampleTimestamp);
|
||||
|
||||
await this._registerVideoSample(trackOptions, outputTrackId, source, sample);
|
||||
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
||||
await source.add(sample);
|
||||
|
||||
if (lastSampleTimestamp !== null) {
|
||||
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
||||
await this._synchronizer.wait(lastSampleTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
sample.close();
|
||||
}
|
||||
@@ -1403,69 +1418,6 @@ export class Conversion {
|
||||
this._outputOwnTrackGroups.push(ownGroup);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
async _registerVideoSample(
|
||||
trackOptions: ConversionVideoOptions,
|
||||
outputTrackId: number,
|
||||
source: VideoSampleSource,
|
||||
sample: VideoSample,
|
||||
) {
|
||||
if (this._canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
||||
|
||||
let finalSamples: VideoSample[];
|
||||
if (!trackOptions.process) {
|
||||
finalSamples = [sample];
|
||||
} else {
|
||||
let processed = trackOptions.process(sample);
|
||||
if (processed instanceof Promise) processed = await processed;
|
||||
|
||||
if (!Array.isArray(processed)) {
|
||||
processed = processed === null ? [] : [processed];
|
||||
}
|
||||
|
||||
finalSamples = processed.map((x) => {
|
||||
if (x instanceof VideoSample) {
|
||||
return x;
|
||||
}
|
||||
|
||||
if (typeof VideoFrame !== 'undefined' && x instanceof VideoFrame) {
|
||||
return new VideoSample(x);
|
||||
}
|
||||
|
||||
// Calling the VideoSample constructor here will automatically handle input validation for us
|
||||
// (it throws for any non-legal argument).
|
||||
return new VideoSample(x as CanvasImageSource, {
|
||||
timestamp: sample.timestamp,
|
||||
duration: sample.duration,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
for (const finalSample of finalSamples) {
|
||||
if (this._canceled) {
|
||||
break;
|
||||
}
|
||||
|
||||
await source.add(finalSample);
|
||||
|
||||
if (this._synchronizer.shouldWait(outputTrackId, finalSample.timestamp)) {
|
||||
await this._synchronizer.wait(finalSample.timestamp);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
for (const finalSample of finalSamples) {
|
||||
if (finalSample !== sample) {
|
||||
finalSample.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
async _processAudioTrack(track: InputAudioTrack, trackOptions: ConversionAudioOptions, outputTrackId: number) {
|
||||
const sourceCodec = await track.getCodec();
|
||||
@@ -1487,16 +1439,18 @@ export class Conversion {
|
||||
|
||||
let numberOfChannels = trackOptions.numberOfChannels ?? originalNumberOfChannels;
|
||||
let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
|
||||
let needsResample = numberOfChannels !== originalNumberOfChannels
|
||||
|| sampleRate !== originalSampleRate
|
||||
|| firstTimestamp < this._startTimestamp
|
||||
|| (firstTimestamp > this._startTimestamp && !this.output.format.supportsTimestampedMediaData);
|
||||
|
||||
const needsTrimming = firstTimestamp < this._startTimestamp;
|
||||
const needsPadding = firstTimestamp > this._startTimestamp && !this.output.format.supportsTimestampedMediaData;
|
||||
|
||||
let audioCodecs = this.output.format.getSupportedAudioCodecs();
|
||||
if (
|
||||
!trackOptions.forceTranscode
|
||||
&& !trackOptions.bitrate
|
||||
&& !needsResample
|
||||
&& numberOfChannels === originalNumberOfChannels
|
||||
&& sampleRate === originalSampleRate
|
||||
&& !needsTrimming
|
||||
&& !needsPadding
|
||||
&& audioCodecs.includes(sourceCodec)
|
||||
&& (!trackOptions.codec || trackOptions.codec === sourceCodec)
|
||||
&& !trackOptions.process
|
||||
@@ -1589,7 +1543,6 @@ export class Conversion {
|
||||
.find(codec => (NON_PCM_AUDIO_CODECS as readonly string[]).includes(codec));
|
||||
if (nonPcmCodec) {
|
||||
// We are able to encode using a non-PCM codec, but it'll require resampling
|
||||
needsResample = true;
|
||||
codecOfChoice = nonPcmCodec;
|
||||
numberOfChannels = FALLBACK_NUMBER_OF_CHANNELS;
|
||||
sampleRate = FALLBACK_SAMPLE_RATE;
|
||||
@@ -1607,44 +1560,86 @@ export class Conversion {
|
||||
return;
|
||||
}
|
||||
|
||||
if (needsResample) {
|
||||
audioSource = this._resampleAudio(
|
||||
track,
|
||||
trackOptions,
|
||||
outputTrackId,
|
||||
codecOfChoice,
|
||||
numberOfChannels,
|
||||
sampleRate,
|
||||
bitrate,
|
||||
);
|
||||
} else {
|
||||
const source = new AudioSampleSource({
|
||||
codec: codecOfChoice,
|
||||
bitrate,
|
||||
});
|
||||
audioSource = source;
|
||||
const encodingConfig: AudioEncodingConfig = {
|
||||
codec: codecOfChoice,
|
||||
bitrate,
|
||||
transform: {
|
||||
sampleFormat: trackOptions.sampleFormat,
|
||||
process: trackOptions.process,
|
||||
},
|
||||
};
|
||||
assert(encodingConfig.transform);
|
||||
|
||||
this._trackPromises.push((async () => {
|
||||
await this._started;
|
||||
if (numberOfChannels !== originalNumberOfChannels) {
|
||||
encodingConfig.transform.numberOfChannels = numberOfChannels;
|
||||
}
|
||||
if (sampleRate !== originalSampleRate) {
|
||||
encodingConfig.transform.sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
const sink = new AudioSampleSink(track);
|
||||
for await (const sample of sink.samples(undefined, this._endTimestamp)) {
|
||||
if (this._canceled) {
|
||||
sample.close();
|
||||
return;
|
||||
}
|
||||
let lastSampleTimestamp: number | null = null;
|
||||
encodingConfig.onEncodedSample = (sample) => {
|
||||
lastSampleTimestamp = sample.timestamp;
|
||||
};
|
||||
|
||||
// Offset the timestamp as needed
|
||||
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
||||
const source = new AudioSampleSource(encodingConfig);
|
||||
audioSource = source;
|
||||
|
||||
await this._registerAudioSample(trackOptions, outputTrackId, source, sample);
|
||||
this._trackPromises.push((async () => {
|
||||
await this._started;
|
||||
|
||||
if (needsPadding) {
|
||||
const paddingLength = firstTimestamp - this._startTimestamp;
|
||||
const paddingLengthSamples = Math.round(paddingLength * originalSampleRate);
|
||||
|
||||
const silentSample = new AudioSample({
|
||||
data: new Float32Array(paddingLengthSamples * originalNumberOfChannels),
|
||||
format: 'f32-planar',
|
||||
numberOfChannels: originalNumberOfChannels,
|
||||
sampleRate: originalSampleRate,
|
||||
timestamp: 0,
|
||||
});
|
||||
await this._registerAudioSample(silentSample, source, outputTrackId, () => lastSampleTimestamp);
|
||||
}
|
||||
|
||||
const sink = new AudioSampleSink(track);
|
||||
for await (let sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
||||
if (this._canceled) {
|
||||
sample.close();
|
||||
return;
|
||||
}
|
||||
|
||||
source.close();
|
||||
this._synchronizer.closeTrack(outputTrackId);
|
||||
})());
|
||||
}
|
||||
let startFrame = 0;
|
||||
let endFrame = sample.numberOfFrames;
|
||||
|
||||
if (sample.timestamp < this._startTimestamp) {
|
||||
startFrame = Math.round((this._startTimestamp - sample.timestamp) * sample.sampleRate);
|
||||
}
|
||||
if (sample.timestamp + sample.duration > this._endTimestamp) {
|
||||
endFrame = Math.round((this._endTimestamp - sample.timestamp) * sample.sampleRate);
|
||||
}
|
||||
|
||||
if (startFrame > 0 || endFrame < sample.numberOfFrames) {
|
||||
// Trim the sample if it sticks out of the trim region on either end
|
||||
const trimmedSample = sample.trim(startFrame, endFrame);
|
||||
sample.close();
|
||||
sample = trimmedSample;
|
||||
|
||||
if (sample.numberOfFrames === 0) {
|
||||
sample.close();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Offset the timestamp as needed
|
||||
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
||||
|
||||
await this._registerAudioSample(sample, source, outputTrackId, () => lastSampleTimestamp);
|
||||
}
|
||||
|
||||
source.close();
|
||||
this._synchronizer.closeTrack(outputTrackId);
|
||||
})());
|
||||
}
|
||||
|
||||
let ownGroup: OutputTrackGroup | null = null;
|
||||
@@ -1670,126 +1665,24 @@ export class Conversion {
|
||||
|
||||
/** @internal */
|
||||
async _registerAudioSample(
|
||||
trackOptions: ConversionAudioOptions,
|
||||
outputTrackId: number,
|
||||
sample: AudioSample,
|
||||
source: AudioSampleSource,
|
||||
inputSample: AudioSample,
|
||||
outputTrackId: number,
|
||||
getLastSampleTimestamp: () => number | null,
|
||||
) {
|
||||
if (this._canceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sample = inputSample;
|
||||
|
||||
if (
|
||||
trackOptions.sampleFormat !== undefined
|
||||
&& toInterleavedAudioFormat(sample.format) !== trackOptions.sampleFormat
|
||||
) {
|
||||
// Do a sample format conversion
|
||||
sample = audioSampleToInterleavedFormat(sample, trackOptions.sampleFormat);
|
||||
}
|
||||
|
||||
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
||||
|
||||
let finalSamples: AudioSample[];
|
||||
if (!trackOptions.process) {
|
||||
finalSamples = [sample];
|
||||
} else {
|
||||
let processed = trackOptions.process(sample);
|
||||
if (processed instanceof Promise) processed = await processed;
|
||||
await source.add(sample);
|
||||
sample.close();
|
||||
|
||||
if (!Array.isArray(processed)) {
|
||||
processed = processed === null ? [] : [processed];
|
||||
}
|
||||
|
||||
if (!processed.every(x => x instanceof AudioSample)) {
|
||||
throw new TypeError(
|
||||
'The audio process function must return an AudioSample, null, or an array of AudioSamples.',
|
||||
);
|
||||
}
|
||||
|
||||
finalSamples = processed;
|
||||
}
|
||||
|
||||
try {
|
||||
for (const finalSample of finalSamples) {
|
||||
if (this._canceled) {
|
||||
break;
|
||||
}
|
||||
|
||||
await source.add(finalSample);
|
||||
|
||||
if (this._synchronizer.shouldWait(outputTrackId, finalSample.timestamp)) {
|
||||
await this._synchronizer.wait(finalSample.timestamp);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (sample !== inputSample) {
|
||||
sample.close();
|
||||
}
|
||||
|
||||
for (const finalSample of finalSamples) {
|
||||
if (finalSample !== inputSample) {
|
||||
finalSample.close();
|
||||
}
|
||||
const lastSampleTimestamp = getLastSampleTimestamp();
|
||||
if (lastSampleTimestamp !== null) {
|
||||
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
||||
await this._synchronizer.wait(lastSampleTimestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_resampleAudio(
|
||||
track: InputAudioTrack,
|
||||
trackOptions: ConversionAudioOptions,
|
||||
outputTrackId: number,
|
||||
codec: AudioCodec,
|
||||
targetNumberOfChannels: number,
|
||||
targetSampleRate: number,
|
||||
bitrate: number | Quality,
|
||||
) {
|
||||
const source = new AudioSampleSource({
|
||||
codec,
|
||||
bitrate,
|
||||
});
|
||||
|
||||
this._trackPromises.push((async () => {
|
||||
await this._started;
|
||||
|
||||
const resampler = new AudioResampler({
|
||||
targetNumberOfChannels,
|
||||
targetSampleRate,
|
||||
startTime: this._startTimestamp,
|
||||
endTime: this._endTimestamp,
|
||||
onSample: async (sample) => {
|
||||
assert(sample.timestamp >= this._startTimestamp);
|
||||
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
||||
|
||||
await this._registerAudioSample(trackOptions, outputTrackId, source, sample);
|
||||
sample.close();
|
||||
},
|
||||
});
|
||||
|
||||
const sink = new AudioSampleSink(track);
|
||||
const iterator = sink.samples(this._startTimestamp, this._endTimestamp);
|
||||
|
||||
for await (const sample of iterator) {
|
||||
if (this._canceled) {
|
||||
sample.close();
|
||||
return;
|
||||
}
|
||||
|
||||
await resampler.add(sample);
|
||||
sample.close();
|
||||
}
|
||||
|
||||
await resampler.finalize();
|
||||
|
||||
source.close();
|
||||
this._synchronizer.closeTrack(outputTrackId);
|
||||
})());
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reportProgress(trackId: number, endTimestamp: number) {
|
||||
if (!this._computeProgress) {
|
||||
|
||||
@@ -74,6 +74,8 @@ export type VideoEncodingConfig = {
|
||||
* WebCodecs API, is created.
|
||||
*/
|
||||
onEncoderConfig?: (config: VideoEncoderConfig) => unknown;
|
||||
/** Called right before a sample is passed to the encoder. */
|
||||
onEncodedSample?: (sample: VideoSample) => unknown;
|
||||
} & VideoEncodingAdditionalOptions;
|
||||
|
||||
/**
|
||||
@@ -232,6 +234,9 @@ export const validateVideoEncodingConfig = (config: VideoEncodingConfig) => {
|
||||
if (config.onEncoderConfig !== undefined && typeof config.onEncoderConfig !== 'function') {
|
||||
throw new TypeError('config.onEncoderConfig, when provided, must be a function.');
|
||||
}
|
||||
if (config.onEncodedSample !== undefined && typeof config.onEncodedSample !== 'function') {
|
||||
throw new TypeError('config.onEncodedSample, when provided, must be a function.');
|
||||
}
|
||||
|
||||
validateVideoEncodingAdditionalOptions(config.codec, config);
|
||||
};
|
||||
@@ -382,6 +387,8 @@ export type AudioEncodingConfig = {
|
||||
* WebCodecs API, is created.
|
||||
*/
|
||||
onEncoderConfig?: (config: AudioEncoderConfig) => unknown;
|
||||
/** Called right before a sample is passed to the encoder. */
|
||||
onEncodedSample?: (sample: AudioSample) => unknown;
|
||||
} & AudioEncodingAdditionalOptions;
|
||||
|
||||
/**
|
||||
@@ -462,6 +469,9 @@ export const validateAudioEncodingConfig = (config: AudioEncodingConfig) => {
|
||||
if (config.onEncoderConfig !== undefined && typeof config.onEncoderConfig !== 'function') {
|
||||
throw new TypeError('config.onEncoderConfig, when provided, must be a function.');
|
||||
}
|
||||
if (config.onEncodedSample !== undefined && typeof config.onEncodedSample !== 'function') {
|
||||
throw new TypeError('config.onEncodedSample, when provided, must be a function.');
|
||||
}
|
||||
|
||||
validateAudioEncodingAdditionalOptions(config.codec, config);
|
||||
};
|
||||
|
||||
+6
-2
@@ -527,8 +527,12 @@ export class Input<S extends Source = Source> extends EventEmitter<InputEvents>
|
||||
}
|
||||
this._sourceRefs.length = 0;
|
||||
|
||||
void this._demuxerPromise
|
||||
?.then(demuxer => demuxer.dispose());
|
||||
if (this._demuxerPromise) {
|
||||
// The demuxer promise may already be rejected after failed format detection.
|
||||
void this._demuxerPromise
|
||||
.then(demuxer => demuxer.dispose())
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2069,8 +2069,14 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
// referenced in the track fragment header.
|
||||
if (this.currentTrack) {
|
||||
const trackData = this.currentFragment.trackData.get(this.currentTrack.id);
|
||||
cond:
|
||||
if (trackData) {
|
||||
this.currentFragment.implicitBaseDataOffset = trackData.currentOffset;
|
||||
if (trackData.samples.length === 0) {
|
||||
// Don't associate the fragment with the track if it has no samples, this simplifies
|
||||
// other code
|
||||
this.currentFragment.trackData.delete(this.currentTrack.id);
|
||||
break cond;
|
||||
}
|
||||
|
||||
trackData.presentationTimestamps = trackData.samples
|
||||
.map((x, i) => ({ presentationTimestamp: x.presentationTimestamp, sampleIndex: i }))
|
||||
@@ -2260,12 +2266,6 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
this.currentFragment.trackData.set(track.id, trackData);
|
||||
}
|
||||
|
||||
if (sampleCount === 0) {
|
||||
// Don't associate the fragment with the track if it has no samples, this simplifies other code
|
||||
this.currentFragment.implicitBaseDataOffset = trackData.currentOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
for (let i = 0; i < sampleCount; i++) {
|
||||
let sampleDuration: number;
|
||||
if (sampleDurationPresent) {
|
||||
@@ -2317,6 +2317,8 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
trackData.currentOffset += sampleSize;
|
||||
trackData.currentTimestamp += sampleDuration;
|
||||
}
|
||||
|
||||
this.currentFragment.implicitBaseDataOffset = trackData.currentOffset;
|
||||
}; break;
|
||||
|
||||
case 'saiz': {
|
||||
|
||||
+20
-2
@@ -265,6 +265,7 @@ class VideoEncoderWrapper {
|
||||
* So, we keep track of the encoder error and throw it as soon as we get the chance.
|
||||
*/
|
||||
private error: Error | null = null;
|
||||
private closed = false;
|
||||
|
||||
private lastMuxerPromise: Promise<void> = Promise.resolve();
|
||||
|
||||
@@ -429,6 +430,8 @@ class VideoEncoderWrapper {
|
||||
return new VideoSample(x);
|
||||
}
|
||||
|
||||
// Calling the VideoSample constructor here will automatically handle input validation for us
|
||||
// (it throws for any non-legal argument).
|
||||
return new VideoSample(x as CanvasImageSource, {
|
||||
timestamp: videoSample.timestamp,
|
||||
duration: videoSample.duration,
|
||||
@@ -457,6 +460,10 @@ class VideoEncoderWrapper {
|
||||
}
|
||||
assert(this.encoderInitialized);
|
||||
|
||||
if (this.closed) {
|
||||
break;
|
||||
}
|
||||
|
||||
const keyFrameInterval = this.encodingConfig.keyFrameInterval ?? 2;
|
||||
const multipleOfKeyFrameInterval = Math.floor(sampleToEncode.timestamp / keyFrameInterval);
|
||||
|
||||
@@ -474,6 +481,8 @@ class VideoEncoderWrapper {
|
||||
};
|
||||
this.lastMultipleOfKeyFrameInterval = multipleOfKeyFrameInterval;
|
||||
|
||||
this.encodingConfig.onEncodedSample?.(sampleToEncode);
|
||||
|
||||
if (this.customEncoder) {
|
||||
this.customEncoderQueueSize++;
|
||||
|
||||
@@ -843,6 +852,8 @@ class VideoEncoderWrapper {
|
||||
await this.padFrameRate(alignedEnd);
|
||||
}
|
||||
|
||||
this.closed = true;
|
||||
|
||||
this.frameRateLastSample?.close();
|
||||
this.frameRateLastSample = null;
|
||||
|
||||
@@ -2033,6 +2044,7 @@ class AudioEncoderWrapper {
|
||||
*/
|
||||
private error: Error | null = null;
|
||||
private lastMuxerPromise: Promise<void> = Promise.resolve();
|
||||
private closed = false;
|
||||
|
||||
constructor(private source: AudioSource, private encodingConfig: AudioEncodingConfig) {}
|
||||
|
||||
@@ -2070,8 +2082,6 @@ class AudioEncoderWrapper {
|
||||
?? audioSample.numberOfChannels,
|
||||
targetSampleRate: config.transform!.sampleRate
|
||||
?? audioSample.sampleRate,
|
||||
startTime: audioSample.timestamp,
|
||||
endTime: Infinity,
|
||||
onSample: async (sample) => {
|
||||
await this.processAndEncode(sample, true);
|
||||
},
|
||||
@@ -2162,6 +2172,10 @@ class AudioEncoderWrapper {
|
||||
}
|
||||
assert(this.encoderInitialized);
|
||||
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle padding of gaps with silence to avoid audio drift over time, like in
|
||||
// https://github.com/Vanilagy/mediabunny/issues/176
|
||||
// TODO An open question is how encoders deal with the first AudioData having a non-zero timestamp, and with
|
||||
@@ -2197,6 +2211,8 @@ class AudioEncoderWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
this.encodingConfig.onEncodedSample?.(audioSample);
|
||||
|
||||
if (this.customEncoder) {
|
||||
this.customEncoderQueueSize++;
|
||||
|
||||
@@ -2536,6 +2552,8 @@ class AudioEncoderWrapper {
|
||||
}
|
||||
this.resampler = null;
|
||||
|
||||
this.closed = true;
|
||||
|
||||
if (this.customEncoder) {
|
||||
if (!forceClose) {
|
||||
void this.customEncoderCallSerializer.call(() => this.customEncoder!.flush());
|
||||
|
||||
+10
-15
@@ -20,41 +20,32 @@ export class AudioResampler {
|
||||
targetSampleRate: number;
|
||||
sourceNumberOfChannels: number | null = null;
|
||||
targetNumberOfChannels: number;
|
||||
endTime: number;
|
||||
startTime: number | null = null;
|
||||
onSample: (sample: AudioSample) => Promise<void>;
|
||||
|
||||
bufferSizeInFrames: number;
|
||||
bufferSizeInSamples: number;
|
||||
outputBuffer: Float32Array;
|
||||
/** Start frame of current buffer */
|
||||
bufferStartFrame: number;
|
||||
bufferStartFrame = 0;
|
||||
/** The highest index written to in the current buffer */
|
||||
maxWrittenFrame: number | null = null;
|
||||
channelMixer!: (sourceData: Float32Array, sourceFrameIndex: number, targetChannelIndex: number) => number;
|
||||
tempSourceBuffer!: Float32Array;
|
||||
timestampOffset: number;
|
||||
|
||||
constructor(options: {
|
||||
targetSampleRate: number;
|
||||
targetNumberOfChannels: number;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
onSample: (sample: AudioSample) => Promise<void>;
|
||||
}) {
|
||||
this.targetSampleRate = options.targetSampleRate;
|
||||
this.targetNumberOfChannels = options.targetNumberOfChannels;
|
||||
this.endTime = options.endTime;
|
||||
this.onSample = options.onSample;
|
||||
|
||||
this.bufferSizeInFrames = Math.floor(this.targetSampleRate * 5.0); // 5 seconds
|
||||
this.bufferSizeInSamples = this.bufferSizeInFrames * this.targetNumberOfChannels;
|
||||
|
||||
this.outputBuffer = new Float32Array(this.bufferSizeInSamples);
|
||||
|
||||
this.bufferStartFrame = Math.floor(options.startTime * this.targetSampleRate);
|
||||
// Set to ensure that if the buffer start frame lands on a fractional sample, that the first timestamp still
|
||||
// comes out as exactly startTime
|
||||
this.timestampOffset = options.startTime - this.bufferStartFrame / this.targetSampleRate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,6 +177,7 @@ export class AudioResampler {
|
||||
// they see fit.
|
||||
this.sourceSampleRate = audioSample.sampleRate;
|
||||
this.sourceNumberOfChannels = audioSample.numberOfChannels;
|
||||
this.startTime = audioSample.timestamp;
|
||||
|
||||
// Pre-allocate temporary buffer for source data
|
||||
this.tempSourceBuffer = new Float32Array(this.sourceSampleRate * this.sourceNumberOfChannels);
|
||||
@@ -193,6 +185,8 @@ export class AudioResampler {
|
||||
this.doChannelMixerSetup();
|
||||
}
|
||||
|
||||
assert(this.startTime !== null);
|
||||
|
||||
const requiredSamples = audioSample.numberOfFrames * audioSample.numberOfChannels;
|
||||
this.ensureTempBufferSize(requiredSamples);
|
||||
|
||||
@@ -201,8 +195,8 @@ export class AudioResampler {
|
||||
const sourceView = new Float32Array(this.tempSourceBuffer.buffer, 0, sourceDataSize / 4);
|
||||
audioSample.copyTo(sourceView, { planeIndex: 0, format: 'f32' });
|
||||
|
||||
const inputStartTime = audioSample.timestamp;
|
||||
const inputEndTime = Math.min(audioSample.timestamp + audioSample.duration, this.endTime);
|
||||
const inputStartTime = audioSample.timestamp - this.startTime;
|
||||
const inputEndTime = inputStartTime + audioSample.duration;
|
||||
|
||||
// Compute which output frames are affected by this sample
|
||||
const outputStartFrame = Math.floor(inputStartTime * this.targetSampleRate);
|
||||
@@ -266,17 +260,18 @@ export class AudioResampler {
|
||||
return; // Nothing to finalize
|
||||
}
|
||||
|
||||
assert(this.startTime !== null);
|
||||
|
||||
const samplesWritten = (this.maxWrittenFrame + 1) * this.targetNumberOfChannels;
|
||||
|
||||
const outputData = new Float32Array(samplesWritten);
|
||||
outputData.set(this.outputBuffer.subarray(0, samplesWritten));
|
||||
|
||||
const timestampSeconds = this.bufferStartFrame / this.targetSampleRate;
|
||||
const audioSample = new AudioSample({
|
||||
format: 'f32',
|
||||
sampleRate: this.targetSampleRate,
|
||||
numberOfChannels: this.targetNumberOfChannels,
|
||||
timestamp: timestampSeconds + this.timestampOffset,
|
||||
timestamp: this.startTime + this.bufferStartFrame / this.targetSampleRate,
|
||||
data: outputData,
|
||||
});
|
||||
|
||||
|
||||
@@ -2712,6 +2712,74 @@ export class AudioSample implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link AudioSample} containing only the frames in the range [startSample, endSample). Both bounds
|
||||
* must lie within this sample's range of frames. The returned sample's timestamp is shifted to match the start of
|
||||
* the trimmed section.
|
||||
*/
|
||||
trim(startSample: number, endSample = this.numberOfFrames) {
|
||||
if (!Number.isInteger(startSample) || startSample < 0) {
|
||||
throw new TypeError('startSample must be a non-negative integer.');
|
||||
}
|
||||
if (!Number.isInteger(endSample) || endSample < 0) {
|
||||
throw new TypeError('endSample must be a non-negative integer.');
|
||||
}
|
||||
if (startSample > this.numberOfFrames) {
|
||||
throw new RangeError('startSample out of range.');
|
||||
}
|
||||
if (endSample > this.numberOfFrames) {
|
||||
throw new RangeError('endSample out of range.');
|
||||
}
|
||||
if (endSample < startSample) {
|
||||
throw new RangeError('endSample must not be less than startSample.');
|
||||
}
|
||||
|
||||
if (this._closed) {
|
||||
throw new Error('AudioSample is closed.');
|
||||
}
|
||||
|
||||
const frameCount = endSample - startSample;
|
||||
const bytesPerSample = getBytesPerSample(this.format);
|
||||
|
||||
let data: Uint8Array;
|
||||
if (formatIsPlanar(this.format)) {
|
||||
const planeSize = frameCount * bytesPerSample;
|
||||
data = new Uint8Array(planeSize * this.numberOfChannels);
|
||||
|
||||
if (frameCount > 0) {
|
||||
// Copy plane-by-plane
|
||||
for (let i = 0; i < this.numberOfChannels; i++) {
|
||||
this.copyTo(data.subarray(i * planeSize, (i + 1) * planeSize), {
|
||||
planeIndex: i,
|
||||
format: this.format,
|
||||
frameOffset: startSample,
|
||||
frameCount,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Trivial
|
||||
data = new Uint8Array(frameCount * this.numberOfChannels * bytesPerSample);
|
||||
|
||||
if (frameCount > 0) {
|
||||
this.copyTo(data, {
|
||||
planeIndex: 0,
|
||||
format: this.format,
|
||||
frameOffset: startSample,
|
||||
frameCount,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new AudioSample({
|
||||
data,
|
||||
format: this.format,
|
||||
sampleRate: this.sampleRate,
|
||||
numberOfChannels: this.numberOfChannels,
|
||||
timestamp: this.timestamp + startSample / this.sampleRate,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this audio sample, releasing held resources. Audio samples should be closed as soon as they are not
|
||||
* needed anymore.
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { AudioSample } from '../../src/sample.js';
|
||||
|
||||
const SAMPLE_RATE = 48000;
|
||||
const NUM_CHANNELS = 2;
|
||||
const NUM_FRAMES = 10;
|
||||
const TIMESTAMP = 1;
|
||||
|
||||
const cellValue = (frame: number, channel: number) => frame * 10 + channel;
|
||||
|
||||
const makeInterleavedSample = () => {
|
||||
const data = new Int16Array(NUM_FRAMES * NUM_CHANNELS);
|
||||
for (let f = 0; f < NUM_FRAMES; f++) {
|
||||
for (let c = 0; c < NUM_CHANNELS; c++) {
|
||||
data[f * NUM_CHANNELS + c] = cellValue(f, c);
|
||||
}
|
||||
}
|
||||
|
||||
return new AudioSample({
|
||||
data,
|
||||
format: 's16',
|
||||
sampleRate: SAMPLE_RATE,
|
||||
numberOfChannels: NUM_CHANNELS,
|
||||
timestamp: TIMESTAMP,
|
||||
});
|
||||
};
|
||||
|
||||
const makePlanarSample = () => {
|
||||
const data = new Float32Array(NUM_FRAMES * NUM_CHANNELS);
|
||||
for (let c = 0; c < NUM_CHANNELS; c++) {
|
||||
for (let f = 0; f < NUM_FRAMES; f++) {
|
||||
data[c * NUM_FRAMES + f] = cellValue(f, c);
|
||||
}
|
||||
}
|
||||
|
||||
return new AudioSample({
|
||||
data,
|
||||
format: 'f32-planar',
|
||||
sampleRate: SAMPLE_RATE,
|
||||
numberOfChannels: NUM_CHANNELS,
|
||||
timestamp: TIMESTAMP,
|
||||
});
|
||||
};
|
||||
|
||||
const readCell = (sample: AudioSample, frame: number, channel: number) => {
|
||||
if (sample.format === 's16') {
|
||||
const out = new Int16Array(sample.numberOfFrames * sample.numberOfChannels);
|
||||
sample.copyTo(out, { planeIndex: 0, format: 's16' });
|
||||
return out[frame * sample.numberOfChannels + channel];
|
||||
} else {
|
||||
const out = new Float32Array(sample.numberOfFrames);
|
||||
sample.copyTo(out, { planeIndex: channel, format: 'f32-planar' });
|
||||
return out[frame];
|
||||
}
|
||||
};
|
||||
|
||||
for (const [label, makeSample] of [
|
||||
['interleaved', makeInterleavedSample],
|
||||
['planar', makePlanarSample],
|
||||
] as const) {
|
||||
test(`trim, normal case (${label})`, () => {
|
||||
using sample = makeSample();
|
||||
using trimmed = sample.trim(3, 8);
|
||||
|
||||
expect(trimmed).not.toBe(sample);
|
||||
expect(trimmed.format).toBe(sample.format);
|
||||
expect(trimmed.sampleRate).toBe(SAMPLE_RATE);
|
||||
expect(trimmed.numberOfChannels).toBe(NUM_CHANNELS);
|
||||
expect(trimmed.numberOfFrames).toBe(5);
|
||||
expect(trimmed.duration).toBeCloseTo(5 / SAMPLE_RATE);
|
||||
expect(trimmed.timestamp).toBeCloseTo(TIMESTAMP + 3 / SAMPLE_RATE);
|
||||
|
||||
// The trimmed frame i corresponds to the original frame i + 3
|
||||
for (let f = 0; f < trimmed.numberOfFrames; f++) {
|
||||
for (let c = 0; c < NUM_CHANNELS; c++) {
|
||||
expect(readCell(trimmed, f, c)).toBe(cellValue(f + 3, c));
|
||||
}
|
||||
}
|
||||
|
||||
// The original sample must be untouched
|
||||
expect(sample.numberOfFrames).toBe(NUM_FRAMES);
|
||||
expect(readCell(sample, 3, 0)).toBe(cellValue(3, 0));
|
||||
});
|
||||
|
||||
test(`trim, full range is an independent copy (${label})`, () => {
|
||||
using sample = makeSample();
|
||||
using trimmed = sample.trim(0, NUM_FRAMES);
|
||||
|
||||
expect(trimmed.numberOfFrames).toBe(NUM_FRAMES);
|
||||
expect(trimmed.timestamp).toBeCloseTo(TIMESTAMP);
|
||||
for (let f = 0; f < NUM_FRAMES; f++) {
|
||||
for (let c = 0; c < NUM_CHANNELS; c++) {
|
||||
expect(readCell(trimmed, f, c)).toBe(cellValue(f, c));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test(`trim, pathological zero-sample case (${label})`, () => {
|
||||
using sample = makeSample();
|
||||
|
||||
// Empty range in the middle
|
||||
using empty = sample.trim(4, 4);
|
||||
expect(empty.numberOfFrames).toBe(0);
|
||||
expect(empty.duration).toBe(0);
|
||||
expect(empty.format).toBe(sample.format);
|
||||
expect(empty.numberOfChannels).toBe(NUM_CHANNELS);
|
||||
expect(empty.timestamp).toBeCloseTo(TIMESTAMP + 4 / SAMPLE_RATE);
|
||||
|
||||
// Empty range right at the end (startSample === numberOfFrames) is still valid
|
||||
using emptyAtEnd = sample.trim(NUM_FRAMES, NUM_FRAMES);
|
||||
expect(emptyAtEnd.numberOfFrames).toBe(0);
|
||||
expect(emptyAtEnd.timestamp).toBeCloseTo(TIMESTAMP + NUM_FRAMES / SAMPLE_RATE);
|
||||
});
|
||||
|
||||
test(`trim, illegal params (${label})`, () => {
|
||||
using sample = makeSample();
|
||||
|
||||
// Non-integer
|
||||
expect(() => sample.trim(1.5, 5)).toThrow(TypeError);
|
||||
expect(() => sample.trim(1, 5.5)).toThrow(TypeError);
|
||||
|
||||
// Negative
|
||||
expect(() => sample.trim(-1, 5)).toThrow(TypeError);
|
||||
expect(() => sample.trim(0, -1)).toThrow(TypeError);
|
||||
|
||||
// Out of range
|
||||
expect(() => sample.trim(0, NUM_FRAMES + 1)).toThrow(RangeError);
|
||||
expect(() => sample.trim(NUM_FRAMES + 1, NUM_FRAMES + 1)).toThrow(RangeError);
|
||||
|
||||
// End before start
|
||||
expect(() => sample.trim(6, 3)).toThrow(RangeError);
|
||||
});
|
||||
}
|
||||
|
||||
test('trim, throws on a closed sample', () => {
|
||||
const sample = makeInterleavedSample();
|
||||
sample.close();
|
||||
expect(() => sample.trim(0, 5)).toThrow('AudioSample is closed.');
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { Input, UnsupportedInputFormatError } from '../../src/input.js';
|
||||
import { ALL_FORMATS } from '../../src/input-format.js';
|
||||
import { BufferSource } from '../../src/source.js';
|
||||
|
||||
test('Disposing after failed format detection does not emit an unhandled rejection', async () => {
|
||||
const input = new Input({
|
||||
source: new BufferSource(new Uint8Array([1, 2, 3, 4])),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
await expect(input.getFormat()).rejects.toThrow(UnsupportedInputFormatError);
|
||||
|
||||
const unhandledRejections: unknown[] = [];
|
||||
const onUnhandledRejection = (reason: unknown) => {
|
||||
unhandledRejections.push(reason);
|
||||
};
|
||||
|
||||
process.on('unhandledRejection', onUnhandledRejection);
|
||||
try {
|
||||
input.dispose();
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
} finally {
|
||||
process.off('unhandledRejection', onUnhandledRejection);
|
||||
}
|
||||
|
||||
expect(unhandledRejections).toEqual([]);
|
||||
});
|
||||
Reference in New Issue
Block a user