@remotion/webcodecs 4.0.229 → 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.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/can-copy-audio-track.d.ts +2 -4
- package/dist/can-copy-audio-track.js +7 -4
- package/dist/can-copy-video-track.d.ts +2 -4
- package/dist/can-copy-video-track.js +6 -6
- package/dist/can-reencode-audio-track.js +1 -6
- package/dist/can-reencode-video-track.js +1 -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 +7 -4
- package/dist/codec-id.js +28 -5
- package/dist/convert-encoded-chunk.d.ts +2 -1
- package/dist/convert-encoded-chunk.js +25 -2
- package/dist/convert-media.d.ts +7 -8
- package/dist/convert-media.js +11 -9
- 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 +277 -106
- 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 +8 -4
- package/dist/index.js +10 -1
- package/dist/io-manager/io-synchronizer.js +2 -2
- package/dist/{resolve-audio-action.d.ts → on-audio-track-handler.d.ts} +5 -5
- package/dist/on-audio-track-handler.js +2 -0
- package/dist/on-audio-track.d.ts +6 -6
- package/dist/on-audio-track.js +37 -10
- package/dist/{resolve-video-action.d.ts → on-video-track-handler.d.ts} +5 -5
- package/dist/on-video-track-handler.js +2 -0
- package/dist/on-video-track.d.ts +6 -6
- package/dist/on-video-track.js +35 -9
- package/dist/video-decoder.d.ts +2 -2
- package/dist/video-decoder.js +5 -0
- package/dist/video-encoder-config.d.ts +2 -1
- package/dist/video-encoder-config.js +7 -2
- package/dist/video-encoder.d.ts +1 -1
- package/dist/video-encoder.js +4 -6
- package/package.json +3 -3
- package/dist/can-reencode-audio.d.ts +0 -7
- package/dist/can-reencode-audio.js +0 -21
- package/dist/can-reencode-video.d.ts +0 -6
- package/dist/can-reencode-video.js +0 -15
- package/dist/event-emitter.d.ts +0 -25
- package/dist/event-emitter.js +0 -23
- package/dist/polyfill-encoded-audio-chunk.d.ts +0 -3
- package/dist/polyfill-encoded-audio-chunk.js +0 -5
- package/dist/resolve-audio-action.js +0 -32
- package/dist/resolve-video-action.js +0 -26
- 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
|
};
|
|
@@ -293,27 +305,28 @@ var createAudioEncoder = ({
|
|
|
293
305
|
// src/can-copy-audio-track.ts
|
|
294
306
|
var canCopyAudioTrack = ({
|
|
295
307
|
inputCodec,
|
|
296
|
-
outputCodec,
|
|
297
308
|
container
|
|
298
309
|
}) => {
|
|
299
|
-
if (
|
|
300
|
-
return inputCodec === "opus"
|
|
310
|
+
if (container === "webm") {
|
|
311
|
+
return inputCodec === "opus";
|
|
312
|
+
}
|
|
313
|
+
if (container === "mp4") {
|
|
314
|
+
return inputCodec === "aac";
|
|
301
315
|
}
|
|
302
|
-
throw new Error(`Unhandled codec: ${
|
|
316
|
+
throw new Error(`Unhandled codec: ${container}`);
|
|
303
317
|
};
|
|
304
318
|
// src/can-copy-video-track.ts
|
|
305
319
|
var canCopyVideoTrack = ({
|
|
306
320
|
inputCodec,
|
|
307
|
-
outputCodec,
|
|
308
321
|
container
|
|
309
322
|
}) => {
|
|
310
|
-
if (
|
|
311
|
-
return inputCodec === "vp8"
|
|
323
|
+
if (container === "webm") {
|
|
324
|
+
return inputCodec === "vp8" || inputCodec === "vp9";
|
|
312
325
|
}
|
|
313
|
-
if (
|
|
314
|
-
return inputCodec === "
|
|
326
|
+
if (container === "mp4") {
|
|
327
|
+
return inputCodec === "h264" || inputCodec === "h265";
|
|
315
328
|
}
|
|
316
|
-
throw new Error(`Unhandled codec: ${
|
|
329
|
+
throw new Error(`Unhandled codec: ${container}`);
|
|
317
330
|
};
|
|
318
331
|
// src/audio-decoder-config.ts
|
|
319
332
|
var getAudioDecoderConfig = async (config) => {
|
|
@@ -330,12 +343,25 @@ var getAudioDecoderConfig = async (config) => {
|
|
|
330
343
|
};
|
|
331
344
|
|
|
332
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
|
+
};
|
|
333
355
|
var getAudioEncoderConfig = async (config) => {
|
|
356
|
+
const actualConfig = {
|
|
357
|
+
...config,
|
|
358
|
+
codec: getCodecString(config.codec)
|
|
359
|
+
};
|
|
334
360
|
if (typeof AudioEncoder === "undefined") {
|
|
335
361
|
return null;
|
|
336
362
|
}
|
|
337
|
-
if ((await AudioEncoder.isConfigSupported(
|
|
338
|
-
return
|
|
363
|
+
if ((await AudioEncoder.isConfigSupported(actualConfig)).supported) {
|
|
364
|
+
return actualConfig;
|
|
339
365
|
}
|
|
340
366
|
return null;
|
|
341
367
|
};
|
|
@@ -346,12 +372,7 @@ var canReencodeAudioTrack = async ({
|
|
|
346
372
|
audioCodec,
|
|
347
373
|
bitrate
|
|
348
374
|
}) => {
|
|
349
|
-
const audioDecoderConfig = await getAudioDecoderConfig(
|
|
350
|
-
codec: track.codec,
|
|
351
|
-
numberOfChannels: track.numberOfChannels,
|
|
352
|
-
sampleRate: track.sampleRate,
|
|
353
|
-
description: track.description
|
|
354
|
-
});
|
|
375
|
+
const audioDecoderConfig = await getAudioDecoderConfig(track);
|
|
355
376
|
const audioEncoderConfig = await getAudioEncoderConfig({
|
|
356
377
|
codec: audioCodec,
|
|
357
378
|
numberOfChannels: track.numberOfChannels,
|
|
@@ -382,17 +403,53 @@ var getVideoDecoderConfigWithHardwareAcceleration = async (config) => {
|
|
|
382
403
|
return null;
|
|
383
404
|
};
|
|
384
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
|
+
|
|
385
441
|
// src/video-encoder-config.ts
|
|
386
442
|
var getVideoEncoderConfig = async ({
|
|
387
|
-
|
|
443
|
+
codec,
|
|
388
444
|
height,
|
|
389
|
-
|
|
445
|
+
width,
|
|
446
|
+
fps
|
|
390
447
|
}) => {
|
|
391
448
|
if (typeof VideoEncoder === "undefined") {
|
|
392
449
|
return null;
|
|
393
450
|
}
|
|
394
451
|
const config = {
|
|
395
|
-
codec: codec === "vp9" ? "vp09.00.10.08" : codec,
|
|
452
|
+
codec: codec === "h264" ? chooseCorrectAvc1Profile({ fps, height, width }) : codec === "vp9" ? "vp09.00.10.08" : codec,
|
|
396
453
|
height,
|
|
397
454
|
width
|
|
398
455
|
};
|
|
@@ -421,19 +478,38 @@ var canReencodeVideoTrack = async ({
|
|
|
421
478
|
const videoEncoderConfig = await getVideoEncoderConfig({
|
|
422
479
|
codec: videoCodec,
|
|
423
480
|
height: track.displayAspectHeight,
|
|
424
|
-
width: track.displayAspectWidth
|
|
481
|
+
width: track.displayAspectWidth,
|
|
482
|
+
fps: track.fps
|
|
425
483
|
});
|
|
426
484
|
const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
|
|
427
485
|
return Boolean(videoDecoderConfig && videoEncoderConfig);
|
|
428
486
|
};
|
|
429
487
|
// src/codec-id.ts
|
|
430
|
-
var
|
|
431
|
-
var
|
|
432
|
-
|
|
433
|
-
|
|
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
|
+
};
|
|
434
510
|
// src/convert-media.ts
|
|
435
511
|
import {
|
|
436
|
-
MediaParserInternals as
|
|
512
|
+
MediaParserInternals as MediaParserInternals4,
|
|
437
513
|
parseMedia
|
|
438
514
|
} from "@remotion/media-parser";
|
|
439
515
|
|
|
@@ -469,52 +545,75 @@ var calculateProgress = ({
|
|
|
469
545
|
var error_cause_default = Error;
|
|
470
546
|
|
|
471
547
|
// src/convert-encoded-chunk.ts
|
|
472
|
-
var convertEncodedChunk = (chunk) => {
|
|
548
|
+
var convertEncodedChunk = (chunk, trackId) => {
|
|
473
549
|
const arr = new Uint8Array(chunk.byteLength);
|
|
474
550
|
chunk.copyTo(arr);
|
|
475
551
|
return {
|
|
476
552
|
data: arr,
|
|
477
553
|
duration: chunk.duration ?? undefined,
|
|
478
554
|
timestamp: chunk.timestamp,
|
|
479
|
-
type: chunk.type
|
|
555
|
+
type: chunk.type,
|
|
556
|
+
cts: chunk.timestamp,
|
|
557
|
+
dts: chunk.timestamp,
|
|
558
|
+
trackId
|
|
480
559
|
};
|
|
481
560
|
};
|
|
482
561
|
|
|
483
|
-
// src/
|
|
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";
|
|
571
|
+
}
|
|
572
|
+
if (container === "mp4") {
|
|
573
|
+
return "aac";
|
|
574
|
+
}
|
|
575
|
+
throw new Error(`Unhandled container: ${container}`);
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// src/default-on-audio-track-handler.ts
|
|
484
579
|
var DEFAULT_BITRATE = 128000;
|
|
485
|
-
var
|
|
580
|
+
var defaultOnAudioTrackHandler = async ({
|
|
486
581
|
track,
|
|
487
|
-
|
|
582
|
+
defaultAudioCodec,
|
|
488
583
|
logLevel,
|
|
489
584
|
container
|
|
490
585
|
}) => {
|
|
491
586
|
const bitrate = DEFAULT_BITRATE;
|
|
492
587
|
const canCopy = canCopyAudioTrack({
|
|
493
588
|
inputCodec: track.codecWithoutConfig,
|
|
494
|
-
outputCodec: audioCodec,
|
|
495
589
|
container
|
|
496
590
|
});
|
|
497
591
|
if (canCopy) {
|
|
498
|
-
Log.verbose(logLevel, `Track ${track.trackId} (audio): Can copy
|
|
592
|
+
MediaParserInternals2.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can copy track, therefore copying`);
|
|
499
593
|
return Promise.resolve({ type: "copy" });
|
|
500
594
|
}
|
|
595
|
+
const audioCodec = defaultAudioCodec ?? getDefaultAudioCodec({ container });
|
|
501
596
|
const canReencode = await canReencodeAudioTrack({
|
|
502
597
|
audioCodec,
|
|
503
598
|
track,
|
|
504
599
|
bitrate
|
|
505
600
|
});
|
|
506
601
|
if (canReencode) {
|
|
507
|
-
Log.verbose(logLevel, `Track ${track.trackId} (audio):
|
|
508
|
-
return Promise.resolve({
|
|
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
|
+
});
|
|
509
608
|
}
|
|
510
|
-
Log.verbose(logLevel, `Track ${track.trackId} (audio): Can re-encode
|
|
511
|
-
return Promise.resolve({ type: "
|
|
609
|
+
MediaParserInternals2.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can neither re-encode nor copy, failing render`);
|
|
610
|
+
return Promise.resolve({ type: "fail" });
|
|
512
611
|
};
|
|
513
612
|
|
|
514
613
|
// src/on-audio-track.ts
|
|
515
614
|
var makeAudioTrackHandler = ({
|
|
516
615
|
state,
|
|
517
|
-
audioCodec,
|
|
616
|
+
defaultAudioCodec: audioCodec,
|
|
518
617
|
convertMediaState,
|
|
519
618
|
controller,
|
|
520
619
|
abortConversion,
|
|
@@ -523,8 +622,8 @@ var makeAudioTrackHandler = ({
|
|
|
523
622
|
logLevel,
|
|
524
623
|
container
|
|
525
624
|
}) => async (track) => {
|
|
526
|
-
const audioOperation = await (onAudioTrack ??
|
|
527
|
-
audioCodec,
|
|
625
|
+
const audioOperation = await (onAudioTrack ?? defaultOnAudioTrackHandler)({
|
|
626
|
+
defaultAudioCodec: audioCodec,
|
|
528
627
|
track,
|
|
529
628
|
logLevel,
|
|
530
629
|
container
|
|
@@ -532,16 +631,27 @@ var makeAudioTrackHandler = ({
|
|
|
532
631
|
if (audioOperation.type === "drop") {
|
|
533
632
|
return null;
|
|
534
633
|
}
|
|
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
|
+
}
|
|
535
637
|
if (audioOperation.type === "copy") {
|
|
536
638
|
const addedTrack = await state.addTrack({
|
|
537
639
|
type: "audio",
|
|
538
|
-
codec:
|
|
640
|
+
codec: track.codecWithoutConfig,
|
|
539
641
|
numberOfChannels: track.numberOfChannels,
|
|
540
642
|
sampleRate: track.sampleRate,
|
|
541
|
-
codecPrivate: track.codecPrivate
|
|
643
|
+
codecPrivate: track.codecPrivate,
|
|
644
|
+
timescale: track.timescale
|
|
542
645
|
});
|
|
646
|
+
Log.verbose(logLevel, `Copying audio track ${track.trackId} as track ${addedTrack.trackNumber}. Timescale = ${track.timescale}, codec = ${track.codecWithoutConfig} (${track.codec}) `);
|
|
543
647
|
return async (audioSample) => {
|
|
544
|
-
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
|
+
});
|
|
545
655
|
convertMediaState.encodedAudioFrames++;
|
|
546
656
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
547
657
|
};
|
|
@@ -566,16 +676,27 @@ var makeAudioTrackHandler = ({
|
|
|
566
676
|
abortConversion(new error_cause_default(`Could not configure audio decoder of track ${track.trackId}`));
|
|
567
677
|
return null;
|
|
568
678
|
}
|
|
679
|
+
const codecPrivate = audioOperation.audioCodec === "aac" ? new Uint8Array([17, 144]) : null;
|
|
569
680
|
const { trackNumber } = await state.addTrack({
|
|
570
681
|
type: "audio",
|
|
571
|
-
codec: audioCodec,
|
|
682
|
+
codec: audioOperation.audioCodec,
|
|
572
683
|
numberOfChannels: track.numberOfChannels,
|
|
573
684
|
sampleRate: track.sampleRate,
|
|
574
|
-
codecPrivate
|
|
685
|
+
codecPrivate,
|
|
686
|
+
timescale: track.timescale
|
|
575
687
|
});
|
|
576
688
|
const audioEncoder = createAudioEncoder({
|
|
689
|
+
onNewAudioSampleRate: (sampleRate) => {
|
|
690
|
+
state.updateTrackSampleRate({ sampleRate, trackNumber });
|
|
691
|
+
},
|
|
577
692
|
onChunk: async (chunk) => {
|
|
578
|
-
await state.addSample(
|
|
693
|
+
await state.addSample({
|
|
694
|
+
chunk: convertEncodedChunk(chunk, trackNumber),
|
|
695
|
+
trackNumber,
|
|
696
|
+
isVideo: false,
|
|
697
|
+
timescale: track.timescale,
|
|
698
|
+
codecPrivate
|
|
699
|
+
});
|
|
579
700
|
convertMediaState.encodedAudioFrames++;
|
|
580
701
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
581
702
|
},
|
|
@@ -584,7 +705,7 @@ var makeAudioTrackHandler = ({
|
|
|
584
705
|
cause: err
|
|
585
706
|
}));
|
|
586
707
|
},
|
|
587
|
-
codec: audioCodec,
|
|
708
|
+
codec: audioOperation.audioCodec,
|
|
588
709
|
signal: controller.signal,
|
|
589
710
|
config: audioEncoderConfig,
|
|
590
711
|
logLevel
|
|
@@ -616,6 +737,52 @@ var makeAudioTrackHandler = ({
|
|
|
616
737
|
};
|
|
617
738
|
};
|
|
618
739
|
|
|
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";
|
|
754
|
+
}
|
|
755
|
+
throw new Error(`Unhandled container: ${container} satisfies never`);
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
// src/default-on-video-track-handler.ts
|
|
759
|
+
var defaultOnVideoTrackHandler = async ({
|
|
760
|
+
track,
|
|
761
|
+
defaultVideoCodec,
|
|
762
|
+
logLevel,
|
|
763
|
+
container
|
|
764
|
+
}) => {
|
|
765
|
+
const canCopy = canCopyVideoTrack({
|
|
766
|
+
inputCodec: track.codecWithoutConfig,
|
|
767
|
+
container
|
|
768
|
+
});
|
|
769
|
+
if (canCopy) {
|
|
770
|
+
MediaParserInternals3.Log.verbose(logLevel, `Track ${track.trackId} (video): Can copy, therefore copying`);
|
|
771
|
+
return Promise.resolve({ type: "copy" });
|
|
772
|
+
}
|
|
773
|
+
const videoCodec = defaultVideoCodec ?? getDefaultVideoCodec({ container });
|
|
774
|
+
const canReencode = await canReencodeVideoTrack({
|
|
775
|
+
videoCodec,
|
|
776
|
+
track
|
|
777
|
+
});
|
|
778
|
+
if (canReencode) {
|
|
779
|
+
MediaParserInternals3.Log.verbose(logLevel, `Track ${track.trackId} (video): Cannot copy, but re-enconde, therefore re-encoding`);
|
|
780
|
+
return Promise.resolve({ type: "reencode", videoCodec });
|
|
781
|
+
}
|
|
782
|
+
MediaParserInternals3.Log.verbose(logLevel, `Track ${track.trackId} (video): Can neither copy nor re-encode, therefore failing`);
|
|
783
|
+
return Promise.resolve({ type: "fail" });
|
|
784
|
+
};
|
|
785
|
+
|
|
619
786
|
// src/on-frame.ts
|
|
620
787
|
var onFrame = async ({
|
|
621
788
|
frame,
|
|
@@ -653,31 +820,6 @@ var onFrame = async ({
|
|
|
653
820
|
}
|
|
654
821
|
};
|
|
655
822
|
|
|
656
|
-
// src/resolve-video-action.ts
|
|
657
|
-
var defaultResolveVideoAction = async ({
|
|
658
|
-
track,
|
|
659
|
-
videoCodec,
|
|
660
|
-
logLevel,
|
|
661
|
-
container
|
|
662
|
-
}) => {
|
|
663
|
-
const canCopy = canCopyVideoTrack({
|
|
664
|
-
inputCodec: track.codecWithoutConfig,
|
|
665
|
-
outputCodec: videoCodec,
|
|
666
|
-
container
|
|
667
|
-
});
|
|
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" });
|
|
679
|
-
};
|
|
680
|
-
|
|
681
823
|
// src/video-decoder.ts
|
|
682
824
|
var createVideoDecoder = ({
|
|
683
825
|
onFrame: onFrame2,
|
|
@@ -747,9 +889,13 @@ var createVideoDecoder = ({
|
|
|
747
889
|
},
|
|
748
890
|
waitForFinish: async () => {
|
|
749
891
|
await videoDecoder.flush();
|
|
892
|
+
Log.verbose(logLevel, "Flushed video decoder");
|
|
750
893
|
await ioSynchronizer.waitForFinish();
|
|
894
|
+
Log.verbose(logLevel, "IO synchro finished");
|
|
751
895
|
await outputQueue;
|
|
896
|
+
Log.verbose(logLevel, "Output queue finished");
|
|
752
897
|
await inputQueue;
|
|
898
|
+
Log.verbose(logLevel, "Input queue finished");
|
|
753
899
|
},
|
|
754
900
|
close,
|
|
755
901
|
flush: async () => {
|
|
@@ -775,17 +921,14 @@ var createVideoEncoder = ({
|
|
|
775
921
|
error(error) {
|
|
776
922
|
onError(error);
|
|
777
923
|
},
|
|
778
|
-
output(chunk) {
|
|
779
|
-
|
|
780
|
-
throw new Error("Duration is null");
|
|
781
|
-
}
|
|
782
|
-
const timestamp = chunk.timestamp + chunk.duration;
|
|
924
|
+
output(chunk, metadata) {
|
|
925
|
+
const timestamp = chunk.timestamp + (chunk.duration ?? 0);
|
|
783
926
|
ioSynchronizer.onOutput(timestamp);
|
|
784
927
|
outputQueue = outputQueue.then(() => {
|
|
785
928
|
if (signal.aborted) {
|
|
786
929
|
return;
|
|
787
930
|
}
|
|
788
|
-
return onChunk(chunk);
|
|
931
|
+
return onChunk(chunk, metadata ?? null);
|
|
789
932
|
}).then(() => {
|
|
790
933
|
ioSynchronizer.onProcessed();
|
|
791
934
|
return Promise.resolve();
|
|
@@ -851,7 +994,7 @@ var makeVideoTrackHandler = ({
|
|
|
851
994
|
abortConversion,
|
|
852
995
|
convertMediaState,
|
|
853
996
|
controller,
|
|
854
|
-
|
|
997
|
+
defaultVideoCodec,
|
|
855
998
|
onVideoTrack,
|
|
856
999
|
logLevel,
|
|
857
1000
|
container
|
|
@@ -859,26 +1002,37 @@ var makeVideoTrackHandler = ({
|
|
|
859
1002
|
if (controller.signal.aborted) {
|
|
860
1003
|
throw new error_cause_default("Aborted");
|
|
861
1004
|
}
|
|
862
|
-
const videoOperation = await (onVideoTrack ??
|
|
1005
|
+
const videoOperation = await (onVideoTrack ?? defaultOnVideoTrackHandler)({
|
|
863
1006
|
track,
|
|
864
|
-
|
|
1007
|
+
defaultVideoCodec,
|
|
865
1008
|
logLevel,
|
|
866
1009
|
container
|
|
867
1010
|
});
|
|
868
1011
|
if (videoOperation.type === "drop") {
|
|
869
1012
|
return null;
|
|
870
1013
|
}
|
|
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
|
+
}
|
|
871
1017
|
if (videoOperation.type === "copy") {
|
|
1018
|
+
Log.verbose(logLevel, `Copying video track with codec ${track.codec} and timescale ${track.timescale}`);
|
|
872
1019
|
const videoTrack = await state.addTrack({
|
|
873
1020
|
type: "video",
|
|
874
1021
|
color: track.color,
|
|
875
1022
|
width: track.codedWidth,
|
|
876
1023
|
height: track.codedHeight,
|
|
877
1024
|
codec: track.codecWithoutConfig,
|
|
878
|
-
codecPrivate: track.codecPrivate
|
|
1025
|
+
codecPrivate: track.codecPrivate,
|
|
1026
|
+
timescale: track.timescale
|
|
879
1027
|
});
|
|
880
1028
|
return async (sample) => {
|
|
881
|
-
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
|
+
});
|
|
882
1036
|
convertMediaState.decodedVideoFrames++;
|
|
883
1037
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
884
1038
|
};
|
|
@@ -886,7 +1040,8 @@ var makeVideoTrackHandler = ({
|
|
|
886
1040
|
const videoEncoderConfig = await getVideoEncoderConfig({
|
|
887
1041
|
codec: videoOperation.videoCodec,
|
|
888
1042
|
height: track.displayAspectHeight,
|
|
889
|
-
width: track.displayAspectWidth
|
|
1043
|
+
width: track.displayAspectWidth,
|
|
1044
|
+
fps: track.fps
|
|
890
1045
|
});
|
|
891
1046
|
const videoDecoderConfig = await getVideoDecoderConfigWithHardwareAcceleration(track);
|
|
892
1047
|
if (videoEncoderConfig === null) {
|
|
@@ -902,12 +1057,20 @@ var makeVideoTrackHandler = ({
|
|
|
902
1057
|
color: track.color,
|
|
903
1058
|
width: track.codedWidth,
|
|
904
1059
|
height: track.codedHeight,
|
|
905
|
-
codec: videoCodec,
|
|
906
|
-
codecPrivate: null
|
|
1060
|
+
codec: videoOperation.videoCodec,
|
|
1061
|
+
codecPrivate: null,
|
|
1062
|
+
timescale: track.timescale
|
|
907
1063
|
});
|
|
1064
|
+
Log.verbose(logLevel, `Created new video track with ID ${trackNumber}, codec ${videoOperation.videoCodec} and timescale ${track.timescale}`);
|
|
908
1065
|
const videoEncoder = createVideoEncoder({
|
|
909
|
-
onChunk: async (chunk) => {
|
|
910
|
-
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
|
+
});
|
|
911
1074
|
convertMediaState.encodedVideoFrames++;
|
|
912
1075
|
onMediaStateUpdate?.({ ...convertMediaState });
|
|
913
1076
|
},
|
|
@@ -941,10 +1104,13 @@ var makeVideoTrackHandler = ({
|
|
|
941
1104
|
logLevel
|
|
942
1105
|
});
|
|
943
1106
|
state.addWaitForFinishPromise(async () => {
|
|
1107
|
+
Log.verbose(logLevel, "Waiting for video decoder to finish");
|
|
944
1108
|
await videoDecoder.waitForFinish();
|
|
945
|
-
await videoEncoder.waitForFinish();
|
|
946
1109
|
videoDecoder.close();
|
|
1110
|
+
Log.verbose(logLevel, "Video decoder finished. Waiting for encoder to finish");
|
|
1111
|
+
await videoEncoder.waitForFinish();
|
|
947
1112
|
videoEncoder.close();
|
|
1113
|
+
Log.verbose(logLevel, "Encoder finished");
|
|
948
1114
|
});
|
|
949
1115
|
return async (chunk) => {
|
|
950
1116
|
await videoDecoder.processSample(chunk);
|
|
@@ -971,13 +1137,10 @@ var convertMedia = async function({
|
|
|
971
1137
|
if (userPassedAbortSignal?.aborted) {
|
|
972
1138
|
return Promise.reject(new error_cause_default("Aborted"));
|
|
973
1139
|
}
|
|
974
|
-
if (container !== "webm") {
|
|
975
|
-
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'));
|
|
976
1142
|
}
|
|
977
|
-
if (
|
|
978
|
-
return Promise.reject(new TypeError('Only `audioCodec: "opus"` is supported currently'));
|
|
979
|
-
}
|
|
980
|
-
if (videoCodec !== "vp8" && videoCodec !== "vp9") {
|
|
1143
|
+
if (videoCodec && videoCodec !== "vp8" && videoCodec !== "vp9") {
|
|
981
1144
|
return Promise.reject(new TypeError('Only `videoCodec: "vp8"` and `videoCodec: "vp9"` are supported currently'));
|
|
982
1145
|
}
|
|
983
1146
|
const { resolve, reject, getPromiseToImmediatelyReturn } = withResolversAndWaitForReturn();
|
|
@@ -1008,7 +1171,8 @@ var convertMedia = async function({
|
|
|
1008
1171
|
}
|
|
1009
1172
|
onMediaStateDoNoCallDirectly?.(newState);
|
|
1010
1173
|
};
|
|
1011
|
-
const
|
|
1174
|
+
const creator = container === "webm" ? MediaParserInternals4.createMatroskaMedia : MediaParserInternals4.createIsoBaseMedia;
|
|
1175
|
+
const state = await creator({
|
|
1012
1176
|
writer: await autoSelectWriter(writer, logLevel),
|
|
1013
1177
|
onBytesProgress: (bytesWritten) => {
|
|
1014
1178
|
convertMediaState.bytesWritten = bytesWritten;
|
|
@@ -1023,7 +1187,8 @@ var convertMedia = async function({
|
|
|
1023
1187
|
});
|
|
1024
1188
|
onMediaStateUpdate?.(convertMediaState);
|
|
1025
1189
|
}
|
|
1026
|
-
}
|
|
1190
|
+
},
|
|
1191
|
+
logLevel
|
|
1027
1192
|
});
|
|
1028
1193
|
const onVideoTrack = makeVideoTrackHandler({
|
|
1029
1194
|
state,
|
|
@@ -1032,14 +1197,14 @@ var convertMedia = async function({
|
|
|
1032
1197
|
abortConversion,
|
|
1033
1198
|
convertMediaState,
|
|
1034
1199
|
controller,
|
|
1035
|
-
videoCodec,
|
|
1200
|
+
defaultVideoCodec: videoCodec ?? null,
|
|
1036
1201
|
onVideoTrack: userVideoResolver ?? null,
|
|
1037
1202
|
logLevel,
|
|
1038
1203
|
container
|
|
1039
1204
|
});
|
|
1040
1205
|
const onAudioTrack = makeAudioTrackHandler({
|
|
1041
1206
|
abortConversion,
|
|
1042
|
-
audioCodec,
|
|
1207
|
+
defaultAudioCodec: audioCodec ?? null,
|
|
1043
1208
|
controller,
|
|
1044
1209
|
convertMediaState,
|
|
1045
1210
|
onMediaStateUpdate: onMediaStateUpdate ?? null,
|
|
@@ -1049,6 +1214,7 @@ var convertMedia = async function({
|
|
|
1049
1214
|
container
|
|
1050
1215
|
});
|
|
1051
1216
|
parseMedia({
|
|
1217
|
+
logLevel,
|
|
1052
1218
|
src,
|
|
1053
1219
|
onVideoTrack,
|
|
1054
1220
|
onAudioTrack,
|
|
@@ -1087,8 +1253,13 @@ var convertMedia = async function({
|
|
|
1087
1253
|
});
|
|
1088
1254
|
};
|
|
1089
1255
|
export {
|
|
1256
|
+
getDefaultVideoCodec,
|
|
1257
|
+
getDefaultAudioCodec,
|
|
1090
1258
|
getAvailableVideoCodecs,
|
|
1259
|
+
getAvailableContainers,
|
|
1091
1260
|
getAvailableAudioCodecs,
|
|
1261
|
+
defaultOnVideoTrackHandler,
|
|
1262
|
+
defaultOnAudioTrackHandler,
|
|
1092
1263
|
createVideoEncoder,
|
|
1093
1264
|
createVideoDecoder,
|
|
1094
1265
|
createAudioEncoder,
|