Remotion LabRemotion Lab
媒體整合影片剪輯:裁切、拼接、配樂——把多段素材做成一支完整影片
videoaudioeditingoffthreadvideointermediate

影片剪輯:裁切、拼接、配樂——把多段素材做成一支完整影片

用 Remotion 做剪輯:多段影片拼接、跳剪、凍結畫面、配樂淡入淡出、播放速度調整。學完能把原本要進剪映/Premiere 的工作搬進 React 程式碼裡。

成品預覽

這就是你跟著教學能做出來的成品——一支 15 秒的科技 YouTuber 頻道 intro/剪輯示範。開場 talking head、跳接到 SaaS 縮圖、2× 加速 coding 蒙太奇、凍結戲劇性瞬間、Subscribe 收尾,五個剪輯技巧都用 React + interpolate 做出來,完全沒有任何外部影音檔案。


這篇會做出什麼

一支 30 秒的影片剪輯示範,把四段原始素材做成一支有節奏的片子:

  1. 0~6 秒:Clip A 開場(原始影片的 10~16 秒段落)
  2. 6~12 秒:Clip B 接上(另一段 0~6 秒,加速 1.5 倍)
  3. 12~18 秒:Clip C 慢動作(0.5 倍速)
  4. 18~24 秒:Clip D 凍結最後一幀做「定格結尾」
  5. 全程:背景音樂淡入 → 中間降 ducking → 結尾淡出

做完你會發現:剪輯 = 切片 + 時間控制 + 音訊混音,三件事用 React 就能搞定。


前置知識

素材準備

  • 四段 .mp4 影片(各自 10 秒以上就好)
  • 一首 .mp3 背景音樂
  • 放在 public/clips/public/music/

Step 1:載入第一段影片

Claude Code:

新增 Composition "VideoEditingDemo":
- 1920x1080
- fps 30
- durationInFrames 900(30 秒)
- 新建 src/compositions/VideoEditingDemo.tsx
- 在 src/Root.tsx 註冊

元件內先:
1. 從 remotion import OffthreadVideo, staticFile
2. 用 <OffthreadVideo src={staticFile("clips/a.mp4")} /> 播第一段
3. 全螢幕填滿

為什麼用 <OffthreadVideo> 而不是 <Video> <Video> 會用瀏覽器的 <video> 元素,渲染時容易被 Chromium 限制同時開啟的 video 數量卡住。<OffthreadVideo> 在 worker thread 解碼,渲染速度更快,尤其是多段影片拼接。

💡 <OffthreadVideo> vs <Video> 完整差異在 /docs/offthreadvideo/docs/video-and-audio


Step 2:只播影片的特定段落(裁切)

A 段影片原本 30 秒,但我們只要第 10~16 秒。用 startFrom / endAt

把第一段影片改成只播第 10~16 秒:

1. <OffthreadVideo> 加 startFrom={10 * 30}
2. 加 endAt={16 * 30}
3. 用 <Sequence from={0} durationInFrames={180}> 包住
   (從 0 秒開始播 6 秒)

startFrom 的單位:是原始影片的幀數,不是 composition 的幀數。這個常搞混。假設影片是 30fps,第 10 秒就是 startFrom={300}

💡 影片裁切、startFromendAt 的細節在 /docs/video-sequence


Step 3:拼接第二段(跳剪)

在 A 段結束後接上 B 段:

1. 新增 <Sequence from={180} durationInFrames={180}>
2. 裡面放 <OffthreadVideo src={staticFile("clips/b.mp4")}
          startFrom={0}
          endAt={180}
          playbackRate={1.5} />
3. playbackRate 1.5 = 播放速度 1.5 倍(6 秒原片壓成 4 秒)
4. 但是我們 Sequence 只給 6 秒,4 秒的內容會剛好填滿(因為加速)

注意playbackRate > 1 時,startFromendAt 還是原片的時間座標。加速只影響播放速度,不影響裁切位置。

💡 playbackRate 的數學與陷阱在 /docs/video-playback-rate。跳剪範式在 /docs/video-jumpcuts


Step 4:慢動作 C 段

接上 C 段(慢動作):

1. <Sequence from={360} durationInFrames={180}>
2. <OffthreadVideo src={staticFile("clips/c.mp4")}
     startFrom={0}
     endAt={90}
     playbackRate={0.5} />
3. 原片 3 秒 ÷ 0.5 = 6 秒,剛好填滿

慢動作的品質:如果原片是 30fps,慢到 0.5 倍後實際上每個「動作」會被重複 2 幀。要真的滑順需要源片是 60fps 以上。


Step 5:D 段定格結尾

最後一段最酷——影片播 4 秒後凍結最後一幀再多停 2 秒:

接上 D 段 + 凍結結尾:

1. <Sequence from={540} durationInFrames={180}>
2. 前 4 秒正常播放:
   用 <Freeze frame={119}> 外層包住
3. <Freeze frame={X}> 會在指定 frame 凍結裡面的時間
   0~120 frame 正常播(4 秒),120~180 frame 凍結在第 119 幀

寫法:
<Sequence from={540} durationInFrames={180}>
  <VideoWithFreeze
    src={staticFile("clips/d.mp4")}
    freezeAfterFrame={120}
  />
</Sequence>

VideoWithFreeze 元件:
- 如果 useCurrentFrame() < freezeAfterFrame → 正常播
- 否則 → 用 <Freeze frame={freezeAfterFrame}> 包 OffthreadVideo

<Freeze> 的用途:做「定格」(關鍵動作停住)或「時間暫停」特效。比自己用 CSS paused hack 乾淨太多。

💡 <Freeze>video-align-duration 的完整用法在 /docs/video-freeze/docs/video-align-duration


Step 6:加背景音樂

在 VideoEditingDemo 根層加背景音樂:

1. <Audio src={staticFile("music/bgm.mp3")} />
2. 讓它播整個 30 秒
3. 用 volume prop 做淡入淡出:
   volume={(frame) => {
     // 0~30 淡入
     if (frame < 30) return interpolate(frame, [0, 30], [0, 0.7]);
     // 最後 1 秒淡出
     if (frame > 870) return interpolate(frame, [870, 900], [0.7, 0]);
     // 中間 Clip B 時 ducking 到 0.3
     if (frame > 180 && frame < 360) return 0.3;
     return 0.7;
   }}

為什麼 volume 用 function? <Audio volume> 可以傳數字(固定音量)或函式(每幀不同音量)。用函式就能做淡入淡出、ducking、節拍同步的音量起伏。

💡 <Audio volume> 函式簽名、動態音量、audio-mutingaudio-trimming 等進階技巧在 /docs/audio-volume/docs/audio-muting/docs/audio-trimming


Step 7:讓影片素材原本的聲音也保留

到目前為止 <OffthreadVideo> 的原聲是預設播放的。但第二段加速了,聲音會變調——Chipmunk 效果。用 muted 關掉:

把 B 段和 C 段的原聲關掉(保留 A、D 段原聲):

1. B 段 <OffthreadVideo muted />
2. C 段 <OffthreadVideo muted />
3. A、D 段保留原聲但用 volume={0.4}
   避免蓋過背景音樂

加速 / 慢速時的音訊處理

  • playbackRate 會改變音高(像轉盤機)
  • 想要「視訊變速但音訊保持」得用 @remotion/media-parser 分離處理,進階題

Step 8:加一個過場黑幀

段落之間加個 3 幀的純黑 frame 當分隔(像紀錄片那樣):

在每個 Sequence 之間加黑幀分隔:

1. 用 <Sequence from={177} durationInFrames={3}>
   <AbsoluteFill style={{ backgroundColor: "black" }} />
2. 類似做 from={357, 537, 717}
3. 這樣段落切換時有 0.1 秒純黑,節奏更明顯

比起 cross-fade,硬切 + 黑幀在紀錄片、訪談片最常見。


Step 9:渲染

渲染 VideoEditingDemo:
- 1920x1080 H.264
- CRF 20
- 輸出 out/video-editing-demo.mp4
- concurrency 4(多段影片拼接吃 CPU)

如果渲染很慢或出現 media-playback-error,看 /docs/media-playback-error/docs/non-seekable-media


完成!回顧學到的概念

概念用在哪
<OffthreadVideo> 多段影片拼接Step 1~5
startFrom / endAt 裁切Step 2
playbackRate 加速 / 慢動作Step 3、4
<Freeze> 凍結畫面Step 5
<Audio volume> 動態函式Step 6
原片音訊 muted 控制Step 7
黑幀分隔段落Step 8

本篇涵蓋的官方文件


常見問題

Q:為什麼渲染時 Remotion 抱怨「video took too long to load」? A:通常是 <OffthreadVideo> 的檔案太大或格式太刁鑽。解法:用 ffmpeg 先轉成 H.264 yuv420p 30fps 的「標準 MP4」。Remotion 對這種格式最順。

Q:playbackRate < 1 時聲音會走音? A:會。這是 <OffthreadVideo> 的已知行為——playbackRate 會同時影響音訊播放速度跟音高。想保持音高就 muted,另外放一軌正常速度的 <Audio>

Q:我想在影片上疊字幕,要怎麼做? A:直接在 <OffthreadVideo> 外面用 <AbsoluteFill> 疊一層文字。因為都是 React,疊層就是寫 JSX。看 T12 短影音自動上字幕

Q:從影片抽出音訊當配音? A:可以。用 <Audio src={staticFile("clips/a.mp4")} />——Remotion 會自動只取音訊軌。見 /docs/audio-from-video


下一步

有問題歡迎到 FB 社群 討論!