JavaScriptでcanvasを録画して動画ファイルに保存する方法
前回の記事「JavaScriptで画像を連続表示してコマ撮りムービー風の表示をしてみる」では連続して画像を表示することで、コマ撮りムービーのようなものをブラウザ上に表示することに成功しました。
じゃあ今度はそれを動画に出来ないものかと調べていたら、MediaRecorder APIというのがあり、これでユーザー側のカメラとマイクを使った録画などができるようです。なるほど、Web上の配信システムってこういうので作られていたのか…。
対応ブラウザが最近のモダンブラウザのみ(Chrome、FireFox、Opera)で、EdgeやIEでは使えないようですが、まぁとりあえず実験なので良いでしょう。
また、今回は別にカメラとマイクは必要なく、単にブラウザ上に表示されたものを録画したいだけですので、canvasあたりの録画が出来ないかと調べてみたら普通に出来るようです。
canvasならMediaRecorderで使うstreamを取得できる
■例
<html>
<body>
<canvas id="preview"></canvas>
</body>
</html>
<script>
stream = document.getElementById('preview').captureStream();
recorder = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp9'});
</script>
とてもシンプルな例ですが、canvasオブジェクトの captureStream(); でストリームを取得し、MediaRecorderクラスを生成しています。
これでrecorderが作成されたため、recorder.start();で録画開始、recorder.stop();で録画終了となるわけです。
ちなみにmimeTypeの指定でwebm形式が指定していますが、video/mp4が指定できなかったのでやむを得ずです。h264とか使えないのかしら。このへんはちょっと課題ですね。
さっそくcanvasに画像を連続表示して録画してみる
HTML例
<div class="anime" style="display:none;">
<img src="[画像ファイル01]" />
<img src="[画像ファイル02]" />
<img src="[画像ファイル03]" />
<img src="[画像ファイル04]" />
<img src="[画像ファイル05]" />
<img src="[画像ファイル06]" />
<img src="[画像ファイル07]" />
<img src="[画像ファイル08]" />
<img src="[画像ファイル09]" />
<img src="[画像ファイル10]" />
<img src="[画像ファイル11]" />
<img src="[画像ファイル12]" />
<img src="[画像ファイル13]" />
</div>
<p><button onclick="frame_start();">動画を作成する</button></p>
<p><a href="#" id="downloadlink" style="display:none;">ダウンロード</a></p>
<p><canvas id="preview"></canvas></p>
解説
最初のimgタグの羅列は連続表示する画像の一覧です。後でJavaScriptでcanvasへ描くのに使われるだけのためサイズ指定なども必要なく、displayもnone指定で非表示状態にしてあります。
(場合によってはimgタグすら用意せずJavaScript内にファイル名等を列挙してロードする方法もアリです)
buttonタグの「動画を作成する」はまんま作業開始のボタンで、後述する自前の関数 frame_start(); を呼び出します。
ダウンロードのアンカータグについては最後に動画保存時に使うため、最初は非表示状態。
最後のcanvasタグが画像の描画と録画で使われるオブジェクトですね。ここのサイズについては用意した画像に合わせて調整してください。
JavaScript例
<script>
var ctx;
var frames;
var recorder;
function frame_start()
{
//canvasの取得
canvas = document.getElementById('preview');
ctx = canvas.getContext('2d');
//canvasからストリームを取得
var stream = canvas.captureStream();
//ストリームからMediaRecorderを生成
recorder = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp9'});
//ダウンロード用のリンクを準備
var anchor = document.getElementById('downloadlink');
//録画終了時に動画ファイルのダウンロードリンクを生成する処理
recorder.ondataavailable = function(e) {
var videoBlob = new Blob([e.data], { type: e.data.type });
blobUrl = window.URL.createObjectURL(videoBlob);
anchor.download = 'movie.webm';
anchor.href = blobUrl;
anchor.style.display = 'block';
}
//録画開始
recorder.start();
//フレーム描画開始
frames = document.getElementsByClassName('anime')[0].getElementsByTagName('img');
viewFrame();
}
function viewFrame(frame_no = -1)
{
//フレーム番号をカウントアップ
frame_no++;
if (frames[frame_no]) {
//次のフレームをキャンバスに画像を描画
ctx.drawImage(frames[frame_no], 0, 0);
setTimeout(function(){viewFrame(frame_no);}, 200);
} else {
//次のスライドがなければ録画終了
recorder.stop();
}
}
</script>
解説
- canvasからコンテキストを取得
- canvasのcaptureStream();でMediaRecorder APIへ渡すストリームを取得
- MediaRecorderはmimeTypeの指定ができるがmp4指定ではエラーになったため、ひとまず video/wembを使ってみた
- 動画を保存する必要もあるため、アンカータグのURLにURI表記のblob(バイナリデータ)を仕込む
- フレーム描画の部分は前回記事とほぼ変わらず、画像表示が ctx.drawImage へ変更されたくらい
実行結果
[動画を作成する]ボタンを押下するとcanvasに13枚の画像が(500ミリ秒ごとに1枚)表示され、表示が終わると[ダウンロード]リンクが表示されるはずです。
その[ダウンロード]リンクをクリックするとwebm形式の動画ファイルがダウンロードできます。
一応、Windows標準のメディアプレーヤー、有名どころの動画再生ソフトMPC、VLCなどで再生は試したので、問題はないかと思います。
あ、そうそう、FireFoxだと下記の部分が失敗するかもなので、
recorder = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp9'});
その場合は下記のとおり、mimeの指定をなくすと良いかも知れません。
recorder = new MediaRecorder(stream);
まとめ
- JavaScriptで連続再生したコマ撮りムービーみたいなものを動画ファイルに保存したい
- MediaRecorder APIでcanvasの録画が出来るらしい
- 画像の表示先をimgからcanvasへ変更
- そのcanvasを録画してみたら意外とうまくいった
- 但し、対応ブラウザはChrome/FireFox/OperaのみでEdgeやIEは非対応
といったところでしょうか。
対応ブラウザの少なさに加え、ChromeとFireFoxでも微妙に書き方を変えないといけないようなので、本気で作るならもうちょっと調査が必要かも知れません。
でもまぁ、ブラウザでcanvasに描いたものをそのまま動画ファイルに出来るってけっこうすごいことですね。
大抵はブラウザ上で済ませば良いじゃん、って話ですが、動画ファイルに変換することでSNSへの投稿へ使えたりもするでしょうから、応用範囲は広そうです。