WebRTC meetup Tokyo 1

  • View
    2.559

  • Download
    2

  • Category

    Internet

Preview:

DESCRIPTION

WebRTC meetup 1のハンズオンの資料です。 シグナリングについてだけ、話します。 次のサイト記事をベースにしています。 http://html5experts.jp/mganeko/5181/ http://html5experts.jp/mganeko/5349/

Citation preview

WebRTC Meetup #1ハンズオンインフォコム株式会社

がねこまさし

今日のテーマ• シグナリングの話だけ、します– Peer-to-Peer がつながる前の話です

• UserMedia の話はしません。ごめんなさい• STUN/TURN 、 DataChannel の話もありませ

元ネタ• HTML5Experts.jp に書いた記事がベース– WebRTC に触ってみたいエンジニア必見!手動

で WebRTC 通信をつなげてみよう• http://html5experts.jp/mganeko/5181/

– WebRTC 初心者でも簡単にできる! Node.js で仲介(シグナリング)を作ってみよう• http://html5experts.jp/mganeko/5349/

• すでに試した方は同じ話です。ごめんなさい

WebRTC の通信はどうなっているの?

• 映像や音声などリアルタイムに取得されたデータを、ブラウザ間で送受信

• RTCPeerConnection が行う– Peer-to-Peer の通信

• ブラウザとブラウザの間で直接通信する

– UDP/IP を使用• TCP/IP のようにパケットの到着は保障しない• オーバーヘッドが少ない• 通信のリアルタイム性を重視• UDP のポート番号は動的に割り振られる( 49152 ~

65535 )

Peer-to-Peer 通信が確立するまで (1)

• Peer-to-Peer を行うには– 相手の IP アドレスを知る必要がある– 動的に割り振られる UDP のポート番号も知る必要が

ある

• Session Description Protocol (SDP) の交換– WebRTC 専用ではなく、 VoIP 等で利用されている– 各エンドポイント(ブラウザ)の情報を示す。例えば、

• メディアの種類(音声、映像)、メディアの形式(コーデック)

• データ転送プロトコル → WebRTC では Secure RTP• 通信で使用する帯域

Peer-to-Peer 通信が確立するまで (2)

• Interactive Connectivity Establishment (ICE) の交換– WebRTC 専用ではなく、 P2P で利用されている– 途中経路の情報を示す。複数ある場合も多い

• P2P による直接通信• STUN による、 NAT 通過のためのポートマッピング

– → 最終的には P2P になる• TURN による、リレーサーバーを介した中継通信

SDP SDPICE

手動編

ということで、やってみましょう• WebRTC に触ってみたいエンジニア必見!手動で WebRTC

通信をつなげてみよう– http://html5experts.jp/mganeko/5181/– 一番下の方の「手動シグナリングの改良版ソース (2014年 4月

21日追加)」のソース• 手動シグナリングは操作が面倒• 必要なテキストを自動で選択するように

DEMO

ということで、やってみましょう• WebRTC に触ってみたいエンジニア必見!手動で WebRTC

通信をつなげてみよう– http://html5experts.jp/mganeko/5181/– 一番下の方の「手動シグナリングの改良版ソース (2014 年 4 月

21 日追加)」のソース• みなさんも、一緒にやってみましょう

– ソースを自分のマシンにコピーして、 Chrome でアクセス– 自前の Web サーバーが必要です– ※file:// ~ ではなく、 http:// ~ の必要があります

• ※ 手動シグナリングは、なぜか動作が不安定– 端末によっては通信が確立しないケースあり

• 今のところ成功は 4 台 /7 台– 原因がさっぱりわからない … 情報求む!

• もし心当たりがあったら教えていただけると助かります

カメラ (UserMedia) を取得

SDP のやりとり

11

PeerConnectionApplication PeerConnection Application

new

new PeerConnection()

function prepareNewConnection() { var pc_config = {"iceServers":[]}; // info of STUN/TURN var peer = null; peer = new webkitRTCPeerConnection(pc_config); peer.onicecandidate = function (evt) { … } peer.addStream(localStream); peer.addEventListener("addstream", onRemoteStreamAdded, false); peer.addEventListener("removestream", onRemoteStreamRemoved, false); return peer;}

SDP のやりとり

13

PeerConnectionApplication PeerConnection Application

new

createOffer()

setLocalDescription(sdp)

offer sdp

sendOffer()

function sendOffer() { peerConnection = prepareNewConnection(); peerConnection.createOffer( function (sessionDescription) { // in case of success peerConnection.setLocalDescription(sessionDescription); sendSDP(sessionDescription); // show in textarea }, function () { // in case of error }, mediaConstraints);}

offer SDP  を生成

SDP のやりとり

16

PeerConnectionApplication PeerConnection Application

new

createOffer()

setLocalDescription(sdp)

offer sdp

sdp Copy & Paste

setRemoteDescription(sdp)

new

setOffer()

function setOffer(evt) { peerConnection = prepareNewConnection(); peerConnection.setRemoteDescription( new RTCSessionDescription(evt) );}

SDP を手動でコピー( offer )

SDP のやりとり

19

PeerConnectionApplication PeerConnection Application

new

createOffer()

setLocalDescription(sdp)

offer sdp

sdp Copy & Paste

createAnswer()

answer sdpsetLocalDescription(sdp)

setRemoteDescription(sdp)

new

sendAnswer()

function sendAnswer(evt) { peerConnection.createAnswer( function (sessionDescription) { // in case of success peerConnection.setLocalDescription(sessionDescription); sendSDP(sessionDescription); // show in textarea }, function () { // in case of error }, mediaConstraints);}

answer SDP を生成

SDP のやりとり

22

PeerConnectionApplication PeerConnection Application

new

createOffer()

setLocalDescription(sdp)

offer sdp

sdp Copy & Paste

createAnswer()

answer sdpsetLocalDescription(sdp)

setRemoteDescription(sdp)

new

sdp Copy & Paste

setRemoteDescription(sdp)

setAnswer()

function setAnswer(evt) { peerConnection.setRemoteDescription( new RTCSessionDescription(evt) );}

SDP を手動でコピー( answer )

SDP のやりとり(全体)

25

PeerConnectionApplication PeerConnection Application

new

createOffer()

setLocalDescription(sdp)

offer sdp

sdp Copy & Paste

createAnswer()

answer sdpsetLocalDescription(sdp)

setRemoteDescription(sdp)

new

sdp Copy & Paste

setRemoteDescription(sdp)

ICE Candidate のやりとり

26

PeerConnectionApplication PeerConnection Application

onIceCandidate(ice)

peer.onIceCandidate()function prepareNewConnection() { var pc_config = {"iceServers":[]}; var peer = null; peer = new webkitRTCPeerConnection(pc_config); peer.onicecandidate = function (evt) { if (evt.candidate) { sendCandidate({ // show in text area type: "candidate", sdpMLineIndex: evt.candidate.sdpMLineIndex, sdpMid: evt.candidate.sdpMid, candidate: evt.candidate.candidate }); } else { // End of candidates. } } … return peer;}

ICE Candidate のやりとり

28

PeerConnectionApplication PeerConnection Application

addIceCandidate(ice)

onIceCandidate(ice)

ice Copy & Paste

addIceCandidate()

function onCandidate(evt) { var candidate = new RTCIceCandidate({ sdpMLineIndex:evt.sdpMLineIndex, sdpMid:evt.sdpMid, candidate:evt.candidate }); peerConnection.addIceCandidate(candidate);}

ICE を手動でコピー

ICE を手動でコピー

ICE Candidate のやりとり

32

PeerConnectionApplication PeerConnection Application

onIceCandidate(ice)

addIceCandidate(ice)

addIceCandidate(ice)

onIceCandidate(ice)

ice Copy & Paste

ice Copy & Paste

peer.onIceCandidate() ※同じfunction prepareNewConnection() { var pc_config = {"iceServers":[]}; var peer = null; peer = new webkitRTCPeerConnection(pc_config); peer.onicecandidate = function (evt) { if (evt.candidate) { sendCandidate({ // show in text area type: "candidate", sdpMLineIndex: evt.candidate.sdpMLineIndex, sdpMid: evt.candidate.sdpMid, candidate: evt.candidate.candidate }); } else { // End of candidates. } } … return peer;}

addIceCandidate() ※同じfunction onCandidate(evt) { var candidate = new RTCIceCandidate({ sdpMLineIndex:evt.sdpMLineIndex, sdpMid:evt.sdpMid, candidate:evt.candidate }); peerConnection.addIceCandidate(candidate);}

ICE を手動でコピー(帰り)

ICE Candidate のやりとり

36

PeerConnectionApplication PeerConnection Application

onIceCandidate(ice)

addIceCandidate(ice)

addIceCandidate(ice)

onIceCandidate(ice)

onIceCandidate() : end of candidate onIceCandidate() : end of candidate

P2P stream transfer

ice Copy & Paste

ice Copy & Paste

ICE を手動でコピー(帰り)

ICE Candidate のやりとり (全体)

38

PeerConnectionApplication PeerConnection Application

onIceCandidate(ice)

addIceCandidate(ice)

addIceCandidate(ice)

onIceCandidate(ice)

onIceCandidate() : end of candidate onIceCandidate() : end of candidate

P2P stream transfer

ice Copy & Paste

ice Copy & Paste

ICE Candidate のやりとり(今回)

39

PeerConnectionApplication PeerConnection Application

onIceCandidate(ice)

addIceCandidate(ice)

addIceCandidate(ice)

onIceCandidate(ice)

P2P stream transfer

ice, ice, ice, ice Copy & Paste

ice, ice, ice, ice Copy & Paste

シグナリングサーバー編

シグナリングサーバーはどうして必要なの?

• シグナリングの過程で、情報を受け渡したい– お互いの IP アドレス– お互いのポート番号

• この段階ではお互い IP アドレスを知らない– 直接やりとりでない

• 仲介役となるシグナリングサーバーが必要– どちらブラウザもサーバーの IP アドレスを知っ

ていることが前提

Peer-to-Peer 通信の開始前に、普通のサーバー / クライアント型の通信が行われる

準備• Node.js をインストール– http://nodejs.jp/nodejs.org_ja/docs/v0.10/

• socket.io もインストール$ sudo npm install socket.io

シグナリングサーバー• WebRTC…Node.js で仲介(シグナリング)を作ってみよう

– http://html5experts.jp/mganeko/5349/

• 例えば、 signaling.js に記述、起動$ node signaling.js

var port = 9001; // as you likevar io = require('socket.io').listen(port);console.log((new Date()) + " Server is listening on port " + port); io.sockets.on('connection', function(socket) { socket.on('message', function(message) { socket.broadcast.emit('message', message); }); socket.on('disconnect', function() { socket.broadcast.emit('user disconnected'); });});

動かしてみます

DEMO

みなさんも、やってみてください

• スタートは、先ほどの手動用 HTML–その HTML/JavaScript を修正

• やることは、 HTML5Experts.jp の記事の内容– WebRTC 初心者でも簡単にできる! Node.js で仲

介(シグナリング)を作ってみよう– http://html5experts.jp/mganeko/5349/

• まず、 socket.io.js を読み込みます<script src="http://localhost:9001/socket.io/socket.io.js"></script>

ソケットの確立

49

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

connect()connect connect()

connect

connect()

var socketReady = false;var port = 9001;var socket = io.connect('http://localhost:' + port + '/');

// socket: channel connectedsocket.on('connect', onOpened) .on('message', onMessage); function onOpened(evt) { console.log('socket opened.'); socketReady = true;}

メッセージ処理// socket: accept connection requestfunction onMessage(evt) { if (evt.type === 'offer') { onOffer(evt); } else if (evt.type === 'answer' && peerStarted) { onAnswer(evt); } else if (evt.type === 'candidate' && peerStarted) { onCandidate(evt); } else if (evt.type === 'user dissconnected' && peerStarted) { stop(); }}

SDP のやりとり

52

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

connect()connect connect()

connectcreateOffer()

offer sdp

setLocalDescription(sdp)

send(sdp)send sdp

send sdp onMessage(sdp)

setRemoteDescription(sdp)

sendSDP()function sendOffer() { peerConnection = prepareNewConnection(); peerConnection.createOffer( function (sessionDescription) { // in case of success peerConnection.setLocalDescription(sessionDescription); sendSDP(sessionDescription); }, function () { // in case of error }, mediaConstraints);}

function sendSDP(sdp) { // send via socket socket.json.send(sdp);}

onMessage()

function onMessage(evt) { if (evt.type === 'offer') { onOffer(evt); } else if (evt.type === 'answer' && peerStarted) { onAnswer(evt); } else if (evt.type === 'candidate' && peerStarted) { onCandidate(evt); } else if (evt.type === 'user dissconnected' && peerStarted) { stop(); }}

onOffer(), setOffer()

function onOffer(evt) { setOffer(evt); sendAnswer(evt);}

function setOffer(evt) { peerConnection = prepareNewConnection(); peerConnection.setRemoteDescription( new RTCSessionDescription(evt) );}

SDP のやりとり

56

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

connect()connect connect()

connectcreateOffer()

offer sdp

setLocalDescription(sdp)

send(sdp)send sdp

send sdp onMessage(sdp)

setRemoteDescription(sdp)

createAnswer()answer sdp

send sdpsend(sdp)

send sdponMessage(sdp)setRemoteDescription(sdp)

setLocalDescription(sdp)

sendSDP()

function sendAnswer(evt) { peerConnection.createAnswer( function (sessionDescription) { // in case of success peerConnection.setLocalDescription(sessionDescription); sendSDP(sessionDescription); }, function () { // in case of error }, mediaConstraints);}

function sendSDP(sdp) { // send via socket socket.json.send(sdp);}

onMessage()

function onMessage(evt) { if (evt.type === 'offer') { onOffer(evt); } else if (evt.type === 'answer' && peerStarted) { onAnswer(evt); } else if (evt.type === 'candidate' && peerStarted) { onCandidate(evt); } else if (evt.type === 'user dissconnected' && peerStarted) { stop(); }}

onAnswer(), setAnswer ()

function onAnswer(evt) { setAnswer(evt);}

function setAnswer(evt) { peerConnection.setRemoteDescription( new RTCSessionDescription(evt) );}

SDP のやりとり(全体)

60

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

connect()connect connect()

connectcreateOffer()

offer sdp

setLocalDescription(sdp)

send(sdp)send sdp

send sdp onMessage(sdp)

setRemoteDescription(sdp)

createAnswer()answer sdp

send sdpsend(sdp)

send sdponMessage(sdp)setRemoteDescription(sdp)

setLocalDescription(sdp)

ICE Candidate のやりとり

61

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

send(ice) send icesend ice

onMessage(ice)

addIceCandidate(ice)

onIceCandidate(ice)

peer.onIceCandidate()function prepareNewConnection() { var pc_config = {"iceServers":[]}; var peer = null; peer = new webkitRTCPeerConnection(pc_config); peer.onicecandidate = function (evt) { if (evt.candidate) { sendCandidate({ type: "candidate", sdpMLineIndex: evt.candidate.sdpMLineIndex, sdpMid: evt.candidate.sdpMid, candidate: evt.candidate.candidate }); } else { // End of candidates. } } … return peer;}

sendCandidate()

function sendCandidate(candidate) { // send via socket socket.json.send(candidate);}

onMessage()

function onMessage(evt) { if (evt.type === 'offer') { onOffer(evt); } else if (evt.type === 'answer' && peerStarted) { onAnswer(evt); } else if (evt.type === 'candidate' && peerStarted) { onCandidate(evt); } else if (evt.type === 'user dissconnected' && peerStarted) { stop(); }}

onCandidate(), addIceCandidate()

function onCandidate(evt) { var candidate = new RTCIceCandidate({ sdpMLineIndex:evt.sdpMLineIndex, sdpMid:evt.sdpMid, candidate:evt.candidate }); peerConnection.addIceCandidate(candidate);}

ICE Candidate のやりとり

66

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

onIceCandidate(ice)

send(ice) send icesend ice

onMessage(ice)

addIceCandidate(ice)

send ice send(ice)send ice

onMessage(ice)

addIceCandidate(ice)

onIceCandidate(ice)

peer.onIceCandidate() ※同じfunction prepareNewConnection() { var pc_config = {"iceServers":[]}; var peer = null; peer = new webkitRTCPeerConnection(pc_config); peer.onicecandidate = function (evt) { if (evt.candidate) { sendCandidate({ type: "candidate", sdpMLineIndex: evt.candidate.sdpMLineIndex, sdpMid: evt.candidate.sdpMid, candidate: evt.candidate.candidate }); } else { // End of candidates. } } … return peer;}

onMessage() ※同じfunction onMessage(evt) { if (evt.type === 'offer') { onOffer(evt); } else if (evt.type === 'answer' && peerStarted) { onAnswer(evt); } else if (evt.type === 'candidate' && peerStarted) { onCandidate(evt); } else if (evt.type === 'user dissconnected' && peerStarted) { stop(); }}

onCandidate(), addIceCandidate() ※同じfunction onCandidate(evt) { var candidate = new RTCIceCandidate({ sdpMLineIndex:evt.sdpMLineIndex, sdpMid:evt.sdpMid, candidate:evt.candidate }); peerConnection.addIceCandidate(candidate);}

ICE Candidate のやりとり

70

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

onIceCandidate(ice)

send(ice) send icesend ice

onMessage(ice)

addIceCandidate(ice)

send ice send(ice)send ice

onMessage(ice)

addIceCandidate(ice)

onIceCandidate(ice)

onIceCandidate() : end of candidateonIceCandidate() : end of candidate

P2P stream transfer

ICE Candidate のやりとり(全体)

71

PeerConnectionsocket

Application SignalingServer

PeerConnectionsocket

Application

onIceCandidate(ice)

send(ice) send icesend ice

onMessage(ice)

addIceCandidate(ice)

send ice send(ice)send ice

onMessage(ice)

addIceCandidate(ice)

onIceCandidate(ice)

onIceCandidate() : end of candidateonIceCandidate() : end of candidate

P2P stream transfer

Peer-to-Peer 通信確立

なぜ WebRTC に注目するのか?通信手段の破壊的進化キャリア型通信

固定電話 携帯電話 (TV 放送)

手段の例

Over The Top

Skype, WebEx (YouTube, USTREAM)

Web ブラウザ型

WebRTC

世界中の人と会話 できる ユーザメリット 世界中の人と無料 /

安価で会話できる 専用アプリ無しで 会話できる

インフラを持つ キャリアが支配市場

キャリアに縛られない 独自の仕組みを提供 する少数のベンダー が参加可能

特別な仕組みは不要 誰でも参加可能

×事業者メリット 限定的な API 提供 連携可能

完全にプログラマブル 部品として利用可能

単独で利用利用方法 ユーザが組み合わ せて利用

製品 / サービスに 組み込んで利用

コールセンター、 EC サイト、情報共有システム、など

WebRTC をどんな風に使うかは、

みなさんのアイデア次第です!

74

Thank you!

75

END