Variable duration and dimensions
You may change the duration of the video based on some asynchronously determined data.
The same goes for the width, height and framerate of the video.
Using the calculateMetadata()
functionv4.0.0
Consider a scenario where a video is dynamically specified as a background and the duration of the composition should be aligned with the duration of the video.
Pass a calculateMetadata
callback function to a <Composition>
. This function should take the combined props and calculate the metadata.
src/Root.tsxtsx
import {parseMedia } from '@remotion/media-parser';import {Composition ,Video } from 'remotion';typeMyCompProps = {src : string;};constMyComp :React .FC <MyCompProps > = ({src }) => {return <Video src ={src } />;};export constRoot :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 {durationInSeconds } = awaitparseMedia ({src :props .src ,fields : {durationInSeconds : true},});if (durationInSeconds === null) {throw newError ('Could not read duration of video');}return {durationInFrames :Math .floor (durationInSeconds * 30),};}}/>);};
src/Root.tsxtsx
import {parseMedia } from '@remotion/media-parser';import {Composition ,Video } from 'remotion';typeMyCompProps = {src : string;};constMyComp :React .FC <MyCompProps > = ({src }) => {return <Video src ={src } />;};export constRoot :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 {durationInSeconds } = awaitparseMedia ({src :props .src ,fields : {durationInSeconds : true},});if (durationInSeconds === null) {throw newError ('Could not read duration of video');}return {durationInFrames :Math .floor (durationInSeconds * 30),};}}/>);};
The props
defaults to the defaultProps
specified, but may be overriden by passing input props to the render.
Return an object with durationInFrames
to change the duration of the video.
In addition or instead, you may also return fps
, width
and height
to update the video's resolution and framerate.
It is also possible to transform the props passed to the component by returning a props
field at the same time.
With useEffect()
and getInputProps()
In the following example, Remotion is instructed to wait for the parseMedia()
promise to resolve before evaluating the composition.
By calling delayRender()
, Remotion will be blocked from proceeding until continueRender()
is called.
src/Root.tsxtsx
import {parseMedia } from '@remotion/media-parser';export constIndex :React .FC = () => {const [handle ] =useState (() =>delayRender ());const [duration ,setDuration ] =useState (1);useEffect (() => {parseMedia ({src : 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',fields : {durationInSeconds : true,},}).then (({durationInSeconds }) => {if (durationInSeconds === null) {throw newError ('Could not read duration of video');}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.tsxtsx
import {parseMedia } from '@remotion/media-parser';export constIndex :React .FC = () => {const [handle ] =useState (() =>delayRender ());const [duration ,setDuration ] =useState (1);useEffect (() => {parseMedia ({src : 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',fields : {durationInSeconds : true,},}).then (({durationInSeconds }) => {if (durationInSeconds === null) {throw newError ('Could not read duration of video');}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 }/>);};
To dynamically pass a video asset, you may pass input props when rendering and retrieve them inside your React code using getInputProps()
.
src/Root.tsxtsx
import {getInputProps } from 'remotion';constinputProps =getInputProps ();constsrc =inputProps .src ;
src/Root.tsxtsx
import {getInputProps } from 'remotion';constinputProps =getInputProps ();constsrc =inputProps .src ;
Drawbacks
This technique is not recommended anymore since v4.0, because the useEffect()
does not only get executed when Remotion is initially calculating the metadata of the video, but also when spawning a render worker.
Since a render process might be highly concurrent, this might lead to unnecessary API calls and rate limitations.
Using together with dimension overrides
Override parameters such as --width
will be given priority and override the variable dimensions you set using calculateMetadata()
.
The --scale
parameter has the highest priority and will be applied after override parameters and calculateMetadata()
.
Changing dimensions and FPS after the video was designed
If you designed your video with certain dimensions and then want to render a different resolution (e.g. 4K instead of Full HD), you can use output scaling.
If you designed your video with certain FPS and then want to change the frame rate, you should refactor the composition to ensure the timing stays the same with changing frame rates.
With the <Player>
The <Player>
will react if the metadata being passed to it changes. There are two viable ways to do dynamically set the metadata of the Player:
useEffect()
:
MyApp.tsxtsx
import {parseMedia } from '@remotion/media-parser';import {useEffect ,useState } from 'react';import {Player } from '@remotion/player';export constIndex :React .FC = () => {const [duration ,setDuration ] =useState (1);useEffect (() => {parseMedia ({src : 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',fields : {durationInSeconds : true,},}).then (({durationInSeconds }) => {if (durationInSeconds === null) {throw newError ('Could not read duration of video');}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.tsxtsx
import {parseMedia } from '@remotion/media-parser';import {useEffect ,useState } from 'react';import {Player } from '@remotion/player';export constIndex :React .FC = () => {const [duration ,setDuration ] =useState (1);useEffect (() => {parseMedia ({src : 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',fields : {durationInSeconds : true,},}).then (({durationInSeconds }) => {if (durationInSeconds === null) {throw newError ('Could not read duration of video');}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 }/>);};
calculateMetadata()
function reused
from your Remotion project:
MyApp.tsxtsx
import {Player } from '@remotion/player';typeProps = {};constcalculateMetadataFunction :CalculateMetadataFunction <Props > = () => {return {props : {},durationInFrames : 1,width : 100,height : 100,fps : 30,};};typeMetadata = {durationInFrames : number;compositionWidth : number;compositionHeight : number;fps : number;props :Props ;};export constIndex :React .FC = () => {const [metadata ,setMetadata ] =useState <Metadata | null>(null);useEffect (() => {Promise .resolve (calculateMetadataFunction ({defaultProps : {},props : {},abortSignal : newAbortController ().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 asProps ,});}).catch ((err ) => {console .log (`Error fetching metadata: ${err }`);});}, []);if (!metadata ) {return null;}return <Player component ={VideoTesting } {...metadata } />;};
MyApp.tsxtsx
import {Player } from '@remotion/player';typeProps = {};constcalculateMetadataFunction :CalculateMetadataFunction <Props > = () => {return {props : {},durationInFrames : 1,width : 100,height : 100,fps : 30,};};typeMetadata = {durationInFrames : number;compositionWidth : number;compositionHeight : number;fps : number;props :Props ;};export constIndex :React .FC = () => {const [metadata ,setMetadata ] =useState <Metadata | null>(null);useEffect (() => {Promise .resolve (calculateMetadataFunction ({defaultProps : {},props : {},abortSignal : newAbortController ().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 asProps ,});}).catch ((err ) => {console .log (`Error fetching metadata: ${err }`);});}, []);if (!metadata ) {return null;}return <Player component ={VideoTesting } {...metadata } />;};