Back to Remotion

Playing videos in sequence

packages/docs/docs/videos/sequence.mdx

4.0.4995.2 KB
Original Source

To play multiple videos one after another:

<Step>1</Step> Render each <Video> in a <Series.Sequence>.

<Step>2</Step> Use Mediabunny to determine the duration of each video.

<Step>3</Step> Return the durations and their sum from calculateMetadata().

Create the video series

Render each <Video> in a <Series.Sequence> and set its durationInFrames:

tsx
import {Video} from '@remotion/media';
import React from 'react';
import {Series, useVideoConfig} from 'remotion';

export type VideoToEmbed = {
  src: string;
  durationInFrames: number | null;
};

export type VideosInSequenceProps = {
  videos: VideoToEmbed[];
};

export const VideosInSequence: React.FC<VideosInSequenceProps> = ({videos}) => {
  const {fps} = useVideoConfig();

  return (
    <Series>
      {videos.map((video) => {
        if (video.durationInFrames === null) {
          throw new Error('Could not get video duration');
        }

        return (
          <Series.Sequence
            key={video.src}
            durationInFrames={video.durationInFrames}
            premountFor={fps}
          >
            <Video src={video.src} />
          </Series.Sequence>
        );
      })}
    </Series>
  );
};

premountFor mounts each video one second before it enters the timeline, giving it time to load in the Player.

Calculate the durations

Create a calculateMetadata() function that gets the duration of each video using Mediabunny's computeDuration(), converts the durations to frames, and returns their sum as the composition duration:

tsx
// @filename: VideosInSequence.tsx
export type VideosInSequenceProps = {
  videos: {src: string; durationInFrames: number | null}[];
};

// @filename: calculate-metadata.ts
// ---cut---
import {ALL_FORMATS, Input, UrlSource} from 'mediabunny';
import type {CalculateMetadataFunction} from 'remotion';
import type {VideosInSequenceProps} from './VideosInSequence';

const getVideoDuration = async (src: string) => {
  const input = new Input({
    formats: ALL_FORMATS,
    source: new UrlSource(src, {
      getRetryDelay: () => null,
    }),
  });

  return input.computeDuration();
};

export const calculateMetadata: CalculateMetadataFunction<
  VideosInSequenceProps
> = async ({props}) => {
  const fps = 30;
  const videos = await Promise.all(
    props.videos.map(async (video) => {
      const durationInSeconds = await getVideoDuration(video.src);

      return {
        ...video,
        durationInFrames: Math.floor(durationInSeconds * fps),
      };
    }),
  );

  const durationInFrames = videos.reduce(
    (sum, video) => sum + video.durationInFrames,
    0,
  );

  return {
    durationInFrames,
    fps,
    props: {
      ...props,
      videos,
    },
  };
};

Mediabunny loads the assets using fetch(). Remote URLs must be CORS-enabled; files referenced with staticFile() are served from the same origin.

Register the composition

In the root file, register the component and pass the videos through defaultProps:

tsx
// @filename: VideosInSequence.tsx
import React from 'react';

export type VideosInSequenceProps = {
  videos: {src: string; durationInFrames: number | null}[];
};

export const VideosInSequence: React.FC<VideosInSequenceProps> = () => null;

// @filename: calculate-metadata.ts
import type {CalculateMetadataFunction} from 'remotion';
import type {VideosInSequenceProps} from './VideosInSequence';

export const calculateMetadata: CalculateMetadataFunction<
  VideosInSequenceProps
> = async ({props}) => ({durationInFrames: 1, props});

// @filename: Root.tsx
// ---cut---
import React from 'react';
import {Composition, staticFile} from 'remotion';
import {calculateMetadata} from './calculate-metadata';
import {VideosInSequence} from './VideosInSequence';

export const RemotionRoot: React.FC = () => {
  return (
    <Composition
      id="VideosInSequence"
      component={VideosInSequence}
      width={1920}
      height={1080}
      defaultProps={{
        videos: [
          {
            durationInFrames: null,
            src: 'https://remotion.media/BigBuckBunny.mp4',
          },
          {
            durationInFrames: null,
            src: staticFile('localvideo.mp4'),
          },
        ],
      }}
      calculateMetadata={calculateMetadata}
    />
  );
};

Browser autoplay policies

Mobile browsers may block videos that start after the beginning of the composition. See the notes about browser autoplay behavior for the available options.

See also