├── .gitignore
├── .npmignore
├── Makefile
├── README.md
├── demo
├── demo.html
└── demo.js
├── docs
└── API.md
├── package-lock.json
├── package.json
├── rollup.config.mjs
├── samples
├── decoder
│ ├── decoder.js
│ └── index.html
├── sample1.opus
├── transcoder
│ ├── index.html
│ └── transcoder.js
├── util.js
└── worker-util.js
├── src
├── bridge.ts
├── build.js
├── demux.ts
├── latowc.ts
├── mux.ts
└── wctola.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/
3 | src/*.js
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*.swp
2 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: dist/libavjs-webcodecs-bridge.min.js
2 |
3 | dist/libavjs-webcodecs-bridge.min.js: src/*.ts node_modules/.bin/tsc
4 | npm run build
5 |
6 | node_modules/.bin/tsc:
7 | npm install
8 |
9 | clean:
10 | npm run clean
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | libavjs-webcodecs-bridge is a bridge to help you use [libav.js](https://github.com/Yahweasel/libav.js/) and [WebCodecs](https://github.com/w3c/webcodecs) (or
2 | [libavjs-webcodecs-polyfill](https://github.com/ennuicastr/libavjs-webcodecs-polyfill)) together.
3 |
4 | WebCodecs does not come with demuxers or muxers. libav.js has those as well as
5 | encoders and decoders, but if you have WebCodecs available, you probably should
6 | use them for en/decoding instead of libav.js. That means it's common to demux
7 | with libav.js then decode with WebCodecs, or encode with WebCodecs then mux with
8 | libav.js. But, they don't speak the same language, so to speak.
9 |
10 | This bridge bridges the gap. It includes conversions from the various libav.js
11 | types to the equivalent WebCodecs types, and vice-versa.
12 |
13 | This project is by the same author as libav.js and libavjs-webcodecs-polyfill.
14 | You do not need libavjs-webcodecs-polyfill to use libavjs-webcodecs-bridge or
15 | vice versa; they have related but orthogonal purposes. For type reasons, this
16 | repository depends on both, but even if you bundle libavjs-webcodecs-bridge,
17 | neither will be included.
18 |
19 | libavjs-webcodecs-bridge's API is documented in [API.md](docs/API.md). The demo
20 | in the `demo` directory is a demonstration of start-to-finish transcoding, and
21 | there are some samples in the `samples` directory as well.
22 |
--------------------------------------------------------------------------------
/demo/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This (un)license applies only to this sample code, and not to
3 | * libavjs-webcodecs-bridge as a whole:
4 | *
5 | * This is free and unencumbered software released into the public domain.
6 | *
7 | * Anyone is free to copy, modify, publish, use, compile, sell, or distribute
8 | * this software, either in source code form or as a compiled binary, for any
9 | * purpose, commercial or non-commercial, and by any means.
10 | *
11 | * In jurisdictions that recognize copyright laws, the author or authors of
12 | * this software dedicate any and all copyright interest in the software to the
13 | * public domain. We make this dedication for the benefit of the public at
14 | * large and to the detriment of our heirs and successors. We intend this
15 | * dedication to be an overt act of relinquishment in perpetuity of all present
16 | * and future rights to this software under copyright law.
17 | *
18 | * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 | class BufferStream extends ReadableStream {
27 | buf = [];
28 | res = null;
29 |
30 | constructor() {
31 | super({
32 | pull: async (controller) => {
33 | while (!this.buf.length) {
34 | await new Promise(res => this.res = res);
35 | }
36 | const next = this.buf.shift();
37 | if (next !== null)
38 | controller.enqueue(next);
39 | else
40 | controller.close();
41 | }
42 | });
43 | }
44 |
45 | push(next) {
46 | this.buf.push(next);
47 | if (this.res) {
48 | const res = this.res;
49 | this.res = null;
50 | res();
51 | }
52 | }
53 | }
54 |
55 | async function main() {
56 | // Get an input file
57 | const fileBox = document.getElementById("file");
58 | await new Promise(res => {
59 | fileBox.onchange = function() {
60 | if (fileBox.files.length)
61 | res();
62 | };
63 | });
64 | const file = fileBox.files[0];
65 | document.getElementById("input-box").style.display = "none";
66 |
67 | // Codec info
68 | const vc = document.getElementById("vc").value;
69 | const ac = document.getElementById("ac").value;
70 |
71 | /* Prepare libav. We're using noworker here because libav is
72 | * loaded from a different origin, but you should simply
73 | * load libav from the same origin! */
74 | const libav = await LibAV.LibAV({noworker: true});
75 | await libav.mkreadaheadfile("input", file);
76 |
77 | // Start demuxer
78 | const [ifc, istreams] =
79 | await libav.ff_init_demuxer_file("input");
80 | const rpkt = await libav.av_packet_alloc();
81 | const wpkt = await libav.av_packet_alloc();
82 |
83 | // Translate all the streams
84 | const iToO = [];
85 | const decoders = [];
86 | const decoderStreams = [];
87 | const decConfigs = [];
88 | const packetToChunks = [];
89 | const encoders = [];
90 | const encoderStreams = [];
91 | const encoderReaders = [];
92 | const encConfigs = [];
93 | const chunkToPackets = [];
94 | const ostreams = [];
95 | for (let streamI = 0; streamI < istreams.length; streamI++) {
96 | const istream = istreams[streamI];
97 | iToO.push(-1);
98 | let streamToConfig, Decoder, packetToChunk,
99 | configToStream, Encoder, chunkToPacket;
100 | if (istream.codec_type === libav.AVMEDIA_TYPE_VIDEO) {
101 | streamToConfig = LibAVWebCodecsBridge.videoStreamToConfig;
102 | Decoder = VideoDecoder;
103 | packetToChunk = LibAVWebCodecsBridge.packetToEncodedVideoChunk;
104 | configToStream = LibAVWebCodecsBridge.configToVideoStream;
105 | Encoder = VideoEncoder;
106 | chunkToPacket = LibAVWebCodecsBridge.encodedVideoChunkToPacket;
107 | } else if (istream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
108 | streamToConfig = LibAVWebCodecsBridge.audioStreamToConfig;
109 | Decoder = AudioDecoder;
110 | packetToChunk = LibAVWebCodecsBridge.packetToEncodedAudioChunk;
111 | configToStream = LibAVWebCodecsBridge.configToAudioStream;
112 | Encoder = AudioEncoder;
113 | chunkToPacket = LibAVWebCodecsBridge.encodedAudioChunkToPacket;
114 | } else {
115 | continue;
116 | }
117 |
118 | // Convert the config
119 | const config = await streamToConfig(libav, istream);
120 | let supported;
121 | try {
122 | supported = await Decoder.isConfigSupported(config);
123 | } catch (ex) {}
124 | if (!supported || !supported.supported)
125 | continue;
126 | iToO[streamI] = decConfigs.length;
127 | decConfigs.push(config);
128 |
129 | // Make the decoder
130 | const stream = new BufferStream();
131 | decoderStreams.push(stream);
132 | const decoder = new Decoder({
133 | output: frame => stream.push(frame),
134 | error: error =>
135 | alert("Decoder " + JSON.stringify(config) + ":\n" + error)
136 | });
137 | decoder.configure(config);
138 | decoders.push(decoder);
139 | packetToChunks.push(packetToChunk);
140 |
141 | // Make the encoder config
142 | const encConfig = {
143 | codec: (istream.codec_type === libav.AVMEDIA_TYPE_VIDEO)
144 | ? vc : ac,
145 | width: config.codedWidth,
146 | height: config.codedHeight,
147 | numberOfChannels: config.numberOfChannels,
148 | sampleRate: config.sampleRate
149 | };
150 | encConfigs.push(encConfig);
151 |
152 | // Make the encoder
153 | const encStream = new BufferStream();
154 | encoderStreams.push(encStream);
155 | encoderReaders.push(encStream.getReader());
156 | const encoder = new Encoder({
157 | output: (chunk, metadata) => encStream.push({chunk, metadata}),
158 | error: error =>
159 | alert("Encoder " + JSON.stringify(encConfig) + ":\n" + error)
160 | });
161 | encoder.configure(encConfig);
162 | encoders.push(encoder);
163 | chunkToPackets.push(chunkToPacket);
164 |
165 | // Make the output stream
166 | ostreams.push(await configToStream(libav, encConfig));
167 | }
168 |
169 | if (!decoders.length)
170 | throw new Error("No decodable streams found!");
171 |
172 | // Demuxer -> decoder
173 | (async () => {
174 | while (true) {
175 | const [res, packets] =
176 | await libav.ff_read_frame_multi(ifc, rpkt, {limit: 1});
177 | if (res !== -libav.EAGAIN &&
178 | res !== 0 &&
179 | res !== libav.AVERROR_EOF)
180 | break;
181 |
182 | for (const idx in packets) {
183 | if (iToO[idx] < 0)
184 | continue;
185 | const o = iToO[idx];
186 | const dec = decoders[o];
187 | const p2c = packetToChunks[o];
188 | for (const packet of packets[idx]) {
189 | const chunk = p2c(packet, istreams[idx]);
190 | while (dec.decodeQueueSize) {
191 | await new Promise(res => {
192 | dec.addEventListener("dequeue", res, {once: true});
193 | });
194 | }
195 | dec.decode(chunk);
196 | }
197 | }
198 |
199 | if (res === libav.AVERROR_EOF)
200 | break;
201 | }
202 |
203 | for (let i = 0; i < decoders.length; i++) {
204 | await decoders[i].flush();
205 | decoders[i].close();
206 | decoderStreams[i].push(null);
207 | }
208 | })();
209 |
210 | // Decoder -> encoder
211 | for (let i = 0; i < decoders.length; i++) {
212 | (async () => {
213 | const decStream = decoderStreams[i];
214 | const decRdr = decStream.getReader();
215 | const enc = encoders[i];
216 | const encStream = encoderStreams[i];
217 |
218 | while (true) {
219 | const {done, value} = await decRdr.read();
220 | if (done)
221 | break;
222 |
223 | /* Pointlessly convert back and forth, just to demonstrate those
224 | * functions */
225 | let frame;
226 | if (value.codedWidth) {
227 | frame = await LibAVWebCodecsBridge.videoFrameToLAFrame(value);
228 | value.close();
229 | frame = LibAVWebCodecsBridge.laFrameToVideoFrame(frame);
230 | } else {
231 | frame = await LibAVWebCodecsBridge.audioDataToLAFrame(value);
232 | value.close();
233 | frame = LibAVWebCodecsBridge.laFrameToAudioData(frame);
234 | }
235 |
236 | enc.encode(frame);
237 | frame.close();
238 | }
239 |
240 | await enc.flush();
241 | enc.close();
242 | encStream.push(null);
243 | })();
244 | }
245 |
246 | // We need to get at least one packet from each stream before we can mux
247 | let ofc, pb;
248 | {
249 | const starterPackets = [];
250 | for (let i = 0; i < ostreams.length; i++) {
251 | const encRdr = encoderReaders[i];
252 | const {done, value} = await encRdr.read();
253 | if (done)
254 | continue;
255 | const chunkToPacket = chunkToPackets[i];
256 | const ostream = ostreams[i];
257 |
258 | // Convert it
259 | const packet = await chunkToPacket(
260 | libav, value.chunk, value.metadata, ostream, i);
261 | starterPackets.push(packet);
262 | }
263 |
264 | // Make the muxer
265 | [ofc, , pb] = await libav.ff_init_muxer({
266 | filename: "output.mkv",
267 | open: true,
268 | codecpars: true
269 | }, ostreams);
270 | await libav.avformat_write_header(ofc, 0);
271 |
272 | // And pass in the starter packets
273 | await libav.ff_write_multi(ofc, wpkt, starterPackets);
274 | }
275 |
276 | // Now pass through everything else
277 | const encPromises = [];
278 | let writePromise = Promise.all([]);
279 | for (let i = 0; i < encoderReaders.length; i++) {
280 | encPromises.push((async () => {
281 | const encRdr = encoderReaders[i];
282 | const chunkToPacket = chunkToPackets[i];
283 | const ostream = ostreams[i];
284 | while (true) {
285 | const {done, value} = await encRdr.read();
286 | if (done)
287 | break;
288 | writePromise = writePromise.then(async () => {
289 | const packet = await chunkToPacket(
290 | libav, value.chunk, value.metadata, ostream, i);
291 | await libav.ff_write_multi(ofc, wpkt, [packet]);
292 | });
293 | }
294 | })());
295 | }
296 |
297 | await Promise.all(encPromises);
298 | await writePromise;
299 |
300 | // And end the stream
301 | await libav.av_write_trailer(ofc);
302 |
303 | // Clean up
304 | await libav.avformat_close_input_js(ifc);
305 | await libav.ff_free_muxer(ofc, pb);
306 | await libav.av_packet_free(rpkt);
307 | await libav.av_packet_free(wpkt);
308 |
309 | // And fetch the file
310 | const output = await libav.readFile("output.mkv");
311 | await libav.terminate();
312 | const ofile = new File([output.buffer], "output.mkv",
313 | {type: "video/x-matroska"});
314 | document.location.href = URL.createObjectURL(ofile);
315 | }
316 |
317 | main();
318 |
319 |
--------------------------------------------------------------------------------
/docs/API.md:
--------------------------------------------------------------------------------
1 | libavjs-webcodecs-bridge has two directions of use: demuxing and muxing. The
2 | demuxing functions are designed to aid when using libav.js for demuxing and
3 | WebCodecs for decoding. The muxing functions are designed to aid when using
4 | WebCodecs for encoding and libav.js for muxing. In either case, it's a BYOL
5 | (bring your own library) system: you must provide your own instance of libav.js,
6 | and if WebCodecs isn't built into your browser, you must bring a polyfill
7 | (presumably libavjs-webcodecs-polyfill).
8 |
9 |
10 | ## Demuxing
11 |
12 | If you are demuxing in libav.js, you will have a libav.js `Stream` object for
13 | each stream, and libav.js `Packet` objects for each packet in the file. Convert
14 | the `Stream` object to a configuration to configure a WebCodecs decoder, and
15 | convert each `Packet` to an encoded chunk to be decoded in WebCodecs.
16 |
17 | ### `audioStreamToConfig`, `videoStreamToConfig`
18 |
19 | ```js
20 | async function audioStreamToConfig(
21 | libav: LibAVJS.LibAV, stream: LibAVJS.Stream
22 | ): Promise;
23 |
24 | async function videoStreamToConfig(
25 | libav: LibAVJS.LibAV, stream: LibAVJS.Stream
26 | ): Promise;
27 | ```
28 |
29 | libav.js `Stream`s can be converted to WebCodecs configurations, to be passed to
30 | `AudioDecoder.configure` or `VideoDecoder.configure`. You must determine whether
31 | you have an audio or video stream yourself, by checking `stream.codec_type`.
32 |
33 | To convert an audio stream to a suitable configuration, use
34 | `await audioStreamToConfig(libav, stream)`. Use `videoStreamToConfig` for video
35 | streams. These functions will *always* return something, regardless of whether
36 | WebCodecs actually supports the codec in question, so make sure to check whether
37 | the configuration is actually supported.
38 |
39 | ### `packetToEncodedAudioChunk`, `packetToEncodedVideoChunk`
40 |
41 | ```js
42 | function packetToEncodedAudioChunk(
43 | packet: LibAVJS.Packet, stream: LibAVJS.Stream, opts: {
44 | EncodedAudioChunk?: any
45 | } = {}
46 | ): LibAVJSWebCodecs.EncodedAudioChunk;
47 |
48 | function packetToEncodedVideoChunk(
49 | packet: LibAVJS.Packet, stream: LibAVJS.Stream, opts: {
50 | EncodedVideoChunk?: any
51 | } = {}
52 | ): LibAVJSWebCodecs.EncodedVideoChunk;
53 | ```
54 |
55 | libav.js `Packet`s can be converted to WebCodecs `EncodedAudioChunk`s or
56 | `EncodedVideoChunk`s.
57 |
58 | To convert an audio packet to an `EncodedAudioChunk`, use
59 | `packetToEncodedAudioChunk(packet, stream)`. Use `packetToEncodedVideoChunk` for
60 | video packets. Note that these functions are synchronous.
61 |
62 | If you're using a polyfill, you can pass the `EncodedAudioChunk` or
63 | `EncodedVideoChunk` constructor as the appropriate field of the third (`opts`)
64 | argument.
65 |
66 | Note that FFmpeg (and thus libav.js) and WebCodecs disagree on the definition of
67 | keyframe with both H.264 and H.265. WebCodecs requires a non-recovery frame,
68 | i.e., a keyframe with no B-frames, whereas FFmpeg takes the keyframe status from
69 | the container, and all container formats mark recovery frames as keyframes
70 | (because they are keyframes). No implementation of WebCodecs actually cares
71 | whether you mark a frame as a keyframe or a delta frame *except* for the first
72 | frame sent to the decoder. The consequence of this is that if you seek to the
73 | middle of an H.264 or H.265 file and read a frame that libav.js indicates is a
74 | keyframe, you may not actually be able to start decoding with that frame. There
75 | is no practical way to fix this on the libavjs-webcodecs-bridge side, because
76 | FFmpeg offers no API to distinguish these frame types; it would be necessary to
77 | manually parse frame data instead. See [issue
78 | 3](https://github.com/Yahweasel/libavjs-webcodecs-bridge/issues/3) for some
79 | suggested workarounds.
80 |
81 |
82 | ## Muxing
83 |
84 | If you are encoding with WebCodecs, you will have a WebCodecs configuration, and
85 | a stream of `EncodedAudioChunk`s or `EncodedVideoChunk`s. Convert the
86 | configuration to a stream configuration used in libav.js's `ff_init_muxer`, and
87 | the encoded chunks to libav.js `Packet`s.
88 |
89 | ### `configToAudioStream`, `configToVideoStream`
90 |
91 | ```js
92 | async function configToAudioStream(
93 | libav: LibAVJS.LibAV, config: LibAVJSWebCodecs.AudioEncoderConfig
94 | ): Promise<[number, number, number]>;
95 |
96 | async function configToVideoStream(
97 | libav: LibAVJS.LibAV, config: LibAVJSWebCodecs.VideoEncoderConfig
98 | ): Promise<[number, number, number]>;
99 | ```
100 |
101 | Configurations for audio or video encoders in WebCodecs can be converted to
102 | stream information sufficient for `ff_init_muxer`. Note that `ff_init_muxer`
103 | expects three pieces of information for each stream: a pointer to stream
104 | information (in this case, `AVCodecParameters`), and the numerator and
105 | denominator of the timebase used. Thus, these functions return those three
106 | numbers, in the array demanded by `ff_init_muxer`.
107 |
108 | To convert an audio configuration to a suitable stream, use
109 | `await configToAudioStream(libav, config)`. Use `configToVideoStream` for video
110 | streams. These functions will *always* return something, regardless of whether
111 | the codec is recognized or libav.js supports it, so make sure to check whether
112 | `ff_init_muxer` actually succeeds.
113 |
114 | Two things of note about this function:
115 | - These return `AVCodecParameters`, so you must set the `codecpars`
116 | option to `true` when calling `ff_init_muxer`.
117 | - Because of differences between libav.js and WebCodecs, you must convert at
118 | least one chunk from each stream to a packet *before* starting the muxer.
119 | This is because of codec parameters that are only passed with the first
120 | encoded chunk. `demo/demo.js` demonstrates this.
121 |
122 | ### `encodedAudioChunkToPacket`, `encodedVideoChunkToPacket`
123 |
124 | ```js
125 | async function encodedAudioChunkToPacket(
126 | libav: LibAVJS.LibAV, chunk: LibAVJSWebCodecs.EncodedAudioChunk, metadata: any,
127 | stream: [number, number, number], streamIndex: number
128 | ): Promise;
129 |
130 | async function encodedVideoChunkToPacket(
131 | libav: LibAVJS.LibAV, chunk: LibAVJSWebCodecs.EncodedVideoChunk, metadata: any,
132 | stream: [number, number, number], streamIndex: number
133 | ): Promise;
134 | ```
135 |
136 | WebCodecs encoded chunks (`EncodedAudioChunk`s and `EncodedVideoChunk`s) can be
137 | converted to libav.js `Packet`s, for use in `ff_write_multi`.
138 |
139 | To convert an audio chunk to a libav.js packet, use
140 | `encodedAudioChunkToPacket(libav, chunk, metadata, stream, streamIndex)`.
141 | `libav` is the libav.js instance in use, `chunk` is the encoded chunk,
142 | `metadata` is the metadata sent with the chunk, `stream` is the stream
143 | information returned by `configToAudioStream`, and `streamIndex` is the index of
144 | the stream in your call to `ff_init_muxer`. Use `encodedVideoChunkToPacket` for
145 | video chunks. Note that these functions are asynchronous, unlike their demuxing
146 | counterparts.
147 |
148 | Due to differences in how libav and WebCodecs handle extra data for codecs, *you
149 | must convert at least one packet from each stream before initializing the
150 | muxer*. These functions convert the packet, but also initialize the extra codec
151 | data (because in WebCodecs it's sent with the first packet), and that extra
152 | codec data must be initialized before the muxer. This can make the ordering of
153 | tasks a bit awkward, but is unavoidable. You may want to look at the demo in
154 | `demo/` for an example of the correct ordering of steps.
155 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "libavjs-webcodecs-bridge",
3 | "version": "0.3.2",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "libavjs-webcodecs-bridge",
9 | "version": "0.3.2",
10 | "license": "ISC",
11 | "dependencies": {
12 | "libavjs-webcodecs-polyfill": "^0.5.3"
13 | },
14 | "devDependencies": {
15 | "@libav.js/variant-webcodecs": "=6.4.7",
16 | "@rollup/plugin-commonjs": "^25.0.7",
17 | "@rollup/plugin-node-resolve": "^15.2.3",
18 | "@rollup/plugin-terser": "^0.4.4",
19 | "rollup": "^4.12.0",
20 | "typescript": "^5.1.6"
21 | }
22 | },
23 | "node_modules/@jridgewell/gen-mapping": {
24 | "version": "0.3.5",
25 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
26 | "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
27 | "dev": true,
28 | "dependencies": {
29 | "@jridgewell/set-array": "^1.2.1",
30 | "@jridgewell/sourcemap-codec": "^1.4.10",
31 | "@jridgewell/trace-mapping": "^0.3.24"
32 | },
33 | "engines": {
34 | "node": ">=6.0.0"
35 | }
36 | },
37 | "node_modules/@jridgewell/resolve-uri": {
38 | "version": "3.1.2",
39 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
40 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
41 | "dev": true,
42 | "engines": {
43 | "node": ">=6.0.0"
44 | }
45 | },
46 | "node_modules/@jridgewell/set-array": {
47 | "version": "1.2.1",
48 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
49 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
50 | "dev": true,
51 | "engines": {
52 | "node": ">=6.0.0"
53 | }
54 | },
55 | "node_modules/@jridgewell/source-map": {
56 | "version": "0.3.5",
57 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
58 | "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
59 | "dev": true,
60 | "dependencies": {
61 | "@jridgewell/gen-mapping": "^0.3.0",
62 | "@jridgewell/trace-mapping": "^0.3.9"
63 | }
64 | },
65 | "node_modules/@jridgewell/sourcemap-codec": {
66 | "version": "1.4.15",
67 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
68 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
69 | "dev": true
70 | },
71 | "node_modules/@jridgewell/trace-mapping": {
72 | "version": "0.3.25",
73 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
74 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
75 | "dev": true,
76 | "dependencies": {
77 | "@jridgewell/resolve-uri": "^3.1.0",
78 | "@jridgewell/sourcemap-codec": "^1.4.14"
79 | }
80 | },
81 | "node_modules/@libav.js/types": {
82 | "version": "6.5.7",
83 | "resolved": "https://registry.npmjs.org/@libav.js/types/-/types-6.5.7.tgz",
84 | "integrity": "sha512-lLSoLw1v36uuegUmmyjjIYycoaWeMZb0eECXTuQuZHP27HJAsnzE3LR0SuTfr9tS32khp4tYwaDgYQDttnAkRA=="
85 | },
86 | "node_modules/@libav.js/variant-webcodecs": {
87 | "version": "6.4.7",
88 | "resolved": "https://registry.npmjs.org/@libav.js/variant-webcodecs/-/variant-webcodecs-6.4.7.tgz",
89 | "integrity": "sha512-+cbV1w3VsGNiUljJZoXQVqZ7S/XaHr/q7Pn+0AYOedhZSL38cCu72m6B6YBniRLA5lhA+Efkg0p9rj6RkinWFg==",
90 | "dev": true
91 | },
92 | "node_modules/@rollup/plugin-commonjs": {
93 | "version": "25.0.7",
94 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz",
95 | "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==",
96 | "dev": true,
97 | "dependencies": {
98 | "@rollup/pluginutils": "^5.0.1",
99 | "commondir": "^1.0.1",
100 | "estree-walker": "^2.0.2",
101 | "glob": "^8.0.3",
102 | "is-reference": "1.2.1",
103 | "magic-string": "^0.30.3"
104 | },
105 | "engines": {
106 | "node": ">=14.0.0"
107 | },
108 | "peerDependencies": {
109 | "rollup": "^2.68.0||^3.0.0||^4.0.0"
110 | },
111 | "peerDependenciesMeta": {
112 | "rollup": {
113 | "optional": true
114 | }
115 | }
116 | },
117 | "node_modules/@rollup/plugin-node-resolve": {
118 | "version": "15.2.3",
119 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
120 | "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
121 | "dev": true,
122 | "dependencies": {
123 | "@rollup/pluginutils": "^5.0.1",
124 | "@types/resolve": "1.20.2",
125 | "deepmerge": "^4.2.2",
126 | "is-builtin-module": "^3.2.1",
127 | "is-module": "^1.0.0",
128 | "resolve": "^1.22.1"
129 | },
130 | "engines": {
131 | "node": ">=14.0.0"
132 | },
133 | "peerDependencies": {
134 | "rollup": "^2.78.0||^3.0.0||^4.0.0"
135 | },
136 | "peerDependenciesMeta": {
137 | "rollup": {
138 | "optional": true
139 | }
140 | }
141 | },
142 | "node_modules/@rollup/plugin-terser": {
143 | "version": "0.4.4",
144 | "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
145 | "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
146 | "dev": true,
147 | "dependencies": {
148 | "serialize-javascript": "^6.0.1",
149 | "smob": "^1.0.0",
150 | "terser": "^5.17.4"
151 | },
152 | "engines": {
153 | "node": ">=14.0.0"
154 | },
155 | "peerDependencies": {
156 | "rollup": "^2.0.0||^3.0.0||^4.0.0"
157 | },
158 | "peerDependenciesMeta": {
159 | "rollup": {
160 | "optional": true
161 | }
162 | }
163 | },
164 | "node_modules/@rollup/pluginutils": {
165 | "version": "5.1.0",
166 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
167 | "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
168 | "dev": true,
169 | "dependencies": {
170 | "@types/estree": "^1.0.0",
171 | "estree-walker": "^2.0.2",
172 | "picomatch": "^2.3.1"
173 | },
174 | "engines": {
175 | "node": ">=14.0.0"
176 | },
177 | "peerDependencies": {
178 | "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
179 | },
180 | "peerDependenciesMeta": {
181 | "rollup": {
182 | "optional": true
183 | }
184 | }
185 | },
186 | "node_modules/@rollup/rollup-android-arm-eabi": {
187 | "version": "4.22.5",
188 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.5.tgz",
189 | "integrity": "sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==",
190 | "cpu": [
191 | "arm"
192 | ],
193 | "dev": true,
194 | "optional": true,
195 | "os": [
196 | "android"
197 | ]
198 | },
199 | "node_modules/@rollup/rollup-android-arm64": {
200 | "version": "4.22.5",
201 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.5.tgz",
202 | "integrity": "sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==",
203 | "cpu": [
204 | "arm64"
205 | ],
206 | "dev": true,
207 | "optional": true,
208 | "os": [
209 | "android"
210 | ]
211 | },
212 | "node_modules/@rollup/rollup-darwin-arm64": {
213 | "version": "4.22.5",
214 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.5.tgz",
215 | "integrity": "sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==",
216 | "cpu": [
217 | "arm64"
218 | ],
219 | "dev": true,
220 | "optional": true,
221 | "os": [
222 | "darwin"
223 | ]
224 | },
225 | "node_modules/@rollup/rollup-darwin-x64": {
226 | "version": "4.22.5",
227 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.5.tgz",
228 | "integrity": "sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==",
229 | "cpu": [
230 | "x64"
231 | ],
232 | "dev": true,
233 | "optional": true,
234 | "os": [
235 | "darwin"
236 | ]
237 | },
238 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
239 | "version": "4.22.5",
240 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.5.tgz",
241 | "integrity": "sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==",
242 | "cpu": [
243 | "arm"
244 | ],
245 | "dev": true,
246 | "optional": true,
247 | "os": [
248 | "linux"
249 | ]
250 | },
251 | "node_modules/@rollup/rollup-linux-arm-musleabihf": {
252 | "version": "4.22.5",
253 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.5.tgz",
254 | "integrity": "sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==",
255 | "cpu": [
256 | "arm"
257 | ],
258 | "dev": true,
259 | "optional": true,
260 | "os": [
261 | "linux"
262 | ]
263 | },
264 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
265 | "version": "4.22.5",
266 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.5.tgz",
267 | "integrity": "sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==",
268 | "cpu": [
269 | "arm64"
270 | ],
271 | "dev": true,
272 | "optional": true,
273 | "os": [
274 | "linux"
275 | ]
276 | },
277 | "node_modules/@rollup/rollup-linux-arm64-musl": {
278 | "version": "4.22.5",
279 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.5.tgz",
280 | "integrity": "sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==",
281 | "cpu": [
282 | "arm64"
283 | ],
284 | "dev": true,
285 | "optional": true,
286 | "os": [
287 | "linux"
288 | ]
289 | },
290 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
291 | "version": "4.22.5",
292 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.5.tgz",
293 | "integrity": "sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==",
294 | "cpu": [
295 | "ppc64"
296 | ],
297 | "dev": true,
298 | "optional": true,
299 | "os": [
300 | "linux"
301 | ]
302 | },
303 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
304 | "version": "4.22.5",
305 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.5.tgz",
306 | "integrity": "sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==",
307 | "cpu": [
308 | "riscv64"
309 | ],
310 | "dev": true,
311 | "optional": true,
312 | "os": [
313 | "linux"
314 | ]
315 | },
316 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
317 | "version": "4.22.5",
318 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.5.tgz",
319 | "integrity": "sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==",
320 | "cpu": [
321 | "s390x"
322 | ],
323 | "dev": true,
324 | "optional": true,
325 | "os": [
326 | "linux"
327 | ]
328 | },
329 | "node_modules/@rollup/rollup-linux-x64-gnu": {
330 | "version": "4.22.5",
331 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz",
332 | "integrity": "sha512-N0jPPhHjGShcB9/XXZQWuWBKZQnC1F36Ce3sDqWpujsGjDz/CQtOL9LgTrJ+rJC8MJeesMWrMWVLKKNR/tMOCA==",
333 | "cpu": [
334 | "x64"
335 | ],
336 | "dev": true,
337 | "optional": true,
338 | "os": [
339 | "linux"
340 | ]
341 | },
342 | "node_modules/@rollup/rollup-linux-x64-musl": {
343 | "version": "4.22.5",
344 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.5.tgz",
345 | "integrity": "sha512-uBa2e28ohzNNwjr6Uxm4XyaA1M/8aTgfF2T7UIlElLaeXkgpmIJ2EitVNQxjO9xLLLy60YqAgKn/AqSpCUkE9g==",
346 | "cpu": [
347 | "x64"
348 | ],
349 | "dev": true,
350 | "optional": true,
351 | "os": [
352 | "linux"
353 | ]
354 | },
355 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
356 | "version": "4.22.5",
357 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.5.tgz",
358 | "integrity": "sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==",
359 | "cpu": [
360 | "arm64"
361 | ],
362 | "dev": true,
363 | "optional": true,
364 | "os": [
365 | "win32"
366 | ]
367 | },
368 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
369 | "version": "4.22.5",
370 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.5.tgz",
371 | "integrity": "sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==",
372 | "cpu": [
373 | "ia32"
374 | ],
375 | "dev": true,
376 | "optional": true,
377 | "os": [
378 | "win32"
379 | ]
380 | },
381 | "node_modules/@rollup/rollup-win32-x64-msvc": {
382 | "version": "4.22.5",
383 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz",
384 | "integrity": "sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==",
385 | "cpu": [
386 | "x64"
387 | ],
388 | "dev": true,
389 | "optional": true,
390 | "os": [
391 | "win32"
392 | ]
393 | },
394 | "node_modules/@types/estree": {
395 | "version": "1.0.6",
396 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
397 | "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
398 | "dev": true
399 | },
400 | "node_modules/@types/resolve": {
401 | "version": "1.20.2",
402 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
403 | "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
404 | "dev": true
405 | },
406 | "node_modules/@ungap/global-this": {
407 | "version": "0.4.4",
408 | "resolved": "https://registry.npmjs.org/@ungap/global-this/-/global-this-0.4.4.tgz",
409 | "integrity": "sha512-mHkm6FvepJECMNthFuIgpAEFmPOk71UyXuIxYfjytvFTnSDBIz7jmViO+LfHI/AjrazWije0PnSP3+/NlwzqtA=="
410 | },
411 | "node_modules/acorn": {
412 | "version": "8.11.3",
413 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
414 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
415 | "dev": true,
416 | "bin": {
417 | "acorn": "bin/acorn"
418 | },
419 | "engines": {
420 | "node": ">=0.4.0"
421 | }
422 | },
423 | "node_modules/balanced-match": {
424 | "version": "1.0.2",
425 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
426 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
427 | "dev": true
428 | },
429 | "node_modules/brace-expansion": {
430 | "version": "2.0.1",
431 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
432 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
433 | "dev": true,
434 | "dependencies": {
435 | "balanced-match": "^1.0.0"
436 | }
437 | },
438 | "node_modules/buffer-from": {
439 | "version": "1.1.2",
440 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
441 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
442 | "dev": true
443 | },
444 | "node_modules/builtin-modules": {
445 | "version": "3.3.0",
446 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
447 | "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
448 | "dev": true,
449 | "engines": {
450 | "node": ">=6"
451 | },
452 | "funding": {
453 | "url": "https://github.com/sponsors/sindresorhus"
454 | }
455 | },
456 | "node_modules/commander": {
457 | "version": "2.20.3",
458 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
459 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
460 | "dev": true
461 | },
462 | "node_modules/commondir": {
463 | "version": "1.0.1",
464 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
465 | "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
466 | "dev": true
467 | },
468 | "node_modules/deepmerge": {
469 | "version": "4.3.1",
470 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
471 | "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
472 | "dev": true,
473 | "engines": {
474 | "node": ">=0.10.0"
475 | }
476 | },
477 | "node_modules/estree-walker": {
478 | "version": "2.0.2",
479 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
480 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
481 | "dev": true
482 | },
483 | "node_modules/fs.realpath": {
484 | "version": "1.0.0",
485 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
486 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
487 | "dev": true
488 | },
489 | "node_modules/fsevents": {
490 | "version": "2.3.3",
491 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
492 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
493 | "dev": true,
494 | "hasInstallScript": true,
495 | "optional": true,
496 | "os": [
497 | "darwin"
498 | ],
499 | "engines": {
500 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
501 | }
502 | },
503 | "node_modules/function-bind": {
504 | "version": "1.1.2",
505 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
506 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
507 | "dev": true,
508 | "funding": {
509 | "url": "https://github.com/sponsors/ljharb"
510 | }
511 | },
512 | "node_modules/glob": {
513 | "version": "8.1.0",
514 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
515 | "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
516 | "dev": true,
517 | "dependencies": {
518 | "fs.realpath": "^1.0.0",
519 | "inflight": "^1.0.4",
520 | "inherits": "2",
521 | "minimatch": "^5.0.1",
522 | "once": "^1.3.0"
523 | },
524 | "engines": {
525 | "node": ">=12"
526 | },
527 | "funding": {
528 | "url": "https://github.com/sponsors/isaacs"
529 | }
530 | },
531 | "node_modules/hasown": {
532 | "version": "2.0.1",
533 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
534 | "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
535 | "dev": true,
536 | "dependencies": {
537 | "function-bind": "^1.1.2"
538 | },
539 | "engines": {
540 | "node": ">= 0.4"
541 | }
542 | },
543 | "node_modules/inflight": {
544 | "version": "1.0.6",
545 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
546 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
547 | "dev": true,
548 | "dependencies": {
549 | "once": "^1.3.0",
550 | "wrappy": "1"
551 | }
552 | },
553 | "node_modules/inherits": {
554 | "version": "2.0.4",
555 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
556 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
557 | "dev": true
558 | },
559 | "node_modules/is-builtin-module": {
560 | "version": "3.2.1",
561 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
562 | "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
563 | "dev": true,
564 | "dependencies": {
565 | "builtin-modules": "^3.3.0"
566 | },
567 | "engines": {
568 | "node": ">=6"
569 | },
570 | "funding": {
571 | "url": "https://github.com/sponsors/sindresorhus"
572 | }
573 | },
574 | "node_modules/is-core-module": {
575 | "version": "2.13.1",
576 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
577 | "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
578 | "dev": true,
579 | "dependencies": {
580 | "hasown": "^2.0.0"
581 | },
582 | "funding": {
583 | "url": "https://github.com/sponsors/ljharb"
584 | }
585 | },
586 | "node_modules/is-module": {
587 | "version": "1.0.0",
588 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
589 | "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
590 | "dev": true
591 | },
592 | "node_modules/is-reference": {
593 | "version": "1.2.1",
594 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
595 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
596 | "dev": true,
597 | "dependencies": {
598 | "@types/estree": "*"
599 | }
600 | },
601 | "node_modules/libavjs-webcodecs-polyfill": {
602 | "version": "0.5.3",
603 | "resolved": "https://registry.npmjs.org/libavjs-webcodecs-polyfill/-/libavjs-webcodecs-polyfill-0.5.3.tgz",
604 | "integrity": "sha512-Awvgi7+OEewT1FureWr0jTTvf58nA/IE3nEpsjk8bmMLTQoUYv9AXonCn/z5lLGfEud5oXCoIKHC4/jbdA0smg==",
605 | "dependencies": {
606 | "@libav.js/types": "^6.5.7",
607 | "@ungap/global-this": "^0.4.4"
608 | }
609 | },
610 | "node_modules/magic-string": {
611 | "version": "0.30.8",
612 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
613 | "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
614 | "dev": true,
615 | "dependencies": {
616 | "@jridgewell/sourcemap-codec": "^1.4.15"
617 | },
618 | "engines": {
619 | "node": ">=12"
620 | }
621 | },
622 | "node_modules/minimatch": {
623 | "version": "5.1.6",
624 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
625 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
626 | "dev": true,
627 | "dependencies": {
628 | "brace-expansion": "^2.0.1"
629 | },
630 | "engines": {
631 | "node": ">=10"
632 | }
633 | },
634 | "node_modules/once": {
635 | "version": "1.4.0",
636 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
637 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
638 | "dev": true,
639 | "dependencies": {
640 | "wrappy": "1"
641 | }
642 | },
643 | "node_modules/path-parse": {
644 | "version": "1.0.7",
645 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
646 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
647 | "dev": true
648 | },
649 | "node_modules/picomatch": {
650 | "version": "2.3.1",
651 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
652 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
653 | "dev": true,
654 | "engines": {
655 | "node": ">=8.6"
656 | },
657 | "funding": {
658 | "url": "https://github.com/sponsors/jonschlinkert"
659 | }
660 | },
661 | "node_modules/randombytes": {
662 | "version": "2.1.0",
663 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
664 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
665 | "dev": true,
666 | "dependencies": {
667 | "safe-buffer": "^5.1.0"
668 | }
669 | },
670 | "node_modules/resolve": {
671 | "version": "1.22.8",
672 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
673 | "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
674 | "dev": true,
675 | "dependencies": {
676 | "is-core-module": "^2.13.0",
677 | "path-parse": "^1.0.7",
678 | "supports-preserve-symlinks-flag": "^1.0.0"
679 | },
680 | "bin": {
681 | "resolve": "bin/resolve"
682 | },
683 | "funding": {
684 | "url": "https://github.com/sponsors/ljharb"
685 | }
686 | },
687 | "node_modules/rollup": {
688 | "version": "4.22.5",
689 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.5.tgz",
690 | "integrity": "sha512-WoinX7GeQOFMGznEcWA1WrTQCd/tpEbMkc3nuMs9BT0CPjMdSjPMTVClwWd4pgSQwJdP65SK9mTCNvItlr5o7w==",
691 | "dev": true,
692 | "dependencies": {
693 | "@types/estree": "1.0.6"
694 | },
695 | "bin": {
696 | "rollup": "dist/bin/rollup"
697 | },
698 | "engines": {
699 | "node": ">=18.0.0",
700 | "npm": ">=8.0.0"
701 | },
702 | "optionalDependencies": {
703 | "@rollup/rollup-android-arm-eabi": "4.22.5",
704 | "@rollup/rollup-android-arm64": "4.22.5",
705 | "@rollup/rollup-darwin-arm64": "4.22.5",
706 | "@rollup/rollup-darwin-x64": "4.22.5",
707 | "@rollup/rollup-linux-arm-gnueabihf": "4.22.5",
708 | "@rollup/rollup-linux-arm-musleabihf": "4.22.5",
709 | "@rollup/rollup-linux-arm64-gnu": "4.22.5",
710 | "@rollup/rollup-linux-arm64-musl": "4.22.5",
711 | "@rollup/rollup-linux-powerpc64le-gnu": "4.22.5",
712 | "@rollup/rollup-linux-riscv64-gnu": "4.22.5",
713 | "@rollup/rollup-linux-s390x-gnu": "4.22.5",
714 | "@rollup/rollup-linux-x64-gnu": "4.22.5",
715 | "@rollup/rollup-linux-x64-musl": "4.22.5",
716 | "@rollup/rollup-win32-arm64-msvc": "4.22.5",
717 | "@rollup/rollup-win32-ia32-msvc": "4.22.5",
718 | "@rollup/rollup-win32-x64-msvc": "4.22.5",
719 | "fsevents": "~2.3.2"
720 | }
721 | },
722 | "node_modules/safe-buffer": {
723 | "version": "5.2.1",
724 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
725 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
726 | "dev": true,
727 | "funding": [
728 | {
729 | "type": "github",
730 | "url": "https://github.com/sponsors/feross"
731 | },
732 | {
733 | "type": "patreon",
734 | "url": "https://www.patreon.com/feross"
735 | },
736 | {
737 | "type": "consulting",
738 | "url": "https://feross.org/support"
739 | }
740 | ]
741 | },
742 | "node_modules/serialize-javascript": {
743 | "version": "6.0.2",
744 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
745 | "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
746 | "dev": true,
747 | "dependencies": {
748 | "randombytes": "^2.1.0"
749 | }
750 | },
751 | "node_modules/smob": {
752 | "version": "1.4.1",
753 | "resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz",
754 | "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==",
755 | "dev": true
756 | },
757 | "node_modules/source-map": {
758 | "version": "0.6.1",
759 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
760 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
761 | "dev": true,
762 | "engines": {
763 | "node": ">=0.10.0"
764 | }
765 | },
766 | "node_modules/source-map-support": {
767 | "version": "0.5.21",
768 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
769 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
770 | "dev": true,
771 | "dependencies": {
772 | "buffer-from": "^1.0.0",
773 | "source-map": "^0.6.0"
774 | }
775 | },
776 | "node_modules/supports-preserve-symlinks-flag": {
777 | "version": "1.0.0",
778 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
779 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
780 | "dev": true,
781 | "engines": {
782 | "node": ">= 0.4"
783 | },
784 | "funding": {
785 | "url": "https://github.com/sponsors/ljharb"
786 | }
787 | },
788 | "node_modules/terser": {
789 | "version": "5.29.1",
790 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz",
791 | "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==",
792 | "dev": true,
793 | "dependencies": {
794 | "@jridgewell/source-map": "^0.3.3",
795 | "acorn": "^8.8.2",
796 | "commander": "^2.20.0",
797 | "source-map-support": "~0.5.20"
798 | },
799 | "bin": {
800 | "terser": "bin/terser"
801 | },
802 | "engines": {
803 | "node": ">=10"
804 | }
805 | },
806 | "node_modules/typescript": {
807 | "version": "5.4.2",
808 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz",
809 | "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==",
810 | "dev": true,
811 | "bin": {
812 | "tsc": "bin/tsc",
813 | "tsserver": "bin/tsserver"
814 | },
815 | "engines": {
816 | "node": ">=14.17"
817 | }
818 | },
819 | "node_modules/wrappy": {
820 | "version": "1.0.2",
821 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
822 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
823 | "dev": true
824 | }
825 | }
826 | }
827 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "libavjs-webcodecs-bridge",
3 | "version": "0.3.2",
4 | "description": "A bridge between libav.js and WebCodecs, to allow easier decoding of files demuxed by libav.js",
5 | "main": "dist/libavjs-webcodecs-bridge.js",
6 | "types": "src/bridge.ts",
7 | "exports": {
8 | "import": "./dist/libavjs-webcodecs-bridge.mjs",
9 | "default": "./dist/libavjs-webcodecs-bridge.js",
10 | "types": "./src/bridge.ts"
11 | },
12 | "scripts": {
13 | "build": "tsc && rollup -c",
14 | "clean": "rm -f dist/",
15 | "test": "echo \"Error: no test specified\" && exit 1"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/Yahweasel/libavjs-webcodecs-bridge.git"
20 | },
21 | "keywords": [
22 | "webcodecs",
23 | "demuxing",
24 | "decoding"
25 | ],
26 | "author": "Yahweasel",
27 | "license": "ISC",
28 | "bugs": {
29 | "url": "https://github.com/Yahweasel/libavjs-webcodecs-bridge/issues"
30 | },
31 | "homepage": "https://github.com/Yahweasel/libavjs-webcodecs-bridge#readme",
32 | "devDependencies": {
33 | "@libav.js/variant-webcodecs": "=6.4.7",
34 | "@rollup/plugin-commonjs": "^25.0.7",
35 | "@rollup/plugin-node-resolve": "^15.2.3",
36 | "@rollup/plugin-terser": "^0.4.4",
37 | "rollup": "^4.12.0",
38 | "typescript": "^5.1.6"
39 | },
40 | "dependencies": {
41 | "libavjs-webcodecs-polyfill": "^0.5.3"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import nodeResolve from "@rollup/plugin-node-resolve";
2 | import commonjs from "@rollup/plugin-commonjs";
3 | import terser from "@rollup/plugin-terser";
4 |
5 | export default {
6 | input: "src/bridge.js",
7 | output: [
8 | {
9 | file: "dist/libavjs-webcodecs-bridge.js",
10 | format: "umd",
11 | name: "LibAVWebCodecsBridge"
12 | }, {
13 | file: "dist/libavjs-webcodecs-bridge.min.js",
14 | format: "umd",
15 | name: "LibAVWebCodecsBridge"
16 | }, {
17 | file: "dist/libavjs-webcodecs-bridge.mjs",
18 | format: "es"
19 | }, {
20 | file: "dist/libavjs-webcodecs-bridge.min.mjs",
21 | format: "es",
22 | plugins: [terser()]
23 | }
24 | ],
25 | context: "this",
26 | plugins: [nodeResolve(), commonjs()]
27 | };
28 |
--------------------------------------------------------------------------------
/samples/decoder/decoder.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This (un)license applies only to this sample code, and not to
3 | * libavjs-webcodecs-bridge as a whole:
4 | *
5 | * This is free and unencumbered software released into the public domain.
6 | *
7 | * Anyone is free to copy, modify, publish, use, compile, sell, or distribute
8 | * this software, either in source code form or as a compiled binary, for any
9 | * purpose, commercial or non-commercial, and by any means.
10 | *
11 | * In jurisdictions that recognize copyright laws, the author or authors of
12 | * this software dedicate any and all copyright interest in the software to the
13 | * public domain. We make this dedication for the benefit of the public at
14 | * large and to the detriment of our heirs and successors. We intend this
15 | * dedication to be an overt act of relinquishment in perpetuity of all present
16 | * and future rights to this software under copyright law.
17 | *
18 | * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 | importScripts("../worker-util.js");
27 |
28 | onmessage = async ev => {
29 | const file = ev.data;
30 | console.error(file);
31 | let streams, configs, allPackets;
32 |
33 | try {
34 | [streams, configs, allPackets] =
35 | await sampleDemux(file);
36 | } catch (ex) {
37 | console.error(ex);
38 | return;
39 | }
40 |
41 | for (let idx = 0; idx < streams.length; idx++) {
42 | const stream = streams[idx];
43 | const config = configs[idx];
44 | if (!config)
45 | continue;
46 | const packets = allPackets[stream.index];
47 |
48 | try {
49 | if (stream.codec_type === 0 /* video */) {
50 | await decodeVideo(config, packets, stream);
51 | } else if (stream.codec_type === 1 /* audio */) {
52 | await decodeAudio(config, packets, stream);
53 | }
54 | } catch (ex) {
55 | console.error(ex);
56 | }
57 | }
58 |
59 | postMessage({c: "done"});
60 | };
61 |
--------------------------------------------------------------------------------
/samples/decoder/index.html:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
29 | LibAVJS WebCodecs Bridge Example: Demuxer/decoder
30 |
31 |
32 |
NOTE: This sample just demonstrates how to get and pass configurations. It is not efficient, and will freeze or fail on large files, as it does not stream data, but demuxes everything in one shot.
NOTE: This sample just demonstrates how to get and pass configurations. It is not efficient, and will freeze or fail on large files, as it does not stream data, but demuxes and muxes everything in one shot.