[それWeb]ブラウザからデバイスのカメラを通して動画を録画する

それWebって全然流行らんですね！
こんにちはみなさん。

ミーティングとかで「ブラウザで録画とかできますよw」とフライング気味に言ってしまったのだけど、ほんとうにできんのか？と不安になったので、調べた結果、できるみたいです。
以前にWeb上でカメラを使って文字を撮影して、OCRに書けるという機構を実験しましたが、今回はビデオを録画しちゃいましょう。

# TL;DR
Gistのコードをindex.htmlにでもコピって、`php -S`やXamppやDocker使ってlocalhostでアクセスすればすぐ使えるし、httpsでアクセスできればlocalhostである必要もないです。
https://gist.github.com/niisan-tokyo/080d224ea2635a953d2c9955d8d59b2a

startボタンを押すと録画を開始し、stopボタンを押すと録画を終了します。
録画終了後は、その内容を別のvideoタグに出力して再生します。

# 技術スタック
今回使用している技術で大事なのは、

- `getUserMedia`による、ユーザーのカメラデバイスへのアクセス
- `MediaRecorder`によって、カメラの映像情報を記録する

全部のコードはGistにあるので、そっち見てもらって、以降では大事な部分だけピックアップして解説します。

## getUserMedia
これは以前の記事でも使いましたが、ユーザーのカメラやマイクにアクセスするためのメソッドです。
これを使用するには、localhostでアクセスしているか、httpsによるアクセスでなければなりません。

使い方はこんな感じです。

```javascript
  var video = document.getElementById('video')// 適当にvideoタグのオブジェクトを取得
  var constrains = { video: true, audio: true }// 映像・音声を取得するかの設定
  navigator.mediaDevices.getUserMedia(constrains)
  .then(function(stream) {
      video.srcObject = stream // streamはユーザーのカメラとマイクの情報で、これをvideoの入力ソースにする
      video.play()
  })
  .catch(function(err) {
      console.log("An error occured! " + err)
  })
```
たったの数行で、カメラの映像とマイクの音声をvideoタグに出力するコードができてしまいます。
ここで取得した`stream`を記録用のオブジェクトに流すと、今度は録画ができるようになります。

## MediaRecorder
探したらあっさり見つかりました。
`MediaRecorder`は、入力ストリームの映像情報を記録してくれるオブジェクトです。
例として、`getUserMedia`から取得した`stream`を入力ソースにして、記録完了時に別のvideoタグに出力するコードを書いてみましょう

```javascript
  // まずはユーザーのカメラ・マイクへのアクセスを実施
  navigator.mediaDevices.getUserMedia(constrains)
  .then(function (stream) {
    recorder = new MediaRecorder(stream) // 映像の入力ソースをユーザーのデバイスから取得
    recorder.ondataavailable = function (e) {
      var testvideo = document.getElementById('test')
      testvideo.setAttribute('controls', '')
      testvideo.setAttribute('width', width)
      testvideo.setAttribute('height', height)
      var outputdata = window.URL.createObjectURL(e.data) // videoタグが扱えるように、記録データを加工
      testvideo.src = outputdata // テスト用のビデオのソースに記録データを設置
    }
  })
```
videoの設定に無駄にコードを割いちゃってますが、それを除けばやはり数行程度で**録画の設定が完了**します。
あくまで設定しているだけなので、録画の開始と終了の処理を用意しておく必要があります。

```javascript
  startbutton.addEventListener('click', function(ev){
    recorder.start()
    ev.preventDefault()
  }, false);

  stopbutton.addEventListener('click', function(ev) {
    recorder.stop()
  })
```
これで、startボタンを押したら録画を開始して、stopボタンを押したら録画を終了します。
録画が終了すると、録画データを使用できるようになるので、そのままテスト用のビデオタグに出力します。

## ダウンロードする
録画したデータをダウンロートすることもできます。
[こちらの記事で紹介されていたの](https://developers.google.com/web/updates/2016/01/mediarecorder)を参考にしてみます。

```javascript
  downloadbutton.addEventListener('click', function(ev) {
    console.log(record_data)
    var blob = new Blob(record_data, { type: 'video/webm' })// 録画ファイルをblob形式に出力
    var url = window.URL.createObjectURL(blob) // データにアクセスするためのURLを作成
    var a = document.createElement('a') // download属性を持ったaタグをクリックするとダウンロードができるので、それをシミュレートする
    document.body.appendChild(a)
    a.style = 'display:none'
    a.href = url;
    a.download = 'test.webm'
    a.click()
    window.URL.revokeObjectURL(url)
  })
```
blob形式を使えば、録画ファイルのアップロードもできちゃいますね。

# まとめ
WebRTCとかがもうすでにあるので、そっち方面の人達にとっては当たり前のことかもしれませんが、ユーザーのカメラを使って映像を録画する機構をHTML5上で実装してみました。
ほんと、ブラウザが進化してきたおかげで、私のようなライトなWeb開発者でも、手軽にネイティブの機構にアクセスできるようになってきたのはありがたいことです。

今回はこんなところです。

# 参考
[MediaDevices.getUserMedia()](https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia)
[MediaRecorder API](https://developer.mozilla.org/ja/docs/Web/API/MediaRecorder_API)
[Record Audio and Video with MediaRecorder](https://developers.google.com/web/updates/2016/01/mediarecorder)