WebRTCはじめました：Webカメラ編

# おまじない

## 関数定義

```js:constants.js
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var URL = window.URL || window.webkitURL;
var RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
var RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription;
var RTCIceCandidate = window.RTCIceCandidate || window.webkitRTCIceCandidate || window.mozRTCIceCandidate;
```

動作環境がPCなら最新ブラウザを想定して一番新しい仕様にしておくのもいいんだけど，PCのChromeとAndroidのChromeですら違ってたりするのでこの手の宣言は必須です．
constants.jsとかにしてscriptタグで読み込むと楽でいいと思います．

## よく使う奴

```js:constants.js
var mediaC = {
  'mandatory': {
    'OfferToReceiveAudio': true,
    'OfferToReceiveVideo': true
  }
};
var ice_config = {
  "iceServers": []
};
```

この辺はよく使うのでまとめて定義．iceServersはちゃんと指定するとNAT越えできるらしいけど，検証環境だと上手くいかなかったので割愛．

## カメラの取得

```js:constants.js
function getVideoSources(callback) {
  if (!navigator.mediaDevices) {
    console.log("MediaStreamTrack");
    MediaStreamTrack.getSources(function(cams) {
      cams.forEach(function(c, i, a) {
        if (c.kind != 'video') return;
        callback({
          name: c.facing,
          id: c.id
        });
      });
    });
  } else {
    navigator.mediaDevices.enumerateDevices().then(function(cams) {
      cams.forEach(function(c, i, a) {
        console.log("mediaDevices", c);
        if (c.kind != 'videoinput') return;
        callback({
          name: c.label,
          id: c.deviceId
        });
      });
    });
  }
}
```

PCにカメラが複数繋がっているってことはあんまり無さそうだけど，スマホだと前面・背面のカメラが普通なので，どちらかを選択する必要がある．
Firefoxならポップアップしてくれるけれど，Chromeはソースコード内で選ばないといけない．Webページ内に埋め込む場合も同様．
で，MediaStreamTrackを使用したカメラ情報の取得方法はDeprecatedなんだけど，AndroidのChromeではmediaDevicesを使えなかったりする．
おまけにスマホの前面・背面はfacingに入ってるのにmediaDevicesには無いのでlabelを使って判別するしかない．
ということでgetVideoSourcesメソッドを自作．Audioはほぼ無視してるので，音声チャットを作りたい人とかは適宜書き換えてください．
これもconstants.jsに追加．


# カメラを表示してみる

```html:SimpleCam.html
<!DOCTYPE html>
<html>

<head>
  <title>Simple Camera</title>
</head>

<body>
  <div>
    <video id="video" />
  </div>
  <form id="control"></form>
  <script src="constants.js"></script>
  <script>
    var video = document.getElementById("video");
    var control = document.getElementById("control");

    getVideoSources(function(cam) {
      console.log("cam", cam);
      var b = document.createElement("input");
      b.type = "button";
      b.value = cam.name;
      b.onclick = getMain(cam.id);
      control.appendChild(b);
      console.log('add button');
    });

    function getMain(cam_id) {
      return function() {
        main(cam_id);
      };
    }

    function main(cam_id) {
      navigator.getUserMedia({
        audio: false,
        video: {
          optional: [
            { sourceId: cam_id}
          ]
        }
      }, function(stream) { // success
        console.log("Start Video", stream);
        localStream = stream;
        video.src = URL.createObjectURL(stream);
        video.play();
        video.volume = 0;
      }, function(e) { // error
        console.error("Error on start video: " + e.code);
      });
    };
  </script>
</body>

</html>
```

とりあえず表示するだけのサンプル．
ボタンがカメラの数だけ出てくるので，クリックするとVideoタグの場所に表示されるはず．
ソースは簡単だけど，実は表示させるのが結構難しい．

## WebRTCのブラウザ対応状況
基本的にChrome, Firefoxなら大丈夫．Safariはダメ．
iOSも動作しない．iOSにChromeやFirefoxをインストールしても動作しない．
（もしかしたらVideo出すだけならできるかも？やってないけど・・・）
Androidの場合はChrome, Firefoxなら動作するけれど，Chromeがオススメ．理由は後述．

## HTMLファイルの置き場所
Ubuntu DesktopにApacheとかNginxをインストールするとか，MacのWebServerとかを使ってlocalhostに配置する場合は問題無い．
ただし，例えば192.168.0.2とかの自分のアドレスにアクセスすると表示できない．

理由は **セキュア（HTTPS）じゃないとgetUserMediaを使えない** から．
HTTPSアクセスじゃ無いのにgetUserMediaを使おうとするとエラーになる．（localhostはOK）
いくつか回避方法はあるみたいだけど，結局はHTTPS対応することになる（と思う）．

なので，例えば外部サーバに置いてそこにアクセスさせる場合でもHTTPS対応が必須になる．
VERISIGNを買えばいいっていうのはその通りだけど，テストとかお試し環境のために証明書買うのもアレだしLet's Encryptよく分からないし，ということでオレオレ証明書を使うことになる（と思う）．

で，ここでFirefoxが問題に．PCなら問題無いけれどAndroidのFirefoxに証明書をインポートさせる方法が無い（分からない）．
Chromeの場合はAndroidに証明書をインポートすれば問題無いが，Firefoxはそちらを読み込んでくれない．
ということでChromeがオススメ．AndroidのFirefoxで証明書インポートできればいいんだけれど・・・．

# まとめ
とりあえずカメラを表示させるのは以上．
簡単なようで落とし穴があるので注意しましょう．