@remotion/media 4.0.445 → 4.0.447
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/audio/props.d.ts +8 -0
- package/dist/draw-with-object-fit.d.ts +14 -0
- package/dist/esm/index.mjs +136 -47
- package/dist/index.d.ts +5 -1
- package/dist/video/props.d.ts +11 -1
- package/dist/video/video-for-preview.d.ts +1 -0
- package/dist/video/video-frame-cache.d.ts +2 -0
- package/dist/video/video.d.ts +5 -1
- package/package.json +3 -3
- package/dist/calculate-playbacktime.d.ts +0 -5
package/dist/audio/props.d.ts
CHANGED
|
@@ -9,6 +9,14 @@ export type FallbackHtml5AudioProps = {
|
|
|
9
9
|
};
|
|
10
10
|
export type AudioProps = {
|
|
11
11
|
src: string;
|
|
12
|
+
/**
|
|
13
|
+
* When set, `<Audio>` applies timing via an inner `<Sequence layout="none">` that is hidden from the timeline (`showInTimeline={false}`) so the clip still appears once as media.
|
|
14
|
+
*/
|
|
15
|
+
from?: number;
|
|
16
|
+
/**
|
|
17
|
+
* When set with `from`, bounds the clip in frames. Defaults to `Infinity` like `<Sequence>`.
|
|
18
|
+
*/
|
|
19
|
+
durationInFrames?: number;
|
|
12
20
|
trimBefore?: number;
|
|
13
21
|
trimAfter?: number;
|
|
14
22
|
volume?: VolumeProp;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ObjectFitValue = 'fill' | 'contain' | 'cover' | 'none' | 'scale-down';
|
|
2
|
+
/**
|
|
3
|
+
* Draws a source image onto a canvas context with the specified object-fit behavior.
|
|
4
|
+
* This implements object-fit at the canvas drawing level, which is more reliable
|
|
5
|
+
* than CSS object-fit on canvas elements.
|
|
6
|
+
*/
|
|
7
|
+
export declare const drawWithObjectFit: (ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, source: CanvasImageSource, options: {
|
|
8
|
+
sourceWidth: number;
|
|
9
|
+
sourceHeight: number;
|
|
10
|
+
destWidth: number;
|
|
11
|
+
destHeight: number;
|
|
12
|
+
fit: ObjectFitValue;
|
|
13
|
+
}) => void;
|
|
14
|
+
export declare const parseObjectFit: (value: string | undefined) => ObjectFitValue | null;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -37,7 +37,7 @@ var __callDispose = (stack, error, hasError) => {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
// src/audio/audio.tsx
|
|
40
|
-
import { Internals as Internals19, useRemotionEnvironment as useRemotionEnvironment2 } from "remotion";
|
|
40
|
+
import { Internals as Internals19, Sequence, useRemotionEnvironment as useRemotionEnvironment2 } from "remotion";
|
|
41
41
|
|
|
42
42
|
// src/audio/audio-for-preview.tsx
|
|
43
43
|
import { useContext as useContext3, useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState2 } from "react";
|
|
@@ -1093,8 +1093,10 @@ var videoIteratorManager = ({
|
|
|
1093
1093
|
let framesRendered = 0;
|
|
1094
1094
|
let currentDelayHandle = null;
|
|
1095
1095
|
if (canvas) {
|
|
1096
|
-
canvas.width
|
|
1097
|
-
|
|
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,
|
|
@@ -4469,29 +4471,40 @@ var audioSchema = {
|
|
|
4469
4471
|
loop: { type: "boolean", default: false, description: "Loop" }
|
|
4470
4472
|
};
|
|
4471
4473
|
var AudioInner = (props) => {
|
|
4472
|
-
const {
|
|
4474
|
+
const {
|
|
4475
|
+
name,
|
|
4476
|
+
stack,
|
|
4477
|
+
showInTimeline,
|
|
4478
|
+
controls,
|
|
4479
|
+
from,
|
|
4480
|
+
durationInFrames,
|
|
4481
|
+
...otherProps
|
|
4482
|
+
} = props;
|
|
4473
4483
|
const environment = useRemotionEnvironment2();
|
|
4474
4484
|
if (typeof props.src !== "string") {
|
|
4475
4485
|
throw new TypeError(`The \`<Audio>\` tag requires a string for \`src\`, but got ${JSON.stringify(props.src)} instead.`);
|
|
4476
4486
|
}
|
|
4477
4487
|
validateMediaProps({ playbackRate: props.playbackRate, volume: props.volume }, "Audio");
|
|
4478
|
-
|
|
4479
|
-
|
|
4488
|
+
return /* @__PURE__ */ jsx3(Sequence, {
|
|
4489
|
+
layout: "none",
|
|
4490
|
+
from: from ?? 0,
|
|
4491
|
+
durationInFrames: durationInFrames ?? Infinity,
|
|
4492
|
+
showInTimeline: false,
|
|
4493
|
+
children: environment.isRendering ? /* @__PURE__ */ jsx3(AudioForRendering, {
|
|
4480
4494
|
...otherProps
|
|
4481
|
-
})
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
controls
|
|
4495
|
+
}) : /* @__PURE__ */ jsx3(AudioForPreview, {
|
|
4496
|
+
name,
|
|
4497
|
+
...otherProps,
|
|
4498
|
+
stack: stack ?? null,
|
|
4499
|
+
controls
|
|
4500
|
+
})
|
|
4488
4501
|
});
|
|
4489
4502
|
};
|
|
4490
4503
|
var Audio = Internals19.wrapInSchema(AudioInner, audioSchema);
|
|
4491
4504
|
Internals19.addSequenceStackTraces(Audio);
|
|
4492
4505
|
|
|
4493
4506
|
// src/video/video.tsx
|
|
4494
|
-
import { Internals as Internals23, useRemotionEnvironment as useRemotionEnvironment4 } from "remotion";
|
|
4507
|
+
import { Internals as Internals23, Sequence as Sequence2, useRemotionEnvironment as useRemotionEnvironment4 } from "remotion";
|
|
4495
4508
|
|
|
4496
4509
|
// src/video/video-for-preview.tsx
|
|
4497
4510
|
import {
|
|
@@ -4510,6 +4523,29 @@ import {
|
|
|
4510
4523
|
useVideoConfig as useVideoConfig3
|
|
4511
4524
|
} from "remotion";
|
|
4512
4525
|
|
|
4526
|
+
// src/video/video-frame-cache.ts
|
|
4527
|
+
var cache = new Map;
|
|
4528
|
+
var cacheVideoFrame = (src, sourceCanvas) => {
|
|
4529
|
+
const { width, height } = sourceCanvas;
|
|
4530
|
+
if (width === 0 || height === 0) {
|
|
4531
|
+
return;
|
|
4532
|
+
}
|
|
4533
|
+
let cached = cache.get(src);
|
|
4534
|
+
if (!cached || cached.width !== width || cached.height !== height) {
|
|
4535
|
+
cached = new OffscreenCanvas(width, height);
|
|
4536
|
+
cache.set(src, cached);
|
|
4537
|
+
}
|
|
4538
|
+
const ctx = cached.getContext("2d");
|
|
4539
|
+
if (!ctx) {
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4542
|
+
ctx.clearRect(0, 0, width, height);
|
|
4543
|
+
ctx.drawImage(sourceCanvas, 0, 0);
|
|
4544
|
+
};
|
|
4545
|
+
var getCachedVideoFrame = (src) => {
|
|
4546
|
+
return cache.get(src) ?? null;
|
|
4547
|
+
};
|
|
4548
|
+
|
|
4513
4549
|
// src/video/warn-object-fit-css.ts
|
|
4514
4550
|
import { Internals as Internals20 } from "remotion";
|
|
4515
4551
|
var OBJECT_FIT_CLASS_PATTERN = /\bobject-(contain|cover|fill|none|scale-down)\b/;
|
|
@@ -4570,7 +4606,8 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
4570
4606
|
onError,
|
|
4571
4607
|
credentials,
|
|
4572
4608
|
controls,
|
|
4573
|
-
objectFit: objectFitProp
|
|
4609
|
+
objectFit: objectFitProp,
|
|
4610
|
+
_experimentalInitiallyDrawCachedFrame
|
|
4574
4611
|
}) => {
|
|
4575
4612
|
const src = usePreload2(unpreloadedSrc);
|
|
4576
4613
|
const canvasRef = useRef2(null);
|
|
@@ -4647,6 +4684,44 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
4647
4684
|
const initialMuted = useRef2(effectiveMuted);
|
|
4648
4685
|
const initialDurationInFrames = useRef2(videoConfig.durationInFrames);
|
|
4649
4686
|
const initialSequenceOffset = useRef2(sequenceOffset);
|
|
4687
|
+
const hasDrawnRealFrameRef = useRef2(false);
|
|
4688
|
+
const isPremountingRef = useRef2(isPremounting);
|
|
4689
|
+
isPremountingRef.current = isPremounting;
|
|
4690
|
+
useLayoutEffect3(() => {
|
|
4691
|
+
if (!_experimentalInitiallyDrawCachedFrame) {
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
4694
|
+
const canvas = canvasRef.current;
|
|
4695
|
+
if (!canvas) {
|
|
4696
|
+
return;
|
|
4697
|
+
}
|
|
4698
|
+
const cached = getCachedVideoFrame(src);
|
|
4699
|
+
if (!cached) {
|
|
4700
|
+
return;
|
|
4701
|
+
}
|
|
4702
|
+
canvas.width = cached.width;
|
|
4703
|
+
canvas.height = cached.height;
|
|
4704
|
+
const ctx = canvas.getContext("2d", {
|
|
4705
|
+
alpha: true,
|
|
4706
|
+
desynchronized: true
|
|
4707
|
+
});
|
|
4708
|
+
if (!ctx) {
|
|
4709
|
+
return;
|
|
4710
|
+
}
|
|
4711
|
+
ctx.drawImage(cached, 0, 0);
|
|
4712
|
+
}, [_experimentalInitiallyDrawCachedFrame, src]);
|
|
4713
|
+
useLayoutEffect3(() => {
|
|
4714
|
+
if (!_experimentalInitiallyDrawCachedFrame) {
|
|
4715
|
+
return;
|
|
4716
|
+
}
|
|
4717
|
+
const canvas = canvasRef.current;
|
|
4718
|
+
return () => {
|
|
4719
|
+
if (!canvas || !hasDrawnRealFrameRef.current || isPremountingRef.current) {
|
|
4720
|
+
return;
|
|
4721
|
+
}
|
|
4722
|
+
cacheVideoFrame(src, canvas);
|
|
4723
|
+
};
|
|
4724
|
+
}, [_experimentalInitiallyDrawCachedFrame, src]);
|
|
4650
4725
|
useEffect3(() => {
|
|
4651
4726
|
if (!sharedAudioContext)
|
|
4652
4727
|
return;
|
|
@@ -4715,6 +4790,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
4715
4790
|
if (result.type === "success") {
|
|
4716
4791
|
setMediaPlayerReady(true);
|
|
4717
4792
|
setMediaDurationInSeconds(result.durationInSeconds);
|
|
4793
|
+
hasDrawnRealFrameRef.current = true;
|
|
4718
4794
|
}
|
|
4719
4795
|
}).catch((error) => {
|
|
4720
4796
|
const [action, errorToUse] = callOnErrorAndResolve({
|
|
@@ -4752,6 +4828,7 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
4752
4828
|
}
|
|
4753
4829
|
setMediaPlayerReady(false);
|
|
4754
4830
|
setShouldFallbackToNativeVideo(false);
|
|
4831
|
+
hasDrawnRealFrameRef.current = false;
|
|
4755
4832
|
};
|
|
4756
4833
|
}, [
|
|
4757
4834
|
audioStreamIndex,
|
|
@@ -5272,7 +5349,8 @@ var InnerVideo = ({
|
|
|
5272
5349
|
onError,
|
|
5273
5350
|
credentials,
|
|
5274
5351
|
controls,
|
|
5275
|
-
objectFit
|
|
5352
|
+
objectFit,
|
|
5353
|
+
_experimentalInitiallyDrawCachedFrame
|
|
5276
5354
|
}) => {
|
|
5277
5355
|
const environment = useRemotionEnvironment4();
|
|
5278
5356
|
if (typeof src !== "string") {
|
|
@@ -5344,7 +5422,8 @@ var InnerVideo = ({
|
|
|
5344
5422
|
onError,
|
|
5345
5423
|
credentials,
|
|
5346
5424
|
controls,
|
|
5347
|
-
objectFit
|
|
5425
|
+
objectFit,
|
|
5426
|
+
_experimentalInitiallyDrawCachedFrame
|
|
5348
5427
|
});
|
|
5349
5428
|
};
|
|
5350
5429
|
var VideoInner = ({
|
|
@@ -5375,38 +5454,48 @@ var VideoInner = ({
|
|
|
5375
5454
|
onError,
|
|
5376
5455
|
credentials,
|
|
5377
5456
|
controls,
|
|
5378
|
-
objectFit
|
|
5457
|
+
objectFit,
|
|
5458
|
+
_experimentalInitiallyDrawCachedFrame,
|
|
5459
|
+
from,
|
|
5460
|
+
durationInFrames
|
|
5379
5461
|
}) => {
|
|
5380
5462
|
const fallbackLogLevel = Internals23.useLogLevel();
|
|
5381
|
-
return /* @__PURE__ */ jsx6(
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5463
|
+
return /* @__PURE__ */ jsx6(Sequence2, {
|
|
5464
|
+
layout: "none",
|
|
5465
|
+
from: from ?? 0,
|
|
5466
|
+
durationInFrames: durationInFrames ?? Infinity,
|
|
5467
|
+
showInTimeline: false,
|
|
5468
|
+
children: /* @__PURE__ */ jsx6(InnerVideo, {
|
|
5469
|
+
audioStreamIndex: audioStreamIndex ?? 0,
|
|
5470
|
+
className,
|
|
5471
|
+
delayRenderRetries: delayRenderRetries ?? null,
|
|
5472
|
+
delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? null,
|
|
5473
|
+
disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo ?? false,
|
|
5474
|
+
fallbackOffthreadVideoProps: fallbackOffthreadVideoProps ?? {},
|
|
5475
|
+
logLevel: logLevel ?? fallbackLogLevel,
|
|
5476
|
+
loop: loop ?? false,
|
|
5477
|
+
loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? "repeat",
|
|
5478
|
+
muted: muted ?? false,
|
|
5479
|
+
name,
|
|
5480
|
+
onVideoFrame,
|
|
5481
|
+
playbackRate: playbackRate ?? 1,
|
|
5482
|
+
showInTimeline: showInTimeline ?? true,
|
|
5483
|
+
src,
|
|
5484
|
+
style: style ?? {},
|
|
5485
|
+
trimAfter,
|
|
5486
|
+
trimBefore,
|
|
5487
|
+
volume: volume ?? 1,
|
|
5488
|
+
toneFrequency: toneFrequency ?? 1,
|
|
5489
|
+
stack,
|
|
5490
|
+
debugOverlay: debugOverlay ?? false,
|
|
5491
|
+
debugAudioScheduling: debugAudioScheduling ?? false,
|
|
5492
|
+
headless: headless ?? false,
|
|
5493
|
+
onError,
|
|
5494
|
+
credentials,
|
|
5495
|
+
controls,
|
|
5496
|
+
objectFit: objectFit ?? "contain",
|
|
5497
|
+
_experimentalInitiallyDrawCachedFrame: _experimentalInitiallyDrawCachedFrame ?? false
|
|
5498
|
+
})
|
|
5410
5499
|
});
|
|
5411
5500
|
};
|
|
5412
5501
|
var Video = Internals23.wrapInSchema(VideoInner, videoSchema);
|
package/dist/index.d.ts
CHANGED
|
@@ -39,7 +39,11 @@ 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
|
-
|
|
42
|
+
_experimentalInitiallyDrawCachedFrame: boolean;
|
|
43
|
+
}> & {
|
|
44
|
+
from?: number | undefined;
|
|
45
|
+
durationInFrames?: number | undefined;
|
|
46
|
+
}>;
|
|
43
47
|
export { AudioForPreview } from './audio/audio-for-preview';
|
|
44
48
|
export { AudioProps, FallbackHtml5AudioProps } from './audio/props';
|
|
45
49
|
export { MediaErrorAction } from './on-error';
|
package/dist/video/props.d.ts
CHANGED
|
@@ -51,7 +51,17 @@ 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
|
-
export type VideoProps = MandatoryVideoProps & Partial<OuterVideoProps> & Partial<OptionalVideoProps
|
|
57
|
+
export type VideoProps = MandatoryVideoProps & Partial<OuterVideoProps> & Partial<OptionalVideoProps> & {
|
|
58
|
+
/**
|
|
59
|
+
* When set, `<Video>` applies timing via an inner `<Sequence layout="none">` that is hidden from the timeline (`showInTimeline={false}`) so the clip still appears once as media.
|
|
60
|
+
*/
|
|
61
|
+
from?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Bounds the clip in frames together with `from`. Defaults to `Infinity` like `<Sequence>`.
|
|
64
|
+
*/
|
|
65
|
+
durationInFrames?: number;
|
|
66
|
+
};
|
|
57
67
|
export {};
|
|
@@ -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;
|
package/dist/video/video.d.ts
CHANGED
|
@@ -31,4 +31,8 @@ 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
|
-
|
|
34
|
+
_experimentalInitiallyDrawCachedFrame: boolean;
|
|
35
|
+
}> & {
|
|
36
|
+
from?: number | undefined;
|
|
37
|
+
durationInFrames?: number | undefined;
|
|
38
|
+
}>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/media",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.447",
|
|
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.
|
|
26
|
+
"remotion": "4.0.447",
|
|
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.
|
|
34
|
+
"@remotion/eslint-config-internal": "4.0.447",
|
|
35
35
|
"@vitest/browser-webdriverio": "4.0.9",
|
|
36
36
|
"eslint": "9.19.0",
|
|
37
37
|
"react": "19.2.3",
|