5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

動画を作りたい Web Developer におすすめ。「Remotion」を紹介するよ

5
Posted at

Remotionっていう「Reactで動画を作れるパッケージ」があって、すごく面白かったからその紹介をするよ。

記事中の画像はみんな、自分が Remotion で作った動画からのスクショなんだ。その動画(の縦長版)はこれ

Remotionのこと1つずつ書いていくね。

Remotionってなに?

公式ドキュメントはこれ。

Remotionでは、JSXとCSSで書いた画面が1フレームずつ描画されて、最終的にmp4になる。

<Composition id="MyVideo" component={MyVideo} durationInFrames={150} fps={30} />

これで、30fpsで150フレーム...つまり5秒の動画の枠組みができた。あとは MyVideo を普通のReactコンポーネントとして書けば好きな動画を作れる。

02-concept.png

コンポーネント設計がそのまま動画の構成になるから、タイトルや本編、アウトロを部品に切って組み合わせられる。React で Web アプリケーションを書くみたいに動画を作れるよ ( なんなら Web アプリケーションを書くみたいにというか、Web アプリケーションが動画になると言った方が正しい )

アニメーションをどう書くか

シンプルなアプローチ。
useCurrentFrame() で「今が何枚目か」を取る。それだけだよ。

const frame = useCurrentFrame();
const opacity = interpolate(frame, [0, 30, 120, 150], [0, 1, 1, 0]);
const x = interpolate(frame, [0, 150], [-140, 140]);

interpolate は入力と出力の配列を対応づけて補間してくれる関数だね。配列を伸ばせば、フェードイン→ホールド→フェードアウトが1本で書けるよ。

03-frame.gif

見ての通り、stateもランダム性も使っていない。同じframeなら、何度再生しても必ず同じ絵になるんだね。

バネっぽい動きが欲しいときは spring() だよ。damping が小さいほど大きく跳ねる。

spring({ frame, fps, config: { damping } })

04-spring.gif

左から damping: 4 / 12 / 30。4は1.5倍あたりまで行き過ぎてから戻ってきて、30は行き過ぎずに寄っていくね。変えたのは数値ひとつだけだよ。

複数シーンをどう並べるか

さっき「タイトル」「本編」「アウトロ」と書いたけど、動画ってたいてい、こういういくつかのシーンでできているよね。じゃあ、それをどう並べるか。

<Series> の中に <Series.Sequence> を置いて、それぞれの長さを書くだけでいいよ。

<Series>
  <Series.Sequence durationInFrames={60}><TitleScene /></Series.Sequence>
  <Series.Sequence durationInFrames={90}><MainScene /></Series.Sequence>
  <Series.Sequence durationInFrames={60}><OutroScene /></Series.Sequence>
</Series>

Sequenceの中では useCurrentFrame() が0から始まる。 CSSの position: relative の時間版だと思ってもらえるといいかな。それぞれのシーンは、自分が動画全体のどこに置かれているかを知らないまま書ける。

この仕組みは、あとからシーンの長さを変えたくなったときに便利。動画作りって、その「ここちょっと伸ばしたい」を延々やる作業なんだよね。

もし動画全体の通し番号でフレームを指定していたら、前のシーンを60フレーム伸ばした瞬間に、以降のシーン全部の数値を書き換える羽目になる。Sequenceなら子のコードに影響が跳ねたりしない。

あとSequenceは、範囲外のシーンをアンマウントしてくれる(出番前の <Audio> が勝手に鳴り出さない)し、重ねることもできる(2つのシーンが同時に存在できるから、クロスフェードが成立する)。「今のframeがこの範囲ならこのシーンを出す」と自前のif文で書き分けていたら、この2つは手に入らないよ。

書き出しって、何をやってるの?

ここまでで動画は組めた。じゃあそれが、どうやってmp4になるんだろう。

ここもすごくシンプルなアプローチを採用してるんだよね。ブラウザで開いて、1フレームずつスクリーンショットを撮っているだけ。最後にffmpegで繋いでmp4になる。

07-render-pipeline.png

素朴だよね。でもこの素朴さのおかげで、ブラウザが描けるものは全部そのまま映像になるんだ。CSS、SVG、Canvas、WebGL、Web Font...つまり Remotion における表現力の限界は、ブラウザの表現力の限界そのものとほとんど同義。新しいCSSが使えるようになったら、その日から動画でも使えるよ。

ただ、ひとつ落とし穴がある。それは、実時間は流れていないということ。

1フレームずつ「座らせて」撮っているから、CSSの transitionsetInterval も進まない。普段の感覚でCSSアニメを書くと、書き出した動画は完全に止まった絵になるよ。

だから時間は必ず useCurrentFrame() から取ろう。

動画の書き出しに Lambda を使える

さっき「同じframeなら必ず同じ絵になる」と書いたよね。これはつまり、500枚目を作るのに499枚目の結果がいらないということだよ。

順番に作る必要がない。バラバラのマシンで同時に作れる。

だから例えば、Lambda を大量に起動して、並列で一気に動画の書き出しをすることもできる。
Remotion自身がそのための パッケージ 「Remotion Lambda」を出している。

動画をchunkに割って、Lambdaに散らして、最後に繋いでくれるよ。

08-parallel-render.gif

上の動画におけるオレンジ色のラインが「1台で順番に」動画の書き出しを行った時のイメージ。一方で、その下の6本が並列に散らしたchunkだよ。並列側が全部終わって結合まで済んでも、1台のほうはまだ先が長いね。

呼び出し方はこんな感じ。書き出しを投げて、あとは進捗を読むだけだよ。

const { renderId, bucketName } = await renderMediaOnLambda({ /* ... */ });

// 進捗を読むのはこう(リアルタイムでレンダリング状況を書き出したいならこれをポーリング)
const p = await getRenderProgress({ renderId, bucketName });
p.overallProgress; // 0 → 1

Lambdaの台数を増やせば、尺が伸びても書き出し時間をほぼ横ばいにできるよ。

※ただし、各chunkは参照アセットを個別にダウンロードするからね。200並列なら同じ画像を最大200回取りに行くことになるから、外部アセットはCDNに置こう。自分で自分にDDoSを仕掛ける羽目になるからね。

Remotion Lambda において、Lambdaには何がデプロイされてるの?

Lambdaが全てを担うわけじゃない。デプロイは2つに分かれているんだ。

  • deployFunction() … 汎用レンダラー(エンジン + ヘッドレスChrome)。動画そのもののコードは1行も入っていない
  • deploySite() … S3上の静的サイト。バンドルされた自分の動画コード

レンダラーがそのURLをブラウザで開いて、1フレームずつ撮る。さっきの「連続スクリーンショット」が、そのままクラウドに載っただけだね。

09-deploy.png

仮に前者と後者が一体化してたら、動画を1行直すたびにLambdaの再デプロイが必要になっちゃってすごく手間だよね。でも、分かれていれば、動画側の変更はS3に上げ直すだけで済む。実用的だよね。

まとめ

  • 時間は useCurrentFrame() から取るよ。CSSの transitionsetInterval も書き出し時には進まないから、そこだけは普段のWeb開発と違うから気をつけてね
  • Sequence の中では useCurrentFrame() が0起点になるよ。おかげで、尺をいじるたびに全部書き直す羽目にならずに済む
  • 書き出しはブラウザの連続スクリーンショットだよ。だからWeb技術がそのまま映像になる
  • フレームが独立しているから、Lambdaに並列で散らせるよ。ただし外部アセットはCDNに置いてね
  • Remotion Lambda でレンダリングさせる時、その Lambda には動画コードが入っていないよ。動画を直したときは deploySite() してからレンダリング.

あと、企業で使うならライセンスだけ先に見ておいてね。個人・従業員3名までの営利企業・非営利なら無料だけど、それを超える規模の営利企業は企業向けライセンスが必要だよ。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?