一、
Socket訊息傳遞
兩個端點之間的連線建立時,兩個端點需要一直與socket Server互相傳遞的訊息,透過SDP的Offer和Answer 的協定,建立雙方通道才能進行P2P資料傳輸。
以上圖供參考,如有錯誤,請見諒
二、
安裝Socket.IO
var http = require('http');
var fs = require('fs');
var port = "8000";
var ip = "192.168.1.63";
var socket_io = require("socket.io"); //注入socket.io
function onRequest(request, response) {
console.log("Request received.");
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write('<head><meta charset="utf-8"/></head>');
fs.readFile('index3.html', 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
response.end(data);
});
}
var server =http.createServer(onRequest).listen(port, ip);
var io = socket_io.listen(server); // socket.io 注入http ; 當client端呼叫http 時,socket.io 也會啟動
io.sockets.on('connection', function (socket) {
// 如果有接收到offer 訊息,立即傳送廣播封包,發送offer 訊息給client 端
socket.on('offer', function (data) {
socket.broadcast.emit('offer', { sdp: data.sdp });
});
// 如果有接收到answer 訊息,立即傳送廣播封包,發送answer 訊息給client 端
socket.on('answer', function (data) {
socket.broadcast.emit('answer', { sdp: data.sdp });
});
// 如果有接收到ice 訊息,立即傳送廣播封包,發送ice 訊息給client 端
socket.on('ice', function (data) {
socket.broadcast.emit('ice', { candidate: data.candidate });
});
// 如果有接收到hangup 訊息,立即傳送廣播封包,發送hangup訊息給client 端
socket.on('hangup', function () {
socket.broadcast.emit('hangup', {});
});
});
|
四、
WebRTC程式範例
網頁要嵌入socket.io.js
檔案,版本有差異檔案路徑也有所不同,目前作者的路徑在\node_modules\socket.io\node_modules\socket.io-client\socket.io.js 將它copy到\node_modules\socket.io\
路徑
<script src="/socket.io/socket.io.js"></script>
|
<script>
var
socket = io.connect('http://192.168.1.63:8000');
//針對moliza 有些仍未整合統一規格,所以針對各家瀏覽器,仍需特殊處理
var
RTCSessionDescription = window.mozRTCSessionDescription
||
window.webkitRTCSessionDescription || window.RTCSessionDescription;
var
RTCIceCandidate = window.mozRTCIceCandidate || window.webkitRTCIceCandidate
||
window.RTCIceCandidate;
var
mediaConstraints = {
"mandatory":
{
"OfferToReceiveAudio":
true,
"OfferToReceiveVideo":
true
}
};
var
localStream;
var
localPeerConnection;
var
remotePeerConnection;
var
localVideo = document.getElementById('localVideo');
var
remoteVideo = document.getElementById('remoteVideo');
var
startButton = document.getElementById('startButton');
var
callButton = document.getElementById('callButton');
var
hangupButton = document.getElementById('hangupButton');
startButton.disabled = false;
callButton.disabled = true;
hangupButton.disabled = true;
startButton.onclick = start;
callButton.onclick = call;
hangupButton.onclick = hangup;
function
gotStream(stream) {
localVideo.src =
URL.createObjectURL(stream);
localStream = stream;
callButton.disabled = false;
}
//針對moliza 的規格,所以要特殊處理
function
getPeerConnection() {
if(navigator.userAgent.indexOf("Chrome") != -1 )
{
localPeerConnection = new
webkitRTCPeerConnection(null);
remotePeerConnection = new
webkitRTCPeerConnection(null);
}
else
if(navigator.userAgent.indexOf("Firefox") != -1 )
{
localPeerConnection = new
mozRTCPeerConnection(null);
remotePeerConnection = new
mozRTCPeerConnection(null);
}
}
function
start() {
startButton.disabled = true;
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
navigator.getUserMedia({
video: true
}, gotStream,
function
(error) {
trace('navigator.getUserMedia
error: ', error);
});
}
function
call() {
callButton.disabled = true;
hangupButton.disabled = false;
getPeerConnection();
//建立RTCPeerConnection
的函式(fireFox
瀏覽器另外處理)
remotePeerConnection.onaddstream =
gotRemoteStream; //產生串流
localPeerConnection.addStream(localStream);
//準備訊息交換的訊息
localPeerConnection.createOffer(function
(sdp) {
localPeerConnection.setLocalDescription(sdp, function
() {
socket.emit('offer', { sdp: sdp }); ,建立 offer 訊號, 等待socket 回傳offer
},Error);
}, function
() {
console.log('create offer error.');
}, mediaConstraints);
}
function
gotRemoteIceCandidate(event) {
if
(!!event.candidate) {
socket.emit('ice', { candidate: e.candidate });
}
}
function
hangup() {
localPeerConnection.close();
remotePeerConnection.close();
localPeerConnection = null;
remotePeerConnection = null;
hangupButton.disabled = true;
callButton.disabled = false;
}
function
gotRemoteStream(event) {
remoteVideo.src =
URL.createObjectURL(event.stream);
}
// 建立 ice ,準備連線的前置作業
socket.on('offer',
function (data) {
remotePeerConnection.onicecandidate
= function (e) {
if
(!!e.candidate) {
socket.emit('ice', { candidate: e.candidate }); // 發送出ice 訊息… 等待socket回傳ice訊息
}
};
var
offer = new
RTCSessionDescription(data.sdp); //
預備要交換的訊息
remotePeerConnection.setRemoteDescription(offer, function
() {
// 遠端電腦接收offer訊息後,要回傳answer訊息,並開始進始多媒體的傳輸作業
remotePeerConnection.createAnswer(function
(sdp) {
remotePeerConnection.setLocalDescription(sdp, function
() {
socket.emit('answer', { sdp: sdp }); //送出socket 的answer 等待,socket 回傳answer 訊息
}, function
(e) {
console.log('set local description error: ' + e);
});
}, function
(e) {
console.log('create answer error: ' + e);
}, mediaConstraints);
}, function
(e) {
console.log('set remote description error: ' + e);
});
});
// 接收遠端電腦回傳的answer 訊息
socket.on('answer',
function (data) {
localPeerConnection.setRemoteDescription(new
RTCSessionDescription(data.sdp)); //準備多媒體傳輸通道
});
//
接收回傳ice
訊息,將自己本地端的ICE
訊息回傳
socket.on('ice',
function (data) {
localPeerConnection.addIceCandidate(new
RTCIceCandidate(data.candidate));
});
}
</script>
|
目前作者想測試桌機瀏覽器Chrome 43版本 和Chrome for Android的連線,電腦無法接收到手機傳出的視訊,測試Firefox 38 和 firefox for Android 狀況也是一樣,截取自fireFox 的錯誤訊息為ICE
failed, see about:webrtc for more details
手機卻成功接收來自電腦傳送的視訊串流… 這真是太神奇了傑克!
您好,最近在學習webrtc這塊 看到您的文章使用了您的範例
回覆刪除但似乎好像無法使用
這篇文章是很早之前寫的,後來的 webrtc 是否有改變呼叫的方法,你可能要去查文件,但後來的webrtc都需要建在https 安全模式之下,才可以連線成功
回覆刪除