ffmpeg.wasmを使ってブラウザ上で画面録画機能を実装した話
はじめに
少し前までLinuxユーザだったので、Blogに使用するgifアニメを作るときにはffmpegを使っていました。最近Windowsに移行しまして、はてgifアニメを作るにはどうしたらいいのかな?と調べていたのですが、普通にffmpegで作れそうなことがわかりました(参考)。まぁ、結論としてはそうだったのですが、Windowsにいろんなアプリケーションをいれると動作不安定になるし嫌だな(久々にWindowsに戻ってきた人間の偏見です。)と思いまして、少し前にバズってたffmpeg.wasmを使ってブラウザ上でスクリーンを録画する機能でも作ってみるかということになりました。これで変なアプリケーションを入れずに済みそうだ。
今回作ったものは、こんな感じのものです。左側でキャプチャ対象のエリアを指定して録画します。右側で録画の結果を表示して、ダウンロードします。
余談ですが、Windows10だとWin+Gから録画できるよ、という話も知っています。知っていますが、まぁ、正直なところ今回のブラウザアプリを作った後に知ったので聞かなかったことにしています。だって、全画面録画とか複数ウィンドウ跨ぎで録画でとかできないらしいし。
ffmpeg.wasmの使い方
ffmpeg.wasmの公式ページはこちらです。その名前の通り、ffmpegをWebassemblyにビルドしたものです。browser上でffmpegの機能を使うことができます。ただし、GPUなどのハードウェアアシストが使えないのでtranscodeは激烈に遅いです。10秒の4K動画の処理に数分待たされる感じでした。こちらの記事にもそんなことが書いてありますね。なのでtranscodeが不要なつくりにしてあげる必要がありそうです。
exampleを見てみましょう。どうやらMediaRecorderでrecordしたBlobをInputとして扱えるようです。変換の前段にBlobデータをMEMFS上にfetchしてffmpeg.wasmから触れるようにしてあげる必要があるようです。そして、ffmpegのおなじみのコマンドで処理を行います。最後にMEMFSから出力を取り出してあげればよさそうです。ffmpegの使い方さえ知っていれば、特に難しいところはありません。MEMFSに関する情報はAPIドキュメントとemscriptenのドキュメントにもう少しだけ詳しく書かれています。
処理の概要
BrowserでディスプレイをキャプチャするにはgetDisplayMediaを使用してMediaStreamを取得します。通常はこれをMediaRecorderに食わせればいいと思いますが、今回は録画するエリアを指定できるようにしたいので、一度HTMLVideoElementにMediaStreamを食わせてから対象のエリアだけをHTMLCanvasElementに書き出ます。そして、このHTMLCanvasElementからMediaStreamを取得してMediaRecorderに食わせます。
MediaRecorderでBlobデータを取得したら、そのBlobデータをMEMFSに取り込みます。ffmpeg.wasmでこのBlobデータをmp4に変換します。変換後のmp4をMEMFSから読みだしてダウンロード可能な状態(anchorに設定)にします。
Source Code
ffmpeg.wasmの実行部分とその前後は下記のようなコードになります。
(1)でMEMFS上にBlobデータをfetchしています。(2)でffmpeg.wasmのコマンドを実行しています。(3)で変換後のデータを読みだしています。
(2)では、transcodeが行われないように -c copy
オプションを付与しています。
とても簡単ですね。
appInfo.ffmpeg.FS('writeFile', name, await fetchFile(new Blob(blobs))); // <---(1)
await appInfo.ffmpeg!.run('-i', name, '-c', 'copy', outName); // <---(2)
const data = appInfo.ffmpeg!.FS('readFile', outName) // <---(3)
デモとリポジトリ
今回作成した録画機能のデモは次のURLで公開されています。transcodeをしていないので意外に処理が速いと思われるのではないかと思います。
また、ソースコードは下記のリポジトリに格納されています。
まとめ
ffmpeg.wasmを使ってブラウザ上でスクリーン録画機能を実装してみました。
意外に使い物になる感じで、個人的には大満足です。
Discussion