Skip to main content

<OffthreadVideo>

Available from Remotion 3.0.11

This component imports and displays a video, similar to <Video/>, but during rendering, extracts the exact frame from the video and displays it in a <Img> tag. This extraction process happens outside the browser using FFMPEG.

This component was designed to combat limitations of the default <Video> element. See: <Video> vs <OffthreadVideo>.

Example

tsx
import { AbsoluteFill, OffthreadVideo, staticFile } from "remotion";
 
export const MyVideo = () => {
return (
<AbsoluteFill>
<OffthreadVideo src={staticFile("video.webm")} />
</AbsoluteFill>
);
};
tsx
import { AbsoluteFill, OffthreadVideo, staticFile } from "remotion";
 
export const MyVideo = () => {
return (
<AbsoluteFill>
<OffthreadVideo src={staticFile("video.webm")} />
</AbsoluteFill>
);
};

You can load a video from an URL as well:

tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" />
</AbsoluteFill>
);
};
tsx
export const MyComposition = () => {
return (
<AbsoluteFill>
<OffthreadVideo src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" />
</AbsoluteFill>
);
};

Props

The props volume, playbackRate, muted and acceptableTimeShiftInSeconds are supported and work the same as in <Video>.

The props onError, className and style are supported and get passed to the underlying HTML element. Remember that during render, this is a <img> element, and during Preview, this is a <video> element.

transparent

optional, boolean If set to true, frames will be extracted as PNG, enabling transparency but also slowing down your render.

If set to false(default), frames will be extracted as bitmap (BMP), which is faster.

imageFormat

removed in v4.0.0

Either jpeg or png. Default jpeg.
With png, transparent videos (VP8, VP9, ProRes) can be displayed, however it is around 40% slower, with VP8 videos being much slower.

allowAmplificationDuringRender

Make values for volume greater than 1 result in amplification during renders.
During Preview, the volume will be limited to 1, since the browser cannot amplify audio.

onError

Handle an error playing the video. From v3.3.89, if you pass an onError callback, then no exception will be thrown. Previously, the error could not be caught.

Performance tips

Avoid embedding a video beyond it's end (for example: Rendering a 5 second video inside 10 second composition). To create parity with the <Video> element, the video still displays its last frame in that case. However, to fetch the last frame specifically is a significantly more expensive operation than a frame from a known timestamp.

Looping a video

Unlike <Video>, OffthreadVideo does not currently implement the loop property. You can use the following snippet that uses @remotion/media-utils to loop a video.

LoopedOffthreadVideo.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
import React, { useEffect, useState } from "react";
import {
cancelRender,
continueRender,
delayRender,
Loop,
OffthreadVideo,
staticFile,
useVideoConfig,
} from "remotion";
 
const src = staticFile("myvideo.mp4");
 
export const LoopedOffthreadVideo: React.FC = () => {
const [duration, setDuration] = useState<null | number>(null);
const [handle] = useState(() => delayRender());
const { fps } = useVideoConfig();
 
useEffect(() => {
getVideoMetadata(src)
.then(({ durationInSeconds }) => {
setDuration(durationInSeconds);
continueRender(handle);
})
.catch((err) => {
cancelRender(handle);
console.log(err);
});
}, [handle]);
 
if (duration === null) {
return null;
}
 
return (
<Loop durationInFrames={Math.floor(fps * duration)}>
<OffthreadVideo src={src} />
</Loop>
);
};
LoopedOffthreadVideo.tsx
tsx
import { getVideoMetadata } from "@remotion/media-utils";
import React, { useEffect, useState } from "react";
import {
cancelRender,
continueRender,
delayRender,
Loop,
OffthreadVideo,
staticFile,
useVideoConfig,
} from "remotion";
 
const src = staticFile("myvideo.mp4");
 
export const LoopedOffthreadVideo: React.FC = () => {
const [duration, setDuration] = useState<null | number>(null);
const [handle] = useState(() => delayRender());
const { fps } = useVideoConfig();
 
useEffect(() => {
getVideoMetadata(src)
.then(({ durationInSeconds }) => {
setDuration(durationInSeconds);
continueRender(handle);
})
.catch((err) => {
cancelRender(handle);
console.log(err);
});
}, [handle]);
 
if (duration === null) {
return null;
}
 
return (
<Loop durationInFrames={Math.floor(fps * duration)}>
<OffthreadVideo src={src} />
</Loop>
);
};

See also