@remotion/webcodecs 4.0.227 → 4.0.229

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/audio-decoder-config.js +3 -0
  2. package/dist/audio-decoder.d.ts +3 -3
  3. package/dist/audio-decoder.js +23 -29
  4. package/dist/audio-encoder.d.ts +3 -2
  5. package/dist/audio-encoder.js +17 -29
  6. package/dist/auto-select-writer.d.ts +3 -0
  7. package/dist/auto-select-writer.js +20 -0
  8. package/dist/calculate-progress.d.ts +4 -0
  9. package/dist/calculate-progress.js +10 -0
  10. package/dist/can-copy-audio-track.d.ts +8 -0
  11. package/dist/can-copy-audio-track.js +10 -0
  12. package/dist/can-copy-video-track.d.ts +8 -0
  13. package/dist/can-copy-video-track.js +13 -0
  14. package/dist/can-reencode-audio-track.d.ts +7 -0
  15. package/dist/can-reencode-audio-track.js +21 -0
  16. package/dist/can-reencode-audio.d.ts +7 -0
  17. package/dist/can-reencode-audio.js +21 -0
  18. package/dist/can-reencode-video-track.d.ts +6 -0
  19. package/dist/can-reencode-video-track.js +15 -0
  20. package/dist/can-reencode-video.d.ts +6 -0
  21. package/dist/can-reencode-video.js +15 -0
  22. package/dist/codec-id.d.ts +7 -2
  23. package/dist/codec-id.js +7 -0
  24. package/dist/convert-encoded-chunk.d.ts +2 -0
  25. package/dist/convert-encoded-chunk.js +15 -0
  26. package/dist/convert-media.d.ts +25 -9
  27. package/dist/convert-media.js +69 -15
  28. package/dist/esm/index.mjs +602 -297
  29. package/dist/event-emitter.d.ts +25 -0
  30. package/dist/event-emitter.js +23 -0
  31. package/dist/index.d.ts +8 -1
  32. package/dist/index.js +12 -1
  33. package/dist/io-manager/event-emitter.d.ts +27 -0
  34. package/dist/io-manager/event-emitter.js +24 -0
  35. package/dist/io-manager/io-synchronizer.d.ts +12 -0
  36. package/dist/io-manager/io-synchronizer.js +95 -0
  37. package/dist/log.d.ts +10 -0
  38. package/dist/log.js +6 -0
  39. package/dist/on-audio-track.d.ts +6 -5
  40. package/dist/on-audio-track.js +23 -21
  41. package/dist/on-frame.d.ts +11 -0
  42. package/dist/on-frame.js +32 -0
  43. package/dist/on-video-track.d.ts +8 -6
  44. package/dist/on-video-track.js +30 -21
  45. package/dist/polyfill-encoded-audio-chunk.d.ts +3 -0
  46. package/dist/polyfill-encoded-audio-chunk.js +5 -0
  47. package/dist/resolve-audio-action.d.ts +15 -11
  48. package/dist/resolve-audio-action.js +23 -21
  49. package/dist/resolve-video-action.d.ts +14 -11
  50. package/dist/resolve-video-action.js +17 -24
  51. package/dist/video-decoder.d.ts +3 -3
  52. package/dist/video-decoder.js +23 -28
  53. package/dist/video-encoder-config.d.ts +6 -1
  54. package/dist/video-encoder-config.js +6 -1
  55. package/dist/video-encoder.d.ts +4 -3
  56. package/dist/video-encoder.js +26 -29
  57. package/dist/wait-until-return.d.ts +4 -0
  58. package/dist/wait-until-return.js +14 -0
  59. package/dist/with-resolvers.d.ts +5 -0
  60. package/dist/with-resolvers.js +16 -1
  61. package/package.json +4 -4
@@ -1,24 +1,175 @@
1
+ // src/log.ts
2
+ import { MediaParserInternals } from "@remotion/media-parser";
3
+ var { Log } = MediaParserInternals;
4
+
5
+ // src/with-resolvers.ts
6
+ var withResolvers = function() {
7
+ let resolve;
8
+ let reject;
9
+ const promise = new Promise((res, rej) => {
10
+ resolve = res;
11
+ reject = rej;
12
+ });
13
+ return { promise, resolve, reject };
14
+ };
15
+ var withResolversAndWaitForReturn = () => {
16
+ const { promise, reject, resolve } = withResolvers();
17
+ const { promise: returnPromise, resolve: resolveReturn } = withResolvers();
18
+ return {
19
+ getPromiseToImmediatelyReturn: () => {
20
+ resolveReturn(undefined);
21
+ return promise;
22
+ },
23
+ reject: (reason) => {
24
+ returnPromise.then(() => reject(reason));
25
+ },
26
+ resolve
27
+ };
28
+ };
29
+
30
+ // src/io-manager/event-emitter.ts
31
+ class IoEventEmitter {
32
+ listeners = {
33
+ input: [],
34
+ output: [],
35
+ processed: []
36
+ };
37
+ addEventListener(name, callback) {
38
+ this.listeners[name].push(callback);
39
+ }
40
+ removeEventListener(name, callback) {
41
+ this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
42
+ }
43
+ dispatchEvent(dispatchName, context) {
44
+ this.listeners[dispatchName].forEach((callback) => {
45
+ callback({ detail: context });
46
+ });
47
+ }
48
+ }
49
+
50
+ // src/io-manager/io-synchronizer.ts
51
+ var makeIoSynchronizer = (logLevel, label) => {
52
+ const eventEmitter = new IoEventEmitter;
53
+ let lastInput = 0;
54
+ let lastInputKeyframe = 0;
55
+ let lastOutput = 0;
56
+ let inputsSinceLastOutput = 0;
57
+ let inputs = [];
58
+ let keyframes = [];
59
+ let unprocessed = 0;
60
+ const getUnprocessed = () => unprocessed;
61
+ const getUnemittedItems = () => {
62
+ inputs = inputs.filter((input) => input > lastOutput);
63
+ return inputs.length;
64
+ };
65
+ const getUnemittedKeyframes = () => {
66
+ keyframes = keyframes.filter((keyframe) => keyframe > lastOutput);
67
+ return keyframes.length;
68
+ };
69
+ const printState = (prefix) => {
70
+ Log.trace(logLevel, `[${label}] ${prefix}, state: Last input = ${lastInput} Last input keyframe = ${lastInputKeyframe} Last output = ${lastOutput} Inputs since last output = ${inputsSinceLastOutput}, Queue = ${getUnemittedItems()} (${getUnemittedKeyframes()} keyframes), Unprocessed = ${getUnprocessed()}`);
71
+ };
72
+ const inputItem = (timestamp, keyFrame) => {
73
+ lastInput = timestamp;
74
+ if (keyFrame) {
75
+ lastInputKeyframe = timestamp;
76
+ keyframes.push(timestamp);
77
+ }
78
+ inputsSinceLastOutput++;
79
+ inputs.push(timestamp);
80
+ eventEmitter.dispatchEvent("input", {
81
+ timestamp,
82
+ keyFrame
83
+ });
84
+ printState("Input item");
85
+ };
86
+ const onOutput = (timestamp) => {
87
+ lastOutput = timestamp;
88
+ inputsSinceLastOutput = 0;
89
+ eventEmitter.dispatchEvent("output", {
90
+ timestamp
91
+ });
92
+ unprocessed++;
93
+ printState("Got output");
94
+ };
95
+ const waitForOutput = () => {
96
+ const { promise, resolve } = withResolvers();
97
+ const on = () => {
98
+ eventEmitter.removeEventListener("output", on);
99
+ resolve();
100
+ };
101
+ eventEmitter.addEventListener("output", on);
102
+ return promise;
103
+ };
104
+ const waitForProcessed = () => {
105
+ const { promise, resolve } = withResolvers();
106
+ const on = () => {
107
+ eventEmitter.removeEventListener("processed", on);
108
+ resolve();
109
+ };
110
+ eventEmitter.addEventListener("processed", on);
111
+ return promise;
112
+ };
113
+ const waitFor = async ({
114
+ _unprocessed,
115
+ unemitted
116
+ }) => {
117
+ while (getUnemittedItems() > unemitted) {
118
+ await waitForOutput();
119
+ }
120
+ while (getUnprocessed() > _unprocessed) {
121
+ await waitForProcessed();
122
+ }
123
+ };
124
+ const waitForFinish = async () => {
125
+ await waitFor({ _unprocessed: 0, unemitted: 0 });
126
+ };
127
+ const onProcessed = () => {
128
+ eventEmitter.dispatchEvent("processed", {});
129
+ unprocessed--;
130
+ };
131
+ return {
132
+ inputItem,
133
+ onOutput,
134
+ waitFor,
135
+ waitForFinish,
136
+ onProcessed,
137
+ getUnprocessed
138
+ };
139
+ };
140
+
1
141
  // src/audio-decoder.ts
2
142
  var createAudioDecoder = ({
3
143
  onFrame,
4
144
  onError,
5
145
  signal,
6
- config
146
+ config,
147
+ logLevel
7
148
  }) => {
8
149
  if (signal.aborted) {
9
150
  throw new Error("Not creating audio decoder, already aborted");
10
151
  }
152
+ const ioSynchronizer = makeIoSynchronizer(logLevel, "Audio decoder");
11
153
  let outputQueue = Promise.resolve();
12
- let outputQueueSize = 0;
13
- let dequeueResolver = () => {
14
- };
15
154
  const audioDecoder = new AudioDecoder({
16
155
  output(inputFrame) {
17
- outputQueueSize++;
18
- outputQueue = outputQueue.then(() => onFrame(inputFrame)).then(() => {
19
- dequeueResolver();
20
- outputQueueSize--;
156
+ ioSynchronizer.onOutput(inputFrame.timestamp);
157
+ const abortHandler = () => {
158
+ inputFrame.close();
159
+ };
160
+ signal.addEventListener("abort", abortHandler, { once: true });
161
+ outputQueue = outputQueue.then(() => {
162
+ if (signal.aborted) {
163
+ return;
164
+ }
165
+ return onFrame(inputFrame);
166
+ }).then(() => {
167
+ ioSynchronizer.onProcessed();
168
+ signal.removeEventListener("abort", abortHandler);
21
169
  return Promise.resolve();
170
+ }).catch((err) => {
171
+ inputFrame.close();
172
+ onError(err);
22
173
  });
23
174
  },
24
175
  error(error) {
@@ -36,32 +187,15 @@ var createAudioDecoder = ({
36
187
  close();
37
188
  };
38
189
  signal.addEventListener("abort", onAbort);
39
- const getQueueSize = () => {
40
- return audioDecoder.decodeQueueSize + outputQueueSize;
41
- };
42
190
  audioDecoder.configure(config);
43
- const waitForDequeue = async () => {
44
- await new Promise((r) => {
45
- dequeueResolver = r;
46
- audioDecoder.addEventListener("dequeue", () => r(), {
47
- once: true
48
- });
49
- });
50
- };
51
- const waitForFinish = async () => {
52
- while (getQueueSize() > 0) {
53
- await waitForDequeue();
54
- }
55
- };
56
191
  const processSample = async (audioSample) => {
57
192
  if (audioDecoder.state === "closed") {
58
193
  return;
59
194
  }
60
- while (getQueueSize() > 10) {
61
- await waitForDequeue();
62
- }
195
+ await ioSynchronizer.waitFor({ unemitted: 100, _unprocessed: 2 });
63
196
  const chunk = new EncodedAudioChunk(audioSample);
64
197
  audioDecoder.decode(chunk);
198
+ ioSynchronizer.inputItem(chunk.timestamp, audioSample.type === "key");
65
199
  };
66
200
  let queue = Promise.resolve();
67
201
  return {
@@ -71,11 +205,10 @@ var createAudioDecoder = ({
71
205
  },
72
206
  waitForFinish: async () => {
73
207
  await audioDecoder.flush();
74
- await waitForFinish();
208
+ await ioSynchronizer.waitForFinish();
75
209
  await outputQueue;
76
210
  },
77
211
  close,
78
- getQueueSize,
79
212
  flush: async () => {
80
213
  await audioDecoder.flush();
81
214
  }
@@ -87,22 +220,27 @@ var createAudioEncoder = ({
87
220
  onError,
88
221
  codec,
89
222
  signal,
90
- config: audioEncoderConfig
223
+ config: audioEncoderConfig,
224
+ logLevel
91
225
  }) => {
92
226
  if (signal.aborted) {
93
227
  throw new Error("Not creating audio encoder, already aborted");
94
228
  }
229
+ const ioSynchronizer = makeIoSynchronizer(logLevel, "Audio encoder");
95
230
  let prom = Promise.resolve();
96
- let outputQueue = 0;
97
- let dequeueResolver = () => {
98
- };
99
231
  const encoder = new AudioEncoder({
100
232
  output: (chunk) => {
101
- outputQueue++;
102
- prom = prom.then(() => onChunk(chunk)).then(() => {
103
- outputQueue--;
104
- dequeueResolver();
233
+ ioSynchronizer.onOutput(chunk.timestamp);
234
+ prom = prom.then(() => {
235
+ if (signal.aborted) {
236
+ return;
237
+ }
238
+ return onChunk(chunk);
239
+ }).then(() => {
240
+ ioSynchronizer.onProcessed();
105
241
  return Promise.resolve();
242
+ }).catch((err) => {
243
+ onError(err);
106
244
  });
107
245
  },
108
246
  error(error) {
@@ -123,34 +261,17 @@ var createAudioEncoder = ({
123
261
  if (codec !== "opus") {
124
262
  throw new Error('Only `codec: "opus"` is supported currently');
125
263
  }
126
- const getQueueSize = () => {
127
- return encoder.encodeQueueSize + outputQueue;
128
- };
129
264
  encoder.configure(audioEncoderConfig);
130
- const waitForDequeue = async () => {
131
- await new Promise((r) => {
132
- dequeueResolver = r;
133
- encoder.addEventListener("dequeue", () => r(), {
134
- once: true
135
- });
136
- });
137
- };
138
- const waitForFinish = async () => {
139
- while (getQueueSize() > 0) {
140
- await waitForDequeue();
141
- }
142
- };
143
265
  const encodeFrame = async (audioData) => {
144
266
  if (encoder.state === "closed") {
145
267
  return;
146
268
  }
147
- while (getQueueSize() > 10) {
148
- await waitForDequeue();
149
- }
269
+ await ioSynchronizer.waitFor({ unemitted: 2, _unprocessed: 2 });
150
270
  if (encoder.state === "closed") {
151
271
  return;
152
272
  }
153
273
  encoder.encode(audioData);
274
+ ioSynchronizer.inputItem(audioData.timestamp, true);
154
275
  };
155
276
  let queue = Promise.resolve();
156
277
  return {
@@ -160,32 +281,48 @@ var createAudioEncoder = ({
160
281
  },
161
282
  waitForFinish: async () => {
162
283
  await encoder.flush();
163
- await waitForFinish();
284
+ await ioSynchronizer.waitForFinish();
164
285
  await prom;
165
286
  },
166
287
  close,
167
- getQueueSize,
168
288
  flush: async () => {
169
289
  await encoder.flush();
170
290
  }
171
291
  };
172
292
  };
173
- // src/convert-media.ts
174
- import {
175
- MediaParserInternals,
176
- parseMedia
177
- } from "@remotion/media-parser";
178
- import { bufferWriter } from "@remotion/media-parser/buffer";
179
- import { canUseWebFsWriter, webFsWriter } from "@remotion/media-parser/web-fs";
180
-
181
- // src/error-cause.ts
182
- var error_cause_default = Error;
183
-
293
+ // src/can-copy-audio-track.ts
294
+ var canCopyAudioTrack = ({
295
+ inputCodec,
296
+ outputCodec,
297
+ container
298
+ }) => {
299
+ if (outputCodec === "opus") {
300
+ return inputCodec === "opus" && container === "webm";
301
+ }
302
+ throw new Error(`Unhandled codec: ${outputCodec}`);
303
+ };
304
+ // src/can-copy-video-track.ts
305
+ var canCopyVideoTrack = ({
306
+ inputCodec,
307
+ outputCodec,
308
+ container
309
+ }) => {
310
+ if (outputCodec === "vp8") {
311
+ return inputCodec === "vp8" && container === "webm";
312
+ }
313
+ if (outputCodec === "vp9") {
314
+ return inputCodec === "vp9" && container === "webm";
315
+ }
316
+ throw new Error(`Unhandled codec: ${outputCodec}`);
317
+ };
184
318
  // src/audio-decoder-config.ts
185
319
  var getAudioDecoderConfig = async (config) => {
186
320
  if (typeof AudioDecoder === "undefined") {
187
321
  return null;
188
322
  }
323
+ if (typeof EncodedAudioChunk === "undefined") {
324
+ return null;
325
+ }
189
326
  if ((await AudioDecoder.isConfigSupported(config)).supported) {
190
327
  return config;
191
328
  }
@@ -203,39 +340,175 @@ var getAudioEncoderConfig = async (config) => {
203
340
  return null;
204
341
  };
205
342
 
206
- // src/resolve-audio-action.ts
207
- var canCopyAudioTrack = (inputCodec, outputCodec) => {
208
- if (outputCodec === "opus") {
209
- return inputCodec === "opus";
343
+ // src/can-reencode-audio-track.ts
344
+ var canReencodeAudioTrack = async ({
345
+ track,
346
+ audioCodec,
347
+ bitrate
348
+ }) => {
349
+ const audioDecoderConfig = await getAudioDecoderConfig({
350
+ codec: track.codec,
351
+ numberOfChannels: track.numberOfChannels,
352
+ sampleRate: track.sampleRate,
353
+ description: track.description
354
+ });
355
+ const audioEncoderConfig = await getAudioEncoderConfig({
356
+ codec: audioCodec,
357
+ numberOfChannels: track.numberOfChannels,
358
+ sampleRate: track.sampleRate,
359
+ bitrate
360
+ });
361
+ return Boolean(audioDecoderConfig && audioEncoderConfig);
362
+ };
363
+ // src/video-decoder-config.ts
364
+ var getVideoDecoderConfigWithHardwareAcceleration = async (config) => {
365
+ if (typeof VideoDecoder === "undefined") {
366
+ return null;
210
367
  }
211
- throw new Error(`Unhandled codec: ${outputCodec}`);
368
+ const hardware = {
369
+ ...config,
370
+ hardwareAcceleration: "prefer-hardware"
371
+ };
372
+ if ((await VideoDecoder.isConfigSupported(hardware)).supported) {
373
+ return hardware;
374
+ }
375
+ const software = {
376
+ ...config,
377
+ hardwareAcceleration: "prefer-software"
378
+ };
379
+ if ((await VideoDecoder.isConfigSupported(software)).supported) {
380
+ return software;
381
+ }
382
+ return null;
383
+ };
384
+
385
+ // src/video-encoder-config.ts
386
+ var getVideoEncoderConfig = async ({
387
+ width,
388
+ height,
389
+ codec
390
+ }) => {
391
+ if (typeof VideoEncoder === "undefined") {
392
+ return null;
393
+ }
394
+ const config = {
395
+ codec: codec === "vp9" ? "vp09.00.10.08" : codec,
396
+ height,
397
+ width
398
+ };
399
+ const hardware = {
400
+ ...config,
401
+ hardwareAcceleration: "prefer-hardware"
402
+ };
403
+ if ((await VideoEncoder.isConfigSupported(hardware)).supported) {
404
+ return hardware;
405
+ }
406
+ const software = {
407
+ ...config,
408
+ hardwareAcceleration: "prefer-software"
409
+ };
410
+ if ((await VideoEncoder.isConfigSupported(software)).supported) {
411
+ return software;
412
+ }
413
+ return null;
212
414
  };
213
- var defaultResolveAudioAction = ({
214
- canReencode,
215
- canCopy
415
+
416
+ // src/can-reencode-video-track.ts
417
+ var canReencodeVideoTrack = async ({
418
+ videoCodec,
419
+ track
216
420
  }) => {
217
- if (canCopy) {
218
- return "copy";
421
+ const videoEncoderConfig = await getVideoEncoderConfig({
422
+ codec: videoCodec,
423
+ height: track.displayAspectHeight,
424
+ width: track.displayAspectWidth
425
+ });
426
+ const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
427
+ return Boolean(videoDecoderConfig && videoEncoderConfig);
428
+ };
429
+ // src/codec-id.ts
430
+ var availableVideoCodecs = ["vp8", "vp9"];
431
+ var getAvailableVideoCodecs = () => availableVideoCodecs;
432
+ var availableAudioCodecs = ["opus"];
433
+ var getAvailableAudioCodecs = () => availableAudioCodecs;
434
+ // src/convert-media.ts
435
+ import {
436
+ MediaParserInternals as MediaParserInternals2,
437
+ parseMedia
438
+ } from "@remotion/media-parser";
439
+
440
+ // src/auto-select-writer.ts
441
+ import { bufferWriter } from "@remotion/media-parser/buffer";
442
+ import { canUseWebFsWriter, webFsWriter } from "@remotion/media-parser/web-fs";
443
+ var autoSelectWriter = async (writer, logLevel) => {
444
+ if (writer) {
445
+ Log.verbose(logLevel, "Using writer provided by user");
446
+ return writer;
219
447
  }
220
- if (canReencode) {
221
- return "reencode";
448
+ Log.verbose(logLevel, "Determining best writer");
449
+ if (await canUseWebFsWriter()) {
450
+ Log.verbose(logLevel, "Using WebFS writer because it is supported");
451
+ return webFsWriter;
452
+ }
453
+ Log.verbose(logLevel, "Using buffer writer because WebFS writer is not supported");
454
+ return bufferWriter;
455
+ };
456
+
457
+ // src/calculate-progress.ts
458
+ var calculateProgress = ({
459
+ millisecondsWritten,
460
+ expectedOutputDurationInMs
461
+ }) => {
462
+ if (expectedOutputDurationInMs === null) {
463
+ return null;
222
464
  }
223
- return "drop";
465
+ return millisecondsWritten / expectedOutputDurationInMs;
224
466
  };
225
- var resolveAudioAction = async ({
226
- audioDecoderConfig,
227
- audioEncoderConfig,
467
+
468
+ // src/error-cause.ts
469
+ var error_cause_default = Error;
470
+
471
+ // src/convert-encoded-chunk.ts
472
+ var convertEncodedChunk = (chunk) => {
473
+ const arr = new Uint8Array(chunk.byteLength);
474
+ chunk.copyTo(arr);
475
+ return {
476
+ data: arr,
477
+ duration: chunk.duration ?? undefined,
478
+ timestamp: chunk.timestamp,
479
+ type: chunk.type
480
+ };
481
+ };
482
+
483
+ // src/resolve-audio-action.ts
484
+ var DEFAULT_BITRATE = 128000;
485
+ var defaultResolveAudioAction = async ({
228
486
  track,
229
487
  audioCodec,
230
- resolverFunction
488
+ logLevel,
489
+ container
231
490
  }) => {
232
- const canReencode = Boolean(audioDecoderConfig && audioEncoderConfig);
233
- const canCopy = canCopyAudioTrack(track.codecWithoutConfig, audioCodec);
234
- const resolved = await resolverFunction({
235
- canReencode,
236
- canCopy
491
+ const bitrate = DEFAULT_BITRATE;
492
+ const canCopy = canCopyAudioTrack({
493
+ inputCodec: track.codecWithoutConfig,
494
+ outputCodec: audioCodec,
495
+ container
237
496
  });
238
- return resolved;
497
+ if (canCopy) {
498
+ Log.verbose(logLevel, `Track ${track.trackId} (audio): Can copy = ${canCopy}, action = copy`);
499
+ return Promise.resolve({ type: "copy" });
500
+ }
501
+ const canReencode = await canReencodeAudioTrack({
502
+ audioCodec,
503
+ track,
504
+ bitrate
505
+ });
506
+ if (canReencode) {
507
+ Log.verbose(logLevel, `Track ${track.trackId} (audio): Can re-encode = ${canReencode}, can copy = ${canCopy}, action = reencode`);
508
+ return Promise.resolve({ type: "reencode", bitrate, audioCodec });
509
+ }
510
+ Log.verbose(logLevel, `Track ${track.trackId} (audio): Can re-encode = ${canReencode}, can copy = ${canCopy}, action = drop`);
511
+ return Promise.resolve({ type: "drop" });
239
512
  };
240
513
 
241
514
  // src/on-audio-track.ts
@@ -247,31 +520,19 @@ var makeAudioTrackHandler = ({
247
520
  abortConversion,
248
521
  onMediaStateUpdate,
249
522
  onAudioTrack,
250
- bitrate
523
+ logLevel,
524
+ container
251
525
  }) => async (track) => {
252
- const audioEncoderConfig = await getAudioEncoderConfig({
253
- codec: audioCodec,
254
- numberOfChannels: track.numberOfChannels,
255
- sampleRate: track.sampleRate,
256
- bitrate
257
- });
258
- const audioDecoderConfig = await getAudioDecoderConfig({
259
- codec: track.codec,
260
- numberOfChannels: track.numberOfChannels,
261
- sampleRate: track.sampleRate,
262
- description: track.description
263
- });
264
- const audioOperation = await resolveAudioAction({
265
- audioDecoderConfig,
266
- audioEncoderConfig,
526
+ const audioOperation = await (onAudioTrack ?? defaultResolveAudioAction)({
267
527
  audioCodec,
268
528
  track,
269
- resolverFunction: onAudioTrack
529
+ logLevel,
530
+ container
270
531
  });
271
- if (audioOperation === "drop") {
532
+ if (audioOperation.type === "drop") {
272
533
  return null;
273
534
  }
274
- if (audioOperation === "copy") {
535
+ if (audioOperation.type === "copy") {
275
536
  const addedTrack = await state.addTrack({
276
537
  type: "audio",
277
538
  codec: audioCodec,
@@ -280,11 +541,23 @@ var makeAudioTrackHandler = ({
280
541
  codecPrivate: track.codecPrivate
281
542
  });
282
543
  return async (audioSample) => {
283
- await state.addSample(new EncodedAudioChunk(audioSample), addedTrack.trackNumber, false);
544
+ await state.addSample(audioSample, addedTrack.trackNumber, false);
284
545
  convertMediaState.encodedAudioFrames++;
285
546
  onMediaStateUpdate?.({ ...convertMediaState });
286
547
  };
287
548
  }
549
+ const audioEncoderConfig = await getAudioEncoderConfig({
550
+ numberOfChannels: track.numberOfChannels,
551
+ sampleRate: track.sampleRate,
552
+ codec: audioOperation.audioCodec,
553
+ bitrate: audioOperation.bitrate
554
+ });
555
+ const audioDecoderConfig = await getAudioDecoderConfig({
556
+ codec: track.codec,
557
+ numberOfChannels: track.numberOfChannels,
558
+ sampleRate: track.sampleRate,
559
+ description: track.description
560
+ });
288
561
  if (!audioEncoderConfig) {
289
562
  abortConversion(new error_cause_default(`Could not configure audio encoder of track ${track.trackId}`));
290
563
  return null;
@@ -302,7 +575,7 @@ var makeAudioTrackHandler = ({
302
575
  });
303
576
  const audioEncoder = createAudioEncoder({
304
577
  onChunk: async (chunk) => {
305
- await state.addSample(chunk, trackNumber, false);
578
+ await state.addSample(convertEncodedChunk(chunk), trackNumber, false);
306
579
  convertMediaState.encodedAudioFrames++;
307
580
  onMediaStateUpdate?.({ ...convertMediaState });
308
581
  },
@@ -313,7 +586,8 @@ var makeAudioTrackHandler = ({
313
586
  },
314
587
  codec: audioCodec,
315
588
  signal: controller.signal,
316
- config: audioEncoderConfig
589
+ config: audioEncoderConfig,
590
+ logLevel
317
591
  });
318
592
  const audioDecoder = createAudioDecoder({
319
593
  onFrame: async (frame) => {
@@ -328,7 +602,8 @@ var makeAudioTrackHandler = ({
328
602
  }));
329
603
  },
330
604
  signal: controller.signal,
331
- config: audioDecoderConfig
605
+ config: audioDecoderConfig,
606
+ logLevel
332
607
  });
333
608
  state.addWaitForFinishPromise(async () => {
334
609
  await audioDecoder.waitForFinish();
@@ -341,62 +616,97 @@ var makeAudioTrackHandler = ({
341
616
  };
342
617
  };
343
618
 
344
- // src/resolve-video-action.ts
345
- var canCopyVideoTrack = (inputCodec, outputCodec) => {
346
- if (outputCodec === "vp8") {
347
- return inputCodec === "vp8";
619
+ // src/on-frame.ts
620
+ var onFrame = async ({
621
+ frame,
622
+ onVideoFrame,
623
+ videoEncoder,
624
+ onMediaStateUpdate,
625
+ track,
626
+ convertMediaState
627
+ }) => {
628
+ const newFrame = onVideoFrame ? await onVideoFrame({ frame, track }) : frame;
629
+ if (newFrame.codedHeight !== frame.codedHeight) {
630
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different codedHeight (${newFrame.codedHeight}) than the input frame (${frame.codedHeight})`);
348
631
  }
349
- if (outputCodec === "vp9") {
350
- return inputCodec === "vp9";
632
+ if (newFrame.codedWidth !== frame.codedWidth) {
633
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different codedWidth (${newFrame.codedWidth}) than the input frame (${frame.codedWidth})`);
351
634
  }
352
- throw new Error(`Unhandled codec: ${outputCodec}`);
353
- };
354
- var defaultResolveVideoAction = ({
355
- canReencode,
356
- canCopy
357
- }) => {
358
- if (canCopy) {
359
- return "copy";
635
+ if (newFrame.displayWidth !== frame.displayWidth) {
636
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${newFrame.displayWidth}) than the input frame (${newFrame.displayHeight})`);
360
637
  }
361
- if (canReencode) {
362
- return "reencode";
638
+ if (newFrame.displayHeight !== frame.displayHeight) {
639
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${newFrame.displayHeight}) than the input frame (${newFrame.displayHeight})`);
640
+ }
641
+ if (newFrame.timestamp !== frame.timestamp) {
642
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different timestamp (${newFrame.timestamp}) than the input frame (${newFrame.timestamp}). When calling new VideoFrame(), pass {timestamp: frame.timestamp} as second argument`);
643
+ }
644
+ if (newFrame.duration !== frame.duration) {
645
+ throw new Error(`Returned VideoFrame of track ${track.trackId} has different duration (${newFrame.duration}) than the input frame (${newFrame.duration}). When calling new VideoFrame(), pass {duration: frame.duration} as second argument`);
646
+ }
647
+ await videoEncoder.encodeFrame(newFrame, newFrame.timestamp);
648
+ convertMediaState.decodedVideoFrames++;
649
+ onMediaStateUpdate?.({ ...convertMediaState });
650
+ newFrame.close();
651
+ if (frame !== newFrame) {
652
+ frame.close();
363
653
  }
364
- return "drop";
365
654
  };
366
- var resolveVideoAction = async ({
367
- videoDecoderConfig,
368
- videoEncoderConfig,
655
+
656
+ // src/resolve-video-action.ts
657
+ var defaultResolveVideoAction = async ({
369
658
  track,
370
659
  videoCodec,
371
- resolverFunction
660
+ logLevel,
661
+ container
372
662
  }) => {
373
- const canReencode = Boolean(videoDecoderConfig && videoEncoderConfig);
374
- const canCopy = canCopyVideoTrack(track.codecWithoutConfig, videoCodec);
375
- const resolved = await resolverFunction({
376
- canReencode,
377
- canCopy
663
+ const canCopy = canCopyVideoTrack({
664
+ inputCodec: track.codecWithoutConfig,
665
+ outputCodec: videoCodec,
666
+ container
378
667
  });
379
- return resolved;
668
+ if (canCopy) {
669
+ Log.verbose(logLevel, `Track ${track.trackId} (video): Can copy, therefore copying`);
670
+ return Promise.resolve({ type: "copy" });
671
+ }
672
+ const canReencode = await canReencodeVideoTrack({ videoCodec, track });
673
+ if (canReencode) {
674
+ Log.verbose(logLevel, `Track ${track.trackId} (video): Cannot copy, but re-enconde, therefore re-encoding`);
675
+ return Promise.resolve({ type: "reencode", videoCodec });
676
+ }
677
+ Log.verbose(logLevel, `Track ${track.trackId} (video): Can neither copy nor re-encode, therefore dropping`);
678
+ return Promise.resolve({ type: "drop" });
380
679
  };
381
680
 
382
681
  // src/video-decoder.ts
383
682
  var createVideoDecoder = ({
384
- onFrame,
683
+ onFrame: onFrame2,
385
684
  onError,
386
685
  signal,
387
- config
686
+ config,
687
+ logLevel
388
688
  }) => {
689
+ const ioSynchronizer = makeIoSynchronizer(logLevel, "Video decoder");
389
690
  let outputQueue = Promise.resolve();
390
- let outputQueueSize = 0;
391
- let dequeueResolver = () => {
392
- };
393
691
  const videoDecoder = new VideoDecoder({
394
692
  output(inputFrame) {
395
- outputQueueSize++;
396
- outputQueue = outputQueue.then(() => onFrame(inputFrame)).then(() => {
397
- outputQueueSize--;
398
- dequeueResolver();
693
+ ioSynchronizer.onOutput(inputFrame.timestamp);
694
+ const abortHandler = () => {
695
+ inputFrame.close();
696
+ };
697
+ signal.addEventListener("abort", abortHandler, { once: true });
698
+ outputQueue = outputQueue.then(() => {
699
+ if (signal.aborted) {
700
+ return;
701
+ }
702
+ return onFrame2(inputFrame);
703
+ }).then(() => {
704
+ ioSynchronizer.onProcessed();
705
+ signal.removeEventListener("abort", abortHandler);
399
706
  return Promise.resolve();
707
+ }).catch((err) => {
708
+ inputFrame.close();
709
+ onError(err);
400
710
  });
401
711
  },
402
712
  error(error) {
@@ -414,37 +724,20 @@ var createVideoDecoder = ({
414
724
  close();
415
725
  };
416
726
  signal.addEventListener("abort", onAbort);
417
- const getQueueSize = () => {
418
- return videoDecoder.decodeQueueSize + outputQueueSize;
419
- };
420
727
  videoDecoder.configure(config);
421
- const waitForDequeue = async () => {
422
- await new Promise((r) => {
423
- dequeueResolver = r;
424
- videoDecoder.addEventListener("dequeue", () => r(), {
425
- once: true
426
- });
427
- });
428
- };
429
- const waitForFinish = async () => {
430
- while (getQueueSize() > 0) {
431
- await waitForDequeue();
432
- }
433
- };
434
728
  const processSample = async (sample) => {
435
729
  if (videoDecoder.state === "closed") {
436
730
  return;
437
731
  }
438
- while (getQueueSize() > 10) {
439
- await waitForDequeue();
440
- }
441
732
  if (videoDecoder.state === "closed") {
442
733
  return;
443
734
  }
735
+ await ioSynchronizer.waitFor({ unemitted: 20, _unprocessed: 2 });
444
736
  if (sample.type === "key") {
445
737
  await videoDecoder.flush();
446
738
  }
447
739
  videoDecoder.decode(new EncodedVideoChunk(sample));
740
+ ioSynchronizer.inputItem(sample.timestamp, sample.type === "key");
448
741
  };
449
742
  let inputQueue = Promise.resolve();
450
743
  return {
@@ -454,64 +747,50 @@ var createVideoDecoder = ({
454
747
  },
455
748
  waitForFinish: async () => {
456
749
  await videoDecoder.flush();
457
- await waitForFinish();
750
+ await ioSynchronizer.waitForFinish();
458
751
  await outputQueue;
459
752
  await inputQueue;
460
753
  },
461
754
  close,
462
- getQueueSize,
463
755
  flush: async () => {
464
756
  await videoDecoder.flush();
465
757
  }
466
758
  };
467
759
  };
468
760
 
469
- // src/video-decoder-config.ts
470
- var getVideoDecoderConfigWithHardwareAcceleration = async (config) => {
471
- if (typeof VideoDecoder === "undefined") {
472
- return null;
473
- }
474
- const hardware = {
475
- ...config,
476
- hardwareAcceleration: "prefer-hardware"
477
- };
478
- if ((await VideoDecoder.isConfigSupported(hardware)).supported) {
479
- return hardware;
480
- }
481
- const software = {
482
- ...config,
483
- hardwareAcceleration: "prefer-software"
484
- };
485
- if ((await VideoDecoder.isConfigSupported(software)).supported) {
486
- return software;
487
- }
488
- return null;
489
- };
490
-
491
761
  // src/video-encoder.ts
492
762
  var createVideoEncoder = ({
493
763
  onChunk,
494
764
  onError,
495
765
  signal,
496
- config
766
+ config,
767
+ logLevel
497
768
  }) => {
498
769
  if (signal.aborted) {
499
770
  throw new Error("Not creating video encoder, already aborted");
500
771
  }
772
+ const ioSynchronizer = makeIoSynchronizer(logLevel, "Video encoder");
501
773
  let outputQueue = Promise.resolve();
502
- let outputQueueSize = 0;
503
- let dequeueResolver = () => {
504
- };
505
774
  const encoder = new VideoEncoder({
506
775
  error(error) {
507
776
  onError(error);
508
777
  },
509
778
  output(chunk) {
510
- outputQueueSize++;
511
- outputQueue = outputQueue.then(() => onChunk(chunk)).then(() => {
512
- outputQueueSize--;
513
- dequeueResolver();
779
+ if (chunk.duration === null) {
780
+ throw new Error("Duration is null");
781
+ }
782
+ const timestamp = chunk.timestamp + chunk.duration;
783
+ ioSynchronizer.onOutput(timestamp);
784
+ outputQueue = outputQueue.then(() => {
785
+ if (signal.aborted) {
786
+ return;
787
+ }
788
+ return onChunk(chunk);
789
+ }).then(() => {
790
+ ioSynchronizer.onProcessed();
514
791
  return Promise.resolve();
792
+ }).catch((err) => {
793
+ onError(err);
515
794
  });
516
795
  }
517
796
  });
@@ -526,37 +805,24 @@ var createVideoEncoder = ({
526
805
  close();
527
806
  };
528
807
  signal.addEventListener("abort", onAbort);
529
- const getQueueSize = () => {
530
- return encoder.encodeQueueSize + outputQueueSize;
531
- };
532
808
  encoder.configure(config);
533
809
  let framesProcessed = 0;
534
- const waitForDequeue = async () => {
535
- await new Promise((r) => {
536
- dequeueResolver = r;
537
- encoder.addEventListener("dequeue", () => r(), {
538
- once: true
539
- });
540
- });
541
- };
542
- const waitForFinish = async () => {
543
- while (getQueueSize() > 0) {
544
- await waitForDequeue();
545
- }
546
- };
547
810
  const encodeFrame = async (frame) => {
548
811
  if (encoder.state === "closed") {
549
812
  return;
550
813
  }
551
- while (getQueueSize() > 10) {
552
- await waitForDequeue();
553
- }
814
+ await ioSynchronizer.waitFor({
815
+ unemitted: 2,
816
+ _unprocessed: 2
817
+ });
554
818
  if (encoder.state === "closed") {
555
819
  return;
556
820
  }
821
+ const keyFrame = framesProcessed % 40 === 0;
557
822
  encoder.encode(frame, {
558
- keyFrame: framesProcessed % 40 === 0
823
+ keyFrame
559
824
  });
825
+ ioSynchronizer.inputItem(frame.timestamp, keyFrame);
560
826
  framesProcessed++;
561
827
  };
562
828
  let inputQueue = Promise.resolve();
@@ -568,38 +834,15 @@ var createVideoEncoder = ({
568
834
  waitForFinish: async () => {
569
835
  await encoder.flush();
570
836
  await outputQueue;
571
- await waitForFinish();
837
+ await ioSynchronizer.waitForFinish();
572
838
  },
573
839
  close,
574
- getQueueSize,
575
840
  flush: async () => {
576
841
  await encoder.flush();
577
842
  }
578
843
  };
579
844
  };
580
845
 
581
- // src/video-encoder-config.ts
582
- var getVideoEncoderConfig = async (config) => {
583
- if (typeof VideoEncoder === "undefined") {
584
- return null;
585
- }
586
- const hardware = {
587
- ...config,
588
- hardwareAcceleration: "prefer-hardware"
589
- };
590
- if ((await VideoEncoder.isConfigSupported(hardware)).supported) {
591
- return hardware;
592
- }
593
- const software = {
594
- ...config,
595
- hardwareAcceleration: "prefer-software"
596
- };
597
- if ((await VideoEncoder.isConfigSupported(software)).supported) {
598
- return software;
599
- }
600
- return null;
601
- };
602
-
603
846
  // src/on-video-track.ts
604
847
  var makeVideoTrackHandler = ({
605
848
  state,
@@ -609,25 +852,23 @@ var makeVideoTrackHandler = ({
609
852
  convertMediaState,
610
853
  controller,
611
854
  videoCodec,
612
- onVideoTrack
855
+ onVideoTrack,
856
+ logLevel,
857
+ container
613
858
  }) => async (track) => {
614
- const videoEncoderConfig = await getVideoEncoderConfig({
615
- codec: videoCodec === "vp9" ? "vp09.00.10.08" : videoCodec,
616
- height: track.displayAspectHeight,
617
- width: track.displayAspectWidth
618
- });
619
- const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
620
- const videoOperation = await resolveVideoAction({
621
- videoDecoderConfig,
622
- videoEncoderConfig,
859
+ if (controller.signal.aborted) {
860
+ throw new error_cause_default("Aborted");
861
+ }
862
+ const videoOperation = await (onVideoTrack ?? defaultResolveVideoAction)({
623
863
  track,
624
864
  videoCodec,
625
- resolverFunction: onVideoTrack
865
+ logLevel,
866
+ container
626
867
  });
627
- if (videoOperation === "drop") {
868
+ if (videoOperation.type === "drop") {
628
869
  return null;
629
870
  }
630
- if (videoOperation === "copy") {
871
+ if (videoOperation.type === "copy") {
631
872
  const videoTrack = await state.addTrack({
632
873
  type: "video",
633
874
  color: track.color,
@@ -636,12 +877,18 @@ var makeVideoTrackHandler = ({
636
877
  codec: track.codecWithoutConfig,
637
878
  codecPrivate: track.codecPrivate
638
879
  });
639
- return (sample) => {
640
- state.addSample(new EncodedVideoChunk(sample), videoTrack.trackNumber, true);
880
+ return async (sample) => {
881
+ await state.addSample(sample, videoTrack.trackNumber, true);
641
882
  convertMediaState.decodedVideoFrames++;
642
883
  onMediaStateUpdate?.({ ...convertMediaState });
643
884
  };
644
885
  }
886
+ const videoEncoderConfig = await getVideoEncoderConfig({
887
+ codec: videoOperation.videoCodec,
888
+ height: track.displayAspectHeight,
889
+ width: track.displayAspectWidth
890
+ });
891
+ const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
645
892
  if (videoEncoderConfig === null) {
646
893
  abortConversion(new error_cause_default(`Could not configure video encoder of track ${track.trackId}`));
647
894
  return null;
@@ -660,7 +907,7 @@ var makeVideoTrackHandler = ({
660
907
  });
661
908
  const videoEncoder = createVideoEncoder({
662
909
  onChunk: async (chunk) => {
663
- await state.addSample(chunk, trackNumber, true);
910
+ await state.addSample(convertEncodedChunk(chunk), trackNumber, true);
664
911
  convertMediaState.encodedVideoFrames++;
665
912
  onMediaStateUpdate?.({ ...convertMediaState });
666
913
  },
@@ -670,23 +917,28 @@ var makeVideoTrackHandler = ({
670
917
  }));
671
918
  },
672
919
  signal: controller.signal,
673
- config: videoEncoderConfig
920
+ config: videoEncoderConfig,
921
+ logLevel
674
922
  });
675
923
  const videoDecoder = createVideoDecoder({
676
924
  config: videoDecoderConfig,
677
925
  onFrame: async (frame) => {
678
- await onVideoFrame?.(frame, track);
679
- await videoEncoder.encodeFrame(frame);
680
- convertMediaState.decodedVideoFrames++;
681
- onMediaStateUpdate?.({ ...convertMediaState });
682
- frame.close();
926
+ await onFrame({
927
+ convertMediaState,
928
+ frame,
929
+ onMediaStateUpdate,
930
+ track,
931
+ videoEncoder,
932
+ onVideoFrame
933
+ });
683
934
  },
684
935
  onError: (err) => {
685
936
  abortConversion(new error_cause_default(`Video decoder of track ${track.trackId} failed (see .cause of this error)`, {
686
937
  cause: err
687
938
  }));
688
939
  },
689
- signal: controller.signal
940
+ signal: controller.signal,
941
+ logLevel
690
942
  });
691
943
  state.addWaitForFinishPromise(async () => {
692
944
  await videoDecoder.waitForFinish();
@@ -699,30 +951,27 @@ var makeVideoTrackHandler = ({
699
951
  };
700
952
  };
701
953
 
702
- // src/with-resolvers.ts
703
- var withResolvers = function() {
704
- let resolve;
705
- let reject;
706
- const promise = new Promise((res, rej) => {
707
- resolve = res;
708
- reject = rej;
709
- });
710
- return { promise, resolve, reject };
711
- };
712
-
713
954
  // src/convert-media.ts
714
- var convertMedia = async ({
955
+ var convertMedia = async function({
715
956
  src,
716
957
  onVideoFrame,
717
- onMediaStateUpdate,
958
+ onMediaStateUpdate: onMediaStateDoNoCallDirectly,
718
959
  audioCodec,
719
- to,
960
+ container,
720
961
  videoCodec,
721
962
  signal: userPassedAbortSignal,
722
963
  onAudioTrack: userAudioResolver,
723
- onVideoTrack: userVideoResolver
724
- }) => {
725
- if (to !== "webm") {
964
+ onVideoTrack: userVideoResolver,
965
+ reader,
966
+ fields,
967
+ logLevel = "info",
968
+ writer,
969
+ ...more
970
+ }) {
971
+ if (userPassedAbortSignal?.aborted) {
972
+ return Promise.reject(new error_cause_default("Aborted"));
973
+ }
974
+ if (container !== "webm") {
726
975
  return Promise.reject(new TypeError('Only `to: "webm"` is supported currently'));
727
976
  }
728
977
  if (audioCodec !== "opus") {
@@ -731,7 +980,7 @@ var convertMedia = async ({
731
980
  if (videoCodec !== "vp8" && videoCodec !== "vp9") {
732
981
  return Promise.reject(new TypeError('Only `videoCodec: "vp8"` and `videoCodec: "vp9"` are supported currently'));
733
982
  }
734
- const { promise, resolve, reject } = withResolvers();
983
+ const { resolve, reject, getPromiseToImmediatelyReturn } = withResolversAndWaitForReturn();
735
984
  const controller = new AbortController;
736
985
  const abortConversion = (errCause) => {
737
986
  reject(errCause);
@@ -747,10 +996,35 @@ var convertMedia = async ({
747
996
  decodedAudioFrames: 0,
748
997
  decodedVideoFrames: 0,
749
998
  encodedVideoFrames: 0,
750
- encodedAudioFrames: 0
999
+ encodedAudioFrames: 0,
1000
+ bytesWritten: 0,
1001
+ millisecondsWritten: 0,
1002
+ expectedOutputDurationInMs: null,
1003
+ overallProgress: 0
751
1004
  };
752
- const canUseWebFs = await canUseWebFsWriter();
753
- const state = await MediaParserInternals.createMedia(canUseWebFs ? webFsWriter : bufferWriter);
1005
+ const onMediaStateUpdate = (newState) => {
1006
+ if (controller.signal.aborted) {
1007
+ return;
1008
+ }
1009
+ onMediaStateDoNoCallDirectly?.(newState);
1010
+ };
1011
+ const state = await MediaParserInternals2.createMedia({
1012
+ writer: await autoSelectWriter(writer, logLevel),
1013
+ onBytesProgress: (bytesWritten) => {
1014
+ convertMediaState.bytesWritten = bytesWritten;
1015
+ onMediaStateUpdate?.(convertMediaState);
1016
+ },
1017
+ onMillisecondsProgress: (millisecondsWritten) => {
1018
+ if (millisecondsWritten > convertMediaState.millisecondsWritten) {
1019
+ convertMediaState.millisecondsWritten = millisecondsWritten;
1020
+ convertMediaState.overallProgress = calculateProgress({
1021
+ millisecondsWritten: convertMediaState.millisecondsWritten,
1022
+ expectedOutputDurationInMs: convertMediaState.expectedOutputDurationInMs
1023
+ });
1024
+ onMediaStateUpdate?.(convertMediaState);
1025
+ }
1026
+ }
1027
+ });
754
1028
  const onVideoTrack = makeVideoTrackHandler({
755
1029
  state,
756
1030
  onVideoFrame: onVideoFrame ?? null,
@@ -759,7 +1033,9 @@ var convertMedia = async ({
759
1033
  convertMediaState,
760
1034
  controller,
761
1035
  videoCodec,
762
- onVideoTrack: userVideoResolver ?? defaultResolveVideoAction
1036
+ onVideoTrack: userVideoResolver ?? null,
1037
+ logLevel,
1038
+ container
763
1039
  });
764
1040
  const onAudioTrack = makeAudioTrackHandler({
765
1041
  abortConversion,
@@ -768,29 +1044,58 @@ var convertMedia = async ({
768
1044
  convertMediaState,
769
1045
  onMediaStateUpdate: onMediaStateUpdate ?? null,
770
1046
  state,
771
- onAudioTrack: userAudioResolver ?? defaultResolveAudioAction,
772
- bitrate: 128000
1047
+ onAudioTrack: userAudioResolver ?? null,
1048
+ logLevel,
1049
+ container
773
1050
  });
774
1051
  parseMedia({
775
1052
  src,
776
1053
  onVideoTrack,
777
1054
  onAudioTrack,
778
- signal: controller.signal
1055
+ signal: controller.signal,
1056
+ fields: {
1057
+ ...fields,
1058
+ durationInSeconds: true
1059
+ },
1060
+ reader,
1061
+ ...more,
1062
+ onDurationInSeconds: (durationInSeconds) => {
1063
+ if (durationInSeconds === null) {
1064
+ return null;
1065
+ }
1066
+ const casted = more;
1067
+ if (casted.onDurationInSeconds) {
1068
+ casted.onDurationInSeconds(durationInSeconds);
1069
+ }
1070
+ const expectedOutputDurationInMs = durationInSeconds * 1000;
1071
+ convertMediaState.expectedOutputDurationInMs = expectedOutputDurationInMs;
1072
+ convertMediaState.overallProgress = calculateProgress({
1073
+ millisecondsWritten: convertMediaState.millisecondsWritten,
1074
+ expectedOutputDurationInMs
1075
+ });
1076
+ onMediaStateUpdate(convertMediaState);
1077
+ }
779
1078
  }).then(() => {
780
1079
  return state.waitForFinish();
781
1080
  }).then(() => {
782
1081
  resolve({ save: state.save, remove: state.remove });
783
1082
  }).catch((err) => {
784
1083
  reject(err);
785
- }).finally(() => {
1084
+ });
1085
+ return getPromiseToImmediatelyReturn().finally(() => {
786
1086
  userPassedAbortSignal?.removeEventListener("abort", onUserAbort);
787
1087
  });
788
- return promise;
789
1088
  };
790
1089
  export {
1090
+ getAvailableVideoCodecs,
1091
+ getAvailableAudioCodecs,
791
1092
  createVideoEncoder,
792
1093
  createVideoDecoder,
793
1094
  createAudioEncoder,
794
1095
  createAudioDecoder,
795
- convertMedia
1096
+ convertMedia,
1097
+ canReencodeVideoTrack,
1098
+ canReencodeAudioTrack,
1099
+ canCopyVideoTrack,
1100
+ canCopyAudioTrack
796
1101
  };