@remotion/webcodecs 4.0.228 → 4.0.230
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/arraybuffer-to-uint8-array.d.ts +1 -0
- package/dist/arraybuffer-to-uint8-array.js +7 -0
- package/dist/audio-decoder-config.js +3 -0
- package/dist/audio-decoder.d.ts +2 -2
- package/dist/audio-encoder-config.js +15 -2
- package/dist/audio-encoder.d.ts +2 -1
- package/dist/audio-encoder.js +16 -4
- package/dist/calculate-progress.d.ts +2 -2
- package/dist/calculate-progress.js +3 -3
- package/dist/can-copy-audio-track.d.ts +6 -0
- package/dist/can-copy-audio-track.js +13 -0
- package/dist/can-copy-video-track.d.ts +6 -0
- package/dist/can-copy-video-track.js +13 -0
- package/dist/can-reencode-audio-track.d.ts +7 -0
- package/dist/can-reencode-audio-track.js +16 -0
- package/dist/can-reencode-video-track.d.ts +6 -0
- package/dist/can-reencode-video-track.js +16 -0
- package/dist/choose-correct-avc1-profile.d.ts +5 -0
- package/dist/choose-correct-avc1-profile.js +54 -0
- package/dist/codec-id.d.ts +10 -2
- package/dist/codec-id.js +30 -0
- package/dist/convert-encoded-chunk.d.ts +3 -0
- package/dist/convert-encoded-chunk.js +38 -0
- package/dist/convert-media.d.ts +22 -14
- package/dist/convert-media.js +25 -20
- package/dist/default-on-audio-track-handler.d.ts +2 -0
- package/dist/default-on-audio-track-handler.js +36 -0
- package/dist/default-on-video-track-handler.d.ts +2 -0
- package/dist/default-on-video-track-handler.js +29 -0
- package/dist/esm/index.mjs +497 -210
- package/dist/get-default-audio-codec.d.ts +4 -0
- package/dist/get-default-audio-codec.js +13 -0
- package/dist/get-default-video-codec.d.ts +4 -0
- package/dist/get-default-video-codec.js +10 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +21 -1
- package/dist/io-manager/io-synchronizer.js +2 -2
- package/dist/on-audio-track-handler.d.ts +19 -0
- package/dist/on-audio-track-handler.js +2 -0
- package/dist/on-audio-track.d.ts +6 -6
- package/dist/on-audio-track.js +54 -27
- package/dist/on-frame.d.ts +11 -0
- package/dist/on-frame.js +32 -0
- package/dist/on-video-track-handler.d.ts +18 -0
- package/dist/on-video-track-handler.js +2 -0
- package/dist/on-video-track.d.ts +10 -9
- package/dist/on-video-track.js +55 -25
- package/dist/video-decoder.d.ts +2 -2
- package/dist/video-decoder.js +5 -0
- package/dist/video-encoder-config.d.ts +7 -1
- package/dist/video-encoder-config.js +11 -1
- package/dist/video-encoder.d.ts +2 -2
- package/dist/video-encoder.js +4 -6
- package/package.json +4 -4
- package/dist/event-emitter.d.ts +0 -25
- package/dist/event-emitter.js +0 -23
- package/dist/resolve-audio-action.d.ts +0 -15
- package/dist/resolve-audio-action.js +0 -30
- package/dist/resolve-video-action.d.ts +0 -15
- package/dist/resolve-video-action.js +0 -33
- package/dist/wait-until-return.d.ts +0 -4
- package/dist/wait-until-return.js +0 -14
package/dist/esm/index.mjs
CHANGED
|
@@ -59,11 +59,11 @@ var makeIoSynchronizer = (logLevel, label) => {
|
|
|
59
59
|
let unprocessed = 0;
|
|
60
60
|
const getUnprocessed = () => unprocessed;
|
|
61
61
|
const getUnemittedItems = () => {
|
|
62
|
-
inputs = inputs.filter((input) => input > lastOutput);
|
|
62
|
+
inputs = inputs.filter((input) => Math.floor(input) > Math.floor(lastOutput));
|
|
63
63
|
return inputs.length;
|
|
64
64
|
};
|
|
65
65
|
const getUnemittedKeyframes = () => {
|
|
66
|
-
keyframes = keyframes.filter((keyframe) => keyframe > lastOutput);
|
|
66
|
+
keyframes = keyframes.filter((keyframe) => Math.floor(keyframe) > Math.floor(lastOutput));
|
|
67
67
|
return keyframes.length;
|
|
68
68
|
};
|
|
69
69
|
const printState = (prefix) => {
|
|
@@ -221,7 +221,8 @@ var createAudioEncoder = ({
|
|
|
221
221
|
codec,
|
|
222
222
|
signal,
|
|
223
223
|
config: audioEncoderConfig,
|
|
224
|
-
logLevel
|
|
224
|
+
logLevel,
|
|
225
|
+
onNewAudioSampleRate
|
|
225
226
|
}) => {
|
|
226
227
|
if (signal.aborted) {
|
|
227
228
|
throw new Error("Not creating audio encoder, already aborted");
|
|
@@ -258,10 +259,10 @@ var createAudioEncoder = ({
|
|
|
258
259
|
close();
|
|
259
260
|
};
|
|
260
261
|
signal.addEventListener("abort", onAbort);
|
|
261
|
-
if (codec !== "opus") {
|
|
262
|
-
throw new Error('Only `codec: "opus"` is supported currently');
|
|
262
|
+
if (codec !== "opus" && codec !== "aac") {
|
|
263
|
+
throw new Error('Only `codec: "opus"` and `codec: "aac"` is supported currently');
|
|
263
264
|
}
|
|
264
|
-
|
|
265
|
+
const wantedSampleRate = audioEncoderConfig.sampleRate;
|
|
265
266
|
const encodeFrame = async (audioData) => {
|
|
266
267
|
if (encoder.state === "closed") {
|
|
267
268
|
return;
|
|
@@ -270,6 +271,17 @@ var createAudioEncoder = ({
|
|
|
270
271
|
if (encoder.state === "closed") {
|
|
271
272
|
return;
|
|
272
273
|
}
|
|
274
|
+
if (encoder.state === "unconfigured") {
|
|
275
|
+
if (audioData.sampleRate === wantedSampleRate) {
|
|
276
|
+
encoder.configure(audioEncoderConfig);
|
|
277
|
+
} else {
|
|
278
|
+
encoder.configure({
|
|
279
|
+
...audioEncoderConfig,
|
|
280
|
+
sampleRate: audioData.sampleRate
|
|
281
|
+
});
|
|
282
|
+
onNewAudioSampleRate(audioData.sampleRate);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
273
285
|
encoder.encode(audioData);
|
|
274
286
|
ioSynchronizer.inputItem(audioData.timestamp, true);
|
|
275
287
|
};
|
|
@@ -290,9 +302,214 @@ var createAudioEncoder = ({
|
|
|
290
302
|
}
|
|
291
303
|
};
|
|
292
304
|
};
|
|
305
|
+
// src/can-copy-audio-track.ts
|
|
306
|
+
var canCopyAudioTrack = ({
|
|
307
|
+
inputCodec,
|
|
308
|
+
container
|
|
309
|
+
}) => {
|
|
310
|
+
if (container === "webm") {
|
|
311
|
+
return inputCodec === "opus";
|
|
312
|
+
}
|
|
313
|
+
if (container === "mp4") {
|
|
314
|
+
return inputCodec === "aac";
|
|
315
|
+
}
|
|
316
|
+
throw new Error(`Unhandled codec: ${container}`);
|
|
317
|
+
};
|
|
318
|
+
// src/can-copy-video-track.ts
|
|
319
|
+
var canCopyVideoTrack = ({
|
|
320
|
+
inputCodec,
|
|
321
|
+
container
|
|
322
|
+
}) => {
|
|
323
|
+
if (container === "webm") {
|
|
324
|
+
return inputCodec === "vp8" || inputCodec === "vp9";
|
|
325
|
+
}
|
|
326
|
+
if (container === "mp4") {
|
|
327
|
+
return inputCodec === "h264" || inputCodec === "h265";
|
|
328
|
+
}
|
|
329
|
+
throw new Error(`Unhandled codec: ${container}`);
|
|
330
|
+
};
|
|
331
|
+
// src/audio-decoder-config.ts
|
|
332
|
+
var getAudioDecoderConfig = async (config) => {
|
|
333
|
+
if (typeof AudioDecoder === "undefined") {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
if (typeof EncodedAudioChunk === "undefined") {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
if ((await AudioDecoder.isConfigSupported(config)).supported) {
|
|
340
|
+
return config;
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// src/audio-encoder-config.ts
|
|
346
|
+
var getCodecString = (audioCodec) => {
|
|
347
|
+
if (audioCodec === "opus") {
|
|
348
|
+
return "opus";
|
|
349
|
+
}
|
|
350
|
+
if (audioCodec === "aac") {
|
|
351
|
+
return "mp4a.40.02";
|
|
352
|
+
}
|
|
353
|
+
throw new Error(`Unsupported audio codec: ${audioCodec}`);
|
|
354
|
+
};
|
|
355
|
+
var getAudioEncoderConfig = async (config) => {
|
|
356
|
+
const actualConfig = {
|
|
357
|
+
...config,
|
|
358
|
+
codec: getCodecString(config.codec)
|
|
359
|
+
};
|
|
360
|
+
if (typeof AudioEncoder === "undefined") {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
if ((await AudioEncoder.isConfigSupported(actualConfig)).supported) {
|
|
364
|
+
return actualConfig;
|
|
365
|
+
}
|
|
366
|
+
return null;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// src/can-reencode-audio-track.ts
|
|
370
|
+
var canReencodeAudioTrack = async ({
|
|
371
|
+
track,
|
|
372
|
+
audioCodec,
|
|
373
|
+
bitrate
|
|
374
|
+
}) => {
|
|
375
|
+
const audioDecoderConfig = await getAudioDecoderConfig(track);
|
|
376
|
+
const audioEncoderConfig = await getAudioEncoderConfig({
|
|
377
|
+
codec: audioCodec,
|
|
378
|
+
numberOfChannels: track.numberOfChannels,
|
|
379
|
+
sampleRate: track.sampleRate,
|
|
380
|
+
bitrate
|
|
381
|
+
});
|
|
382
|
+
return Boolean(audioDecoderConfig && audioEncoderConfig);
|
|
383
|
+
};
|
|
384
|
+
// src/video-decoder-config.ts
|
|
385
|
+
var getVideoDecoderConfigWithHardwareAcceleration = async (config) => {
|
|
386
|
+
if (typeof VideoDecoder === "undefined") {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
const hardware = {
|
|
390
|
+
...config,
|
|
391
|
+
hardwareAcceleration: "prefer-hardware"
|
|
392
|
+
};
|
|
393
|
+
if ((await VideoDecoder.isConfigSupported(hardware)).supported) {
|
|
394
|
+
return hardware;
|
|
395
|
+
}
|
|
396
|
+
const software = {
|
|
397
|
+
...config,
|
|
398
|
+
hardwareAcceleration: "prefer-software"
|
|
399
|
+
};
|
|
400
|
+
if ((await VideoDecoder.isConfigSupported(software)).supported) {
|
|
401
|
+
return software;
|
|
402
|
+
}
|
|
403
|
+
return null;
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// src/choose-correct-avc1-profile.ts
|
|
407
|
+
var chooseCorrectAvc1Profile = ({
|
|
408
|
+
width,
|
|
409
|
+
height,
|
|
410
|
+
fps
|
|
411
|
+
}) => {
|
|
412
|
+
const profiles = [
|
|
413
|
+
{ level: "3.1", hex: "1F", width: 1280, height: 720, fps: 30 },
|
|
414
|
+
{ level: "3.2", hex: "20", width: 1280, height: 1024, fps: 42.2 },
|
|
415
|
+
{ level: "4.0", hex: "28", width: 2048, height: 1024, fps: 30 },
|
|
416
|
+
{ level: "4.1", hex: "29", width: 2048, height: 1024, fps: 30 },
|
|
417
|
+
{ level: "4.2", hex: "2A", width: 2048, height: 1080, fps: 60 },
|
|
418
|
+
{ level: "5.0", hex: "32", width: 3672, height: 1536, fps: 26.7 },
|
|
419
|
+
{ level: "5.1", hex: "33", width: 4096, height: 2304, fps: 26.7 },
|
|
420
|
+
{ level: "5.2", hex: "34", width: 4096, height: 2304, fps: 56.3 },
|
|
421
|
+
{ level: "6.0", hex: "3C", width: 8192, height: 4320, fps: 30.2 },
|
|
422
|
+
{ level: "6.1", hex: "3D", width: 8192, height: 4320, fps: 60.4 },
|
|
423
|
+
{ level: "6.2", hex: "3E", width: 8192, height: 4320, fps: 120.8 }
|
|
424
|
+
];
|
|
425
|
+
const profile = profiles.find((p) => {
|
|
426
|
+
if (width > p.width) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
if (height > p.height) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
const fallbackFps = fps ?? 60;
|
|
433
|
+
return fallbackFps <= p.fps;
|
|
434
|
+
});
|
|
435
|
+
if (!profile) {
|
|
436
|
+
throw new Error(`No suitable AVC1 profile found for ${width}x${height}@${fps}fps`);
|
|
437
|
+
}
|
|
438
|
+
return `avc1.6400${profile.hex}`;
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// src/video-encoder-config.ts
|
|
442
|
+
var getVideoEncoderConfig = async ({
|
|
443
|
+
codec,
|
|
444
|
+
height,
|
|
445
|
+
width,
|
|
446
|
+
fps
|
|
447
|
+
}) => {
|
|
448
|
+
if (typeof VideoEncoder === "undefined") {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
const config = {
|
|
452
|
+
codec: codec === "h264" ? chooseCorrectAvc1Profile({ fps, height, width }) : codec === "vp9" ? "vp09.00.10.08" : codec,
|
|
453
|
+
height,
|
|
454
|
+
width
|
|
455
|
+
};
|
|
456
|
+
const hardware = {
|
|
457
|
+
...config,
|
|
458
|
+
hardwareAcceleration: "prefer-hardware"
|
|
459
|
+
};
|
|
460
|
+
if ((await VideoEncoder.isConfigSupported(hardware)).supported) {
|
|
461
|
+
return hardware;
|
|
462
|
+
}
|
|
463
|
+
const software = {
|
|
464
|
+
...config,
|
|
465
|
+
hardwareAcceleration: "prefer-software"
|
|
466
|
+
};
|
|
467
|
+
if ((await VideoEncoder.isConfigSupported(software)).supported) {
|
|
468
|
+
return software;
|
|
469
|
+
}
|
|
470
|
+
return null;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/can-reencode-video-track.ts
|
|
474
|
+
var canReencodeVideoTrack = async ({
|
|
475
|
+
videoCodec,
|
|
476
|
+
track
|
|
477
|
+
}) => {
|
|
478
|
+
const videoEncoderConfig = await getVideoEncoderConfig({
|
|
479
|
+
codec: videoCodec,
|
|
480
|
+
height: track.displayAspectHeight,
|
|
481
|
+
width: track.displayAspectWidth,
|
|
482
|
+
fps: track.fps
|
|
483
|
+
});
|
|
484
|
+
const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
|
|
485
|
+
return Boolean(videoDecoderConfig && videoEncoderConfig);
|
|
486
|
+
};
|
|
487
|
+
// src/codec-id.ts
|
|
488
|
+
var availableContainers = ["webm", "mp4"];
|
|
489
|
+
var getAvailableContainers = () => {
|
|
490
|
+
return availableContainers;
|
|
491
|
+
};
|
|
492
|
+
var getAvailableVideoCodecs = (container) => {
|
|
493
|
+
if (container === "mp4") {
|
|
494
|
+
return ["h264"];
|
|
495
|
+
}
|
|
496
|
+
if (container === "webm") {
|
|
497
|
+
return ["vp8", "vp9"];
|
|
498
|
+
}
|
|
499
|
+
throw new Error(`Unsupported container: ${container}`);
|
|
500
|
+
};
|
|
501
|
+
var getAvailableAudioCodecs = (container) => {
|
|
502
|
+
if (container === "mp4") {
|
|
503
|
+
return ["aac"];
|
|
504
|
+
}
|
|
505
|
+
if (container === "webm") {
|
|
506
|
+
return ["opus"];
|
|
507
|
+
}
|
|
508
|
+
throw new Error(`Unsupported container: ${container}`);
|
|
509
|
+
};
|
|
293
510
|
// src/convert-media.ts
|
|
294
511
|
import {
|
|
295
|
-
MediaParserInternals as
|
|
512
|
+
MediaParserInternals as MediaParserInternals4,
|
|
296
513
|
parseMedia
|
|
297
514
|
} from "@remotion/media-parser";
|
|
298
515
|
|
|
@@ -316,122 +533,141 @@ var autoSelectWriter = async (writer, logLevel) => {
|
|
|
316
533
|
// src/calculate-progress.ts
|
|
317
534
|
var calculateProgress = ({
|
|
318
535
|
millisecondsWritten,
|
|
319
|
-
|
|
536
|
+
expectedOutputDurationInMs
|
|
320
537
|
}) => {
|
|
321
|
-
if (
|
|
538
|
+
if (expectedOutputDurationInMs === null) {
|
|
322
539
|
return null;
|
|
323
540
|
}
|
|
324
|
-
return millisecondsWritten /
|
|
541
|
+
return millisecondsWritten / expectedOutputDurationInMs;
|
|
325
542
|
};
|
|
326
543
|
|
|
327
544
|
// src/error-cause.ts
|
|
328
545
|
var error_cause_default = Error;
|
|
329
546
|
|
|
330
|
-
// src/
|
|
331
|
-
var
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
547
|
+
// src/convert-encoded-chunk.ts
|
|
548
|
+
var convertEncodedChunk = (chunk, trackId) => {
|
|
549
|
+
const arr = new Uint8Array(chunk.byteLength);
|
|
550
|
+
chunk.copyTo(arr);
|
|
551
|
+
return {
|
|
552
|
+
data: arr,
|
|
553
|
+
duration: chunk.duration ?? undefined,
|
|
554
|
+
timestamp: chunk.timestamp,
|
|
555
|
+
type: chunk.type,
|
|
556
|
+
cts: chunk.timestamp,
|
|
557
|
+
dts: chunk.timestamp,
|
|
558
|
+
trackId
|
|
559
|
+
};
|
|
339
560
|
};
|
|
340
561
|
|
|
341
|
-
// src/audio-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
562
|
+
// src/default-on-audio-track-handler.ts
|
|
563
|
+
import { MediaParserInternals as MediaParserInternals2 } from "@remotion/media-parser";
|
|
564
|
+
|
|
565
|
+
// src/get-default-audio-codec.ts
|
|
566
|
+
var getDefaultAudioCodec = ({
|
|
567
|
+
container
|
|
568
|
+
}) => {
|
|
569
|
+
if (container === "webm") {
|
|
570
|
+
return "opus";
|
|
345
571
|
}
|
|
346
|
-
if (
|
|
347
|
-
return
|
|
572
|
+
if (container === "mp4") {
|
|
573
|
+
return "aac";
|
|
348
574
|
}
|
|
349
|
-
|
|
575
|
+
throw new Error(`Unhandled container: ${container}`);
|
|
350
576
|
};
|
|
351
577
|
|
|
352
|
-
// src/
|
|
353
|
-
var
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
var defaultResolveAudioAction = ({
|
|
360
|
-
canReencode,
|
|
361
|
-
canCopy
|
|
578
|
+
// src/default-on-audio-track-handler.ts
|
|
579
|
+
var DEFAULT_BITRATE = 128000;
|
|
580
|
+
var defaultOnAudioTrackHandler = async ({
|
|
581
|
+
track,
|
|
582
|
+
defaultAudioCodec,
|
|
583
|
+
logLevel,
|
|
584
|
+
container
|
|
362
585
|
}) => {
|
|
586
|
+
const bitrate = DEFAULT_BITRATE;
|
|
587
|
+
const canCopy = canCopyAudioTrack({
|
|
588
|
+
inputCodec: track.codecWithoutConfig,
|
|
589
|
+
container
|
|
590
|
+
});
|
|
363
591
|
if (canCopy) {
|
|
364
|
-
|
|
592
|
+
MediaParserInternals2.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can copy track, therefore copying`);
|
|
593
|
+
return Promise.resolve({ type: "copy" });
|
|
365
594
|
}
|
|
595
|
+
const audioCodec = defaultAudioCodec ?? getDefaultAudioCodec({ container });
|
|
596
|
+
const canReencode = await canReencodeAudioTrack({
|
|
597
|
+
audioCodec,
|
|
598
|
+
track,
|
|
599
|
+
bitrate
|
|
600
|
+
});
|
|
366
601
|
if (canReencode) {
|
|
367
|
-
|
|
602
|
+
MediaParserInternals2.Log.verbose(logLevel, `Track ${track.trackId} (audio): Cannot copy, but re-encode, therefore re-encoding`);
|
|
603
|
+
return Promise.resolve({
|
|
604
|
+
type: "reencode",
|
|
605
|
+
bitrate,
|
|
606
|
+
audioCodec
|
|
607
|
+
});
|
|
368
608
|
}
|
|
369
|
-
|
|
370
|
-
};
|
|
371
|
-
var resolveAudioAction = async ({
|
|
372
|
-
audioDecoderConfig,
|
|
373
|
-
audioEncoderConfig,
|
|
374
|
-
track,
|
|
375
|
-
audioCodec,
|
|
376
|
-
resolverFunction
|
|
377
|
-
}) => {
|
|
378
|
-
const canReencode = Boolean(audioDecoderConfig && audioEncoderConfig);
|
|
379
|
-
const canCopy = canCopyAudioTrack(track.codecWithoutConfig, audioCodec);
|
|
380
|
-
const resolved = await resolverFunction({
|
|
381
|
-
canReencode,
|
|
382
|
-
canCopy
|
|
383
|
-
});
|
|
384
|
-
return resolved;
|
|
609
|
+
MediaParserInternals2.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can neither re-encode nor copy, failing render`);
|
|
610
|
+
return Promise.resolve({ type: "fail" });
|
|
385
611
|
};
|
|
386
612
|
|
|
387
613
|
// src/on-audio-track.ts
|
|
388
614
|
var makeAudioTrackHandler = ({
|
|
389
615
|
state,
|
|
390
|
-
audioCodec,
|
|
616
|
+
defaultAudioCodec: audioCodec,
|
|
391
617
|
convertMediaState,
|
|
392
618
|
controller,
|
|
393
619
|
abortConversion,
|
|
394
620
|
onMediaStateUpdate,
|
|
395
621
|
onAudioTrack,
|
|
396
|
-
|
|
397
|
-
|
|
622
|
+
logLevel,
|
|
623
|
+
container
|
|
398
624
|
}) => async (track) => {
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
numberOfChannels: track.numberOfChannels,
|
|
402
|
-
sampleRate: track.sampleRate,
|
|
403
|
-
bitrate
|
|
404
|
-
});
|
|
405
|
-
const audioDecoderConfig = await getAudioDecoderConfig({
|
|
406
|
-
codec: track.codec,
|
|
407
|
-
numberOfChannels: track.numberOfChannels,
|
|
408
|
-
sampleRate: track.sampleRate,
|
|
409
|
-
description: track.description
|
|
410
|
-
});
|
|
411
|
-
const audioOperation = await resolveAudioAction({
|
|
412
|
-
audioDecoderConfig,
|
|
413
|
-
audioEncoderConfig,
|
|
414
|
-
audioCodec,
|
|
625
|
+
const audioOperation = await (onAudioTrack ?? defaultOnAudioTrackHandler)({
|
|
626
|
+
defaultAudioCodec: audioCodec,
|
|
415
627
|
track,
|
|
416
|
-
|
|
628
|
+
logLevel,
|
|
629
|
+
container
|
|
417
630
|
});
|
|
418
|
-
if (audioOperation === "drop") {
|
|
631
|
+
if (audioOperation.type === "drop") {
|
|
419
632
|
return null;
|
|
420
633
|
}
|
|
421
|
-
if (audioOperation === "
|
|
634
|
+
if (audioOperation.type === "fail") {
|
|
635
|
+
throw new error_cause_default(`Audio track with ID ${track.trackId} could resolved with {"type": "fail"}. This could mean that this audio track could neither be copied to the output container or re-encoded. You have the option to drop the track instead of failing it: https://remotion.dev/docs/webcodecs/track-transformation`);
|
|
636
|
+
}
|
|
637
|
+
if (audioOperation.type === "copy") {
|
|
422
638
|
const addedTrack = await state.addTrack({
|
|
423
639
|
type: "audio",
|
|
424
|
-
codec:
|
|
640
|
+
codec: track.codecWithoutConfig,
|
|
425
641
|
numberOfChannels: track.numberOfChannels,
|
|
426
642
|
sampleRate: track.sampleRate,
|
|
427
|
-
codecPrivate: track.codecPrivate
|
|
643
|
+
codecPrivate: track.codecPrivate,
|
|
644
|
+
timescale: track.timescale
|
|
428
645
|
});
|
|
646
|
+
Log.verbose(logLevel, `Copying audio track ${track.trackId} as track ${addedTrack.trackNumber}. Timescale = ${track.timescale}, codec = ${track.codecWithoutConfig} (${track.codec}) `);
|
|
429
647
|
return async (audioSample) => {
|
|
430
|
-
await state.addSample(
|
|
648
|
+
await state.addSample({
|
|
649
|
+
chunk: audioSample,
|
|
650
|
+
trackNumber: addedTrack.trackNumber,
|
|
651
|
+
isVideo: false,
|
|
652
|
+
timescale: track.timescale,
|
|
653
|
+
codecPrivate: track.codecPrivate
|
|
654
|
+
});
|
|
431
655
|
convertMediaState.encodedAudioFrames++;
|
|
432
656
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
433
657
|
};
|
|
434
658
|
}
|
|
659
|
+
const audioEncoderConfig = await getAudioEncoderConfig({
|
|
660
|
+
numberOfChannels: track.numberOfChannels,
|
|
661
|
+
sampleRate: track.sampleRate,
|
|
662
|
+
codec: audioOperation.audioCodec,
|
|
663
|
+
bitrate: audioOperation.bitrate
|
|
664
|
+
});
|
|
665
|
+
const audioDecoderConfig = await getAudioDecoderConfig({
|
|
666
|
+
codec: track.codec,
|
|
667
|
+
numberOfChannels: track.numberOfChannels,
|
|
668
|
+
sampleRate: track.sampleRate,
|
|
669
|
+
description: track.description
|
|
670
|
+
});
|
|
435
671
|
if (!audioEncoderConfig) {
|
|
436
672
|
abortConversion(new error_cause_default(`Could not configure audio encoder of track ${track.trackId}`));
|
|
437
673
|
return null;
|
|
@@ -440,16 +676,27 @@ var makeAudioTrackHandler = ({
|
|
|
440
676
|
abortConversion(new error_cause_default(`Could not configure audio decoder of track ${track.trackId}`));
|
|
441
677
|
return null;
|
|
442
678
|
}
|
|
679
|
+
const codecPrivate = audioOperation.audioCodec === "aac" ? new Uint8Array([17, 144]) : null;
|
|
443
680
|
const { trackNumber } = await state.addTrack({
|
|
444
681
|
type: "audio",
|
|
445
|
-
codec: audioCodec,
|
|
682
|
+
codec: audioOperation.audioCodec,
|
|
446
683
|
numberOfChannels: track.numberOfChannels,
|
|
447
684
|
sampleRate: track.sampleRate,
|
|
448
|
-
codecPrivate
|
|
685
|
+
codecPrivate,
|
|
686
|
+
timescale: track.timescale
|
|
449
687
|
});
|
|
450
688
|
const audioEncoder = createAudioEncoder({
|
|
689
|
+
onNewAudioSampleRate: (sampleRate) => {
|
|
690
|
+
state.updateTrackSampleRate({ sampleRate, trackNumber });
|
|
691
|
+
},
|
|
451
692
|
onChunk: async (chunk) => {
|
|
452
|
-
await state.addSample(
|
|
693
|
+
await state.addSample({
|
|
694
|
+
chunk: convertEncodedChunk(chunk, trackNumber),
|
|
695
|
+
trackNumber,
|
|
696
|
+
isVideo: false,
|
|
697
|
+
timescale: track.timescale,
|
|
698
|
+
codecPrivate
|
|
699
|
+
});
|
|
453
700
|
convertMediaState.encodedAudioFrames++;
|
|
454
701
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
455
702
|
},
|
|
@@ -458,7 +705,7 @@ var makeAudioTrackHandler = ({
|
|
|
458
705
|
cause: err
|
|
459
706
|
}));
|
|
460
707
|
},
|
|
461
|
-
codec: audioCodec,
|
|
708
|
+
codec: audioOperation.audioCodec,
|
|
462
709
|
signal: controller.signal,
|
|
463
710
|
config: audioEncoderConfig,
|
|
464
711
|
logLevel
|
|
@@ -490,47 +737,92 @@ var makeAudioTrackHandler = ({
|
|
|
490
737
|
};
|
|
491
738
|
};
|
|
492
739
|
|
|
493
|
-
// src/
|
|
494
|
-
var
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
740
|
+
// src/arraybuffer-to-uint8-array.ts
|
|
741
|
+
var arrayBufferToUint8Array = (buffer) => {
|
|
742
|
+
return buffer ? new Uint8Array(buffer) : null;
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
// src/default-on-video-track-handler.ts
|
|
746
|
+
import { MediaParserInternals as MediaParserInternals3 } from "@remotion/media-parser";
|
|
747
|
+
|
|
748
|
+
// src/get-default-video-codec.ts
|
|
749
|
+
var getDefaultVideoCodec = ({
|
|
750
|
+
container
|
|
751
|
+
}) => {
|
|
752
|
+
if (container === "webm") {
|
|
753
|
+
return "vp8";
|
|
500
754
|
}
|
|
501
|
-
throw new Error(`Unhandled
|
|
755
|
+
throw new Error(`Unhandled container: ${container} satisfies never`);
|
|
502
756
|
};
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
757
|
+
|
|
758
|
+
// src/default-on-video-track-handler.ts
|
|
759
|
+
var defaultOnVideoTrackHandler = async ({
|
|
760
|
+
track,
|
|
761
|
+
defaultVideoCodec,
|
|
762
|
+
logLevel,
|
|
763
|
+
container
|
|
506
764
|
}) => {
|
|
765
|
+
const canCopy = canCopyVideoTrack({
|
|
766
|
+
inputCodec: track.codecWithoutConfig,
|
|
767
|
+
container
|
|
768
|
+
});
|
|
507
769
|
if (canCopy) {
|
|
508
|
-
|
|
770
|
+
MediaParserInternals3.Log.verbose(logLevel, `Track ${track.trackId} (video): Can copy, therefore copying`);
|
|
771
|
+
return Promise.resolve({ type: "copy" });
|
|
509
772
|
}
|
|
773
|
+
const videoCodec = defaultVideoCodec ?? getDefaultVideoCodec({ container });
|
|
774
|
+
const canReencode = await canReencodeVideoTrack({
|
|
775
|
+
videoCodec,
|
|
776
|
+
track
|
|
777
|
+
});
|
|
510
778
|
if (canReencode) {
|
|
511
|
-
|
|
779
|
+
MediaParserInternals3.Log.verbose(logLevel, `Track ${track.trackId} (video): Cannot copy, but re-enconde, therefore re-encoding`);
|
|
780
|
+
return Promise.resolve({ type: "reencode", videoCodec });
|
|
512
781
|
}
|
|
513
|
-
|
|
782
|
+
MediaParserInternals3.Log.verbose(logLevel, `Track ${track.trackId} (video): Can neither copy nor re-encode, therefore failing`);
|
|
783
|
+
return Promise.resolve({ type: "fail" });
|
|
514
784
|
};
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
785
|
+
|
|
786
|
+
// src/on-frame.ts
|
|
787
|
+
var onFrame = async ({
|
|
788
|
+
frame,
|
|
789
|
+
onVideoFrame,
|
|
790
|
+
videoEncoder,
|
|
791
|
+
onMediaStateUpdate,
|
|
518
792
|
track,
|
|
519
|
-
|
|
520
|
-
resolverFunction
|
|
793
|
+
convertMediaState
|
|
521
794
|
}) => {
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
795
|
+
const newFrame = onVideoFrame ? await onVideoFrame({ frame, track }) : frame;
|
|
796
|
+
if (newFrame.codedHeight !== frame.codedHeight) {
|
|
797
|
+
throw new Error(`Returned VideoFrame of track ${track.trackId} has different codedHeight (${newFrame.codedHeight}) than the input frame (${frame.codedHeight})`);
|
|
798
|
+
}
|
|
799
|
+
if (newFrame.codedWidth !== frame.codedWidth) {
|
|
800
|
+
throw new Error(`Returned VideoFrame of track ${track.trackId} has different codedWidth (${newFrame.codedWidth}) than the input frame (${frame.codedWidth})`);
|
|
801
|
+
}
|
|
802
|
+
if (newFrame.displayWidth !== frame.displayWidth) {
|
|
803
|
+
throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${newFrame.displayWidth}) than the input frame (${newFrame.displayHeight})`);
|
|
804
|
+
}
|
|
805
|
+
if (newFrame.displayHeight !== frame.displayHeight) {
|
|
806
|
+
throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${newFrame.displayHeight}) than the input frame (${newFrame.displayHeight})`);
|
|
807
|
+
}
|
|
808
|
+
if (newFrame.timestamp !== frame.timestamp) {
|
|
809
|
+
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`);
|
|
810
|
+
}
|
|
811
|
+
if (newFrame.duration !== frame.duration) {
|
|
812
|
+
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`);
|
|
813
|
+
}
|
|
814
|
+
await videoEncoder.encodeFrame(newFrame, newFrame.timestamp);
|
|
815
|
+
convertMediaState.decodedVideoFrames++;
|
|
816
|
+
onMediaStateUpdate?.({ ...convertMediaState });
|
|
817
|
+
newFrame.close();
|
|
818
|
+
if (frame !== newFrame) {
|
|
819
|
+
frame.close();
|
|
820
|
+
}
|
|
529
821
|
};
|
|
530
822
|
|
|
531
823
|
// src/video-decoder.ts
|
|
532
824
|
var createVideoDecoder = ({
|
|
533
|
-
onFrame,
|
|
825
|
+
onFrame: onFrame2,
|
|
534
826
|
onError,
|
|
535
827
|
signal,
|
|
536
828
|
config,
|
|
@@ -549,7 +841,7 @@ var createVideoDecoder = ({
|
|
|
549
841
|
if (signal.aborted) {
|
|
550
842
|
return;
|
|
551
843
|
}
|
|
552
|
-
return
|
|
844
|
+
return onFrame2(inputFrame);
|
|
553
845
|
}).then(() => {
|
|
554
846
|
ioSynchronizer.onProcessed();
|
|
555
847
|
signal.removeEventListener("abort", abortHandler);
|
|
@@ -597,9 +889,13 @@ var createVideoDecoder = ({
|
|
|
597
889
|
},
|
|
598
890
|
waitForFinish: async () => {
|
|
599
891
|
await videoDecoder.flush();
|
|
892
|
+
Log.verbose(logLevel, "Flushed video decoder");
|
|
600
893
|
await ioSynchronizer.waitForFinish();
|
|
894
|
+
Log.verbose(logLevel, "IO synchro finished");
|
|
601
895
|
await outputQueue;
|
|
896
|
+
Log.verbose(logLevel, "Output queue finished");
|
|
602
897
|
await inputQueue;
|
|
898
|
+
Log.verbose(logLevel, "Input queue finished");
|
|
603
899
|
},
|
|
604
900
|
close,
|
|
605
901
|
flush: async () => {
|
|
@@ -608,28 +904,6 @@ var createVideoDecoder = ({
|
|
|
608
904
|
};
|
|
609
905
|
};
|
|
610
906
|
|
|
611
|
-
// src/video-decoder-config.ts
|
|
612
|
-
var getVideoDecoderConfigWithHardwareAcceleration = async (config) => {
|
|
613
|
-
if (typeof VideoDecoder === "undefined") {
|
|
614
|
-
return null;
|
|
615
|
-
}
|
|
616
|
-
const hardware = {
|
|
617
|
-
...config,
|
|
618
|
-
hardwareAcceleration: "prefer-hardware"
|
|
619
|
-
};
|
|
620
|
-
if ((await VideoDecoder.isConfigSupported(hardware)).supported) {
|
|
621
|
-
return hardware;
|
|
622
|
-
}
|
|
623
|
-
const software = {
|
|
624
|
-
...config,
|
|
625
|
-
hardwareAcceleration: "prefer-software"
|
|
626
|
-
};
|
|
627
|
-
if ((await VideoDecoder.isConfigSupported(software)).supported) {
|
|
628
|
-
return software;
|
|
629
|
-
}
|
|
630
|
-
return null;
|
|
631
|
-
};
|
|
632
|
-
|
|
633
907
|
// src/video-encoder.ts
|
|
634
908
|
var createVideoEncoder = ({
|
|
635
909
|
onChunk,
|
|
@@ -647,17 +921,14 @@ var createVideoEncoder = ({
|
|
|
647
921
|
error(error) {
|
|
648
922
|
onError(error);
|
|
649
923
|
},
|
|
650
|
-
output(chunk) {
|
|
651
|
-
|
|
652
|
-
throw new Error("Duration is null");
|
|
653
|
-
}
|
|
654
|
-
const timestamp = chunk.timestamp + chunk.duration;
|
|
924
|
+
output(chunk, metadata) {
|
|
925
|
+
const timestamp = chunk.timestamp + (chunk.duration ?? 0);
|
|
655
926
|
ioSynchronizer.onOutput(timestamp);
|
|
656
927
|
outputQueue = outputQueue.then(() => {
|
|
657
928
|
if (signal.aborted) {
|
|
658
929
|
return;
|
|
659
930
|
}
|
|
660
|
-
return onChunk(chunk);
|
|
931
|
+
return onChunk(chunk, metadata ?? null);
|
|
661
932
|
}).then(() => {
|
|
662
933
|
ioSynchronizer.onProcessed();
|
|
663
934
|
return Promise.resolve();
|
|
@@ -715,28 +986,6 @@ var createVideoEncoder = ({
|
|
|
715
986
|
};
|
|
716
987
|
};
|
|
717
988
|
|
|
718
|
-
// src/video-encoder-config.ts
|
|
719
|
-
var getVideoEncoderConfig = async (config) => {
|
|
720
|
-
if (typeof VideoEncoder === "undefined") {
|
|
721
|
-
return null;
|
|
722
|
-
}
|
|
723
|
-
const hardware = {
|
|
724
|
-
...config,
|
|
725
|
-
hardwareAcceleration: "prefer-hardware"
|
|
726
|
-
};
|
|
727
|
-
if ((await VideoEncoder.isConfigSupported(hardware)).supported) {
|
|
728
|
-
return hardware;
|
|
729
|
-
}
|
|
730
|
-
const software = {
|
|
731
|
-
...config,
|
|
732
|
-
hardwareAcceleration: "prefer-software"
|
|
733
|
-
};
|
|
734
|
-
if ((await VideoEncoder.isConfigSupported(software)).supported) {
|
|
735
|
-
return software;
|
|
736
|
-
}
|
|
737
|
-
return null;
|
|
738
|
-
};
|
|
739
|
-
|
|
740
989
|
// src/on-video-track.ts
|
|
741
990
|
var makeVideoTrackHandler = ({
|
|
742
991
|
state,
|
|
@@ -745,44 +994,56 @@ var makeVideoTrackHandler = ({
|
|
|
745
994
|
abortConversion,
|
|
746
995
|
convertMediaState,
|
|
747
996
|
controller,
|
|
748
|
-
|
|
997
|
+
defaultVideoCodec,
|
|
749
998
|
onVideoTrack,
|
|
750
|
-
logLevel
|
|
999
|
+
logLevel,
|
|
1000
|
+
container
|
|
751
1001
|
}) => async (track) => {
|
|
752
1002
|
if (controller.signal.aborted) {
|
|
753
1003
|
throw new error_cause_default("Aborted");
|
|
754
1004
|
}
|
|
755
|
-
const
|
|
756
|
-
codec: videoCodec === "vp9" ? "vp09.00.10.08" : videoCodec,
|
|
757
|
-
height: track.displayAspectHeight,
|
|
758
|
-
width: track.displayAspectWidth
|
|
759
|
-
});
|
|
760
|
-
const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
|
|
761
|
-
const videoOperation = await resolveVideoAction({
|
|
762
|
-
videoDecoderConfig,
|
|
763
|
-
videoEncoderConfig,
|
|
1005
|
+
const videoOperation = await (onVideoTrack ?? defaultOnVideoTrackHandler)({
|
|
764
1006
|
track,
|
|
765
|
-
|
|
766
|
-
|
|
1007
|
+
defaultVideoCodec,
|
|
1008
|
+
logLevel,
|
|
1009
|
+
container
|
|
767
1010
|
});
|
|
768
|
-
if (videoOperation === "drop") {
|
|
1011
|
+
if (videoOperation.type === "drop") {
|
|
769
1012
|
return null;
|
|
770
1013
|
}
|
|
771
|
-
if (videoOperation === "
|
|
1014
|
+
if (videoOperation.type === "fail") {
|
|
1015
|
+
throw new error_cause_default(`Video track with ID ${track.trackId} could resolved with {"type": "fail"}. This could mean that this video track could neither be copied to the output container or re-encoded. You have the option to drop the track instead of failing it: https://remotion.dev/docs/webcodecs/track-transformation`);
|
|
1016
|
+
}
|
|
1017
|
+
if (videoOperation.type === "copy") {
|
|
1018
|
+
Log.verbose(logLevel, `Copying video track with codec ${track.codec} and timescale ${track.timescale}`);
|
|
772
1019
|
const videoTrack = await state.addTrack({
|
|
773
1020
|
type: "video",
|
|
774
1021
|
color: track.color,
|
|
775
1022
|
width: track.codedWidth,
|
|
776
1023
|
height: track.codedHeight,
|
|
777
1024
|
codec: track.codecWithoutConfig,
|
|
778
|
-
codecPrivate: track.codecPrivate
|
|
1025
|
+
codecPrivate: track.codecPrivate,
|
|
1026
|
+
timescale: track.timescale
|
|
779
1027
|
});
|
|
780
1028
|
return async (sample) => {
|
|
781
|
-
await state.addSample(
|
|
1029
|
+
await state.addSample({
|
|
1030
|
+
chunk: sample,
|
|
1031
|
+
trackNumber: videoTrack.trackNumber,
|
|
1032
|
+
isVideo: true,
|
|
1033
|
+
timescale: track.timescale,
|
|
1034
|
+
codecPrivate: track.codecPrivate
|
|
1035
|
+
});
|
|
782
1036
|
convertMediaState.decodedVideoFrames++;
|
|
783
1037
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
784
1038
|
};
|
|
785
1039
|
}
|
|
1040
|
+
const videoEncoderConfig = await getVideoEncoderConfig({
|
|
1041
|
+
codec: videoOperation.videoCodec,
|
|
1042
|
+
height: track.displayAspectHeight,
|
|
1043
|
+
width: track.displayAspectWidth,
|
|
1044
|
+
fps: track.fps
|
|
1045
|
+
});
|
|
1046
|
+
const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
|
|
786
1047
|
if (videoEncoderConfig === null) {
|
|
787
1048
|
abortConversion(new error_cause_default(`Could not configure video encoder of track ${track.trackId}`));
|
|
788
1049
|
return null;
|
|
@@ -796,12 +1057,20 @@ var makeVideoTrackHandler = ({
|
|
|
796
1057
|
color: track.color,
|
|
797
1058
|
width: track.codedWidth,
|
|
798
1059
|
height: track.codedHeight,
|
|
799
|
-
codec: videoCodec,
|
|
800
|
-
codecPrivate: null
|
|
1060
|
+
codec: videoOperation.videoCodec,
|
|
1061
|
+
codecPrivate: null,
|
|
1062
|
+
timescale: track.timescale
|
|
801
1063
|
});
|
|
1064
|
+
Log.verbose(logLevel, `Created new video track with ID ${trackNumber}, codec ${videoOperation.videoCodec} and timescale ${track.timescale}`);
|
|
802
1065
|
const videoEncoder = createVideoEncoder({
|
|
803
|
-
onChunk: async (chunk) => {
|
|
804
|
-
await state.addSample(
|
|
1066
|
+
onChunk: async (chunk, metadata) => {
|
|
1067
|
+
await state.addSample({
|
|
1068
|
+
chunk: convertEncodedChunk(chunk, trackNumber),
|
|
1069
|
+
trackNumber,
|
|
1070
|
+
isVideo: true,
|
|
1071
|
+
timescale: track.timescale,
|
|
1072
|
+
codecPrivate: arrayBufferToUint8Array(metadata?.decoderConfig?.description ?? null)
|
|
1073
|
+
});
|
|
805
1074
|
convertMediaState.encodedVideoFrames++;
|
|
806
1075
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
807
1076
|
},
|
|
@@ -817,11 +1086,14 @@ var makeVideoTrackHandler = ({
|
|
|
817
1086
|
const videoDecoder = createVideoDecoder({
|
|
818
1087
|
config: videoDecoderConfig,
|
|
819
1088
|
onFrame: async (frame) => {
|
|
820
|
-
await
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1089
|
+
await onFrame({
|
|
1090
|
+
convertMediaState,
|
|
1091
|
+
frame,
|
|
1092
|
+
onMediaStateUpdate,
|
|
1093
|
+
track,
|
|
1094
|
+
videoEncoder,
|
|
1095
|
+
onVideoFrame
|
|
1096
|
+
});
|
|
825
1097
|
},
|
|
826
1098
|
onError: (err) => {
|
|
827
1099
|
abortConversion(new error_cause_default(`Video decoder of track ${track.trackId} failed (see .cause of this error)`, {
|
|
@@ -832,10 +1104,13 @@ var makeVideoTrackHandler = ({
|
|
|
832
1104
|
logLevel
|
|
833
1105
|
});
|
|
834
1106
|
state.addWaitForFinishPromise(async () => {
|
|
1107
|
+
Log.verbose(logLevel, "Waiting for video decoder to finish");
|
|
835
1108
|
await videoDecoder.waitForFinish();
|
|
836
|
-
await videoEncoder.waitForFinish();
|
|
837
1109
|
videoDecoder.close();
|
|
1110
|
+
Log.verbose(logLevel, "Video decoder finished. Waiting for encoder to finish");
|
|
1111
|
+
await videoEncoder.waitForFinish();
|
|
838
1112
|
videoEncoder.close();
|
|
1113
|
+
Log.verbose(logLevel, "Encoder finished");
|
|
839
1114
|
});
|
|
840
1115
|
return async (chunk) => {
|
|
841
1116
|
await videoDecoder.processSample(chunk);
|
|
@@ -848,7 +1123,7 @@ var convertMedia = async function({
|
|
|
848
1123
|
onVideoFrame,
|
|
849
1124
|
onMediaStateUpdate: onMediaStateDoNoCallDirectly,
|
|
850
1125
|
audioCodec,
|
|
851
|
-
|
|
1126
|
+
container,
|
|
852
1127
|
videoCodec,
|
|
853
1128
|
signal: userPassedAbortSignal,
|
|
854
1129
|
onAudioTrack: userAudioResolver,
|
|
@@ -862,13 +1137,10 @@ var convertMedia = async function({
|
|
|
862
1137
|
if (userPassedAbortSignal?.aborted) {
|
|
863
1138
|
return Promise.reject(new error_cause_default("Aborted"));
|
|
864
1139
|
}
|
|
865
|
-
if (
|
|
866
|
-
return Promise.reject(new TypeError('Only `to: "webm"` is supported currently'));
|
|
1140
|
+
if (container !== "webm" && container !== "mp4") {
|
|
1141
|
+
return Promise.reject(new TypeError('Only `to: "webm"` and `to: "mp4"` is supported currently'));
|
|
867
1142
|
}
|
|
868
|
-
if (
|
|
869
|
-
return Promise.reject(new TypeError('Only `audioCodec: "opus"` is supported currently'));
|
|
870
|
-
}
|
|
871
|
-
if (videoCodec !== "vp8" && videoCodec !== "vp9") {
|
|
1143
|
+
if (videoCodec && videoCodec !== "vp8" && videoCodec !== "vp9") {
|
|
872
1144
|
return Promise.reject(new TypeError('Only `videoCodec: "vp8"` and `videoCodec: "vp9"` are supported currently'));
|
|
873
1145
|
}
|
|
874
1146
|
const { resolve, reject, getPromiseToImmediatelyReturn } = withResolversAndWaitForReturn();
|
|
@@ -890,7 +1162,7 @@ var convertMedia = async function({
|
|
|
890
1162
|
encodedAudioFrames: 0,
|
|
891
1163
|
bytesWritten: 0,
|
|
892
1164
|
millisecondsWritten: 0,
|
|
893
|
-
|
|
1165
|
+
expectedOutputDurationInMs: null,
|
|
894
1166
|
overallProgress: 0
|
|
895
1167
|
};
|
|
896
1168
|
const onMediaStateUpdate = (newState) => {
|
|
@@ -899,7 +1171,8 @@ var convertMedia = async function({
|
|
|
899
1171
|
}
|
|
900
1172
|
onMediaStateDoNoCallDirectly?.(newState);
|
|
901
1173
|
};
|
|
902
|
-
const
|
|
1174
|
+
const creator = container === "webm" ? MediaParserInternals4.createMatroskaMedia : MediaParserInternals4.createIsoBaseMedia;
|
|
1175
|
+
const state = await creator({
|
|
903
1176
|
writer: await autoSelectWriter(writer, logLevel),
|
|
904
1177
|
onBytesProgress: (bytesWritten) => {
|
|
905
1178
|
convertMediaState.bytesWritten = bytesWritten;
|
|
@@ -910,11 +1183,12 @@ var convertMedia = async function({
|
|
|
910
1183
|
convertMediaState.millisecondsWritten = millisecondsWritten;
|
|
911
1184
|
convertMediaState.overallProgress = calculateProgress({
|
|
912
1185
|
millisecondsWritten: convertMediaState.millisecondsWritten,
|
|
913
|
-
|
|
1186
|
+
expectedOutputDurationInMs: convertMediaState.expectedOutputDurationInMs
|
|
914
1187
|
});
|
|
915
1188
|
onMediaStateUpdate?.(convertMediaState);
|
|
916
1189
|
}
|
|
917
|
-
}
|
|
1190
|
+
},
|
|
1191
|
+
logLevel
|
|
918
1192
|
});
|
|
919
1193
|
const onVideoTrack = makeVideoTrackHandler({
|
|
920
1194
|
state,
|
|
@@ -923,22 +1197,24 @@ var convertMedia = async function({
|
|
|
923
1197
|
abortConversion,
|
|
924
1198
|
convertMediaState,
|
|
925
1199
|
controller,
|
|
926
|
-
videoCodec,
|
|
927
|
-
onVideoTrack: userVideoResolver ??
|
|
928
|
-
logLevel
|
|
1200
|
+
defaultVideoCodec: videoCodec ?? null,
|
|
1201
|
+
onVideoTrack: userVideoResolver ?? null,
|
|
1202
|
+
logLevel,
|
|
1203
|
+
container
|
|
929
1204
|
});
|
|
930
1205
|
const onAudioTrack = makeAudioTrackHandler({
|
|
931
1206
|
abortConversion,
|
|
932
|
-
audioCodec,
|
|
1207
|
+
defaultAudioCodec: audioCodec ?? null,
|
|
933
1208
|
controller,
|
|
934
1209
|
convertMediaState,
|
|
935
1210
|
onMediaStateUpdate: onMediaStateUpdate ?? null,
|
|
936
1211
|
state,
|
|
937
|
-
onAudioTrack: userAudioResolver ??
|
|
938
|
-
|
|
939
|
-
|
|
1212
|
+
onAudioTrack: userAudioResolver ?? null,
|
|
1213
|
+
logLevel,
|
|
1214
|
+
container
|
|
940
1215
|
});
|
|
941
1216
|
parseMedia({
|
|
1217
|
+
logLevel,
|
|
942
1218
|
src,
|
|
943
1219
|
onVideoTrack,
|
|
944
1220
|
onAudioTrack,
|
|
@@ -957,11 +1233,11 @@ var convertMedia = async function({
|
|
|
957
1233
|
if (casted.onDurationInSeconds) {
|
|
958
1234
|
casted.onDurationInSeconds(durationInSeconds);
|
|
959
1235
|
}
|
|
960
|
-
const
|
|
961
|
-
convertMediaState.
|
|
1236
|
+
const expectedOutputDurationInMs = durationInSeconds * 1000;
|
|
1237
|
+
convertMediaState.expectedOutputDurationInMs = expectedOutputDurationInMs;
|
|
962
1238
|
convertMediaState.overallProgress = calculateProgress({
|
|
963
1239
|
millisecondsWritten: convertMediaState.millisecondsWritten,
|
|
964
|
-
|
|
1240
|
+
expectedOutputDurationInMs
|
|
965
1241
|
});
|
|
966
1242
|
onMediaStateUpdate(convertMediaState);
|
|
967
1243
|
}
|
|
@@ -977,9 +1253,20 @@ var convertMedia = async function({
|
|
|
977
1253
|
});
|
|
978
1254
|
};
|
|
979
1255
|
export {
|
|
1256
|
+
getDefaultVideoCodec,
|
|
1257
|
+
getDefaultAudioCodec,
|
|
1258
|
+
getAvailableVideoCodecs,
|
|
1259
|
+
getAvailableContainers,
|
|
1260
|
+
getAvailableAudioCodecs,
|
|
1261
|
+
defaultOnVideoTrackHandler,
|
|
1262
|
+
defaultOnAudioTrackHandler,
|
|
980
1263
|
createVideoEncoder,
|
|
981
1264
|
createVideoDecoder,
|
|
982
1265
|
createAudioEncoder,
|
|
983
1266
|
createAudioDecoder,
|
|
984
|
-
convertMedia
|
|
1267
|
+
convertMedia,
|
|
1268
|
+
canReencodeVideoTrack,
|
|
1269
|
+
canReencodeAudioTrack,
|
|
1270
|
+
canCopyVideoTrack,
|
|
1271
|
+
canCopyAudioTrack
|
|
985
1272
|
};
|