JavaScriptでcanvasへ動画を描画しMediaRecorderで録画
もう3年以上前になるのですが、「JavaScriptでcanvasを録画して動画ファイルに保存する方法」という記事を投稿しました。
Google Analyticsによると少なくとも15000人以上には読まれているようですが、本日はじめてコメントが入りました。やった~!(棒
…さて、そんな貴重なコメントで、「動画保存バーションを行うにはどうすれば良いのでしょうか」という質問があったため、出来る限り有益な回答をしたいのですが、ぼくの読解力が足りないため、いまひとつ意図するところがわかりません。
該当記事では、canvas上に静止画をスライドショーのように連続して描画して、それを動画ファイルに変換しております。その画像を動画にしたバージョンをお求めとのことですが、動画なら普通はvideoタグで書くでしょうし、それならファイル名を指定しているハズで、なら録画自体が必要なくね…?と混乱した次第。
ただ、ぼくの想像力が足りないだけで、canvas上に動画を描画したいケースがあるのかも知れない…っ!
そんなわけで、(技術的な興味もありましたし)サクサクっと30分くらいでコードを書いてみました。
canvasへ動画を描画しMediaRecorderで録画するサンプル
実行例
解説
半分ジョークで作った29秒の動画をサンプルにしているため、動画の内容自体は気にしないでくださいw
autoplay属性を付けているため、たぶん自動再生されていると思いますが、そうでなければ再生ボタンをポチっとお願いします。
その後、[録画開始]ボタンを押下すると、「録画用のcanvas」という画面が表示され、これが録画中の画面内容になります。
最後に[録画停止]ボタンを押下すると「録画用のcanvas」は消去され、代わりに「ダウンロード」というリンクが表示されますので、これをポチっと押すと動画(webm形式)がダウンロードできます。
録画処理はすべて貴方のブラウザの中で実行されるため、サーバー上に録画データが保存されたりとか、そういう面倒なことは一切ありません。
HTML部分
<div>
■録画したい動画<br>
<video id="video" controls src="[動画ファイル名]" width="500" autoplay loop muted></video>
</div>
<div id="canvas_wrapper">
■録画用のcanvas<br>
<canvas id="canvas"></canvas>
</div>
<div>
<button id="rec_start">録画開始</button>
<button id="rec_stop" disabled>録画停止</button>
</div>
<div>
<a href="#" id="downloadlink">ダウンロード</a>
</div>
<style>
#canvas_wrapper {
display:none;
}
#canvas {
width:500px;
border:1px solid red;
}
#rec_stop {
enabled:false;
}
#downloadlink {
display:none;
}
</style>
解説
HTML部分は特に難しいことはしていません。普通にvideoタグで再生する動画ファイルを指定して、録画開始時に表示するcanvasタグ、そして録画停止時に表示するダウンロードリンク用のタグを準備しているだけです。
尚、videoタグにはautoplay loop muted(自動再生/ループ/音声はミュート)を指定してありますが、これはChromeブラウザでは音声をミュートにしないと自動再生が有効にならないからです。
爆音の動画がいきなり自動再生されたら困りますもんね。mutedを付けないと自動再生できない仕様なのは当然かなと思います。
chrome以外ではテストしてないため、正常に動かなかったらごめんなさいね。
JavaScript部分
<script>
window.addEventListener('load', function () {
let video = document.getElementById('video');
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let intervalID = null;
//canvasからストリームを取得し、MediaRecorderを生成
let stream = canvas.captureStream();
let recorder = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp9'});
//録画停止時にダウンロードリンクを生成する
recorder.ondataavailable = function(e) {
let videoBlob = new Blob([e.data], { type: e.data.type });
let blobUrl = window.URL.createObjectURL(videoBlob);
let anchor = document.getElementById('downloadlink');
anchor.download = 'rec.webm';
anchor.href = blobUrl;
anchor.style.display = 'block';
}
//録画開始ボタン
document.getElementById('rec_start').onclick = function () {
//録画再生ボタンを無効にし、録画停止ボタンを有効にする
this.disabled = true;
document.getElementById('rec_stop').disabled = false;
//canvasを表示し、動画の描画も開始
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
document.getElementById('canvas_wrapper').style.display = 'block';
intervalID = setInterval(function(){
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
}, 1000/30);
//録画開始
recorder.start();
};
//録画停止ボタン
document.getElementById('rec_stop').onclick = function () {
//録画停止ボタンを無効にし、録画停止ボタンを有効にする
this.disabled = true;
document.getElementById('rec_start').disabled = false;
document.getElementById('canvas_wrapper').style.display = 'none';
//動画の描画を停止
clearInterval(intervalID);
//録画開始
recorder.stop();
};
});
</script>
JavaScript部分
大部分は「JavaScriptでcanvasを録画して動画ファイルに保存する方法」で解説したため、MediaRecorderに関する解説はここではしません。
しかし、残るcanvasへの動画描画についてもこの3行だけなんですよね…。
intervalID = setInterval(function(){
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
}, 1000/30);
canvasへ動画を描画するといってもフレーム単位の話で、drawImageを実行した瞬間に画面上に表示されている1フレームを描画するに過ぎません。
そのため、setIntervalを利用して1秒間に30回(30fps)の速度で1フレームずつ描画してるんです。すんごい力業でしょ。
まとめ
こんなふうにcanvasタグに無理やり動画を描画しなければならないケースってあるんかなぁ?
仮にcanvas描画するところまでは、例えばブラウザゲームを開発したり、動画をブラウザ内で加工しながら表示したい、という要件で必要になることもあるのかも知れません。しかし、はてさてそれを録画したい、ということがあるのだろうか。
ゲームのシェア機能的なアレを実装したい、とか?
ともあれ、必要性はわかりませんが、
- canvasに動画を描画する方法
- その内容を動画に保存する方法
について、個人的にも技術的な興味は出たので、サクサクっと書いてみた次第です。
コード自体は30分くらいでちゃちゃっと書いたものなので、バグとかあるかも知れません。いえきっとあります。あくまで参考程度にお願いしますね。