WebRTC Conference Japan 2016
WebRTC Boot Camp ハンズオン
インフォコム株式会社
がねこまさし
リソース
• スライドのURL
– http://www.slideshare.net/mganeko/webrtc-
bootcamp-handson
– 短縮 http://goo.gl/cA9CV5
• 元になるソースコード
– https://github.com/mganeko/bootcamp
– 短縮 https://goo.gl/HfXTmT
• 参考
– WebRTCを試すために必要なもの (Qiita)
• http://goo.gl/hSfYc9
– お手軽なWebサーバーの立て方 (Qiita)
• http://goo.gl/0C18j5
PART 1
カメラを使ってみよう
navigator.getUserMedia()
• カメラの映像/マイクの音声を取得できます
• ベンダーブレフィックスが付いています
– Chrome: navigator.webkitGetUserMedia()
– Firefox: navigator.mozGetUserMedia()
• 最新の仕様では、navigator.MediaDevices.getUserMedia()
– ※今日は使いません
• ユーザに許可を求めるダイアログが表示されます
• 取得に成功するとMediaStream オブジェクトが得られます
• ※Chrome 47からは https://〜でしか利用できなくなりました
– http://localhost は例外として利用できます
コード例
// プレフィックスを吸収する
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
// カメラの映像を取得する場合
navigator.getUserMedia( {video : true},
function(stream) {
// 成功時のコールバック
},
function(err) {
// エラー時のコールバック
}
);
マイクの音声も取得する場合
// カメラの映像とマイクの音声を取得する場合
navigator.getUserMedia( {video : true, audio: true},
function(stream) {
// 成功時のコールバック
},
function(err) {
// エラー時のコールバック
}
);
※会場でやるとうるさいので、今日は音声無しでお願いします
映像をvideo要素で再生
• window.URL.createObjectURL(stream) でURL
を取得
– blob://〜 という表記のURLを返す
• video要素のsrc に指定して再生
video.src = window.URL.createObjectURL(stream);
video.play();
実際にやってみよう(1)(2)
• media_0.html をエディタで開いてください
• startCamera() 関数の (1), (2) を記述してみて
ください
• [start]ボタンを押して、試してみましょう
解答例
function startCamera() {
// (1) getUserMediaを使って、cameraからメディアストリームを取得してください
// また、それをlocalStreamにセットしておいてください
navigator.getUserMedia( {video : true},
function(stream) { // success
localStream = stream;
// (2) それを localVideo に表示してください
localVideo.src = window.URL.createObjectURL(localStream);
localVideo.play();
},
function(err) { // error
console.error('getUserMedia error', err);
}
);
}
映像の停止
• video.pause()
• window.URL.revokeObjectURL(url) でURLを解放
• ストリームを停止
– MediaStream.stop() はChrome 47から使えなくなりました
– MediaTrack.stop() を使う必要があります
MediaStream
MediaTrack
MediaTrack
実際にやってみよう(3)(4)
• 先ほどの続きで、stopCamera() 関数の (3), (4)
を記述してみてくだい
– MediaStreamを停止させるための関数を
stopStream(stream) として用意していますので、
ご利用ください
• [start]→[stop]ボタンを押して、試してみましょ
う
解答例
function stopCamera() {
// (3) localVideoの再生を停止させてください
localVideo.pause();
window.URL.revokeObjectURL(localVideo.src);
localVideo.src = ''; // Firefox ではWARNING
// (4) メディアストリームを停止させてください
stopStream(localStream);
localStream = null;
}
参考:新しい getUserMedia
• Media Capture and Streamsでは、新しいAPIが定義され
ています
– http://www.w3.org/TR/mediacapture-streams/
• navigator.MediaDevices.getUserMedia()
• Promiseを返します
navigator.mediaDevices.getUserMedia(
{video: true}
).then(function (stream) {
// 成功時の処理
}).catch(function (reason) {
// 例外時の処理
});
PART 2
通信してみよう
RTCPeerConnection
• WebRTC通信には、RTCPeerConnetionを使用
– ブラウザとブラウザの間で直接Peer-to-Peer通信を行う
– UDP/IPを使用
• TCP/IPのようにパケットの到着は保障しない
• オーバーヘッドが少ない
• 通信のリアルタイム性を重視
• UDPのポート番号は動的に割り振られる(49152 ~ 65535)
• ベンダーブレフィックスが付いている
– Chrome: window.webkitRTCPeerConnetion
– Firefox: window.mozRTCPeerConnection
RTCPeerConnection
• Peer-to-Peer 通信を確立するために、2種類の情報を
交換する
– SDP (Session Description Protocol)
– ICE Candidate (Interactive Connectivity Establishment
Candidate)
• SDP ... 通信するPeerの能力や、条件について
– 映像、音声、データーの有無
– 使いたいコーデック、使いたい帯域幅、など
– 通信を始める側: Offer
– 通信に応答する側: Answer
• ICE Candidate
– 通信に使う経路の情報
SDP と ICE
• ICE Candidate…経路の情報を示す。複数ある場合も多い
• P2Pによる直接通信
• STUNによる、NAT通過のためのポートマッピング
– → 最終的にはP2Pになる
• TURNによる、リレーサーバーを介した中継通信
SDP SDPICE
ご注意
• ICE Candidate を収集するには、デバイスが
ネットワークに繋がっている必要があります
– ネットワーク無し、localhostのみだとICE candidate
が収集できない
• ハンズオンは、テザリング等でデバイスをネッ
トワークに接続してから、実行してください
– 外部と実際には通信しませんが、外部と接続でき
るネットワークが必要です
シグナリング
• 最初は通信相手のことをお互い知らない
• 情報をなんらかの方法で交換する必要あり
– → シグナリング
• 通常は仲介役となるサーバーを用意
– → シグナリングサーバー
• 今日は、仲介役は「あなた」
– → 手動でシグナリング
デモ
SDPの交換の概略: Offer側
• Offer SDP を生成
– RTCPeerConnection.createOffer()
• 自分のSDPを覚える
– RTCPeerConnection.setLocalDescription()
• 相手に送る
• 相手から Answer SDP を受け取ったら、それを覚える
– RTCPeerConnection.setRemoteDescription()
※非同期処理になるので、コールバックを使って記述
SDPの交換の概略: Answer側
• 相手から Offer SDP を受け取ったら、それを覚える
– RTCPeerConnection.setRemoteDescription()
• Answer SDP を生成
– RTCPeerConnection.createAnswer()
• 自分のSDPを覚える
– RTCPeerConnection.setLocalDescription()
• 相手に送り返す
※非同期処理になるので、コールバックを使って記述
コードの概略
// プレフィックスを吸収する
RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection;
// PeerConnectionオブジェクトを生成する
var peerConnection = new RTCPeerConnection({"iceServers":[]});
// iceServersは NAT/Firewall越えを行う場合に指定
// 今回は指定は空っぽでOK
// Offer SDPを生成する
peerConnection.createOffer(
function(sessionDescription) {
// 成功した場合
},
function(err) {
// 失敗した場合
},
{} // オプション指定
);
コードの概略(続き)
// Answer SDPを生成する
peerConnection.createAnswer(
function(sessionDescription) {
// 成功した場合
},
function(err) {
// 失敗した場合
},
{} // オプション指定
);
// SDP を受け取った場合
peerConnection.setRemoteDescription(sessionDescription,
function() {
// 成功
},
function(err) {
// 失敗
}
);
ICE Candidateの交換の概略
• ICE Candidate (通信経路の候補)は複数個ある
• 非同期に順々に収集される
– RTCPeerConnection.onicecandidate()
• 早く見つかったものから随時交換… Trcikle ICE
– 早く繋がるケースが多い
• 全て見つけてから、まとめて交換… Vanilla ICE
– 処理はシンプル。※今日はこちらを利用
Trickle ICE によるシグナリング
Vanilla ICE によるシグナリング
コードの概略
peerConection.onicecandidate = function (evt) {
if (evt.candidate) {
// 個々のcandidateを収集をした時の処理
// Trikle ICE の場合、ここで処理する
} else {
// 全てのICE candidateが収集し終わったタイミング
// Vanilla ICE の場合、ここで処理する
var sdpWithICE = peerConnection.localDescription.sdp;
}
};
手動シグナリングをやってみよう
(1)〜(7)
• hand_vanilla_0.html をエディタで開いてください
• makeOffer()関数の(1),(2)を記述してみてください
• setOfferText()関数の(3),(4)を記述してみてください
• makeAnswer()関数の(5),(6)を記述してみてください
• setAnswerText()関数の(7)を記述してみてください
解答例
function makeOffer() {
// (1) createOfferを呼び出して、Offer SDPを生成する
peerConnection.createOffer(function (sessionDescription) { // in case of success
// (2) 生成されたSDPを、peerConnetionにセットする
// ※Vanilla ICEを用いるため、すぐにはOfferを相手に送信しない
peerConnection.setLocalDescription(sessionDescription,
function() {
console.log('setLocalDescription() succsess');
// ※Trickle ICEの場合は、このタイミングでOffer SDPを相手に送信する
// ※Vanilla ICEの場合には、まだ送らない
},
function(err) {
console.error('setLocalDescription() ERROR: ', err);
}
);
console.log('-- Create Offer SDP --');
console.log(sessionDescription);
}, function (err) { // in case of error
console.error('Create Offer failed:', err);
}, mediaConstraints);
}
解答例
// Offerを受け取る
function setOfferText(text) {
// SDPの文字列からRTCSessionDescriptionのオブジェクトを生成
var offer = new RTCSessionDescription({
type : 'offer', sdp : text,
});
// (3) 生成したオブジェクトを、peerConnetionにセットする
// 相手側のSDPをセットする
peerConnection.setRemoteDescription(offer,
function() {
console.log('setRemoteDescription(offer) succsess');
// (4) さらに、適切なタイミングで用意している関数 makeAnswer() を呼び出す
// コールバックが呼ばれたら、Answerを生成する
makeAnswer();
},
function(err) {
console.error('setRemoteDescription(offer) ERROR: ', err);
}
);
}
解答例
// Answer を生成する
function makeAnswer() {
// (5) createAnswerを呼び出して、Answer SDPを生成する
peerConnection.createAnswer(function (sessionDescription) { // in case of success
// (6) 生成されたSDPを、peerConnetionにセットする
// ※Vanilla ICEを用いるため、すぐにはOfferを相手に送信しない
peerConnection.setLocalDescription(sessionDescription,
function() {
console.log('setLocalDescription() succsess');
// ※Trickle ICEの場合は、このタイミングでAnswer SDPを相手に送信する
// ※Vanilla ICEの場合には、まだ送らない
},
function(err) {
console.error('setLocalDescription() ERROR: ', err);
}
);
console.log(sessionDescription);
}, function (err) { // in case of error
console.error('Create Answer failed:', err);
}, mediaConstraints);
}
解答例
// Answerを受け取る
function setAnswerText(text) {
// SDPの文字列からRTCSessionDescriptionのオブジェクトを生成
var answer = new RTCSessionDescription({
type : 'answer',
sdp : text,
});
// (7) 生成したオブジェクトを、peerConnetionにセットする
// 相手側のSDPをセットする
peerConnection.setRemoteDescription(answer,
function() {
console.log('setRemoteDescription(answer) succsess');
},
function(err) {
console.error('setRemoteDescription(answer) ERROR: ', err);
}
);
}
Thank you!
34
END

Webrtc bootcamp handson

  • 1.
    WebRTC Conference Japan2016 WebRTC Boot Camp ハンズオン インフォコム株式会社 がねこまさし
  • 2.
    リソース • スライドのURL – http://www.slideshare.net/mganeko/webrtc- bootcamp-handson –短縮 http://goo.gl/cA9CV5 • 元になるソースコード – https://github.com/mganeko/bootcamp – 短縮 https://goo.gl/HfXTmT • 参考 – WebRTCを試すために必要なもの (Qiita) • http://goo.gl/hSfYc9 – お手軽なWebサーバーの立て方 (Qiita) • http://goo.gl/0C18j5
  • 3.
  • 4.
    navigator.getUserMedia() • カメラの映像/マイクの音声を取得できます • ベンダーブレフィックスが付いています –Chrome: navigator.webkitGetUserMedia() – Firefox: navigator.mozGetUserMedia() • 最新の仕様では、navigator.MediaDevices.getUserMedia() – ※今日は使いません • ユーザに許可を求めるダイアログが表示されます • 取得に成功するとMediaStream オブジェクトが得られます • ※Chrome 47からは https://〜でしか利用できなくなりました – http://localhost は例外として利用できます
  • 5.
    コード例 // プレフィックスを吸収する navigator.getUserMedia =navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; // カメラの映像を取得する場合 navigator.getUserMedia( {video : true}, function(stream) { // 成功時のコールバック }, function(err) { // エラー時のコールバック } );
  • 6.
    マイクの音声も取得する場合 // カメラの映像とマイクの音声を取得する場合 navigator.getUserMedia( {video: true, audio: true}, function(stream) { // 成功時のコールバック }, function(err) { // エラー時のコールバック } ); ※会場でやるとうるさいので、今日は音声無しでお願いします
  • 7.
    映像をvideo要素で再生 • window.URL.createObjectURL(stream) でURL を取得 –blob://〜 という表記のURLを返す • video要素のsrc に指定して再生 video.src = window.URL.createObjectURL(stream); video.play();
  • 8.
    実際にやってみよう(1)(2) • media_0.html をエディタで開いてください •startCamera() 関数の (1), (2) を記述してみて ください • [start]ボタンを押して、試してみましょう
  • 9.
    解答例 function startCamera() { //(1) getUserMediaを使って、cameraからメディアストリームを取得してください // また、それをlocalStreamにセットしておいてください navigator.getUserMedia( {video : true}, function(stream) { // success localStream = stream; // (2) それを localVideo に表示してください localVideo.src = window.URL.createObjectURL(localStream); localVideo.play(); }, function(err) { // error console.error('getUserMedia error', err); } ); }
  • 10.
    映像の停止 • video.pause() • window.URL.revokeObjectURL(url)でURLを解放 • ストリームを停止 – MediaStream.stop() はChrome 47から使えなくなりました – MediaTrack.stop() を使う必要があります MediaStream MediaTrack MediaTrack
  • 11.
    実際にやってみよう(3)(4) • 先ほどの続きで、stopCamera() 関数の(3), (4) を記述してみてくだい – MediaStreamを停止させるための関数を stopStream(stream) として用意していますので、 ご利用ください • [start]→[stop]ボタンを押して、試してみましょ う
  • 12.
    解答例 function stopCamera() { //(3) localVideoの再生を停止させてください localVideo.pause(); window.URL.revokeObjectURL(localVideo.src); localVideo.src = ''; // Firefox ではWARNING // (4) メディアストリームを停止させてください stopStream(localStream); localStream = null; }
  • 13.
    参考:新しい getUserMedia • MediaCapture and Streamsでは、新しいAPIが定義され ています – http://www.w3.org/TR/mediacapture-streams/ • navigator.MediaDevices.getUserMedia() • Promiseを返します navigator.mediaDevices.getUserMedia( {video: true} ).then(function (stream) { // 成功時の処理 }).catch(function (reason) { // 例外時の処理 });
  • 14.
  • 15.
    RTCPeerConnection • WebRTC通信には、RTCPeerConnetionを使用 – ブラウザとブラウザの間で直接Peer-to-Peer通信を行う –UDP/IPを使用 • TCP/IPのようにパケットの到着は保障しない • オーバーヘッドが少ない • 通信のリアルタイム性を重視 • UDPのポート番号は動的に割り振られる(49152 ~ 65535) • ベンダーブレフィックスが付いている – Chrome: window.webkitRTCPeerConnetion – Firefox: window.mozRTCPeerConnection
  • 16.
    RTCPeerConnection • Peer-to-Peer 通信を確立するために、2種類の情報を 交換する –SDP (Session Description Protocol) – ICE Candidate (Interactive Connectivity Establishment Candidate) • SDP ... 通信するPeerの能力や、条件について – 映像、音声、データーの有無 – 使いたいコーデック、使いたい帯域幅、など – 通信を始める側: Offer – 通信に応答する側: Answer • ICE Candidate – 通信に使う経路の情報
  • 17.
    SDP と ICE •ICE Candidate…経路の情報を示す。複数ある場合も多い • P2Pによる直接通信 • STUNによる、NAT通過のためのポートマッピング – → 最終的にはP2Pになる • TURNによる、リレーサーバーを介した中継通信 SDP SDPICE
  • 18.
    ご注意 • ICE Candidateを収集するには、デバイスが ネットワークに繋がっている必要があります – ネットワーク無し、localhostのみだとICE candidate が収集できない • ハンズオンは、テザリング等でデバイスをネッ トワークに接続してから、実行してください – 外部と実際には通信しませんが、外部と接続でき るネットワークが必要です
  • 19.
    シグナリング • 最初は通信相手のことをお互い知らない • 情報をなんらかの方法で交換する必要あり –→ シグナリング • 通常は仲介役となるサーバーを用意 – → シグナリングサーバー • 今日は、仲介役は「あなた」 – → 手動でシグナリング
  • 20.
  • 21.
    SDPの交換の概略: Offer側 • OfferSDP を生成 – RTCPeerConnection.createOffer() • 自分のSDPを覚える – RTCPeerConnection.setLocalDescription() • 相手に送る • 相手から Answer SDP を受け取ったら、それを覚える – RTCPeerConnection.setRemoteDescription() ※非同期処理になるので、コールバックを使って記述
  • 22.
    SDPの交換の概略: Answer側 • 相手からOffer SDP を受け取ったら、それを覚える – RTCPeerConnection.setRemoteDescription() • Answer SDP を生成 – RTCPeerConnection.createAnswer() • 自分のSDPを覚える – RTCPeerConnection.setLocalDescription() • 相手に送り返す ※非同期処理になるので、コールバックを使って記述
  • 23.
    コードの概略 // プレフィックスを吸収する RTCPeerConnection =window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; // PeerConnectionオブジェクトを生成する var peerConnection = new RTCPeerConnection({"iceServers":[]}); // iceServersは NAT/Firewall越えを行う場合に指定 // 今回は指定は空っぽでOK // Offer SDPを生成する peerConnection.createOffer( function(sessionDescription) { // 成功した場合 }, function(err) { // 失敗した場合 }, {} // オプション指定 );
  • 24.
    コードの概略(続き) // Answer SDPを生成する peerConnection.createAnswer( function(sessionDescription){ // 成功した場合 }, function(err) { // 失敗した場合 }, {} // オプション指定 ); // SDP を受け取った場合 peerConnection.setRemoteDescription(sessionDescription, function() { // 成功 }, function(err) { // 失敗 } );
  • 25.
    ICE Candidateの交換の概略 • ICECandidate (通信経路の候補)は複数個ある • 非同期に順々に収集される – RTCPeerConnection.onicecandidate() • 早く見つかったものから随時交換… Trcikle ICE – 早く繋がるケースが多い • 全て見つけてから、まとめて交換… Vanilla ICE – 処理はシンプル。※今日はこちらを利用
  • 26.
  • 27.
  • 28.
    コードの概略 peerConection.onicecandidate = function(evt) { if (evt.candidate) { // 個々のcandidateを収集をした時の処理 // Trikle ICE の場合、ここで処理する } else { // 全てのICE candidateが収集し終わったタイミング // Vanilla ICE の場合、ここで処理する var sdpWithICE = peerConnection.localDescription.sdp; } };
  • 29.
    手動シグナリングをやってみよう (1)〜(7) • hand_vanilla_0.html をエディタで開いてください •makeOffer()関数の(1),(2)を記述してみてください • setOfferText()関数の(3),(4)を記述してみてください • makeAnswer()関数の(5),(6)を記述してみてください • setAnswerText()関数の(7)を記述してみてください
  • 30.
    解答例 function makeOffer() { //(1) createOfferを呼び出して、Offer SDPを生成する peerConnection.createOffer(function (sessionDescription) { // in case of success // (2) 生成されたSDPを、peerConnetionにセットする // ※Vanilla ICEを用いるため、すぐにはOfferを相手に送信しない peerConnection.setLocalDescription(sessionDescription, function() { console.log('setLocalDescription() succsess'); // ※Trickle ICEの場合は、このタイミングでOffer SDPを相手に送信する // ※Vanilla ICEの場合には、まだ送らない }, function(err) { console.error('setLocalDescription() ERROR: ', err); } ); console.log('-- Create Offer SDP --'); console.log(sessionDescription); }, function (err) { // in case of error console.error('Create Offer failed:', err); }, mediaConstraints); }
  • 31.
    解答例 // Offerを受け取る function setOfferText(text){ // SDPの文字列からRTCSessionDescriptionのオブジェクトを生成 var offer = new RTCSessionDescription({ type : 'offer', sdp : text, }); // (3) 生成したオブジェクトを、peerConnetionにセットする // 相手側のSDPをセットする peerConnection.setRemoteDescription(offer, function() { console.log('setRemoteDescription(offer) succsess'); // (4) さらに、適切なタイミングで用意している関数 makeAnswer() を呼び出す // コールバックが呼ばれたら、Answerを生成する makeAnswer(); }, function(err) { console.error('setRemoteDescription(offer) ERROR: ', err); } ); }
  • 32.
    解答例 // Answer を生成する functionmakeAnswer() { // (5) createAnswerを呼び出して、Answer SDPを生成する peerConnection.createAnswer(function (sessionDescription) { // in case of success // (6) 生成されたSDPを、peerConnetionにセットする // ※Vanilla ICEを用いるため、すぐにはOfferを相手に送信しない peerConnection.setLocalDescription(sessionDescription, function() { console.log('setLocalDescription() succsess'); // ※Trickle ICEの場合は、このタイミングでAnswer SDPを相手に送信する // ※Vanilla ICEの場合には、まだ送らない }, function(err) { console.error('setLocalDescription() ERROR: ', err); } ); console.log(sessionDescription); }, function (err) { // in case of error console.error('Create Answer failed:', err); }, mediaConstraints); }
  • 33.
    解答例 // Answerを受け取る function setAnswerText(text){ // SDPの文字列からRTCSessionDescriptionのオブジェクトを生成 var answer = new RTCSessionDescription({ type : 'answer', sdp : text, }); // (7) 生成したオブジェクトを、peerConnetionにセットする // 相手側のSDPをセットする peerConnection.setRemoteDescription(answer, function() { console.log('setRemoteDescription(answer) succsess'); }, function(err) { console.error('setRemoteDescription(answer) ERROR: ', err); } ); }
  • 34.