Playing videos in sequence
If you would like to play multiple videos in sequence, you can:
Define a component that renders a<Series>
of <OffthreadVideo>
components.
calculateMetadata()
function that fetches the duration of each video.
<Composition>
that specifies a list of videos.
Basic example
Start off by creating a component that renders a list of videos using the <Series>
and <OffthreadVideo>
component:
VideosInSequence.tsxtsx
importReact from 'react';import {OffthreadVideo ,Series } from 'remotion';typeVideoToEmbed = {src : string;durationInFrames : number | null;};typeProps = {videos :VideoToEmbed [];};export constVideosInSequence :React .FC <Props > = ({videos }) => {return (<Series >{videos .map ((vid ) => {if (vid .durationInFrames === null) {throw newError ('Could not get video duration');}return (<Series .Sequence key ={vid .src }durationInFrames ={vid .durationInFrames }><OffthreadVideo src ={vid .src } /></Series .Sequence >);})}</Series >);};
VideosInSequence.tsxtsx
importReact from 'react';import {OffthreadVideo ,Series } from 'remotion';typeVideoToEmbed = {src : string;durationInFrames : number | null;};typeProps = {videos :VideoToEmbed [];};export constVideosInSequence :React .FC <Props > = ({videos }) => {return (<Series >{videos .map ((vid ) => {if (vid .durationInFrames === null) {throw newError ('Could not get video duration');}return (<Series .Sequence key ={vid .src }durationInFrames ={vid .durationInFrames }><OffthreadVideo src ={vid .src } /></Series .Sequence >);})}</Series >);};
In the same file, create a function that calculates the metadata for the composition:
CallsparseMedia()
to get the duration of
each video.
Create a calculateMetadata()
function that fetches the duration of each video.
Sums up all durations to get the total duration of the
composition.
VideosInSequence.tsxtsx
export constcalculateMetadata :CalculateMetadataFunction <Props > = async ({props ,}) => {constfps = 30;constvideos = awaitPromise .all ([...props .videos .map (async (video ):Promise <VideoToEmbed > => {const {durationInSeconds } = awaitparseMedia ({src :video .src ,fields : {durationInSeconds : true,},});if (durationInSeconds === null) {throw newError (`Could not get video duration of ${durationInSeconds }`);}return {durationInFrames :Math .floor (durationInSeconds *fps ),src :video .src ,};}),]);consttotalDurationInFrames =videos .reduce ((acc ,video ) =>acc + (video .durationInFrames ?? 0),0,);return {props : {...props ,videos ,},fps ,durationInFrames :totalDurationInFrames ,};};
VideosInSequence.tsxtsx
export constcalculateMetadata :CalculateMetadataFunction <Props > = async ({props ,}) => {constfps = 30;constvideos = awaitPromise .all ([...props .videos .map (async (video ):Promise <VideoToEmbed > => {const {durationInSeconds } = awaitparseMedia ({src :video .src ,fields : {durationInSeconds : true,},});if (durationInSeconds === null) {throw newError (`Could not get video duration of ${durationInSeconds }`);}return {durationInFrames :Math .floor (durationInSeconds *fps ),src :video .src ,};}),]);consttotalDurationInFrames =videos .reduce ((acc ,video ) =>acc + (video .durationInFrames ?? 0),0,);return {props : {...props ,videos ,},fps ,durationInFrames :totalDurationInFrames ,};};
In your root file, create a <Composition>
that uses the VideosInSequence
component and the exported calculateMetadata
function:
Root.tsxtsx
importReact from 'react';import {Composition ,staticFile } from 'remotion';import {VideosInSequence ,calculateMetadata } from './VideosInSequence';export constRoot :React .FC = () => {return (<Composition id ="VideosInSequence"component ={VideosInSequence }width ={1920}height ={1080}defaultProps ={{videos : [{durationInFrames : null,src : 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',},{durationInFrames : null,src :staticFile ('localvideo.mp4'),},],}}calculateMetadata ={calculateMetadata }/>);};
Root.tsxtsx
importReact from 'react';import {Composition ,staticFile } from 'remotion';import {VideosInSequence ,calculateMetadata } from './VideosInSequence';export constRoot :React .FC = () => {return (<Composition id ="VideosInSequence"component ={VideosInSequence }width ={1920}height ={1080}defaultProps ={{videos : [{durationInFrames : null,src : 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',},{durationInFrames : null,src :staticFile ('localvideo.mp4'),},],}}calculateMetadata ={calculateMetadata }/>);};
Adding premounting
If you only care about the video looking smooth when rendered, you may skip this step.
If you also want smooth preview playback in the Player, consider this:
A video will only load when it is about to be played.
To create a smoother preview playback, we should do two things to all videos:
premountFor
prop to <Series.Sequence>
. This will
invisibly mount the video tag before it is played, giving it some time to load.
Add the pauseWhenBuffering
prop. This will transition the Player into a buffering state, should the video still need to load.
VideosInSequence.tsxtsx
export constVideosInSequence :React .FC <Props > = ({videos }) => {const {fps } =useVideoConfig ();return (<Series >{videos .map ((vid ) => {if (vid .durationInFrames === null) {throw newError ('Could not get video duration');}return (<Series .Sequence key ={vid .src }premountFor ={4 *fps }durationInFrames ={vid .durationInFrames }><OffthreadVideo pauseWhenBuffering src ={vid .src } /></Series .Sequence >);})}</Series >);};
VideosInSequence.tsxtsx
export constVideosInSequence :React .FC <Props > = ({videos }) => {const {fps } =useVideoConfig ();return (<Series >{videos .map ((vid ) => {if (vid .durationInFrames === null) {throw newError ('Could not get video duration');}return (<Series .Sequence key ={vid .src }premountFor ={4 *fps }durationInFrames ={vid .durationInFrames }><OffthreadVideo pauseWhenBuffering src ={vid .src } /></Series .Sequence >);})}</Series >);};
Browser autoplay policies
Mobile browsers are more aggressive in blocking autoplaying videos that enter after the start of the composition.
If you want to ensure a smooth playback experience for all videos, also read the notes about browser autoplay behavior and customize the behavior if needed.