@remotion/media 4.0.446 → 4.0.448

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.
@@ -0,0 +1,14 @@
1
+ export declare class AudioDecodeScheduler {
2
+ private activeTurns;
3
+ private nextId;
4
+ private queue;
5
+ private pendingBatch;
6
+ private batchTimer;
7
+ requestTurn(priority: number): Promise<number>;
8
+ releaseTurn(id: number): void;
9
+ private processBatch;
10
+ private grant;
11
+ private getMinActivePriority;
12
+ private grantEligibleWaiters;
13
+ }
14
+ export declare const getAudioDecodeScheduler: (audioContext: AudioContext) => AudioDecodeScheduler;
@@ -1,5 +1,5 @@
1
1
  export declare const TARGET_NUMBER_OF_CHANNELS = 2;
2
- export declare const TARGET_SAMPLE_RATE = 48000;
2
+ export declare const getTargetSampleRate: () => number;
3
3
  export declare const resampleAudioData: ({ srcNumberOfChannels, sourceChannels, destination, targetFrames, chunkSize, }: {
4
4
  srcNumberOfChannels: number;
5
5
  sourceChannels: Int16Array<ArrayBufferLike>;
@@ -381,7 +381,7 @@ var isAlreadyQueued = (time, queuedPeriod) => {
381
381
 
382
382
  // src/make-iterator-with-priming.ts
383
383
  var AUDIO_PRIMING_SECONDS = 0.5;
384
- var PREDECODE_AHEAD_SECONDS = 8;
384
+ var PREDECODE_AHEAD_SECONDS = 2;
385
385
  function makePredecodingIterator(inner) {
386
386
  const buffer = [];
387
387
  let consumerEndTime = 0;
@@ -564,7 +564,7 @@ var makePrewarmedAudioIteratorCache = (audioSink) => {
564
564
  };
565
565
 
566
566
  // src/audio-iterator-manager.ts
567
- var MAX_BUFFER_AHEAD_SECONDS = 8;
567
+ var MAX_BUFFER_AHEAD_SECONDS = 2;
568
568
  var audioIteratorManager = ({
569
569
  audioTrack,
570
570
  delayPlaybackHandleIfNotPremounting,
@@ -1093,8 +1093,10 @@ var videoIteratorManager = ({
1093
1093
  let framesRendered = 0;
1094
1094
  let currentDelayHandle = null;
1095
1095
  if (canvas) {
1096
- canvas.width = videoTrack.displayWidth;
1097
- canvas.height = videoTrack.displayHeight;
1096
+ if (canvas.width !== videoTrack.displayWidth || canvas.height !== videoTrack.displayHeight) {
1097
+ canvas.width = videoTrack.displayWidth;
1098
+ canvas.height = videoTrack.displayHeight;
1099
+ }
1098
1100
  }
1099
1101
  const canvasSink = new CanvasSink(videoTrack, {
1100
1102
  poolSize: 2,
@@ -3259,7 +3261,12 @@ var applyVolume = (array, volume) => {
3259
3261
 
3260
3262
  // src/convert-audiodata/resample-audiodata.ts
3261
3263
  var TARGET_NUMBER_OF_CHANNELS = 2;
3262
- var TARGET_SAMPLE_RATE = 48000;
3264
+ var getTargetSampleRate = () => {
3265
+ if (typeof window !== "undefined" && window.remotion_sampleRate) {
3266
+ return window.remotion_sampleRate;
3267
+ }
3268
+ return 48000;
3269
+ };
3263
3270
  var fixFloatingPoint = (value) => {
3264
3271
  if (value % 1 < 0.0000001) {
3265
3272
  return Math.floor(value);
@@ -3392,7 +3399,7 @@ var convertAudioData = ({
3392
3399
  sampleRate: currentSampleRate,
3393
3400
  numberOfFrames
3394
3401
  } = audioData;
3395
- const ratio = currentSampleRate / TARGET_SAMPLE_RATE;
3402
+ const ratio = currentSampleRate / getTargetSampleRate();
3396
3403
  const frameOffset = Math.floor(fixFloatingPoint2(trimStartInSeconds * audioData.sampleRate));
3397
3404
  const unroundedFrameCount = numberOfFrames - trimEndInSeconds * audioData.sampleRate - frameOffset;
3398
3405
  const frameCount = isLast ? ceilButNotIfFloatingPointIssue(unroundedFrameCount) : Math.round(unroundedFrameCount);
@@ -3439,7 +3446,7 @@ var convertAudioData = ({
3439
3446
  data: srcChannels,
3440
3447
  numberOfFrames: newNumberOfFrames,
3441
3448
  timestamp: audioDataTimestamp * 1e6 + fixFloatingPoint2(timestampOffsetMicroseconds),
3442
- durationInMicroSeconds: fixFloatingPoint2(newNumberOfFrames / TARGET_SAMPLE_RATE * 1e6)
3449
+ durationInMicroSeconds: fixFloatingPoint2(newNumberOfFrames / getTargetSampleRate() * 1e6)
3443
3450
  };
3444
3451
  }
3445
3452
  resampleAudioData({
@@ -3453,7 +3460,7 @@ var convertAudioData = ({
3453
3460
  data,
3454
3461
  numberOfFrames: newNumberOfFrames,
3455
3462
  timestamp: audioDataTimestamp * 1e6 + fixFloatingPoint2(timestampOffsetMicroseconds),
3456
- durationInMicroSeconds: fixFloatingPoint2(newNumberOfFrames / TARGET_SAMPLE_RATE * 1e6)
3463
+ durationInMicroSeconds: fixFloatingPoint2(newNumberOfFrames / getTargetSampleRate() * 1e6)
3457
3464
  };
3458
3465
  return newAudioData;
3459
3466
  };
@@ -3725,12 +3732,12 @@ var extractAudioInternal = async ({
3725
3732
  if (isFirstSample) {
3726
3733
  trimStartInSeconds = fixFloatingPoint2(timeInSeconds - sample.timestamp);
3727
3734
  if (trimStartInSeconds < 0) {
3728
- const silenceFrames = Math.ceil(fixFloatingPoint2(-trimStartInSeconds * TARGET_SAMPLE_RATE));
3735
+ const silenceFrames = Math.ceil(fixFloatingPoint2(-trimStartInSeconds * getTargetSampleRate()));
3729
3736
  leadingSilence = {
3730
3737
  data: new Int16Array(silenceFrames * TARGET_NUMBER_OF_CHANNELS),
3731
3738
  numberOfFrames: silenceFrames,
3732
3739
  timestamp: timeInSeconds * 1e6,
3733
- durationInMicroSeconds: silenceFrames / TARGET_SAMPLE_RATE * 1e6
3740
+ durationInMicroSeconds: silenceFrames / getTargetSampleRate() * 1e6
3734
3741
  };
3735
3742
  trimStartInSeconds = 0;
3736
3743
  }
@@ -4381,7 +4388,7 @@ var AudioForRendering = ({
4381
4388
  audio: environment.isClientSideRendering ? audio.data : Array.from(audio.data),
4382
4389
  frame: absoluteFrame,
4383
4390
  timestamp: audio.timestamp,
4384
- duration: audio.numberOfFrames / TARGET_SAMPLE_RATE * 1e6,
4391
+ duration: audio.numberOfFrames / getTargetSampleRate() * 1e6,
4385
4392
  toneFrequency: toneFrequency ?? 1
4386
4393
  });
4387
4394
  }
@@ -4521,6 +4528,29 @@ import {
4521
4528
  useVideoConfig as useVideoConfig3
4522
4529
  } from "remotion";
4523
4530
 
4531
+ // src/video/video-frame-cache.ts
4532
+ var cache = new Map;
4533
+ var cacheVideoFrame = (src, sourceCanvas) => {
4534
+ const { width, height } = sourceCanvas;
4535
+ if (width === 0 || height === 0) {
4536
+ return;
4537
+ }
4538
+ let cached = cache.get(src);
4539
+ if (!cached || cached.width !== width || cached.height !== height) {
4540
+ cached = new OffscreenCanvas(width, height);
4541
+ cache.set(src, cached);
4542
+ }
4543
+ const ctx = cached.getContext("2d");
4544
+ if (!ctx) {
4545
+ return;
4546
+ }
4547
+ ctx.clearRect(0, 0, width, height);
4548
+ ctx.drawImage(sourceCanvas, 0, 0);
4549
+ };
4550
+ var getCachedVideoFrame = (src) => {
4551
+ return cache.get(src) ?? null;
4552
+ };
4553
+
4524
4554
  // src/video/warn-object-fit-css.ts
4525
4555
  import { Internals as Internals20 } from "remotion";
4526
4556
  var OBJECT_FIT_CLASS_PATTERN = /\bobject-(contain|cover|fill|none|scale-down)\b/;
@@ -4581,7 +4611,8 @@ var VideoForPreviewAssertedShowing = ({
4581
4611
  onError,
4582
4612
  credentials,
4583
4613
  controls,
4584
- objectFit: objectFitProp
4614
+ objectFit: objectFitProp,
4615
+ _experimentalInitiallyDrawCachedFrame
4585
4616
  }) => {
4586
4617
  const src = usePreload2(unpreloadedSrc);
4587
4618
  const canvasRef = useRef2(null);
@@ -4658,6 +4689,44 @@ var VideoForPreviewAssertedShowing = ({
4658
4689
  const initialMuted = useRef2(effectiveMuted);
4659
4690
  const initialDurationInFrames = useRef2(videoConfig.durationInFrames);
4660
4691
  const initialSequenceOffset = useRef2(sequenceOffset);
4692
+ const hasDrawnRealFrameRef = useRef2(false);
4693
+ const isPremountingRef = useRef2(isPremounting);
4694
+ isPremountingRef.current = isPremounting;
4695
+ useLayoutEffect3(() => {
4696
+ if (!_experimentalInitiallyDrawCachedFrame) {
4697
+ return;
4698
+ }
4699
+ const canvas = canvasRef.current;
4700
+ if (!canvas) {
4701
+ return;
4702
+ }
4703
+ const cached = getCachedVideoFrame(src);
4704
+ if (!cached) {
4705
+ return;
4706
+ }
4707
+ canvas.width = cached.width;
4708
+ canvas.height = cached.height;
4709
+ const ctx = canvas.getContext("2d", {
4710
+ alpha: true,
4711
+ desynchronized: true
4712
+ });
4713
+ if (!ctx) {
4714
+ return;
4715
+ }
4716
+ ctx.drawImage(cached, 0, 0);
4717
+ }, [_experimentalInitiallyDrawCachedFrame, src]);
4718
+ useLayoutEffect3(() => {
4719
+ if (!_experimentalInitiallyDrawCachedFrame) {
4720
+ return;
4721
+ }
4722
+ const canvas = canvasRef.current;
4723
+ return () => {
4724
+ if (!canvas || !hasDrawnRealFrameRef.current || isPremountingRef.current) {
4725
+ return;
4726
+ }
4727
+ cacheVideoFrame(src, canvas);
4728
+ };
4729
+ }, [_experimentalInitiallyDrawCachedFrame, src]);
4661
4730
  useEffect3(() => {
4662
4731
  if (!sharedAudioContext)
4663
4732
  return;
@@ -4726,6 +4795,7 @@ var VideoForPreviewAssertedShowing = ({
4726
4795
  if (result.type === "success") {
4727
4796
  setMediaPlayerReady(true);
4728
4797
  setMediaDurationInSeconds(result.durationInSeconds);
4798
+ hasDrawnRealFrameRef.current = true;
4729
4799
  }
4730
4800
  }).catch((error) => {
4731
4801
  const [action, errorToUse] = callOnErrorAndResolve({
@@ -4763,6 +4833,7 @@ var VideoForPreviewAssertedShowing = ({
4763
4833
  }
4764
4834
  setMediaPlayerReady(false);
4765
4835
  setShouldFallbackToNativeVideo(false);
4836
+ hasDrawnRealFrameRef.current = false;
4766
4837
  };
4767
4838
  }, [
4768
4839
  audioStreamIndex,
@@ -5087,7 +5158,7 @@ var VideoForRendering = ({
5087
5158
  audio: environment.isClientSideRendering ? audio.data : Array.from(audio.data),
5088
5159
  frame: absoluteFrame,
5089
5160
  timestamp: audio.timestamp,
5090
- duration: audio.numberOfFrames / TARGET_SAMPLE_RATE * 1e6,
5161
+ duration: audio.numberOfFrames / getTargetSampleRate() * 1e6,
5091
5162
  toneFrequency
5092
5163
  });
5093
5164
  }
@@ -5283,7 +5354,8 @@ var InnerVideo = ({
5283
5354
  onError,
5284
5355
  credentials,
5285
5356
  controls,
5286
- objectFit
5357
+ objectFit,
5358
+ _experimentalInitiallyDrawCachedFrame
5287
5359
  }) => {
5288
5360
  const environment = useRemotionEnvironment4();
5289
5361
  if (typeof src !== "string") {
@@ -5355,7 +5427,8 @@ var InnerVideo = ({
5355
5427
  onError,
5356
5428
  credentials,
5357
5429
  controls,
5358
- objectFit
5430
+ objectFit,
5431
+ _experimentalInitiallyDrawCachedFrame
5359
5432
  });
5360
5433
  };
5361
5434
  var VideoInner = ({
@@ -5387,6 +5460,7 @@ var VideoInner = ({
5387
5460
  credentials,
5388
5461
  controls,
5389
5462
  objectFit,
5463
+ _experimentalInitiallyDrawCachedFrame,
5390
5464
  from,
5391
5465
  durationInFrames
5392
5466
  }) => {
@@ -5424,7 +5498,8 @@ var VideoInner = ({
5424
5498
  onError,
5425
5499
  credentials,
5426
5500
  controls,
5427
- objectFit: objectFit ?? "contain"
5501
+ objectFit: objectFit ?? "contain",
5502
+ _experimentalInitiallyDrawCachedFrame: _experimentalInitiallyDrawCachedFrame ?? false
5428
5503
  })
5429
5504
  });
5430
5505
  };
@@ -5435,6 +5510,7 @@ Internals23.addSequenceStackTraces(Video);
5435
5510
  var experimental_Audio = Audio;
5436
5511
  var experimental_Video = Video;
5437
5512
  export {
5513
+ getTargetSampleRate,
5438
5514
  experimental_Video,
5439
5515
  experimental_Audio,
5440
5516
  Video,
package/dist/index.d.ts CHANGED
@@ -39,6 +39,7 @@ export declare const experimental_Video: import("react").ComponentType<{
39
39
  onError: import("./on-error").MediaOnError | undefined;
40
40
  credentials: RequestCredentials | undefined;
41
41
  objectFit: import(".").VideoObjectFit;
42
+ _experimentalInitiallyDrawCachedFrame: boolean;
42
43
  }> & {
43
44
  from?: number | undefined;
44
45
  durationInFrames?: number | undefined;
@@ -47,4 +48,5 @@ export { AudioForPreview } from './audio/audio-for-preview';
47
48
  export { AudioProps, FallbackHtml5AudioProps } from './audio/props';
48
49
  export { MediaErrorAction } from './on-error';
49
50
  export { FallbackOffthreadVideoProps, VideoObjectFit, VideoProps, } from './video/props';
51
+ export { getTargetSampleRate } from './convert-audiodata/resample-audiodata';
50
52
  export { Audio, Video };
@@ -51,6 +51,7 @@ type OptionalVideoProps = {
51
51
  onError: MediaOnError | undefined;
52
52
  credentials: RequestCredentials | undefined;
53
53
  objectFit: VideoObjectFit;
54
+ _experimentalInitiallyDrawCachedFrame: boolean;
54
55
  };
55
56
  export type InnerVideoProps = MandatoryVideoProps & OuterVideoProps & OptionalVideoProps;
56
57
  export type VideoProps = MandatoryVideoProps & Partial<OuterVideoProps> & Partial<OptionalVideoProps> & {
@@ -27,6 +27,7 @@ type VideoForPreviewProps = {
27
27
  readonly onError: MediaOnError | undefined;
28
28
  readonly credentials: RequestCredentials | undefined;
29
29
  readonly objectFit: VideoObjectFit;
30
+ readonly _experimentalInitiallyDrawCachedFrame: boolean;
30
31
  };
31
32
  export declare const VideoForPreview: React.FC<VideoForPreviewProps & {
32
33
  readonly controls: SequenceControls | undefined;
@@ -0,0 +1,2 @@
1
+ export declare const cacheVideoFrame: (src: string, sourceCanvas: HTMLCanvasElement | OffscreenCanvas) => void;
2
+ export declare const getCachedVideoFrame: (src: string) => OffscreenCanvas | null;
@@ -31,6 +31,7 @@ export declare const Video: React.ComponentType<{
31
31
  onError: import("../on-error").MediaOnError | undefined;
32
32
  credentials: RequestCredentials | undefined;
33
33
  objectFit: import("./props").VideoObjectFit;
34
+ _experimentalInitiallyDrawCachedFrame: boolean;
34
35
  }> & {
35
36
  from?: number | undefined;
36
37
  durationInFrames?: number | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/media",
3
- "version": "4.0.446",
3
+ "version": "4.0.448",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "mediabunny": "1.39.2",
26
- "remotion": "4.0.446",
26
+ "remotion": "4.0.448",
27
27
  "zod": "4.3.6"
28
28
  },
29
29
  "peerDependencies": {
@@ -31,7 +31,7 @@
31
31
  "react-dom": ">=16.8.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@remotion/eslint-config-internal": "4.0.446",
34
+ "@remotion/eslint-config-internal": "4.0.448",
35
35
  "@vitest/browser-webdriverio": "4.0.9",
36
36
  "eslint": "9.19.0",
37
37
  "react": "19.2.3",