IT系おじさんのチラシの裏
2018年10月~
当サイトの記事にはアフィリエイト広告のリンクが含まれる場合があります

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への投稿へ使えたりもするでしょうから、応用範囲は広そうです。

関連記事

JavaScriptのcanvasで縦書きを行う際の備忘録

canvas上に縦書きの文章を出力して画像化しようと考えてちょっとハマったので忘れないうちにメモしておきます。 結論から先に書くと、基本的にcanvasにはwriting-modeの設定が効くの

JavaScriptのWebAudioAPIで楽曲の口パクをしてみる

以前、ソフトウェアの解説動画を作った際、おっさんの声だけ流れてるのもアレだし、最近流行りのアバターにしゃべらせるやつやってみよう、と思ったことがあります。 vtuberとかおじさんよくわからない

コメント

  • 大変勉強になる記事ありがとうございます。
    もしよろしければ教えていただきたいのですが、今回の画像でしたが、動画保存バーションを行うにはどうすれば良いのでしょうか。
    [返信]
    • すみません。正しくは「今回の画像」→「今回は画像」です。
      [返信]
      • 最初から動画なのであれば普通はcanvasタグは使わず、videoタグで埋め込むでしょうから、そこで指定したファイルをDLすれば良いかと思います。

        …そういうことではなく? 何らかの事情でcanvasタグにdrawImageで動画を描画しているのでしたら、録画の仕方は同じはずです。
        こんなのが必要なケースってあるのかなぁ、と思いつつ一応サンプルを書いてみました。何かの参考になれば幸いです。
        https://blog.ver001.com/javascript-canvas-video-mediarecorder/
        [返信]
  • すぐにご回答いただいていたのに返信が遅くなってしまい申し訳ございません。
    実は今転職するためのポートフォリオを作成しておりMediapipe.jsという姿勢推定のライブラリを使いたいと考え、色々試行錯誤しております。
    姿勢推定の流れとしては
    おそらくですが、そもそも認識が間違っているかもしれませんが
    ①動画をわたす。
    ②関節の位置等を推定する
    ③ブラウザ上のcanvasタグ内に関節の位置等を描画する。
    だと考えていて、
    画面上で関節の位置等を描画した動画は再生されるのですが、
    その動画を保存しようとしても、元の推定前の動画しか保存できませんでした。
    それならcanvas上に表示されている画面を、そのまま保存する方法がないか探している中でこちらの記事に辿り着き、コメント欄から連絡させていただいた次第です。
    まだまだ勉強不足で、書いていただいたコードを一つ一つ調べながらでないと理解できないのですが、行き詰まっておりましたので、とても感謝しております。
    ありがとうございます。
    長文になってしまいました。申し訳ございません。
    [返信]
    • ご丁寧な説明ありがとうございます。
      このようなライブラリがあるとは知りませんでしたし、録画したいという要望についてもすごくよくわかりました!
      MediaRecorderならWebカメラ画像もcanvas画像も録画できるため、canvas上に表示された関節の録画は出来るはずです。

      MediaRecorder APIはstartで録画開始、stopで停止時にondataavailableイベントが発火するため、そこで保存処理をするだけの簡単な仕組みです。がんばって実装してみてください~。
      [返信]
  • 投稿、お疲れ様です。そしてありがとうございます。
    アプリの機能の参考にさせていたただきました。

    1点、分かったことがありますのでコメントさせていただきます。
    JavaScript例のコードの
    14.recorder = new MediaRecorder(stream, {mimeType:'video/webm;codecs=vp9'});
    に関してです。
    codecs = vp9では、ブラウザでは正しく再生できますが、Windows標準の映画&amp;テレビでは拡張機能を導入しても正しく再生できませんでした。
    codecs = vp8では、ブラウザでも映画&amp;テレビでも正しく再生できました。
    原因はわからず、私のミスかもしれませんが、汎用性を求めるならばvp8のほうが良いのかもしれません。
    [返信]
    • ご指摘ありがとうございます!
      あちゃー、うちの環境ではvp9のコーデックが入っていたからメディアプレイヤーでも再生できちゃったのかも知れません。
      MediaRecorder.isTypeSupported();でブラウザがサポートする(MediaRecorderで生成できる)動画の種類を確認したところ、普通にh264にも対応していたので、こちらのほうが更に汎用的かも知れません。
      https://developer.mozilla.org/ja/docs/Web/API/MediaRecorder/isTypeSupported

      例)
      let recorder = new MediaRecorder(stream, {mimeType:'video/webm;codecs=h264'});
      [返信]
  • 追記です。
    video/mp4ではエラーになったとのことですが、この形式はFireFoxにのみ対応しているようです。
    参考 : https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers#mpeg-4_mp4
    [返信]

新しいコメントを投稿する

[新規投稿]
 
TOP