Created
February 5, 2018 01:38
Star
You must be signed in to star a gist
WebRTCTest
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var SSL_KEY = '/usr/local/etc/openssl/certs/cert.pem'; | |
var SSL_CERT = '/usr/local/etc/openssl/certs/cert.pem'; | |
var port = 9001; | |
var fs = require('fs'); | |
var io = require('socket.io').listen(port); | |
console.log((new Date()) + " Server is listening on port " + port); | |
io.sockets.on('connection', function (socket) { | |
// 入室 | |
socket.on('enter', function (roomname) { | |
socket.roomname = roomname; | |
socket.join(roomname); | |
}); | |
socket.on('message', function (message) { | |
// 送信元のidをメッセージに追加(相手が分かるように) | |
message.from = socket.id; | |
// 送信先が指定されているか? | |
var target = message.sendto; | |
if (target) { | |
// 送信先が指定されていた場合は、その相手のみに送信 | |
socket.to(target).json.emit('message', message); | |
return; | |
} | |
// 特に指定がなければ、ブロードキャスト | |
emitMessage('message', message); | |
}); | |
socket.on('disconnect', function () { | |
emitMessage('user disconnected'); | |
}); | |
// 会議室名が指定されていたら、室内だけに通知 | |
function emitMessage(type, message) { | |
var roomname = socket.roomname; | |
if (roomname) { socket.broadcast.to(roomname).emit(type, message); } | |
else { socket.broadcast.emit(type, message); } | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>WebRTC Test!</title> | |
</head> | |
<body> | |
<button type="button" onclick="startVideo();">Start video</button> | |
<button type="button" onclick="stopVideo();">Stop video</button> | |
| |
<button type="button" onclick="call();">Connect</button> | |
<button type="button" onclick="hangUp();">Hang Up</button> | |
<br /> | |
<div style="position: relative;"> | |
<video id="local-video" autoplay style="width: 240px; height: 180px; border: 1px solid black;"></video> | |
<video id="webrtc-remote-video-0" autoplay style="position: absolute; top: 250px; left: 0px; width: 320px; height: 240px; border: 1px solid black; "></video> | |
<video id="webrtc-remote-video-1" autoplay style="position: absolute; top: 250px; left: 330px; width: 320px; height: 240px; border: 1px solid black; "></video> | |
<video id="webrtc-remote-video-2" autoplay style="position: absolute; top: 0px; left: 330px; width: 320px; height: 240px; border: 1px solid black; "></video> | |
</div> | |
<!---- socket ------> | |
<script src="https://wstest.momiage.com/socket.io/socket.io.js"></script> | |
<script> | |
var audioBandwidth = 50; | |
var videoBandwidth = 128; | |
function setBandwidth(sdp) { | |
sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + audioBandwidth + '\r\n'); | |
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + videoBandwidth + '\r\n'); | |
return sdp; | |
} | |
var localVideo = document.getElementById('local-video'); | |
//var remoteVideo = document.getElementById('remote-video'); | |
var localStream = null; | |
var mediaConstraints = { 'mandatory': { 'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true } }; | |
// ---- multi people video & audio ---- | |
var videoElementsInUse = {}; | |
var videoElementsStandBy = {}; | |
pushVideoStandBy(getVideoForRemote(0)); | |
pushVideoStandBy(getVideoForRemote(1)); | |
pushVideoStandBy(getVideoForRemote(2)); | |
function getVideoForRemote(index) { | |
var elementID = 'webrtc-remote-video-' + index; | |
var element = document.getElementById(elementID); | |
return element; | |
} | |
// ---- video element management --- | |
function pushVideoStandBy(element) { | |
videoElementsStandBy[element.id] = element; | |
} | |
function popVideoStandBy() { | |
var element = null; | |
for (var id in videoElementsStandBy) { | |
element = videoElementsStandBy[id]; | |
delete videoElementsStandBy[id]; | |
return element; | |
} | |
return null; | |
} | |
function pushVideoInUse(id, element) { | |
videoElementsInUse[id] = element; | |
} | |
function popVideoInUse(id) { | |
element = videoElementsInUse[id]; | |
delete videoElementsInUse[id]; | |
return element; | |
} | |
function attachVideo(id, stream) { | |
console.log('try to attach video. id=' + id); | |
var videoElement = popVideoStandBy(); | |
if (videoElement) { | |
videoElement.src = window.URL.createObjectURL(stream); | |
console.log("videoElement.src=" + videoElement.src); | |
pushVideoInUse(id, videoElement); | |
videoElement.style.display = 'block'; | |
} | |
else { | |
console.error('--- no video element stand by.'); | |
} | |
} | |
function detachVideo(id) { | |
console.log('try to detach video. id=' + id); | |
var videoElement = popVideoInUse(id); | |
if (videoElement) { | |
videoElement.pause(); | |
videoElement.src = ""; | |
console.log("videoElement.src=" + videoElement.src); | |
pushVideoStandBy(videoElement); | |
} | |
else { | |
console.warn('warning --- no video element using with id=' + id); | |
} | |
} | |
function detachAllVideo() { | |
var element = null; | |
for (var id in videoElementsInUse) { | |
detachVideo(id); | |
} | |
} | |
function getFirstVideoInUse() { | |
var element = null; | |
for (var id in videoElementsInUse) { | |
element = videoElementsInUse[id]; | |
return element; | |
} | |
return null; | |
} | |
function getVideoCountInUse() { | |
var count = 0; | |
for (var id in videoElementsInUse) { | |
count++; | |
} | |
return count; | |
} | |
function isLocalStreamStarted() { | |
if (localStream) { | |
return true; | |
} | |
else { | |
return false; | |
} | |
} | |
// -------------- multi connections -------------------- | |
var MAX_CONNECTION_COUNT = 3; | |
var connections = {}; // Connection hash | |
function Connection() { // Connection Class | |
var self = this; | |
var id = ""; // socket.id of partner | |
var peerconnection = null; // RTCPeerConnection instance | |
var established = false; // is Already Established | |
var iceReady = false; | |
} | |
function getConnection(id) { | |
var con = null; | |
con = connections[id]; | |
return con; | |
} | |
function addConnection(id, connection) { | |
connections[id] = connection; | |
} | |
function getConnectionCount() { | |
var count = 0; | |
for (var id in connections) { | |
count++; | |
} | |
console.log('getConnectionCount=' + count); | |
return count; | |
} | |
function isConnectPossible() { | |
if (getConnectionCount() < MAX_CONNECTION_COUNT) | |
return true; | |
else | |
return false; | |
} | |
function getConnectionIndex(id_to_lookup) { | |
var index = 0; | |
for (var id in connections) { | |
if (id == id_to_lookup) { | |
return index; | |
} | |
index++; | |
} | |
// not found | |
return -1; | |
} | |
function deleteConnection(id) { | |
delete connections[id]; | |
} | |
function stopAllConnections() { | |
for (var id in connections) { | |
var conn = connections[id]; | |
conn.peerconnection.close(); | |
conn.peerconnection = null; | |
delete connections[id]; | |
} | |
} | |
function stopConnection(id) { | |
var conn = connections[id]; | |
if (conn) { | |
console.log('stop and delete connection with id=' + id); | |
conn.peerconnection.close(); | |
conn.peerconnection = null; | |
delete connections[id]; | |
} | |
else { | |
console.log('try to stop connection, but not found id=' + id); | |
} | |
} | |
function isPeerStarted() { | |
if (getConnectionCount() > 0) { | |
return true; | |
} | |
else { | |
return false; | |
} | |
} | |
// ---- socket ------ | |
// create socket | |
var socketReady = false; | |
var port = 9001; | |
var socket = io.connect('https://wstest.momiage.com/'); | |
// socket: channel connected | |
socket.on('connect', onOpened) | |
.on('message', onMessage); | |
function onOpened(evt) { | |
console.log('socket opened.'); | |
socketReady = true; | |
var roomname = getRoomName(); // 会議室名を取得する | |
socket.emit('enter', roomname); | |
console.log('enter to ' + roomname); | |
} | |
// socket: accept connection request | |
function onMessage(evt) { | |
var id = evt.from; | |
var target = evt.sendto; | |
var conn = getConnection(id); | |
if (evt.type === 'call') { | |
if (!isLocalStreamStarted()) { | |
return; | |
} | |
if (conn) { | |
return; // already connected | |
} | |
if (isConnectPossible()) { | |
socket.json.send({ type: "response", sendto: id }); | |
} | |
else { | |
console.warn('max connections. so ignore call'); | |
} | |
return; | |
} | |
else if (evt.type === 'response') { | |
sendOffer(id); | |
return; | |
} else if (evt.type === 'offer') { | |
console.log("Received offer, set offer, sending answer....") | |
onOffer(evt); | |
} else if (evt.type === 'answer' && isPeerStarted()) { // ** | |
console.log('Received answer, settinng answer SDP'); | |
onAnswer(evt); | |
} else if (evt.type === 'candidate' && isPeerStarted()) { // ** | |
console.log('Received ICE candidate...'); | |
onCandidate(evt); | |
} else if (evt.type === 'user dissconnected' && isPeerStarted()) { // ** | |
console.log("disconnected"); | |
//stop(); | |
detachVideo(id); // force detach video | |
stopConnection(id); | |
} | |
} | |
function getRoomName() { // たとえば、 URLに ?roomname とする | |
var url = document.location.href; | |
var args = url.split('?'); | |
if (args.length > 1) { | |
var room = args[1]; | |
if (room != "") { | |
return room; | |
} | |
} | |
return "_defaultroom"; | |
} | |
function onOffer(evt) { | |
console.log("Received offer...") | |
console.log(evt); | |
setOffer(evt); | |
sendAnswer(evt); | |
//peerStarted = true; -- | |
} | |
function onAnswer(evt) { | |
console.log("Received Answer...") | |
console.log(evt); | |
setAnswer(evt); | |
} | |
function onCandidate(evt) { | |
var id = evt.from; | |
var conn = getConnection(id); | |
if (!conn) { | |
console.error('peerConnection not exist!'); | |
return; | |
} | |
// --- check if ice ready --- | |
if (!conn.iceReady) { | |
console.warn("PeerConn is not ICE ready, so ignore"); | |
return; | |
} | |
var candidate = new RTCIceCandidate({ sdpMLineIndex: evt.sdpMLineIndex, sdpMid: evt.sdpMid, candidate: evt.candidate }); | |
console.log("Received Candidate...") | |
console.log(candidate); | |
conn.peerconnection.addIceCandidate(candidate); | |
} | |
function sendSDP(sdp) { | |
var text = JSON.stringify(sdp); | |
console.log("---sending sdp text ---"); | |
console.log(text); | |
// send via socket | |
socket.json.send(sdp); | |
} | |
function sendCandidate(candidate) { | |
var text = JSON.stringify(candidate); | |
console.log("---sending candidate text ---"); | |
console.log(text); | |
// send via socket | |
socket.json.send(candidate); | |
} | |
// ---------------------- video handling ----------------------- | |
// start local video | |
function startVideo() { | |
navigator.webkitGetUserMedia({ video: true, audio: true }, | |
function (stream) { // success | |
localStream = stream; | |
localVideo.src = window.webkitURL.createObjectURL(stream); | |
localVideo.play(); | |
localVideo.volume = 0; | |
}, | |
function (error) { // error | |
console.error('An error occurred:'); | |
console.error(error); | |
return; | |
} | |
); | |
} | |
// stop local video | |
function stopVideo() { | |
localVideo.src = ""; | |
localStream.stop(); | |
} | |
// ---------------------- connection handling ----------------------- | |
function prepareNewConnection(id) { | |
var pc_config = { "iceServers": [] }; | |
var peer = null; | |
try { | |
peer = new webkitRTCPeerConnection(pc_config); | |
} catch (e) { | |
console.log("Failed to create PeerConnection, exception: " + e.message); | |
} | |
var conn = new Connection(); | |
conn.id = id; | |
conn.peerconnection = peer; | |
peer.id = id; | |
addConnection(id, conn); | |
// send any ice candidates to the other peer | |
peer.onicecandidate = function (evt) { | |
if (evt.candidate) { | |
console.log(evt.candidate); | |
sendCandidate({ | |
type: "candidate", | |
sendto: conn.id, | |
sdpMLineIndex: evt.candidate.sdpMLineIndex, | |
sdpMid: evt.candidate.sdpMid, | |
candidate: evt.candidate.candidate | |
}); | |
} else { | |
console.log("End of candidates. ------------------- phase=" + evt.eventPhase); | |
conn.established = true; | |
} | |
}; | |
console.log('Adding local stream...'); | |
peer.addStream(localStream); | |
peer.addEventListener("addstream", onRemoteStreamAdded, false); | |
peer.addEventListener("removestream", onRemoteStreamRemoved, false) | |
// when remote adds a stream, hand it on to the local video element | |
function onRemoteStreamAdded(event) { | |
console.log("Added remote stream"); | |
attachVideo(this.id, event.stream); | |
} | |
// when remote removes a stream, remove it from the local video element | |
function onRemoteStreamRemoved(event) { | |
console.log("Remove remote stream"); | |
detachVideo(this.id); | |
} | |
return conn; | |
} | |
function sendOffer(id) { | |
var conn = getConnection(id); | |
if (!conn) { | |
conn = prepareNewConnection(id); | |
} | |
conn.peerconnection.createOffer(function (sessionDescription) { // in case of success | |
sessionDescription.sdp = setBandwidth(sessionDescription.sdp); | |
conn.iceReady = true; | |
conn.peerconnection.setLocalDescription(sessionDescription); | |
sessionDescription.sendto = id; | |
sendSDP(sessionDescription); | |
}, function () { // in case of error | |
console.log("Create Offer failed"); | |
}, mediaConstraints); | |
conn.iceReady = true; | |
} | |
function setOffer(evt) { | |
var id = evt.from; | |
var conn = getConnection(id); | |
if (!conn) { | |
conn = prepareNewConnection(id); | |
conn.peerconnection.setRemoteDescription(new RTCSessionDescription(evt)); | |
} | |
else { | |
console.error('peerConnection alreay exist!'); | |
} | |
} | |
function sendAnswer(evt) { | |
console.log('sending Answer. Creating remote session description...'); | |
var id = evt.from; | |
var conn = getConnection(id); | |
if (!conn) { | |
console.error('peerConnection not exist!'); | |
return | |
} | |
conn.peerconnection.createAnswer(function (sessionDescription) { | |
sessionDescription.sdp = setBandwidth(sessionDescription.sdp); | |
// in case of success | |
conn.iceReady = true; | |
conn.peerconnection.setLocalDescription(sessionDescription); | |
sessionDescription.sendto = id; | |
sendSDP(sessionDescription); | |
}, function () { // in case of error | |
console.log("Create Answer failed"); | |
}, mediaConstraints); | |
conn.iceReady = true; | |
} | |
function setAnswer(evt) { | |
var id = evt.from; | |
var conn = getConnection(id); | |
if (!conn) { | |
console.error('peerConnection not exist!'); | |
return | |
} | |
conn.peerconnection.setRemoteDescription(new RTCSessionDescription(evt)); | |
} | |
// call others before connecting peer | |
function call() { | |
if (!isLocalStreamStarted()) { | |
alert("Local stream not running yet. Please [Start Video] or [Start Screen]."); | |
return; | |
} | |
if (!socketReady) { | |
alert("Socket is not connected to server. Please reload and try again."); | |
return; | |
} | |
// call others, in same room | |
console.log("call others in same room, befeore offer"); | |
socket.json.send({ type: "call" }); | |
} | |
// stop the connection upon user request | |
function hangUp() { | |
console.log("Hang up."); | |
socket.json.send({ type: "bye" }); | |
detachAllVideo(); | |
stopAllConnections(); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment