Avoiding flickering in <Player>
Consider the following markup:
MyComponent.tsxtsx
import {AbsoluteFill ,Sequence ,Video } from "remotion";constMyComponent :React .FC = () => {return (<AbsoluteFill ><Sequence from ={0}durationInFrames ={120}><Video src ="https://example.com/video1.mp4" /></Sequence ><Sequence from ={120}durationInFrames ={120}><Video src ="https://example.com/video2.mp4" /></Sequence ></AbsoluteFill >);};
MyComponent.tsxtsx
import {AbsoluteFill ,Sequence ,Video } from "remotion";constMyComponent :React .FC = () => {return (<AbsoluteFill ><Sequence from ={0}durationInFrames ={120}><Video src ="https://example.com/video1.mp4" /></Sequence ><Sequence from ={120}durationInFrames ={120}><Video src ="https://example.com/video2.mp4" /></Sequence ></AbsoluteFill >);};
Since Remotion is only aware of the current frame, the video with the source video2.mp4
will only start loading once it starts appearing in the scene. This can lead to an effect in the Player where some frames will be empty, since the loading of the video will usually not complete immediately.
This is a design tradeoff of Remotion, but can be fought with different levels of aggression.
Strategies
It should be mentioned that this effect is only happening in the Remotion Studio and the Remotion Player, and will not appear in the rendered video. If you are only looking for a frame-perfect rendered video, you do not need to take additional steps.
{"
when media is buffering
You may want to pause the <Player>
temporarily to allow loading of the assets and resume once the assets are ready for playback. See here for how to implement this.
You may signal to the browser to preload videos and other assets, so that when the embedded element appears in the video, it can save the network request.
See Preloading for instructions on how to achieve this.
The signal that you give to the browser may be ignored, for example if the device has data saver or battery saver mode enabled. This is specifically a concern for mobile devices.
By prefetching an asset, it will be downloaded and cached into memory. Unlike preloading, you force the browser to download the asset, however, you can only use the loaded asset once it has fully downloaded.
In Safari prefetching as described in
Alternatively, the prefetch()
function allows to fetch an asset and save it in memory as Base64, which does not require the blob URL to be loaded from disk through HTTP.
As the most aggressive strategy, you can pre-mount a video to already trigger decoding of the video. Use the following component to pre-mount a video:
Premount.tsxtsx
importReact , {useContext ,useMemo } from "react";import {Internals ,TimelineContextValue ,useVideoConfig } from "remotion";typePremountProps = {for : number;children :React .ReactNode ;};export constPremount :React .FC <PremountProps > = ({for :premountFor ,children ,}) => {constsequenceContext =useContext (Internals .SequenceContext );const {id } =useVideoConfig ();if (typeofpremountFor === "undefined") {throw newError (`The <Premount /> component requires a 'for' prop, but none was passed.`);}if (typeofpremountFor !== "number") {throw newError (`The 'for' prop of <Premount /> must be a number, but is of type ${typeofpremountFor }`);}if (Number .isNaN (premountFor )) {throw newError (`The 'for' prop of <Premount /> must be a real number, but it is NaN.`);}if (!Number .isFinite (premountFor )) {throw newError (`The 'for' prop of <Premount /> must be a finite number, but it is ${premountFor }.`);}constcontext =useContext (Internals .Timeline .TimelineContext );constvalue :TimelineContextValue =useMemo (() => {constcontextOffset =sequenceContext ?sequenceContext .cumulatedFrom +sequenceContext .relativeFrom : 0;// v3// const currentFrame = context.frame - contextOffset;// v4constcurrentFrame = (context .frame [id ] ?? 0) -contextOffset ;return {...context ,playing :currentFrame <premountFor ? false :context .playing ,imperativePlaying : {current :currentFrame <premountFor ? false:context .imperativePlaying .current ,},// Remotion v4frame : { [id ]:Math .max (0,currentFrame -premountFor ) +contextOffset },// Remotion v3// frame: Math.max(0, currentFrame - premountFor) + contextOffset,};}, [context ,premountFor ,sequenceContext ,id ]);return (<Internals .Timeline .TimelineContext .Provider value ={value }>{children }</Internals .Timeline .TimelineContext .Provider >);};
Premount.tsxtsx
importReact , {useContext ,useMemo } from "react";import {Internals ,TimelineContextValue ,useVideoConfig } from "remotion";typePremountProps = {for : number;children :React .ReactNode ;};export constPremount :React .FC <PremountProps > = ({for :premountFor ,children ,}) => {constsequenceContext =useContext (Internals .SequenceContext );const {id } =useVideoConfig ();if (typeofpremountFor === "undefined") {throw newError (`The <Premount /> component requires a 'for' prop, but none was passed.`);}if (typeofpremountFor !== "number") {throw newError (`The 'for' prop of <Premount /> must be a number, but is of type ${typeofpremountFor }`);}if (Number .isNaN (premountFor )) {throw newError (`The 'for' prop of <Premount /> must be a real number, but it is NaN.`);}if (!Number .isFinite (premountFor )) {throw newError (`The 'for' prop of <Premount /> must be a finite number, but it is ${premountFor }.`);}constcontext =useContext (Internals .Timeline .TimelineContext );constvalue :TimelineContextValue =useMemo (() => {constcontextOffset =sequenceContext ?sequenceContext .cumulatedFrom +sequenceContext .relativeFrom : 0;// v3// const currentFrame = context.frame - contextOffset;// v4constcurrentFrame = (context .frame [id ] ?? 0) -contextOffset ;return {...context ,playing :currentFrame <premountFor ? false :context .playing ,imperativePlaying : {current :currentFrame <premountFor ? false:context .imperativePlaying .current ,},// Remotion v4frame : { [id ]:Math .max (0,currentFrame -premountFor ) +contextOffset },// Remotion v3// frame: Math.max(0, currentFrame - premountFor) + contextOffset,};}, [context ,premountFor ,sequenceContext ,id ]);return (<Internals .Timeline .TimelineContext .Provider value ={value }>{children }</Internals .Timeline .TimelineContext .Provider >);};
The following usage would make the video mount 60 frames before it appears in the scene:
MyComponent.tsxtsx
import {AbsoluteFill ,Sequence ,Video } from "remotion";constMyComponent :React .FC = () => {return (<AbsoluteFill ><Sequence from ={0}durationInFrames ={120}><Video src ="https://example.com/video1.mp4" /></Sequence ><Sequence from ={60}durationInFrames ={180}><Premount for ={60}><Video src ="https://example.com/video2.mp4" /></Premount ></Sequence ></AbsoluteFill >);};
MyComponent.tsxtsx
import {AbsoluteFill ,Sequence ,Video } from "remotion";constMyComponent :React .FC = () => {return (<AbsoluteFill ><Sequence from ={0}durationInFrames ={120}><Video src ="https://example.com/video1.mp4" /></Sequence ><Sequence from ={60}durationInFrames ={180}><Premount for ={60}><Video src ="https://example.com/video2.mp4" /></Premount ></Sequence ></AbsoluteFill >);};
You are responsible yourself for making the video invisible while it is being pre-mounted.