こんにちは! がねこまさしです。2014年に連載した「WebRTCを使ってみよう!」シリーズですが、内容がすっかり古くなってしまいました。そこで2016年6月の最新情報に基づき、内容をアップデートして改めてお届けしたいと思います。
WebRTCとは?
WebRTCとは”Web Real-Time Communication”の略で、Webブラウザ上でビデオ/オーディオの通信や、データ通信を行うための規格です。HTML5で新しく策定されたもので、複数の技術の連携で成り立っています。 ちなみに策定には複数の団体が絡んでいています。
APIの策定作業はWebRTC 1.0に向けて大詰めに入っています。またより詳細な低レベルのAPIを定義しているORTCも登場し、将来の統合に向けた動きも始まっています。
コーデックの選定では、ブラウザがサポートしなければならないビデオコーデックに、既にデファクトスタンダードともいえるH.264が加わりました。また、Googleが開発しているオープンソースのビデオコーデック「VP9」をサポートするブラウザも増えています。
WebRTCで何ができるの?
WebRTCは厳密に言うとビデオ/オーディオ/データ通信を行うための仕組みですが、他にも関連が深い技術があります。この連載では3つをまとめてWebRTC(とその仲間たち)ということで扱います。
- カメラ、マイクといったデバイスへのアクセスする … Media Capture and Streams
- ビデオ/オーディオ/データ通信を行う … WebRTC 1.0: Real-time Communication Between Browsers
- ビデオ/オーディオの録画/録音を行う … MediaStream Recording
この3兄弟に、HTML5の様々な要素を組み合わせて活用することができます。
- JavaScript(大前提)
- videoタグ、audioタグ
- CSS3
- Canvas
- WebGL
- Web Audio API
- WebSocket
新しいAPIでカメラを使ってみよう
カメラやマイクと言ったデバイスにアクセスするには、従来はnavigator.getUserMedia()
というAPIを使いました。2016年6月現在では、これに代わる新しいAPIが登場しています。
navigator.mediaDevices.getUserMedia()
になった- ベンダープレフィックスが取れた
- コールバックではなく
Promise
ベースになった
またデスクトップブラウザの場合は次のブラウザで利用することができます。
- Firefox 47 … 利用可能
- Chrome 51 … 利用する場合にはフラグ設定が必要
- chrome://flagsというURLを開く
- 「試験運用版のウェブ プラットフォームの機能 #enable-experimental-web-platform-features」を有効に
- Chromeを再起動
- Edge 25 … Windows 10 のEdgeでも利用可能
サンプルコード
それではカメラから映像を取得してみましょう。サンプルコードはこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Camera with mediaDevice</title> </head> <body> <button onclick="startVideo()">Start</button> <br /> <video id="local_video" autoplay style="width: 320px; height: 240px; border: 1px solid black;"></video> </body> <script type="text/javascript"> let localVideo = document.getElementById('local_video'); let localStream; // start local video function startVideo() { navigator.mediaDevices.getUserMedia({video: true, audio: false}) .then(function (stream) { // success localStream = stream; localVideo.src = window.URL.createObjectURL(localStream); }).catch(function (error) { // error console.error('mediaDevice.getUserMedia() error:', error); return; }); } </script> </html> |
このHTMLファイルに、(Webサーバ経由で)ブラウザからアクセスしてみてください。GitHub Pagesでも公開していますので、すぐに試すことができます。
- GitHub Pages で試す camera_new.html(※Chromeの場合は、上記のフラグ設定が必要です)
- GitHub でソースを見る camera_new.html
ブラウザで[Start]ボタンをクリックすると、カメラへのアクセスの許可を求めるダイアログが表示されますので、許可してください。
Firefoxの場合
Edgeの場合
ウィンドウの下の部分にこのように表示されます。他のブラウザと違うので、ちょっと気が付きにくいかもしれません。
HTMLファイルの置き場所
カメラやマイクにアクセスするには、サンプルのHTMLファイルをWebサーバーに配置する必要がありますが、実はブラウザによって条件が異なります。
- Chromeの場合 … 原則 https://~ のみ。例外として http://localhost/~ でも利用可能
- Firefoxの場合 … https://~ および http://~ の両方で利用可能。さらに file://~ でも利用可能
- Edgeの場合 … https://~ および http://~ の両方で利用可能。さらに file://~ でも利用可能
FirefoxやEdgeの場合は、ローカルのHTMLファイルを直接読み込んでも、利用可能です。
トラブルシューティング
カメラのアクセスの許可を求められた際に「常に拒否」すると、そのサイトからはカメラの映像を取得することができなくなります。その場合は明示的に再許可してあげる必要があります。
Edgeの場合
※いまのところ「常に拒否」することはできないようなので、このケースは発生しません。
新旧 getUserMedia() をラップしてみると
新しいAPIである navigator.mediaDevices.getUserMedia()は、まだChromeではデフォルトの状態では使えません。これは、引数で渡すオプションの指定方法が、まだ新しい書き方に沿っていないことが原因です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
let deviceID = getSelectedIDsomehow(); // デバイスIDを指定する // Firefoxの場合 let constraints = { audio: false, video: { deviceId: {exact: deviceID} } }; // Chromeの場合 let constraintsForChrome = { audio: false, video: { optional: [{sourceId: deviceID}] } }; |
そのため、Chromeではしばらく新しいAPIはデフォルトでは使えない状態が続くと思われます。そこで古いAPIをPromise型にラップする例を用意してみました。(オプション指定の違いは吸収できていません)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// --- prefix ----- navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; // ---- 新旧APIをPromiseでラップする ---- function getDeviceStream(option) { if ('getUserMedia' in navigator.mediaDevices) { console.log('navigator.mediaDevices.getUserMadia'); return navigator.mediaDevices.getUserMedia(option); } else { console.log('wrap navigator.getUserMadia with Promise'); return new Promise(function(resolve, reject){ navigator.getUserMedia(option, resolve, reject ); }); } } // 利用例 function startVideo() { getDeviceStream({video: true, audio: false}) .then(function (stream) { // success localStream = stream; localVideo.src = window.URL.createObjectURL(localStream); }).catch(function (error) { // error console.error('getUserMedia error:', error); return; }); } |
これを使ってカメラ映像を取得する例もご用意しました。Chrome 51でもデフォルトのままで利用できます。
- GitHub Pages で試す camera_old_new.html
- GitHub でソースを見る camera_old_new.html
様々なWebRTC関連のブラウザの違いを吸収する adapter.js
が 本家のGitHubで公開されています。より便利に使いたい場合は、そちらをご利用ください。
CSS3と組み合わせてみよう
WebRTCは他の要素と組み合わせて使うことができると書きました。実際にCSS3と組み合わせてみましょう。
1 2 3 4 5 6 7 8 9 10 11 |
<!-- 通常 --> <video id="local_video" autoplay style="width: 320px; height: 240px; border: 1px solid black;"></video> <!-- 左右反転 --> <video id="flip_video" autoplay style="transform: scaleX(-1); width: 320px; height: 240px; border: 1px solid black;"></video> <!-- 角丸 --> <video id="round_video" autoplay style="border-radius: 80px 80px 80px 80px; width: 320px; height: 240px; border: 1px solid black;"></video> <!-- セピア --> <video id="filter_video" autoplay style="filter: sepia(100%); -webkit-filter: sepia(100%); width: 320px; height: 240px; border: 1px solid black;"></video> |
ほかにも様々なバリエーションが考えられます。ぜひ自分でもいろいろ試してみてください。
また、CSS3アニメーションを使うこともできます。例えばこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Camera with mediaDevice</title> <style type="text/css"> #rotate_video { animation-duration:4s; animation-iteration-count:infinite; animation-timing-function:linear; animation-name:rotate360; } #shake_video { animation-duration:0.5s; animation-iteration-count:infinite; animation-timing-function:ease-in-out; animation-name:shake; } @keyframes rotate360 { 0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);} } @keyframes shake { 0%{transform:rotate(-20deg);} 50%{transform:rotate(20deg);} 100%{transform:rotate(-20deg);} } </style> </head> <body> Camera with mediaDevice.getUserMedia()<br /> <button onclick="startVideo()">Start</button> <br /> <!-- 通常 --> <video id="local_video" autoplay style="width: 320px; height: 240px; border: 1px solid black;"></video> <br /> <!-- 回転アニメーション --> <video id="rotate_video" autoplay style="width: 320px; height: 240px; border: 1px solid black;"></video> <!-- 振動アニメーション --> <video id="shake_video" autoplay style="width: 320px; height: 240px; border: 1px solid black;"></video> </body> <script type="text/javascript"> // --- prefix ----- navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; let localVideo = document.getElementById('local_video'); let localStream; // start local video function startVideo() { getDeviceStream({video: true, audio: false}) .then(function (stream) { // success localStream = stream; localVideo.src = window.URL.createObjectURL(localStream); document.getElementById('rotate_video').src = localVideo.src; document.getElementById('shake_video').src = localVideo.src; }).catch(function (error) { // error console.error('mediaDevice.getUserMedia() error:', error); return; }); } // ---- 新旧APIをPromiseでラップする ---- function getDeviceStream(option) { if ('getUserMedia' in navigator.mediaDevices) { console.log('navigator.mediaDevices.getUserMadia'); return navigator.mediaDevices.getUserMedia(option); } else { console.log('wrap navigator.getUserMadia with Promise'); return new Promise(function(resolve, reject){ navigator.getUserMedia(option, resolve, reject ); }); } } </script> </html> |
CSS3と組み合わせたサンプルも、GitHub Pagesでも公開していますので、試してみてください。(ラップ関数を使っているので、デフォルトのChrome 51でも利用できます)
- GitHub Pages で試す camera_css_wrap.html
- GitHub でソースを見る camera_css_wrap.html
次回は
今回は新しいAPIを使って、カメラの映像を取得してみました。次回はWebRTCによる通信を手動で接続してみます。