More Related Content Similar to WebRTCの技術解説 公開版 (20) WebRTCの技術解説 公開版1. WebRTCの技術解説
WebRTC 勉強会 @ 第1回 NTT-WEST学生向けアプリ開発コンテスト
https://www.facebook.com/nttw.w.con
2014/08/05
NTT西日本
http://ntt-west.co.jp/
公開版
3. きんじょう ゆう
金城 雄
Twitter @youkinjoh
GitHub @youkinjoh
SlideShare @You_Kinjoh
講師紹介
gihyo.jp
Jettyで始めるWebSocket超入門
http://gihyo.jp/dev/feature/01/websocket/0001
15. キャリア型通信 Over The Top Webブラウザ型
手段の例
市場
ユーザ
メリット
事業者
メリット
利用方法
固定電話
携帯電話
(TV放送)
Skype, WebEx
(Youtube,
Ustream)
WebRTC
インフラを持つ
キャリアが支配
キャリアに縛られない
独自の仕組みを提供
する少数のベンダー
が参加可能
特別な仕組みは不要
誰でも参加可能
世界中の人と
会話できる
世界中の人と無料/
安価で会話できる
専用アプリ無しで
会話できる
×
限定的なAPI提供
一部連携可能
完全にプログラマブル
部品として利用可能
単独で利用
ユーザが組み合わせて
利用
製品/サービスに
組み込んで利用
http://www.slideshare.net/mganeko/2013-web-rtctechcross/6 より改変して引用
19. 主な2つの仕様
Media Capture and Streams
ブラウザからカメラやマイクの
メディアストリームを取得するための仕様
WebRTC 1.0: Real-time
Communication Between
Browsers
ブラウザとブラウザをP2Pで接続し
通信を行なうための仕様
20. Media Capture and Streams
(getUserMedia)
ブラウザからマイクやカメラにアクセス
利用範囲はWebRTC以外とも
音声処理(with Web Audio API)
ボイスチェンジャー etc.
画像処理(with Canvas)
顔検出 etc.
顔認識ができるようになるのも時間の問題。
23. 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>
24. 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
ベンダープレフィックスの処理
ユーザメディアの取得
38. 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>
45. WebRTC 1.0: Real-time
Communication Between
Browsers
DataChannel (SCTP)
テキストデータ・バイナリデータ
SCTP
TCPとUDPの良いところ取りをしたプロトコル
標準ではTCPに似た動作をする
設定で信頼性と引き換えにUDPに似た動作にでき、
リアルタイム性の向上が可能
96. 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>
97. 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
98. <!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>
99. <!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開始用のボタンと
ビデオ要素の表示。
102. var ws = null;
var peer = null;
function initialize() {
// other slides
}
window.addEventListener('load', initialize);
WebSocketオブジェクトと
RTCPeerConnectionオブジェクトの
変数宣言と、初期化関数の定義。
103. 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'}
]
});
シグナリングに使う
WebSocketの接続開始。
initialize内
104. 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'}
]
});
RTCPeerConnection初期化。
STUNサーバ/TURNサーバを
引数に指定(複数可)。
initialize内
105. 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);
}
);
ユーザメディアを取得し表示。
メディアストリームを
RTCPeerConnectionに登録。
initialize内
106. 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);
}
);
ユーザメディアの取得・表示。
メディアストリームを
RTCPeerConnectionに登録。
initialize内
107. 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);
});
WebSocketに
イベントハンドラを指定。
initialize内
108. 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);
});
WSのメッセージ受信時の処理を指定。
(SDP用)
initialize内
109. 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);
});
SDPが含まれているか判定。
RTCSessionDescriptionを生成。
initialize内
110. 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);
});
RTCSessionDescriptionを
PeerConnectionに登録。
合わせてcallbackを指定。
initialize内
111. 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);
});
受け取ったSDPがofferだった場合、
自動的にanswer処理を実行。
answerの内容は後述。
initialize内
112. 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);
});
initialize内
WSのメッセージ受信時の処理を指定。
(経路情報用)
113. 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);
});
initialize内
経路情報が含まれているか判定。
RTCIceCandidateを生成。
114. 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);
});
initialize内
RTCIceCandidateを
PeerConnectionに登録。
115. 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();
});
initialize内
RTCPeerConnectionに
イベントハンドラを指定。
116. 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();
});
RTCPeerConnectionの
経路候補取得時の処理を指定。
initialize内
117. 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();
});
経路候補が含まれているか判定。
経路候補を対向に送信。
initialize内
118. 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();
});
RTCPeerConnectionの
Stream(対向)取得時の処理を指定。
initialize内
119. 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();
});
対向のMediaStreamを表示。
initialize内
120. var offerbtn = document.getElementById('offer_button');
offerbtn.addEventListener('click', offer);
offer開始用のボタンのクリック時に
offer処理を実行。
offerの内容は後述。
initialize内
137. 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>
143. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var PEERJS_API_KEY = '[PEERJS_API_KEY]';
var ws = null;
var peer = null;
var selfid = null;
var localStream = null;
function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
function initializeMedia(callback) {
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
localStream = stream;
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
callback();
},
function(error) {
console.error(error);
}
);
}
function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
initializeWebSocket(function() {
ws.send(selfid);
});
});
});
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
<script src="scripts/script.js"></script>
<title>PeerJS Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
144. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var PEERJS_API_KEY = '[PEERJS_API_KEY]';
var ws = null;
var peer = null;
var selfid = null;
var localStream = null;
function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
function initializeMedia(callback) {
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
localStream = stream;
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
callback();
},
function(error) {
console.error(error);
}
);
}
function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
initializeWebSocket(function() {
ws.send(selfid);
});
});
});
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
<script src="scripts/script.js"></script>
<title>PeerJS Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
HTML
ベンダープレフィックスの
処理
API Keyと変数宣言
PeerJSの
初期化とイベント登録
ユーザメディアの取得
WebSocketの
初期化とイベント登録
MediaConnectionの
設定
(主にイベント登録)
onloadと
初期化処理の呼び出しと
シグナリング開始
145. <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
<script src="scripts/script.js"></script>
<title>PeerJS Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
146. <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
<script src="scripts/script.js"></script>
<title>PeerJS Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
LibraryとScriptの読み込みと
ビデオ要素の表示と
対向の表示領域の準備。
148. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var PEERJS_API_KEY = '[PEERJS_API_KEY]';
var ws = null;
var peer = null;
var selfid = null;
var localStream = null;
API Key、WebSocketとPeerJS、
自分のidとStream用の変数宣言。
PeerServer Cloud service の API Key 取得は以下のURLから。
http://peerjs.com/peerserver
149. function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}PeerJSの各種設定。
150. function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
PeerJSの初期化。
要API Key。
151. function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
PeerServerと接続時に
自身のPeerIDを取得する。
ローカル変数にPeerIDを保存。
152. function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
初期化処理のためのcallback。
callbackの詳細は後述。
153. function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
相手から通話依頼に、自動応答させる。
MediaConnectionの設定。
MediaConnectionの設定の詳細は後述。
154. function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
PeerServerとの通信が切断した時、
Peerオブジェクトを破棄。
155. function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
エラー処理。
ここではエラーログを出力しているだけ。
159. function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
WebSocketの各種設定。
160. function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
PeerIDを自動で交換して接続するための
WebSocketの接続開始。
161. function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
WebSocket接続時の処理を指定。
162. function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
初期化処理のためのcallback。
callbackの詳細は後述。
163. function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
WSのメッセージ受信時の処理を指定。
(PeerID用)
164. function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
受け取ったPeerIDに、自動発信させる。
MediaConnectionの設定。
MediaConnectionの設定の詳細は後述。
165. function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
発信/応答時に得た
MediaConnectionに設定。
(主にイベント登録。)
166. function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
発信/応答先のPeerIDを取得。
StreamとVideo要素の保存領域。
closure使用。
167. function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
Stream(対向)取得時の処理を指定。
168. function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
対向のMediaStreamを表示。
169. function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
MediaConnectionが切断した時、
URLオブジェクトの削除と
Video要素を削除。
170. function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
エラー処理。
ここではエラーログを出力しているだけ。
172. function initialize() {
initializePeer(function() {
initializeMedia(function() {
initializeWebSocket(function() {
ws.send(selfid);
});
});
});
}
window.addEventListener('load', initialize);
Peerとメディアの初期化が終わらないと
PeerIDの送信が行なえないため。
DefferedやPromiseを使えばもっと綺麗に書ける。
173. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var PEERJS_API_KEY = '[PEERJS_API_KEY]';
var ws = null;
var peer = null;
var selfid = null;
var localStream = null;
function initializePeer(callback) {
peer = new Peer({key: PEERJS_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
function initializeMedia(callback) {
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
localStream = stream;
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
callback();
},
function(error) {
console.error(error);
}
);
}
function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
initializeWebSocket(function() {
ws.send(selfid);
});
});
});
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
<script src="scripts/script.js"></script>
<title>PeerJS Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
177. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var SKYWAY_API_KEY = '[SKYWAY_API_KEY]';
var REST_API_LIST = 'https://skyway.io/v2/active/list/';
var peer = null;
var selfid = null;
var localStream = null;
function initializePeer(callback) {
peer = new Peer({key: SKYWAY_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
function initializeMedia(callback) {
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
localStream = stream;
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
callback();
},
function(error) {
console.error(error);
}
);
}
function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
callRemoteAll();
});
});
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="https://skyway.io/dist/v2/0.3/peer.js"></
script>
<script src="scripts/script.js"></script>
<title>SkyWay Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
178. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var SKYWAY_API_KEY = '[SKYWAY_API_KEY]';
var REST_API_LIST = 'https://skyway.io/v2/active/list/';
var peer = null;
var selfid = null;
var localStream = null;
function initializePeer(callback) {
peer = new Peer({key: SKYWAY_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
function initializeMedia(callback) {
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
localStream = stream;
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
callback();
},
function(error) {
console.error(error);
}
);
}
function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
callRemoteAll();
});
});
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="https://skyway.io/dist/v2/0.3/peer.js"></
script>
<script src="scripts/script.js"></script>
<title>SkyWay Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
HTML PeerJSの
初期化とイベント登録
ユーザメディアの取得
MediaConnectionの
設定
(主にイベント登録)
onloadと
初期化処理の呼び出しと
発信処理開始
接続先の一覧所得と
発信処理
ベンダープレフィックスの
処理
API Keyと変数宣言
179. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var SKYWAY_API_KEY = '[SKYWAY_API_KEY]';
var REST_API_LIST = 'https://skyway.io/v2/active/list/';
var peer = null;
var selfid = null;
var localStream = null;
function initializePeer(callback) {
peer = new Peer({key: SKYWAY_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
function initializeMedia(callback) {
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
localStream = stream;
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
callback();
},
function(error) {
console.error(error);
}
);
}
function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
callRemoteAll();
});
});
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="https://skyway.io/dist/v2/0.3/peer.js"></
script>
<script src="scripts/script.js"></script>
<title>SkyWay Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
主に
PeerJS版との
差分のみを解説
180. ...
<link rel="stylesheet" href="styles/style.css" />
<script src="http://cdn.peerjs.com/0.3/peer.js"></script>
<script src="scripts/script.js"></script>
...
...
<link rel="stylesheet" href="styles/style.css" />
<script src="https://skyway.io/dist/v2/0.3/peer.js"></script>
<script src="scripts/script.js"></script>
...
読み込むLibraryの変更。
181. var PEERJS_API_KEY = '[PEERJS_API_KEY]';
var SKYWAY_API_KEY = '[SKYWAY_API_KEY]';
var REST_API_LIST = 'https://skyway.io/v2/active/list/';
API KeyをSkyWay用に変更。
接続しているPeerIDの
一覧を取得するRestAPIのURL。
SkyWay の API Key 取得は以下のURLから。
http://nttcom.github.io/skyway/registration.html
182. ...
var ws = null;
...
function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
...
183. ...
var ws = null;
...
function initializeWebSocket(callback) {
var secure = location.protocol === 'https:';
var protocol = secure ? 'wss' : 'ws';
var url = protocol + '://' + location.host + '/';
ws = new WebSocket(url);
ws.addEventListener('open', function() {
callback();
});
ws.addEventListener('message', function(evt) {
var remoteid = evt.data;
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
});
}
...
WebSocketの処理は削除。
184. function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
RestAPIからPeerIDの一覧を取得し、
全てに発信。
185. function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}RestAPIのURL + API Key。
186. function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
Ajax(XHR)お決まりの記述。
187. function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}接続しているPeerIDの一覧を取得。
188. function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
RestAPIからPeerIDの一覧を取得し、
全てに自動発信。
189. function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
MediaConnectionの設定。
190. function initialize() {
initializePeer(function() {
initializeMedia(function() {
initializeWebSocket(function() {
ws.send(selfid);
});
});
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
callRemoteAll();
});
});
}
191. function initialize() {
initializePeer(function() {
initializeMedia(function() {
initializeWebSocket(function() {
ws.send(selfid);
});
});
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
callRemoteAll();
});
});
}
WebSocketの初期化削除。
シグナリング開始処理削除。
発信処理追加。
192. navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ;
window.URL =
window.URL ||
window.webkitURL ;
var SKYWAY_API_KEY = '[SKYWAY_API_KEY]';
var REST_API_LIST = 'https://skyway.io/v2/active/list/';
var peer = null;
var selfid = null;
var localStream = null;
function initializePeer(callback) {
peer = new Peer({key: SKYWAY_API_KEY});
peer.on('open', function(id) {
selfid = id;
callback();
});
peer.on('call', function(mediaConnection) {
mediaConnection.answer(localStream);
settingMediaConnection(mediaConnection);
});
peer.on('close', function() {
peer.destroy();
});
peer.on('error', function(err) {
console.error(err);
});
}
function initializeMedia(callback) {
navigator.getUserMedia(
{audio: true, video: true},
function(stream) {
localStream = stream;
var video = document.getElementById('local');
video.src = URL.createObjectURL(stream);
video.play();
callback();
},
function(error) {
console.error(error);
}
);
}
function callRemoteAll() {
var url = REST_API_LIST + SKYWAY_API_KEY;
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', function() {
if (xhr.readyState != 4) {return;}
if (xhr.status != 200) {return;}
var remoteids = JSON.parse(xhr.responseText);
for (var i = 0; i < remoteids.length; i++) {
var remoteid = remoteids[i];
var mediaConnection =
peer.call(remoteid, localStream);
settingMediaConnection(mediaConnection);
}
});
xhr.open('GET', url);
xhr.send();
}
function settingMediaConnection(mediaConnection) {
var remoteid = mediaConnection.peer;
var remoteStream = null;
var video = null;
mediaConnection.on('stream', function(stream) {
video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
var parent = document.getElementById('remotes');
parent.appendChild(video);
});
mediaConnection.on('close', function() {
URL.revokeObjectURL(video.src);
video.parentNode.removeChild(video);
});
mediaConnection.on('error', function() {
console.error(err);
});
}
function initialize() {
initializePeer(function() {
initializeMedia(function() {
callRemoteAll();
});
});
}
window.addEventListener('load', initialize);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/style.css" />
<script src="https://skyway.io/dist/v2/0.3/peer.js"></
script>
<script src="scripts/script.js"></script>
<title>SkyWay Sample</title>
</head>
<body>
<video id="local"></video>
<div id="remotes"></div>
</body>
</html>
198. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
199. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
ユーザメディアを
Web Audio APIで編集し、
RTCPeerConnectionに渡す。
200. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
getUserMediaで音声を取得。
201. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
Web Audio APIの
AudioContextをインスタンス化。
ベンダープレフィックス(例:webkitAudioContext)
202. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
MediaStreamをWeb Audio APIの
Source(入力側)にする。
WebRTCの世界からWeb Audio APIの世界へ。
203. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
音声処理をJavaScriptで行なうための
ScriptProcessorをインスタンス化。
旧名:JavaScriptNode。古い資料を見るときは注意。
204. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
Web Audio APIの
Destination(出力側)をインスタンス化。
205. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
音声処理に使うfunction(後述)を
ScriptProcessorのイベントに登録。
206. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
source(入力側)
scriptProcessor
destination(出力側)の順に接続。
207. navigator.getUserMedia({audio: true},
function(inputStream) {
var audioContext = new AudioContext(); // 要ベンダープレフィックス
var mediastreamsource = audioContext.createMediaStreamSource(inputStream);
var scriptProcessor = audioContext.createScriptProcessor(bufferSize, 1, 1);
var mediastreamdestination = audioContext.createMediaStreamDestination();
scriptProcessor.addEventListener('audioprocess', onAudioProcess);
mediastreamsource.connect(scriptProcessor);
scriptProcessor.connect(mediastreamdestination);
var outputStream = mediastreamdestination.stream;
peer.addStream(outputStream);
},
function(error) {}
);
function onAudioProcess(evt) {
var input = evt.inputBuffer.getChannelData(0);
var output = evt.outputBuffer.getChannelData(0);
var bufferData = new Float32Array(bufferSize);
for (var i = 0; i < bufferSize; i++) {
bufferData[i] = (
(input[(i * 2) % bufferSize] + input[(i * 2 + 1) % bufferSize]) / 2 +
input[Math.floor(i / 2) % bufferSize]
) / 2;
}
output.set(bufferData);
}
Web Audio APIのSource(入力側)を
MediaStreamにする。
Web Audio APIの世界からWebRTCの世界へ。