以下の内容はhttps://www.randpy.tokyo/entry/remotion-4-assetsより取得しました。


Remotion で画像・動画・音楽素材を使う例: staticFile と専用コンポーネント【Remotion 第4回】

youtu.be

📚 Remotion シリーズ 全8回の記事一覧

  1. 第1回: 全体像をまとめる
    Composition / AbsoluteFill / Sequence で動画の骨組みを作る
  2. 第2回: Hooks を理解する
    useCurrentFrame / useVideoConfig でフレーム番号と設定を取得する
  3. 第3回: アニメーションを作る
    interpolate / spring でフレーム番号を CSS の値に変換する
  4. ▶ 第4回: 素材を使う(この記事)
    画像・動画・音楽素材を組み込む
  5. 第5回: テロップを入れる
    テキストオーバーレイの作り方
  6. 第6回: シーンを構成する
    Series / TransitionSeries でシーン切り替えとトランジション
  7. 第7回: GitHub Actions で自動レンダリング
    CI/CD で動画レンダリングを自動化する
  8. 第8回: 応用テクニック(準備中)
    GIF・静止画書き出し / FFmpeg カスタマイズ / Lambda / 3D

はじめに

前回までで、Remotion の基本的な仕組みを一通り見てきました。

  • Composition / AbsoluteFill / Sequence で動画の骨組みを作る(第1回
  • useCurrentFrame / useVideoConfig でフレーム番号と設定を取得する(第2回
  • interpolate / spring でフレーム番号を CSS の値に変換する(第3回

ここまではテンプレートのコードを読み解く話でしたが、ここからは 実際に自分の動画を作る ための実践的な機能に入ります。まずは最も基本的な「手元の画像や動画ファイルを Remotion で使う方法」から見ていきましょう。

public フォルダと staticFile

ファイルの置き場所

Remotion で画像・動画・音声ファイルを使うには、プロジェクトの public/ フォルダに置きます。

my-video/
├─ public/
│  ├─ background.jpg      ← 背景画像
│  ├─ intro-clip.mp4      ← 動画素材
│  ├─ bgm.mp3             ← BGM
│  ├─ logo.png            ← ロゴ
│  └─ fonts/
│     └─ NotoSansJP.woff2 ← フォントファイル
├─ src/
│  ├─ Root.tsx
│  └─ ...
├─ package.json

public/remotion 依存がある package.json と同じ階層に置く必要があります。

staticFile() でパスを解決する

public/ のファイルを参照するには staticFile() 関数を使います。

import { staticFile } from "remotion";

const logoUrl = staticFile("/logo.png");
// → "/logo.png" のような URL 文字列を返す

staticFile()単なるパス変換関数 で、引数のファイル名から正しい URL を生成するだけです。Python でいうと pathlib.Path("public") / "logo.png" のイメージですね。

リモート URL も使える

staticFile() を使わずに、インターネット上のファイルを直接指定することもできます。

<Img src="https://example.com/photo.jpg" />
<Video src="https://example.com/clip.mp4" />

Remotion 専用コンポーネント

画像を表示するだけなら HTML の <img> タグでもいいように思えますよね。動画なら <video>

でも Remotion では

  • <Img>
  • <Video>
  • <Audio>

という専用コンポーネントを使います。

理由は レンダリングの確実性 にあります。

問題:レンダリング時に素材が真っ白になる

Remotion が動画をレンダリング(npx remotion render)するとき、内部的にはヘッドレスブラウザで1フレームずつ画面を画像としてキャプチャし、最後にそれを動画に合成していきます。

もし素材の読み込みが完了する前にキャプチャされたら、そのフレームは素材が表示されていない状態(真っ白や黒背景)になってしまいます。

Remotion 版は素材の準備完了を保証する

<Img> は内部で「この画像がロードされるまでフレームのレンダリングを止める」という仕組みを持っています。

<Video><Audio> はさらに踏み込んでいて、レンダリング時に Mediabunny という仕組みで「そのフレームに対応する映像・音声」を正確に抽出します。<Video> の場合は抽出したフレームを <canvas> に描画するので、ブラウザの動画再生に頼らず確実に正しいフレームが表示されます。

まぁ、一旦難しいことは置いておいて、専用のタグを使えばいいと思いましょう!

使い分けまとめ

やりたいこと 使うもの import 元
画像を表示 <Img> remotion
動画を埋め込み <Video> @remotion/media
音声を再生 <Audio> @remotion/media

また、サーバーサイドレンダリングで正確なフレーム抽出が必要な場合は <OffthreadVideo>remotion から import)という選択肢もあるようです。

画像を表示する:<Img>

基本的な使い方

import { Img, staticFile } from "remotion";

export const MyScene: React.FC = () => {
  return <Img src={staticFile("/background.jpg")} />;
};

これだけで public/background.jpg が表示されます。

サイズと位置の指定

CSS の style でサイズと位置を制御します。

<Img
  src={staticFile("/logo.png")}
  style={{
    width: 200,
    height: 200,
    position: "absolute",
    top: 50,
    left: 50,
  }}
/>

画像をキャンバス全体に敷き詰める

背景画像として使いたい場合はこうします。

import { AbsoluteFill, Img, staticFile } from "remotion";

export const BackgroundImage: React.FC = () => {
  return (
    <AbsoluteFill>
      <Img
        src={staticFile("/background.jpg")}
        style={{
          width: "100%",
          height: "100%",
          objectFit: "cover",
        }}
      />
    </AbsoluteFill>
  );
};

objectFit は画像のフィット方法を指定する CSS プロパティです。

動作 Python 画像処理でいうと
"cover" アスペクト比を保ちつつキャンバスを埋める(はみ出た部分はクロップ) ImageOps.fit()
"contain" アスペクト比を保ちつつキャンバスに収める(余白ができる) Image.thumbnail()
"fill" アスペクト比を無視して引き伸ばす Image.resize()

動画の背景に使うなら cover が一番良さそうです。

画像にアニメーションをつける

<Img> も React コンポーネントなので、style にフレーム番号から計算した値を入れればアニメーションできます。

import { AbsoluteFill, Img, staticFile, useCurrentFrame, interpolate } from "remotion";

export const ZoomingBackground: React.FC = () => {
  const frame = useCurrentFrame();

  // 0フレーム目: 100% → 150フレーム目: 120% にゆっくりズーム
  const scale = interpolate(frame, [0, 150], [1, 1.2]);

  return (
    <AbsoluteFill>
      <Img
        src={staticFile("/landscape.jpg")}
        style={{
          width: "100%",
          height: "100%",
          objectFit: "cover",
          transform: `scale(${scale})`,
        }}
      />
    </AbsoluteFill>
  );
};
frame:    0 ------- 150
scale:    1.0       1.2
          ↑ じわじわとズームイン

これは写真のスライドショーでよく見る「Ken Burns 効果」です。静止画にゆっくりズームをかけることで、動きがないはずの写真に動画的な演出が加わります。

interpolate の入出力を変えれば、パン(横スクロール)も作れます。

const translateX = interpolate(frame, [0, 150], [0, -100]);

style={{ transform: `translateX(${translateX}px)` }}

動的なパス:連番画像

テンプレートリテラルでフレームごとに画像を切り替えられます。

const frame = useCurrentFrame();

// フレームごとに異なる画像を表示(パラパラ漫画)
<Img src={staticFile(`/frames/frame${frame}.png`)} />

Python でいう f"frames/frame{frame}.png" と同じです。

public/frames/frame0.png, frame1.png, ... と連番ファイルを置いておけば、フレーム番号に対応した画像が自動で表示されます。

動画を埋め込む:<Video>

パッケージのインストール

<Video>@remotion/media パッケージに入っています。プロジェクト作成直後にはないので、最初にインストールが必要です。

npx remotion add @remotion/media

基本的な使い方

import { Video } from "@remotion/media";
import { staticFile } from "remotion";

export const MyScene: React.FC = () => {
  return <Video src={staticFile("/intro-clip.mp4")} />;
};

「同期して再生される」とは

Remotion に埋め込んだ動画は、Composition のタイムラインに 完全に同期 して再生されます。

普通の Web ページに <video> を置いた場合、動画は「再生ボタンを押したら自分のペースで再生する」独立した存在ですよね。でも Remotion では、フレーム番号と動画の再生位置が常に一致します。

トリミング:一部だけ使う

30秒の動画素材のうち、2秒目から10秒目だけを使いたい場合はこうします。

const { fps } = useVideoConfig();

<Video
  src={staticFile("/interview.mp4")}
  trimBefore={2 * fps}   // 最初の2秒をスキップ
  trimAfter={10 * fps}   // 10秒目で終了
/>

trimBeforetrimAfter の値は フレーム数 で指定します。「秒 × fps」でフレーム数に変換すると簡単です。

遅延表示:動画を途中から登場させる

Composition の開始と同時ではなく、少し遅れて動画を表示したい場合は <Sequence> で囲みます。

import { Sequence } from "remotion";
import { Video } from "@remotion/media";

const { fps } = useVideoConfig();

<Sequence from={2 * fps}>
  <Video src={staticFile("/intro-clip.mp4")} />
</Sequence>

以下の記事で説明した通り、<Sequence> の中では useCurrentFrame() がローカル時間(0 から始まる)を返します。<Video> も同じ仕組みで、Sequence の開始をフレーム0として再生を始めます。

www.randpy.tokyo

サイズと位置

<Video>style でサイズと位置を制御できます。

<Video
  src={staticFile("/clip.mp4")}
  style={{
    width: 640,
    height: 360,
    position: "absolute",
    top: 100,
    left: 200,
    objectFit: "cover",
    borderRadius: 16,
  }}
/>

borderRadius をつけると角丸動画になります。objectFit は画像と同じように使えます。

音量の制御

// 固定音量(50%)
<Video src={staticFile("/clip.mp4")} volume={0.5} />

// 完全に無音にする
<Video src={staticFile("/clip.mp4")} muted />

volume にはコールバック関数も渡せます。引数 f はその動画の再生開始からのフレーム数です。

const { fps } = useVideoConfig();

// 最初の1秒でフェードイン
<Video
  src={staticFile("/clip.mp4")}
  volume={(f) =>
    interpolate(f, [0, 1 * fps], [0, 1], { extrapolateRight: "clamp" })
  }
/>

ここで interpolate が登場するのが面白いですね。前回の記事で opacity のフェードインに使ったのと全く同じ関数を、音量のフェードインにも使えます。

www.randpy.tokyo

再生速度

// 2倍速
<Video src={staticFile("/clip.mp4")} playbackRate={2} />

// 0.5倍速(スローモーション)
<Video src={staticFile("/clip.mp4")} playbackRate={0.5} />

playbackRate={2} にすると、動画は2倍速で再生されます。つまり同じフレーム数の Sequence に入れた場合、動画の2倍の範囲が再生されることになります。

ループ

// 無限ループ
<Video src={staticFile("/clip.mp4")} loop />

短い動画素材を繰り返し再生したいときに使います。Composition の durationInFrames が動画の長さより長くても、ループで埋めてくれます。

音声を再生する:<Audio>

<Audio><Video> と同じ @remotion/media パッケージに入っています。

基本的な使い方

import { Audio } from "@remotion/media";
import { staticFile } from "remotion";

export const MyScene: React.FC = () => {
  return <Audio src={staticFile("/bgm.mp3")} />;
};

BGM や効果音を Composition のタイムラインに同期して再生できます。画面には何も表示されませんが、<Video> と同じようにフレームに完全同期して再生されます。

主な props

<Audio> の props は <Video> とほぼ同じです。

prop 説明
volume 音量(0〜1、またはコールバック関数) volume={0.3}
muted ミュートする muted
loop ループ再生する loop
playbackRate 再生速度 playbackRate={1.5}
trimBefore 開始位置(フレーム数) trimBefore={2 * fps}
trimAfter 終了位置(フレーム数) trimAfter={10 * fps}

効果音を特定のタイミングで鳴らす

BGM は動画全体に敷くことが多いですが、効果音は特定のタイミングで鳴らしたいですよね。これも <Sequence> で囲むだけです。

import { Sequence } from "remotion";
import { Audio } from "@remotion/media";

const { fps } = useVideoConfig();

// 3秒目に効果音を鳴らす
<Sequence from={3 * fps}>
  <Audio src={staticFile("/se-pop.mp3")} volume={0.5} />
</Sequence>

<Video> の遅延表示と全く同じパターンですね。

まとめ

ここまで見てきて分かったのは、素材の扱いも Remotion らしくシンプル ということです。

概念 何をするか ポイント
public/ 素材ファイルの置き場所 プロジェクトルート直下
staticFile() public/ のファイルを URL に変換 デプロイ時のパス解決を吸収
<Img> 画像を表示 HTML の <img> ではなくこちらを使う
<Video> 動画を埋め込み フレーム同期、トリミング、音量制御が可能
<Audio> 音声を再生 BGM・効果音に。<Video> と同じ props が使える

一番大事なポイントは なぜ Remotion 専用コンポーネントを使うのか で、レンダリング時に素材の準備完了を保証するためでした。<Img> はロード完了を待ち、<Video> / <Audio> は Mediabunny で正確なフレームの映像・音声を抽出します。HTML ネイティブの <img><video> を使うと、レンダリング時に真っ白なフレームが生成される可能性があります。ここだけ押さえておけば、あとは普通の React と同じ感覚で使えますね。

サンプルアニメーションとソースコード

これを組み合わせると、以下みたいな動画は簡単に作ることができます。

youtu.be

コンポーネント部分だけですが、ソースコードも Gist で一応置いておきます

https://gist.github.com/chan-ume/f3aa0bdaa5e86476c9db3c49ea6e55cc



次の第5回は、これらの素材の上に重ねる「テロップ(テキストオーバーレイ)」の作り方を見ていきます。

www.randpy.tokyo




以上の内容はhttps://www.randpy.tokyo/entry/remotion-4-assetsより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14