Skip to main content
WebRTCの技術解説
WebRTC 勉強会 @ 第1回 NTT-WEST学生向けアプリ開発コンテスト
https://www.facebook.com/nttw.w.con
2014/08/05
NTT西日本
http://ntt-west.co.jp/
公開版
資料公開にあたり
当資料は学生向けWebRTCコンテストの
関連イベントであるWebRTC勉強会で
使用されたものです。
コンテストの詳細については、
下記URLをご参照下さい。
第1回 NTT-WEST学生向けアプリ開発コンテスト
https://www.facebook.com/nttw.w.con
きんじょう ゆう
金城 雄
Twitter @youkinjoh
GitHub @youkinjoh
SlideShare @You_Kinjoh
講師紹介
gihyo.jp
Jettyで始めるWebSocket超入門
http://gihyo.jp/dev/feature/01/websocket/0001
デモ参加のお願い
IN
FO
R
M
A
T
IO
N
IN
FO
R
M
A
T
IO
N
参加型デモ
カメラ付きの端末でお願いします。
できるだけ
新しい
Chrome
Android
パソコン
IN
FO
R
M
A
T
IO
N
IN
FO
R
M
A
T
IO
N
WebRTCの概要
WebRTCの2つの仕様
PeerJS
SkyWay
ユーザメディアを操作する
その他雑多な内容
今日お話しすること
質疑応答について
WebRTC
Web Real-Time Communication
リアルタイムコミュニケーションのAPI
ボイスチャット・ビデオチャットが
プラグインなしにブラウザでできる
テキストデータ・バイナリデータも送信可
P2P
WebRTC
これまでの
リアルタイム
コミュニケーション
との違い
http://www.slideshare.net/mganeko/2013-web-rtctechcross/6 より引用
キャリア型通信
手段の例
市場
ユーザ
メリット
事業者
メリット
利用方法
固定電話 携帯電話 (TV放送)
インフラを持つキャリアが支配
世界中の人と会話できる
×
単独で利用
http://www.slideshare.net/mganeko/2013-web-rtctechcross/6 より改変して引用
Over The Top
手段の例
市場
ユーザ
メリット
事業者
メリット
利用方法
Skype, WebEx
(YouTube, Ustream)
キャリアに縛られない独自の仕組みを
提供する少数のベンダーが参加可能
世界中の人と無料/安価で会話できる
限定的なAPI提供
一部連携可能
ユーザが組み合わせて利用
http://www.slideshare.net/mganeko/2013-web-rtctechcross/6 より改変して引用
Webブラウザ型
手段の例
市場
ユーザ
メリット
事業者
メリット
利用方法
WebRTC
特別な仕組みは不要
誰でも参加可能
専用アプリ無しで会話できる
完全にプログラマブル部品として
利用可能
製品/サービスに組み込んで利用
http://www.slideshare.net/mganeko/2013-web-rtctechcross/6 より改変して引用
キャリア型通信 Over The Top Webブラウザ型
手段の例
市場
ユーザ
メリット
事業者
メリット
利用方法
固定電話
携帯電話
(TV放送)
Skype, WebEx
(Youtube,
Ustream)
WebRTC
インフラを持つ
キャリアが支配
キャリアに縛られない
独自の仕組みを提供
する少数のベンダー
が参加可能
特別な仕組みは不要
誰でも参加可能
世界中の人と
会話できる
世界中の人と無料/
安価で会話できる
専用アプリ無しで
会話できる
×
限定的なAPI提供
一部連携可能
完全にプログラマブル
部品として利用可能
単独で利用
ユーザが組み合わせて
利用
製品/サービスに
組み込んで利用
http://www.slideshare.net/mganeko/2013-web-rtctechcross/6 より改変して引用
対応ブラウザ
※ 但し、iOSのブラウザは全て未対応
(2014/07現在)
今後もまだ仕様が変更になる可能性がある
ベンダープレフィックスが必要
先行したバージョンの使用がお勧め
Google Chrome Canary
Firefox Bata
Firefox Aurora
まだ策定中
ベンダープレフィックス
先行実装であることを示す慣習
ブラウザベンダーによって違う
メソッドの挙動が同じであれば、
代入してメソッド名の違いを吸収できる
navigator.getUserMedia =
navigator.getUserMedia || //Specification
navigator.webkitGetUserMedia || //for Chrome
navigator.mozGetUserMedia ; //for Firefox
ベンダープレフィックスの現状は以下を参照のこと。
https://plus.google.com/app/basic/stream/z121hnjxtqq2svgni23mznm4cxnctznc5
主な2つの仕様
Media Capture and Streams
ブラウザからカメラやマイクの
メディアストリームを取得するための仕様
WebRTC 1.0: Real-time
Communication Between
Browsers
ブラウザとブラウザをP2Pで接続し
通信を行なうための仕様
Media Capture and Streams
(getUserMedia)
ブラウザからマイクやカメラにアクセス
利用範囲はWebRTC以外とも
音声処理(with Web Audio API)
ボイスチェンジャー etc.
画像処理(with Canvas)
顔検出 etc.
顔認識ができるようになるのも時間の問題。
DEMO
SAMPLE
音声と映像を取得
Media Capture and Streams
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="script.js"></script>
<title>getUserMedia Sample</title>
</head>
<body>
<video id="video"></video>
</body>
</html>
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="script.js"></script>
<title>getUserMedia Sample</title>
</head>
<body>
<video id="video"></video>
</body>
</html>
HTML
ベンダープレフィックスの処理
ユーザメディアの取得
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="script.js"></script>
<title>getUserMedia Sample</title>
</head>
<body>
<video id="video"></video>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="script.js"></script>
<title>getUserMedia Sample</title>
</head>
<body>
<video id="video"></video>
</body>
</html>
Scriptの読み込みと
ビデオ要素の表示。
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
ベンダープレフィックス
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
ページ読み込み時の処理を指定。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
ユーザメディアの取得開始。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
取得するユーザメディアは
カメラとマイク。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
ユーザメディア取得時の動作を指定。
streamはMediaStreamオブジェクト。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
ビデオ要素の取得。
ユーザメディアの指定。
MediaStreamをURLにし、src属性に指定。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
再生開始。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
ユーザメディア取得失敗時の処理を指定。
以前はエラー用のcallbackなしでも動きましたが、
最近のブラウザでは指定が必須のようです。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
ユーザメディア取得失敗時の処理。
ここではエラーログを出力しているだけ。
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
window.addEventListener('load', function() {
navigator.getUserMedia(
{video: true, audio: true},
function(stream) {
var video = document.getElementById('video');
video.src = window.URL.createObjectURL(stream);
video.play();
},
function(error) {
console.error(error);
}
);
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="script.js"></script>
<title>getUserMedia Sample</title>
</head>
<body>
<video id="video"></video>
</body>
</html>
WebRTC 1.0: Real-time
Communication Between
Browsers
ブラウザとブラウザをP2Pで接続
通信は全て暗号化される
P2Pの前に要シグナリング
シグナリングサーバが必要
WebSocketが良く使われている
Node.jsならSocket.IO
WebRTC 1.0: Real-time
Communication Between
Browsers
APIが複雑でわかりにくい
抽象化した仕様の多いHTML5の
他のAPIと比べると非常に複雑
それでも、従来のリアルタイム通信の
処理よりは断然楽
ジッタやパケットロス等の対策は、
ブラウザが全て対応してくれる
DEMO
WebRTC 1.0: Real-time
Communication Between
Browsers
ICE (STUN + TURN + α)
NAT通過・ネゴシエーション
STUN
P2P・UDPホールパンチング
TURN
P2Pが不可能ならサーバ経由で通信
WebRTC 1.0: Real-time
Communication Between
Browsers
SDP
セッションプロトコルを記述
セッション開始に必要な情報
DTLS (UDP等のデータグラム向けのTLS)
データ通信は全て暗号化される
WebRTC 1.0: Real-time
Communication Between
Browsers
MediaStream (SRTP・SRTCP)
音声データ・映像データ
SRTP (RTPのセキュア版)
リアルタイムデータ配信の仕様
SRTCP (RTCPのセキュア版)
配信用制御プロトコル
WebRTC 1.0: Real-time
Communication Between
Browsers
DataChannel (SCTP)
テキストデータ・バイナリデータ
SCTP
TCPとUDPの良いところ取りをしたプロトコル
標準ではTCPに似た動作をする
設定で信頼性と引き換えにUDPに似た動作にでき、
リアルタイム性の向上が可能
どの処理をブラウザが
勝手にやってくれるのか、
どの処理を自分で
実装する必要があるのかを
把握しないと
実装時に大変混乱する。
どうやって
ブラウザで
P2Pを
実現しているのか
?????????? ??????????
画面上部にメソッド名等の
キーワードが表示されます。
実装時にご活用下さい。
Browser Browser
NAT NAT
NATが邪魔して直接通信ができない。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
HTML+JS+CSS
Global IP/Port
signaling
HTML+JS+CSS
Global IP/Port
signaling
data
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ブラウザでWebRTCを使った
ページにアクセス。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
HTML+JS+CSS
HTTP Request
HTTP Response
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Signaling Serverとして利用する
WebSocket Serverに接続。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
signaling
WebSocket open
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
signaling
対向のブラウザもWebRTCを使った
ページにアクセス。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
HTML+JS+CSS
HTTP Request
HTTP Response
signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
signaling
対向のブラウザも
WebSocket Serverに接続。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
signaling signaling
WebSocket open
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
signaling signaling
以後、WebSocketは接続を継続する。
この先、説明の都合上、接続中でもグレーアウトします。
データが流れる時だけ色がつきます。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
接続を開始する側でoffer開始。
SDPを生成・自身に登録後送信。
対向のブラウザは受け取ったSDPを登録。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Offer SDP
createOffer
SDPは以下の
情報を含む。
メディアタイプ
コーデック
帯域幅 etc.
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Offer SDP
setLocalDescription
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
WebSocket send
Offer SDP
signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Offer SDP
signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
setRemoteDescription
Offer SDP
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
受け取ったofferに対してanswer開始。
SDPを生成・自身に登録後送信。
offerした側でも対向のSDPを登録。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
createAnswer
SDPは以下の
情報を含む。
メディアタイプ
コーデック
帯域幅 etc.
Answer SDP
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
setLocalDescription
Answer SDP
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
WebSocket send
Answer SDP
signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Answer SDP
signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
setRemoteDescription
Answer SDP
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
P2Pの為に相手にIPを伝える必要がある。
その為にSTUNを使い自身のIPを調べる。
ブラウザが裏で処理してくれる。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ここの
Global IP/Port
を知りたい
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Global IP/Port
Global
IP/Port
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Global IP/Port
Global
IP/Port
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ここの
Global IP/Port
を知りたい
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Global IP/Port
Global
IP/Port
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
Global IP/Port
Global
IP/Port
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
自身のIP/Portは把握。
自身に接続できそうな経路の候補を
相手側に伝える必要がある。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ICE
candidate
ブラウザは
これまでの
情報を元に
裏で経路候補
(candidate)を
生成。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ICE
candidate
icecandidate Event
イベントに
登録した
ハンドラ経由で
経路候補を取得。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
WebSocket send
ICE
candidatesignaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ICE
candidate
signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
addIceCandidate
ICE
candidate
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ブラウザは
STUNからの
情報を元に
裏で経路候補
(candidate)を
生成。
ICE
candidate
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
icecandidate Event
ICE
candidate
イベントに
登録した
ハンドラ経由で
経路候補を取得。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
WebSocket send
ICE
candidate signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
ICE
candidate
signaling
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
addIceCandidate
ICE
candidate
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
P2Pで通信する為の情報が
(やっと)整った。
通信開始。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
data
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
それでもP2Pが無理だった場合、
TURNサーバが中継を行なう。
TURN経由でもデータの暗号化は解かれずセキュア。
Web Server
WebSocket
Server
ICE Server
(STUN + TURN)
Browser Browser
NAT NAT
data data
RTCPeerConnection
ストリーミングを扱うための
WebRTCの中心となるオブジェクト
RTCSessionDescription
SDPを扱うオブジェクト
RTCIceCandidate
経路情報を扱うオブジェクト
RTCDataChannel
テキスト・バイナリ用のデータチャネル
SAMPLE
このサンプルでは細かい制御は行なっていないので注意してください。
ビデオチャット
(二者間通信)
WebRTC 1.0: Real-time
Communication Between
Browsers
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
window.RTCPeerConnection =
window.RTCPeerConnection ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection ;
window.RTCSessionDescription =
window.RTCSessionDescription ||
window.webkitRTCSessionDescription ||
window.mozRTCSessionDescription ;
window.RTCIceCandidate =
window.RTCIceCandidate ||
window.webkitRTCIceCandidate ||
window.mozRTCIceCandidate ;
var ws = null;
var peer = null;
function initialize() {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
peer = new RTCPeerConnection({
iceServers: [
{url: 'stun:stun.l.google.com:19302'},
{url: 'stun:23.21.150.121'}
]
});
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
peer.addStream(stream);
},
function(error) {
console.error(error);
}
);
ws.addEventListener('message', function(evt) {
var data = JSON.parse(evt.data);
if (!data.sdp) {return;}
var sdp = data.sdp;
var description = new RTCSessionDescription(sdp);
peer.setRemoteDescription(description, function() {
if (description.type === 'offer') {
answer();
}
});
});
ws.addEventListener('message', function(evt) {
var data = JSON.parse(evt.data);
if (!data.candidate) {return;}
var candidate = new RTCIceCandidate(data.candidate);
peer.addIceCandidate(candidate);
});
peer.addEventListener('icecandidate', function(evt) {
if (!evt.candidate) {return;}
var candidate = evt.candidate;
ws.send(JSON.stringify({candidate: candidate}));
});
peer.addEventListener('addstream', function(evt) {
var video = document.getElementById('remote');
video.src = URL.createObjectURL(evt.stream);
video.play();
});
var offerbtn = document.getElementById('offer_button');
offerbtn.addEventListener('click', offer);
}
function offer() {
peer.createOffer(
function(offer) {
peer.setLocalDescription(offer, function() {
ws.send(JSON.stringify({sdp: offer}));
});
},
function(error) {
console.error(error);
}
);
}
function answer() {
peer.createAnswer(
function(answer) {
peer.setLocalDescription(answer, function() {
ws.send(JSON.stringify({sdp: answer}));
});
},
function(error) {
console.error(error);
}
);
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="scripts/script.js"></script>
<title>WebRTC Sample</title>
</head>
<body>
<input type="button" value="offer" id="offer_button" />
<video id="local" autoplay="autoplay"></video>
<video id="remote" autoplay="autoplay"></video>
</body>
</html>
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
window.RTCPeerConnection =
window.RTCPeerConnection ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection ;
window.RTCSessionDescription =
window.RTCSessionDescription ||
window.webkitRTCSessionDescription ||
window.mozRTCSessionDescription ;
window.RTCIceCandidate =
window.RTCIceCandidate ||
window.webkitRTCIceCandidate ||
window.mozRTCIceCandidate ;
var ws = null;
var peer = null;
function initialize() {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
peer = new RTCPeerConnection({
iceServers: [
{url: 'stun:stun.l.google.com:19302'},
{url: 'stun:23.21.150.121'}
]
});
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
peer.addStream(stream);
},
function(error) {
console.error(error);
}
);
ws.addEventListener('message', function(evt) {
var data = JSON.parse(evt.data);
if (!data.sdp) {return;}
var sdp = data.sdp;
var description = new RTCSessionDescription(sdp);
peer.setRemoteDescription(description, function() {
if (description.type === 'offer') {
answer();
}
});
});
ws.addEventListener('message', function(evt) {
var data = JSON.parse(evt.data);
if (!data.candidate) {return;}
var candidate = new RTCIceCandidate(data.candidate);
peer.addIceCandidate(candidate);
});
peer.addEventListener('icecandidate', function(evt) {
if (!evt.candidate) {return;}
var candidate = evt.candidate;
ws.send(JSON.stringify({candidate: candidate}));
});
peer.addEventListener('addstream', function(evt) {
var video = document.getElementById('remote');
video.src = URL.createObjectURL(evt.stream);
video.play();
});
var offerbtn = document.getElementById('offer_button');
offerbtn.addEventListener('click', offer);
}
function offer() {
peer.createOffer(
function(offer) {
peer.setLocalDescription(offer, function() {
ws.send(JSON.stringify({sdp: offer}));
});
},
function(error) {
console.error(error);
}
);
}
function answer() {
peer.createAnswer(
function(answer) {
peer.setLocalDescription(answer, function() {
ws.send(JSON.stringify({sdp: answer}));
});
},
function(error) {
console.error(error);
}
);
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="scripts/script.js"></script>
<title>WebRTC Sample</title>
</head>
<body>
<input type="button" value="offer" id="offer_button" />
<video id="local" autoplay="autoplay"></video>
<video id="remote" autoplay="autoplay"></video>
</body>
</html>
HTML
ベンダープレフィックスの
処理
WebSocketと
PeerConnectionの
初期化
ユーザメディアの取得
WebSocketの
イベント登録
PeerConnectionの
イベント登録
変数宣言
ボタンのイベント登録
offer処理
answer処理
onload
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="scripts/script.js"></script>
<title>WebRTC Sample</title>
</head>
<body>
<input type="button" value="offer" id="offer_button" />
<video id="local" autoplay="autoplay"></video>
<video id="remote" autoplay="autoplay"></video>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="scripts/script.js"></script>
<title>WebRTC Sample</title>
</head>
<body>
<input type="button" value="offer" id="offer_button" />
<video id="local" autoplay="autoplay"></video>
<video id="remote" autoplay="autoplay"></video>
</body>
</html>
Scriptの読み込みと
offer開始用のボタンと
ビデオ要素の表示。
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
ベンダープレフィックス