MediaRecorderとffmpeg.wasmでp5.jsの超簡単録画ツール(p5.MovRec)を作った話

`ffmpeg.wasm`が気軽に使えなくなったので、`webm`生成に特化したツールに変更しました。
詳しくは下記をご参照ください。
https://qiita.com/tetunori_lego/items/1e9bd35d5ef5a3cd1025

以下もアーカイブとして残しておきます。
-------------------------------------------------

# サマリ🎥
`MediaRecorder`と`ffmpeg.wasm`を使って、`p5.js`上で、簡単にスケッチの録画ができるツール(`p5.MovRec`)を開発しました。既存の`p5.js`のスケッチを汚すことなく、HTMLファイルに2行追加して、キーボードを押すだけで爆速で動画が生成される簡単ツールとなっています。TwitterとかYouTubeとかに作品をアップされている方にお勧めです。

# 成果物
[p5MovRec: A simple movie recording tool for p5.js](https://github.com/tetunori/p5MovRec)
2021/6/5 現在のversionは`0.5.0`のalpha releaseです。

# きっかけ
僕はよく自分の`p5.js`の作品を動画にしてTwitterにアップすることが多いのですが、以前に[p5.jsで作ったジェネラティブアートをtwitterに動画で貼り付ける際のメモ](https://qiita.com/tetunori_lego/items/798cbe2cf37aabfd9d3f)にも書いたように、きれいな動画作品としてTwitterへ投稿するのは割と大変です。
静止画を切り出して、PC上の`ffmpeg`で連結して数分間レンダリングを待つ・・・という大変手間と時間がかかることをしていたわけです。
2021年の現在も、SNS上でもどうやって動画化していますか？という問いは定期的にみられますが、あまり的確なソリューションはでてきていないなぁという実感がありました。
また、特にTwitterとかが顕著ですが、アップしてもエンコードされてしまうので、個人的には画像がそこまできれいでなくてもよいから、もっと手間も時間もかからない動画化方法はないだろうか？とずっと考えていました。

で、最近`WebAssembly`という技術に興味を持っていて、その中に`ffmpeg.wasm`という『`ffmpeg`をブラウザ上で実行できるツール』があることを知りました。もともとJSには`MediaRecorder`で`canvas`の録画ができたはずで、これをTwitterの要求する`mp4/h264`にトランスコードできないかな？と着手したのがきっかけです。

# 簡単な使い方
## 実行環境
- PC(Win/Mac) 環境のみを想定
- `MediaRecorder`と`ffmpeg.wasm`はたぶん結構いろんなブラウザで動くのかなーとは思いつつ、`Google Chrome`のみを対象とさせてください。  
- なお、このツールは[P5 Editor](https://editor.p5js.org/)と[OpenProcessing](https://www.openprocessing.org/)の両方でも正しく動作します！**Webのサービスの中から、即時動画が生成される様子は『未来が来たな』という印象を受けます。**

## ライブラリのインポート
`p5.js`スクリプトの後に、以下の２行を追加するだけです。終了。

```html 
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>

<!-- INSERT HERE -->
<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.10.0/dist/ffmpeg.min.js"></script>
<script src="https://tetunori.github.io/p5MovRec/dist/0.5.0/basic/p5MovRec.js"></script>
```

## もう、すぐRecできる
1. `r`キーを押すと録画開始です。デバッグコンソールに`🎥Start Recording.`のテキストが表示されるはずです。画面の中から勝手に`canvas`を探してそこだけrecしてくれています。
<img src="https://tetunori.github.io/p5MovRec/images/startRecording.png" alt="startRecording" width="375px">  

2. 次に、`w`キーを押すと録画が終了し、即時`webm`フォーマットの動画が生成されます！**文字通り、秒で生成されます。**こちらもコンソールに`✅Recorded.`のテキストが表示されます。この`webm`フォーマットは聞きなれないかもしれないのですが、Chromeブラウザでは確実に再生ができると思います。
<img src="https://tetunori.github.io/p5MovRec/images/stopRecording.png" alt="stopRecording" width="375px"> 

3. 終了時に`m`キーを押して録画を終了させると、今度は(少し時間がかかりますが)`mp4`フォーマットの動画が生成されます。この動画形式でTwitterなどにアップすることができるようになります。Chromeですと、ブラウザ下部に、ダウンロードされたファイル(`YYYYMMDDhhmmss.webm/mp4`という名前)が表示されます。
<img src="https://tetunori.github.io/p5MovRec/images/downloadMovies.png" alt="downloadMovies" width="500px"> 

4. このツールは内部で`keyPressed()`を使っているので、もしすでにスケッチ内で`keyPressed()`を使っている場合はそちらで上書きされてしまいます。よって、本ツールを使うときは、以下のように録画開始終了の関数を`keyPressed()`内で呼んでください。インスタンス`p5MovRec`とそのメソッド`startRec()`、 `stopRec()`と`setMovType()`を使用します。

```javascript
function keyPressed() {
  switch (keyCode) {
    case 49: //1: Start record
      p5MovRec.startRec();
      break;
    case 50: //2: set webm, stop
      p5MovRec.setMovType(P5MovRec.movTypeId.webm);
      p5MovRec.stopRec();
      break;
    case 51: //3: set mp4, stop
      p5MovRec.setMovType(P5MovRec.movTypeId.mp4);
      p5MovRec.stopRec();
      break;
    default:
      break;
  }
}
```

### キー操作の表
|キー| 説明  |
| --- | --- |
|  `r` (**R**ecord)  |  録画開始。  |
|  `w` (**W**ebm)  |  録画終了。`Webm`フォーマットの動画を生成。  |
|  `m` (**M**p4)  |  録画終了。`mp4`フォーマットの動画を生成。 |

## サンプル
 - [Basic Sample On GitHub](https://tetunori.github.io/p5MovRec/sample/basic/)
 - [Basic Sample On P5 Web Editor](https://editor.p5js.org/tetunori/sketches/cWvkz1E2_)  
 - [Basic Sample On OpenProcessing](https://openprocessing.org/sketch/1212512)

## 注意！
### `canvas`について
このツールは`createCanvas()`で生成された`canvas`の`stream`から動画を作成するため、`#つぶやきProcessing`や`#p5t`の作品においては、`createCanvas()`を`setup()`の模範的な位置に動かす必要あります。例えば、下記のように。

#### Starndard `#つぶやきProcessing` code: 
```javascript
t=0 
draw=_=>{
  createCanvas(w=720,w)
  noStroke(fill('#つぶやきProcessing'))
  ...
```

#### Please change as:
```javascript
t=0 
w=720
setup=_=>{ createCanvas(w,w) }
draw=_=>{
  noStroke(fill('#つぶやきProcessing'))
  ...
```

### 動画の品質について
ちょっと長くなるので、下のほうに[Tips](https://qiita.com/tetunori_lego/items/c942eb2aad844b72dadb#tips)の一部として挙げておきます。

# もっと高画質にもできるし、微調整も可能
上記はとにかく簡単に使える方法のご紹介でしたが、Twitterにアップすることを目的としていたので、コーデックは`h264`が選択される様になっていました。実は、このツールでは、`vp9`コーデックもサポートしており、こっちのほうがかなりきれいな動画になります。それ以外にも録画開始終了のタイミングや各種パラメータを設定できたりするので、ご紹介します。

## ライブラリのインポート
こちらも`p5.js`スクリプトの後に、以下の２行を追加するだけで終了ですが、**URLの一部が異なっていること**に注意してください。

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>

<!-- INSERT HERE -->
<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.10.0/dist/ffmpeg.min.js"></script>
<script src="https://tetunori.github.io/p5MovRec/dist/0.5.0/advanced/p5MovRec.js"></script>
```

## p5.jsスケッチの中での使い方
### `createCanvas()`直後にインスタンス生成
`codecId`を指定して、`P5MovRec()`を`new`しましょう。この`codecId`でコーデックが指定できます。
`P5MovRec.codecId.vp9`と`P5MovRec.codecId.h264`が選べるようになっています。`advanced`ではデフォルトは`vp9`がデフォルトで設定されています。

```javascript
let myP5MovRec;  // Please prepare instance in global 

function setup() {
  createCanvas( 720, 720 );
  myP5MovRec = new P5MovRec();  // P5MovRec.codecId.vp9 is selected by default.
  // myP5MovRec = new P5MovRec(P5MovRec.codecId.vp9);  // Same as above.
  // myP5MovRec = new P5MovRec(P5MovRec.codecId.h264); // For h264 codec.
}
```

### 録画の開始・停止
これは、basic側の使い方とそれほど変わりません。`movType`のデフォルト値は`P5MovRec.movTypeId.webm`になっています。

```javascript
function keyPressed() {
  switch (keyCode) {
    case 49: //1: Start record
      myP5MovRec.startRec();
      break;
    case 50: //2: set webm, stop
      // myP5MovRec.setMovType(P5MovRec.movTypeId.webm); // webm is default value
      myP5MovRec.stopRec();
      break;
    case 51: //3: set mp4, stop
      myP5MovRec.setMovType(P5MovRec.movTypeId.mp4); // for mp4 container
      myP5MovRec.stopRec();
      break;
    default:
      break;
  }
}
```

## サンプル
 - [Advanced Sample On GitHub](https://tetunori.github.io/p5MovRec/sample/advanced/)
 - [Advanced Sample On P5 Web Editor](https://editor.p5js.org/tetunori/sketches/OFj1Ne9Bn)  
 - [Advanced Sample On OpenProcessing](https://openprocessing.org/sketch/1212513)

## Tips
### 動画品質について
#### コーデック
このツールの`h264`コーデックでの出力動画品質は`vp9`に比べて若干低めです。もし動画の品質で悩むようでしたら、ぜひ`vp9`設定にトライしてください。ただし、繰り返しの注意ですがTwitterでは、`vp9`コーデックの動画がサポート外ですので、ご注意ください。YouTubeはアップロードできると思います。たぶん。

#### フレームレート
出力動画のフレームレートが気になる場合もあると思います。PCなどの環境都合で、フレームレートが速くなりすぎたりということもままありました。そのような場合は、`p5.js`のスケッチ上で、 `frameRate()`を調整してみてください。`frameRate(30)`、`frameRate(40)`、`frameRate(60)`とかで上限を設定してあげると所望の動画が手に入りやすくなります。

#### Canvasのサイズ
これはTwitter向けの話ですが`720`がベストにエンコードされる値かと思います。ブラウザの拡縮機能で大きさが変わっていると、この拡縮値が結果に影響することもありますので、動画のクオリティが気になる際は、Zoomを100%にもどしたり、80%に小さくしたりなどで改善する可能性があります。出力動画の縦横サイズもチェックしてみてください。

### 動画のビットレート
作品によっては、出力動画のビットレートがすごく高い値になってしまうことがあると思います。（画面全体でひっきりなしに動く動画など。）
特にTwitterでは高ビットレートの動画はエンコード結果が悪いので、動画の時点でなるべく下げておきたいところです。
今回のツールでは、`P5MovRec()`の第2引数に、`videoBPS`という録画時のビットレート指定ができるようにしています。ただし、この設定値通りになるわけではなく、わりとやんわりとそれらしい値に近づく、くらいの機能だと思ってください。使用方法は以下の通りですが、`Advanced Sample`で実際に見ることができます。
なお、ビットレートについても、`canvas`のサイズやフレームレートが影響する場合があるので、こちらも調整すると効果が出る場合があります。

```javascript
function setup() {
  createCanvas( 720, 720 );
  const vbps = 2000000; // 2000kbps
  myP5MovRec = new P5MovRec(P5MovRec.codecId.vp9, vbps );
  ...
}
```

# リファレンス
- ffmpeg.wasm
    - https://github.com/ffmpegwasm/ffmpeg.wasm  
    - https://github.com/ffmpegwasm/ffmpeg.wasm/blob/master/examples/browser/webcam.html  
    - MIT License, Copyright (c) 2019 Jerome Wu

- Screen Capture on your browser with ffmpeg.wasm
    - https://dannadori.medium.com/screen-capture-on-your-browser-with-ffmpeg-wasm-b9ce333067aa

- MediaRecorder
    - https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder