@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.
Files changed (59) hide show
  1. package/dist/arraybuffer-to-uint8-array.d.ts +1 -0
  2. package/dist/arraybuffer-to-uint8-array.js +7 -0
  3. package/dist/audio-decoder.d.ts +2 -2
  4. package/dist/audio-encoder-config.js +15 -2
  5. package/dist/audio-encoder.d.ts +2 -1
  6. package/dist/audio-encoder.js +16 -4
  7. package/dist/can-copy-audio-track.d.ts +2 -4
  8. package/dist/can-copy-audio-track.js +7 -4
  9. package/dist/can-copy-video-track.d.ts +2 -4
  10. package/dist/can-copy-video-track.js +6 -6
  11. package/dist/can-reencode-audio-track.js +1 -6
  12. package/dist/can-reencode-video-track.js +1 -0
  13. package/dist/choose-correct-avc1-profile.d.ts +5 -0
  14. package/dist/choose-correct-avc1-profile.js +54 -0
  15. package/dist/codec-id.d.ts +7 -4
  16. package/dist/codec-id.js +28 -5
  17. package/dist/convert-encoded-chunk.d.ts +2 -1
  18. package/dist/convert-encoded-chunk.js +25 -2
  19. package/dist/convert-media.d.ts +7 -8
  20. package/dist/convert-media.js +11 -9
  21. package/dist/default-on-audio-track-handler.d.ts +2 -0
  22. package/dist/default-on-audio-track-handler.js +36 -0
  23. package/dist/default-on-video-track-handler.d.ts +2 -0
  24. package/dist/default-on-video-track-handler.js +29 -0
  25. package/dist/esm/index.mjs +277 -106
  26. package/dist/get-default-audio-codec.d.ts +4 -0
  27. package/dist/get-default-audio-codec.js +13 -0
  28. package/dist/get-default-video-codec.d.ts +4 -0
  29. package/dist/get-default-video-codec.js +10 -0
  30. package/dist/index.d.ts +8 -4
  31. package/dist/index.js +10 -1
  32. package/dist/io-manager/io-synchronizer.js +2 -2
  33. package/dist/{resolve-audio-action.d.ts → on-audio-track-handler.d.ts} +5 -5
  34. package/dist/on-audio-track-handler.js +2 -0
  35. package/dist/on-audio-track.d.ts +6 -6
  36. package/dist/on-audio-track.js +37 -10
  37. package/dist/{resolve-video-action.d.ts → on-video-track-handler.d.ts} +5 -5
  38. package/dist/on-video-track-handler.js +2 -0
  39. package/dist/on-video-track.d.ts +6 -6
  40. package/dist/on-video-track.js +35 -9
  41. package/dist/video-decoder.d.ts +2 -2
  42. package/dist/video-decoder.js +5 -0
  43. package/dist/video-encoder-config.d.ts +2 -1
  44. package/dist/video-encoder-config.js +7 -2
  45. package/dist/video-encoder.d.ts +1 -1
  46. package/dist/video-encoder.js +4 -6
  47. package/package.json +3 -3
  48. package/dist/can-reencode-audio.d.ts +0 -7
  49. package/dist/can-reencode-audio.js +0 -21
  50. package/dist/can-reencode-video.d.ts +0 -6
  51. package/dist/can-reencode-video.js +0 -15
  52. package/dist/event-emitter.d.ts +0 -25
  53. package/dist/event-emitter.js +0 -23
  54. package/dist/polyfill-encoded-audio-chunk.d.ts +0 -3
  55. package/dist/polyfill-encoded-audio-chunk.js +0 -5
  56. package/dist/resolve-audio-action.js +0 -32
  57. package/dist/resolve-video-action.js +0 -26
  58. package/dist/wait-until-return.d.ts +0 -4
  59. package/dist/wait-until-return.js +0 -14
@@ -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
- encoder.configure(audioEncoderConfig);
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 (outputCodec === "opus") {
300
- return inputCodec === "opus" && container === "webm";
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: ${outputCodec}`);
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 (outputCodec === "vp8") {
311
- return inputCodec === "vp8" && container === "webm";
323
+ if (container === "webm") {
324
+ return inputCodec === "vp8" || inputCodec === "vp9";
312
325
  }
313
- if (outputCodec === "vp9") {
314
- return inputCodec === "vp9" && container === "webm";
326
+ if (container === "mp4") {
327
+ return inputCodec === "h264" || inputCodec === "h265";
315
328
  }
316
- throw new Error(`Unhandled codec: ${outputCodec}`);
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(config)).supported) {
338
- return config;
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
- width,
443
+ codec,
388
444
  height,
389
- codec
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 availableVideoCodecs = ["vp8", "vp9"];
431
- var getAvailableVideoCodecs = () => availableVideoCodecs;
432
- var availableAudioCodecs = ["opus"];
433
- var getAvailableAudioCodecs = () => availableAudioCodecs;
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 MediaParserInternals2,
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/resolve-audio-action.ts
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 defaultResolveAudioAction = async ({
580
+ var defaultOnAudioTrackHandler = async ({
486
581
  track,
487
- audioCodec,
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 = ${canCopy}, action = 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): Can re-encode = ${canReencode}, can copy = ${canCopy}, action = reencode`);
508
- return Promise.resolve({ type: "reencode", bitrate, audioCodec });
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 = ${canReencode}, can copy = ${canCopy}, action = drop`);
511
- return Promise.resolve({ type: "drop" });
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 ?? defaultResolveAudioAction)({
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: audioCodec,
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(audioSample, addedTrack.trackNumber, false);
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: null
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(convertEncodedChunk(chunk), trackNumber, false);
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
- if (chunk.duration === null) {
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
- videoCodec,
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 ?? defaultResolveVideoAction)({
1005
+ const videoOperation = await (onVideoTrack ?? defaultOnVideoTrackHandler)({
863
1006
  track,
864
- videoCodec,
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(sample, videoTrack.trackNumber, true);
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(convertEncodedChunk(chunk), trackNumber, true);
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 (audioCodec !== "opus") {
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 state = await MediaParserInternals2.createMedia({
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,