Skip to main content

可变持续时间和尺寸

您可以根据一些异步确定的数据更改视频的持续时间。
同样适用于视频的宽度、高度和帧速率。

使用 calculateMetadata() 函数v4.0.0

考虑这样一个场景,视频被动态指定为背景,合成的持续时间应与视频的持续时间对齐。

将一个 calculateMetadata 回调函数传递给 <Composition>。此函数应接受组合的 props并计算元数据。

src/Root.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
import { Composition, Video } from "remotion";
 
type MyCompProps = {
src: string;
};
 
const MyComp: React.FC<MyCompProps> = ({ src }) => {
return <Video src={src} />;
};
 
export const Root: React.FC = () => {
return (
<Composition
id="MyComp"
component={MyComp}
durationInFrames={300}
fps={30}
width={1920}
height={1080}
defaultProps={{
src: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
}}
calculateMetadata={async ({ props }) => {
const data = await getVideoMetadata(props.src);
 
return {
durationInFrames: Math.floor(data.durationInSeconds * 30),
};
}}
/>
);
};
src/Root.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
import { Composition, Video } from "remotion";
 
type MyCompProps = {
src: string;
};
 
const MyComp: React.FC<MyCompProps> = ({ src }) => {
return <Video src={src} />;
};
 
export const Root: React.FC = () => {
return (
<Composition
id="MyComp"
component={MyComp}
durationInFrames={300}
fps={30}
width={1920}
height={1080}
defaultProps={{
src: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
}}
calculateMetadata={async ({ props }) => {
const data = await getVideoMetadata(props.src);
 
return {
durationInFrames: Math.floor(data.durationInSeconds * 30),
};
}}
/>
);
};

props 默认为指定的 defaultProps,但可以通过将输入 props 传递给渲染来覆盖。

返回一个带有 durationInFrames 的对象以更改视频的持续时间。 此外,您还可以返回 fpswidthheight 来更新视频的分辨率和帧速率。

还可以通过同时返回一个 props 字段转换传递给组件的 props

使用 useEffect()getInputProps()

在以下示例中,Remotion 被指示在评估合成之前等待 getVideoMetadata() 承诺解析。

通过调用 delayRender(),Remotion 将被阻止继续进行,直到调用 continueRender()

src/Root.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
 
export const Index: React.FC = () => {
const [handle] = useState(() => delayRender());
const [duration, setDuration] = useState(1);
 
useEffect(() => {
getVideoMetadata(
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
)
.then(({ durationInSeconds }) => {
setDuration(Math.round(durationInSeconds * 30));
continueRender(handle);
})
.catch((err) => {
console.log(`Error fetching metadata: ${err}`);
});
}, [handle]);
 
return (
<Composition
id="dynamic-duration"
component={VideoTesting}
width={1080}
height={1080}
fps={30}
durationInFrames={duration}
/>
);
};
src/Root.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
 
export const Index: React.FC = () => {
const [handle] = useState(() => delayRender());
const [duration, setDuration] = useState(1);
 
useEffect(() => {
getVideoMetadata(
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
)
.then(({ durationInSeconds }) => {
setDuration(Math.round(durationInSeconds * 30));
continueRender(handle);
})
.catch((err) => {
console.log(`Error fetching metadata: ${err}`);
});
}, [handle]);
 
return (
<Composition
id="dynamic-duration"
component={VideoTesting}
width={1080}
height={1080}
fps={30}
durationInFrames={duration}
/>
);
};

要动态传递视频资产,您可以在渲染时传递输入 props,并在 React 代码中使用 getInputProps() 检索它们。

src/Root.tsx
tsx
import { getInputProps } from "remotion";
 
const inputProps = getInputProps();
const src = inputProps.src;
src/Root.tsx
tsx
import { getInputProps } from "remotion";
 
const inputProps = getInputProps();
const src = inputProps.src;

缺点

自 v4.0 起不再推荐使用此技术,因为 useEffect() 不仅在 Remotion 最初计算视频的元数据时执行,还在生成渲染工作者时执行。

由于渲染过程可能高度并发,这可能导致不必要的 API 调用和速率限制。

与尺寸覆盖一起使用

覆盖参数,如 --width,将被优先考虑,并覆盖您使用 calculateMetadata() 设置的变量尺寸。

--scale 参数具有最高优先级,并将在覆盖参数和 calculateMetadata() 之后应用。

在设计视频后更改尺寸和 FPS

如果您使用特定尺寸设计了您的视频,然后希望以不同的分辨率进行渲染(例如 4K 而不是全高清),您可以使用 输出缩放

如果您使用特定 FPS 设计了您的视频,然后想要更改帧速率,您可以使用 <FpsConverter> 片段。

使用 <Player>

<Player> 将在传递给它的元数据发生变化时做出反应。有两种可行的方法可以动态设置播放器的元数据:

使用 useEffect() 进行数据获取:

MyApp.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
import { useEffect, useState } from "react";
import { Player } from "@remotion/player";
 
export const Index: React.FC = () => {
const [duration, setDuration] = useState(1);
 
useEffect(() => {
getVideoMetadata(
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
)
.then(({ durationInSeconds }) => {
setDuration(Math.round(durationInSeconds * 30));
})
.catch((err) => {
console.log(`Error fetching metadata: ${err}`);
});
}, []);
 
return (
<Player
component={VideoTesting}
compositionWidth={1080}
compositionHeight={1080}
fps={30}
durationInFrames={duration}
/>
);
};
MyApp.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
import { useEffect, useState } from "react";
import { Player } from "@remotion/player";
 
export const Index: React.FC = () => {
const [duration, setDuration] = useState(1);
 
useEffect(() => {
getVideoMetadata(
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
)
.then(({ durationInSeconds }) => {
setDuration(Math.round(durationInSeconds * 30));
})
.catch((err) => {
console.log(`Error fetching metadata: ${err}`);
});
}, []);
 
return (
<Player
component={VideoTesting}
compositionWidth={1080}
compositionHeight={1080}
fps={30}
durationInFrames={duration}
/>
);
};

Call your own calculateMetadata() function reused from your Remotion project:

MyApp.tsx
tsx
import { Player } from "@remotion/player";
 
type Props = {};
 
const calculateMetadataFunction: CalculateMetadataFunction<Props> = () => {
return {
props: {},
durationInFrames: 1,
width: 100,
height: 100,
fps: 30,
};
};
 
type Metadata = {
durationInFrames: number;
compositionWidth: number;
compositionHeight: number;
fps: number;
props: Props;
};
 
export const Index: React.FC = () => {
const [metadata, setMetadata] = useState<Metadata | null>(null);
 
useEffect(() => {
Promise.resolve(
calculateMetadataFunction({
defaultProps: {},
props: {},
abortSignal: new AbortController().signal,
compositionId: "MyComp",
}),
)
.then(({ durationInFrames, props, width, height, fps }) => {
setMetadata({
durationInFrames: durationInFrames as number,
compositionWidth: width as number,
compositionHeight: height as number,
fps: fps as number,
props: props as Props,
});
})
.catch((err) => {
console.log(`Error fetching metadata: ${err}`);
});
}, []);
 
if (!metadata) {
return null;
}
 
return <Player component={VideoTesting} {...metadata} />;
};
MyApp.tsx
tsx
import { Player } from "@remotion/player";
 
type Props = {};
 
const calculateMetadataFunction: CalculateMetadataFunction<Props> = () => {
return {
props: {},
durationInFrames: 1,
width: 100,
height: 100,
fps: 30,
};
};
 
type Metadata = {
durationInFrames: number;
compositionWidth: number;
compositionHeight: number;
fps: number;
props: Props;
};
 
export const Index: React.FC = () => {
const [metadata, setMetadata] = useState<Metadata | null>(null);
 
useEffect(() => {
Promise.resolve(
calculateMetadataFunction({
defaultProps: {},
props: {},
abortSignal: new AbortController().signal,
compositionId: "MyComp",
}),
)
.then(({ durationInFrames, props, width, height, fps }) => {
setMetadata({
durationInFrames: durationInFrames as number,
compositionWidth: width as number,
compositionHeight: height as number,
fps: fps as number,
props: props as Props,
});
})
.catch((err) => {
console.log(`Error fetching metadata: ${err}`);
});
}, []);
 
if (!metadata) {
return null;
}
 
return <Player component={VideoTesting} {...metadata} />;
};

参见