メモログ

Convert video files with the ffmpeg.js worker

Convert screen capture to animation GIFの話で ffmpeg を使ってキャプチャを GIF に変えるということをした。Mac だと ffmpeg のインストールは簡単だしその方が良いと思うけど、環境によっては難しい場合もあるだろうし、そもそもインストールしてはならない状況もあるかもしれない。そんな時にffmpeg.jsを Web Workers で動かすのはどうだろうかと思ってやってみた。

デモ。ソースコードはmemolog/ffmpeg_workerあたりにある(とりあえず動くというだけの実装だけど)。ファイルを選択すると、読み込んだファイルを webm に変換して表示する。見た目は下のキャプチャような感じになる。このデモサイトでは、mp4 や mov ファイルを webm に変換できるけれど、iPhone で撮った動画はエラーになって変換できない(おそらくエンコーダ的に ffmpeg-worker-mp4.js の方を使わないといけない)

基本的な実装は二つで、一つは Web Worker を起動させて実行結果を受け取る処理。ffmpeg.js は変換処理が終わると Worker 自身にdoneイベントを送るのでそこで実行結果を受け取る。

this.worker = new Worker("./lib/ffmpeg-worker-webm.js");
this.worker.addEventListener("message", (ev) => {
  const msg = ev.data;
  const type = msg?.type ?? "";
  switch (type) {
    case "done":
      const data = msg?.data?.MEMFS[0]?.data;
      this.setState({
        converted: URL.createObjectURL(new Blob([data])),
      });
      break;
  }
});

もう一つはtype="file"の change イベントで、event.target.filesからファイルオブジェクトを受け取って、ArrayBuffer にして worker に渡す処理。

const reader = new FileReader();
reader.onload = () => {
  const result = reader.result;
  if (result instanceof ArrayBuffer) {
    this.worker.postMessage({
      type: "run",
      MEMFS: [{ name: file.name, data: result }],
      arguments: ["-y", "-i", file.name, "-crf", "30", "-b:v", "0", "out.webm"],
    });
  }
};
reader.readAsArrayBuffer(file);

上のキャプチャで使った動画はCosmic Origin of the Chemical Elementsの講義からダウンロードしてきたものだけど、12MB のデータを 10MB に、見た感じ劣化もなく変換することができたと思う。実行時間は測ってないけど 10 分とかもうちょっとかかってたかも。

それで、動画を GIF に変換できるのかというと、、できない。Github のGIF Encoding?の issue によると、Makefile に gif を追加してビルドすれば対応できるらしい。それでBuild instructionsを参考にビルドしようと試みたのだが、Can’t compile with recent emscripten.と同じところでエラーになって手詰まりになって諦めた。

というメモ。

私について

Yutaka Yamaguchi
東京在住。TypeScript, Node.js, Reactなどフロンエンドが主力。Perlも書く。SwiftやRubyも過去には使ってた。過去のTOEIC 860くらい。