依序播放多段影片
學習如何在 Remotion 中使用 Series 和 OffthreadVideo 將多段影片依序串接播放,並透過 calculateMetadata 動態計算總時長
依序播放多段影片
在 Remotion 中,你可以輕鬆將多段影片串接起來依序播放。本文介紹完整的實作方式,包含基礎範例、動態時長計算,以及讓預覽播放更流暢的預先掛載技巧。
實作架構概覽
整體流程分為三個步驟:
- 建立一個使用
<Series>和<OffthreadVideo>渲染多段影片的元件 - 撰寫
calculateMetadata()函式,自動抓取每段影片的時長 - 在
<Composition>中註冊元件並傳入影片清單
基礎範例
建立序列影片元件
首先建立一個元件,透過 <Series> 和 <OffthreadVideo> 渲染影片清單:
import React from 'react';
import {OffthreadVideo, Series} from 'remotion';
type VideoToEmbed = {
src: string;
durationInFrames: number | null;
};
type Props = {
videos: VideoToEmbed[];
};
export const VideosInSequence: React.FC<Props> = ({videos}) => {
return (
<Series>
{videos.map((vid) => {
if (vid.durationInFrames === null) {
throw new Error('Could not get video duration');
}
return (
<Series.Sequence key={vid.src} durationInFrames={vid.durationInFrames}>
<OffthreadVideo src={vid.src} />
</Series.Sequence>
);
})}
</Series>
);
};撰寫 calculateMetadata 函式
在同一個檔案中,建立計算合成元資料的函式:
- 呼叫
parseMedia()取得每段影片的時長 - 將所有時長加總,算出合成的總時長
import {CalculateMetadataFunction} from 'remotion';
import {parseMedia} from '@remotion/media-parser';
export const calculateMetadata: CalculateMetadataFunction<Props> = async ({props}) => {
const fps = 30;
const videos = await Promise.all([
...props.videos.map(async (video): Promise<VideoToEmbed> => {
const {slowDurationInSeconds} = await parseMedia({
src: video.src,
fields: {
slowDurationInSeconds: true,
},
});
return {
durationInFrames: Math.floor(slowDurationInSeconds * fps),
src: video.src,
};
}),
]);
const totalDurationInFrames = videos.reduce(
(acc, video) => acc + (video.durationInFrames ?? 0),
0
);
return {
props: {
...props,
videos,
},
fps,
durationInFrames: totalDurationInFrames,
};
};在根元件中註冊合成
在你的根元件(Root.tsx)中,建立一個使用 VideosInSequence 元件的 <Composition>:
import React from 'react';
import {Composition, staticFile} from 'remotion';
import {VideosInSequence, calculateMetadata} from './VideosInSequence';
export const Root: 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}
/>
);
};加入預先掛載(Premounting)
若你只在意渲染輸出的品質而不在意瀏覽器預覽的流暢度,可以跳過此步驟。
若想讓 Player 元件的預覽播放更流暢,需要考慮以下問題:
影片只有在即將播放時才會開始載入,這會導致在切換到下一段影片時出現短暫的空白或卡頓。
解決方式:對所有影片做兩件事:
- 在
<Series.Sequence>加上premountForprop,讓影片標籤在播放前就在背景悄悄載入 - 加上
pauseWhenBufferingprop,讓 Player 在影片尚未載入完成時自動進入緩衝狀態
import {useVideoConfig} from 'remotion';
export const VideosInSequence: React.FC<Props> = ({videos}) => {
const {fps} = useVideoConfig();
return (
<Series>
{videos.map((vid) => {
if (vid.durationInFrames === null) {
throw new Error('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>
);
};premountFor={4 * fps}表示在影片開始播放前 4 秒就預先掛載 DOM 元素pauseWhenBuffering確保影片未就緒時 Player 會暫停等待,而非強行播放
重點整理
<Series>和<Series.Sequence>是 Remotion 官方建議的多段影片串接方案calculateMetadata讓合成時長動態對應所有影片加總後的實際時長premountFor和pauseWhenBuffering搭配使用,可大幅改善 Player 的播放流暢度- 渲染輸出永遠是逐幀精確的,這些最佳化只影響瀏覽器預覽體驗