昨日に引き続き、2日目。
ただBiz向けではなくDev向けのTrackにずっといたので、偏りがありますw
WebRTC Boot Camp
いわゆるハンズオン。
https://github.com/mganeko/bootcamp
- カメラ・オーディオストリーム
- WebRTCの制御
- シグナリング(呼制御)
この3ステップを理解してないと、WebRTC開発はつらいよというところからハンズオン開始。
以下、完成したサンプルをベタ貼り。
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>WebRTC Handson</title> <style> body { font-size: 120%; text-align: center; } video { background-color: #000; } </style> </head> <body> <p>1から順に、AB画面それぞれでやるとつながるよ</p> <div> <video id="myVideo" autoplay muted width="320" height="180"></video> <video id="yourVideo" autoplay muted width="320" height="180"></video> <br> <button type="button" id="cameraOn">[A1 / B1] Camera on</button> <button type="button" id="cameraOff">Camera off</button> </div> <p> [A2 / B4] Copy <br> <textarea id="sendSdp" rows="5" cols="70" readonly="readonly"></textarea> <br> <button type="button" id="syn">[A2] Syn</button> </p> <p> [B3 / A5] Paste <br> <textarea id="receiveSdp" rows="5" cols="70"></textarea> <br> <button type="button" id="synAck">[B3] SynAck</button> <button type="button" id="ack">[A5] Ack</button> <button type="button" id="disconn">Disconnect</button> </p> <script src="./main.js"></script> </body> </html>
js
(function(global) { 'use strict'; // お約束 navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; global.RTCPeerConnection = global.RTCPeerConnection || global.webkitRTCPeerConnection || global.mozRTCPeerConnection; global.RTCSessionDescription = global.RTCSessionDescription || global.webkitRTCSessionDescription || global.mozRTCSessionDescription; // めんどくさいのでヘルパ function getElmByIdAndBindClick(id, func) { var el = document.getElementById(id); el.addEventListener('click', func, false); } function noop() {}; function handleError(err) { console.error(err); } // グローバルにあると便利 var myVideo = document.getElementById('myVideo'); var yourVideo = document.getElementById('yourVideo'); var sendSDP = document.getElementById('sendSdp'); var receiveSDP = document.getElementById('receiveSdp'); var myStream = null; var peer = null; // bind! getElmByIdAndBindClick('sendSdp', function(ev) { ev.target.select(); }); getElmByIdAndBindClick('cameraOn', startCamera); getElmByIdAndBindClick('cameraOff', stopCamera); getElmByIdAndBindClick('disconn', disconnect); // 厳密にはSYN/ACKとかじゃないけど便宜的に getElmByIdAndBindClick('syn', syn); getElmByIdAndBindClick('synAck', synAck); getElmByIdAndBindClick('ack', ack); // 自分用カメラON function startCamera() { navigator.getUserMedia( { video: true }, function(stream) { myStream = stream; myVideo.src = global.URL.createObjectURL(myStream); }, handleError ); } // 自分用カメラOFF function stopCamera() { // 横着する myStream.getTracks().forEach(function(t) { t.stop(); }); myStream = null; myVideo.pause(); global.URL.revokeObjectURL(myVideo.src); myVideo.src = ''; } // 通信先に自分のSDPを用意する function syn() { _openPeer(); peer.createOffer(function(sdp) { peer.setLocalDescription(sdp, noop, handleError); }, handleError, {}); } // もらったSDPでもって相手の情報を保存しつつ、自分のSDPを用意する function synAck() { _openPeer(); var offer = new RTCSessionDescription({ type: 'offer', // 相手側からコピーしてきたSDP sdp: receiveSDP.value }); peer.setRemoteDescription( offer, function() { peer.createAnswer(function(sdp) { peer.setLocalDescription(sdp, noop, handleError); }, handleError, {}); }, handleError ); } // 相手のSDPをもらったら、それで相手情報を保存する function ack() { var answer = new RTCSessionDescription({ type: 'answer', // 相手側からコピーしてきたSDP sdp: receiveSDP.value }); peer.setRemoteDescription(answer, noop, handleError); } function disconnect() { _closePeer(); } function _openPeer() { peer = new RTCPeerConnection({ iceServers:[] }); peer.addEventListener('icecandidate', function(ev) { // 全部揃うまで待つ if (ev.candidate) { return; } sendSDP.value = peer.localDescription.sdp; }, false); peer.addEventListener('addstream', function(ev) { yourVideo.src = global.URL.createObjectURL(ev.stream); }, false); peer.addEventListener('removestream', function() { yourVideo.pause(); global.URL.revokeObjectURL(yourVideo.src); yourVideo.src = ''; }, false); peer.addStream(myStream); } function _closePeer() { peer.close(); peer = null; } }(this));
もちろん細かい制御はサボってます。
時間足りん!
- streamを用意
- peerConnectionを作る
- offerしてSDPを用意
- SDPを伝える
- もらったSDPでanswerしてSDPを用意
- SDPを伝える
- どちらも相手情報を取得できたので、スタート
peer.jsとか使わずに書くと、やっぱそれなりに煩雑なコードになっちゃうし、まったく手軽ではないよなー・・。
てかコレを時間内に完成できる人、このハンズオン出る必要ない!
そして参加者の9割は絶対に完成できないってやる前からわかってたと思うの・・。
WebRTC on Native App (iOS/Android)
SkyWayのiOS/Android用のSDKを作ったときの話。
- Google作のlibjingleというC++ラッパを使った
- GoogleTalkで使われてるやつ
- 2GB超えの大作SDKになった
- libjingle Core
- ビデオ・オーディオコーデックの処理(SW Codec)
- RTP/RTCP
- STUN/TURN
- SDP ICE
- libjingle自体の変化も速くてつらい
- OS/実機 or Sim/32 or 64bit/Chip などの差でもコードがころころ変わる
- 一括ビルドツールもあるけど・・
- ハードウェアによってプロファイルが違うとかある
- Androidはメーカーによって挙動違ったりする
- オーディオまわりのほうが闇が深いらしい
門外漢過ぎてさっぱりわからんかった。
ここがつらいよWebRTC - WebRTC開発の落とし穴
パネルディスカッション。
ブラウザのバージョン差異をどうするか
- adapter.js: https://github.com/webrtc/adapter
- peer.jsとかは元から差異対応してくれてる
- SkyWayなら・・
端末性能
- 端末の内蔵カメラはそれなりに動くがマイクは怪しい
- 外付けマイク強い
- 元からWebRTCはVoLTE並の音質
つながらない問題
- つながらないって言われる・・
- PCのスペックが足りないとかはまだマシ
- 接続数が増えてきてつながらないケースとかは切り分けつらい
- 社内ネットワークでそもそもつながらない
- っていうケースがあるだけで、基本的にはつながる
- getUserMediaの許可まわりが一番多い
- 全画面で説明するくらいでもいい
- https://test.webrtc.org/ の社内版を用意したり
WebRTCアプリケーションのテストの課題・解決方法について
SkyWayにおけるテストの話。
- テストつらい
- そもそもノウハウが少ない
- 大企業でもリソースが・・
- やるにしても手動つらい・・
- VoIPのノウハウが使えない
- Web屋の知らないスタックが多すぎる
- NATの種類によっては、マッピング・フィルタリングの挙動が違う
- UDPホールパンチングの可否がそれによって変わる
なので、自動テストツールつくった。
- 先述のNATのタイプまでテストするので、AWSのVPCで動かす
- テストパターンが多いのでそれなりに時間はかかる
- http://status.skyway.io/
- 今は映像だけをテストしてる(= 音声トラックはバンドルされてるのでたぶん大丈夫(質は別))
- Seleniumを動かすためのシェルが一番泥臭い
Web屋の知らないスタックが多すぎる問題は本当に感じていて、そんなのを意識せずに使えるようになる日がこないものかと待ち望んでいる感じです・・。
WebRTC SFU コトハジメ
WebRTCといえばの人。
資料はコレ: https://gist.github.com/voluntas/4d2bd3e878965bdd747a
聞くのに忙しくてメモはありません。
というか、資料が全てとのこと。
すーぱーわかりやすく説明してもらってると思うんですけど、なるほどわからん!
P2PのWebRTCが結局スケールしなくてSFUになるなら、そもそもWebSocketで似たようなスタックは作れないものなのかってのが気になるので試してみようと思う
WebRTC Next Version時代のJavaScript開発
- ライブラリやらプラットフォームが流行るとWebRTC力が下がる
- 生身だと何もできないわからない -> 危険!
- 次期WebRTC(ORTCとの統合的な)に備えよ
- EdgeにはORTCの一部が実装されてる
EdgeのORTCシーケンス
- [事前準備]
- MediaStream
- RTCIceGatherOptions
- IceTransport / DtlsTransport
- RtpSender / RtpReceiver
- [/事前準備]
- ここまでしてやっと通信開始
- MediaStreamにaddTrack
例のadapter.jsだと、EdgeでもWebRTC1.0のコードで書けるらしい。
あとEdgeは同一のカメラを同一のブラウザで触れないとのこと。
WebRTCショートセッション4本勝負!
2日間の締めくくり。
スマホチャットアプリにおけるWebRTC通話アプリ開発事例
TriFort社の人。
サービスはコレかな?: http://startalk.trifort.jp/
WebRTCアプリを個人で開発・運営してみた話
イカデンワの人こと@mzsm_jより。
色々なデバイスの映像を使ったWebブラウザでのWebRTC映像中継
- WebRTC非対応の端末にプラグインをつないで
- GotAPI
- aka デバイスWebAPI
- THETAで撮った映像を、VRで見たり
- https://github.com/DeviceConnect/DeviceConnect
WebRTC x IoT その現状と可能性を探る
- 北米のIoT事情
- Hueが普通に量販店で売ってる
- http://service.amaryllo.eu/
- リモートカメラでWebRTC
- IoTをMicroService化して組みあわせる視点
- これからセキュリティやプライバシーも焦点になってくる
- IoT x WebRTCで
- 安心: カメラで見たい
- AIと融合: 人の代わりに考えたり
- コミュニケーションの多様化: ロボットが代わりに
- 操作性: 操作をデータ化してアーカイブ
- 近視眼的に捉えない
- 海外社会の背景など加味しつつ、今できることを
おわりに
参加費がちょっと高かったけど、WebRTC情報って珍しい気がするし、色んなジャンルの話が聞けてよかったかなーと。
お弁当も出るし事前に資料を冊子にして渡してくれるし、いつもの勉強会とは違う感じでした。
WebRTCそれ自体に関してはやっぱりWeb屋としてはつらい分野が多くて、きっと実案件でも困るのはそのあたりな気がしてます。
限られた環境での簡単なビデオチャットとかなら作れる気はするけど、それ以上のニーズってどんなんがあるんやろ?