Merge branch 'master' into disconnect_handling

This commit is contained in:
Arya Vohra 2020-05-31 20:16:25 +08:00 committed by GitHub
commit dd26284e13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 156 additions and 134 deletions

2
README.md vendored
View File

@ -20,12 +20,14 @@ technology.
<img align="right" width="400" height="auto" src="public/images/preview.gif"> <img align="right" width="400" height="auto" src="public/images/preview.gif">
- Screen sharing - Screen sharing
- Group call with up to 4 peers
- Picture in picture - Picture in picture
- Live captions - Live captions
- Text chat - Text chat
- Auto-scaling video quality - Auto-scaling video quality
- No download required, entirely browser based - No download required, entirely browser based
- Direct peer to peer connection ensures lowest latency - Direct peer to peer connection ensures lowest latency
- No SFU servers, group calls use mesh networks
- Single use disposable chat rooms - Single use disposable chat rooms
## Quick start ## Quick start

3
public/chat.html vendored
View File

@ -46,8 +46,7 @@
</div> </div>
<p id="remote-video-text"></p> <p id="remote-video-text"></p>
<div id="wrapper"> <div id="wrapper"></div>
</div>
<div id="moveable"> <div id="moveable">
<p id="local-video-text">No webcam input</p> <p id="local-video-text">No webcam input</p>
<video id="local-video" autoplay muted playsinline></video> <video id="local-video" autoplay muted playsinline></video>

33
public/css/chat.css vendored
View File

@ -110,8 +110,10 @@ a {
} }
#wrapper { #wrapper {
display: grid; display: flex;
grid-gap: 10px; flex-direction: column;
align-items: center;
flex-wrap: wrap;
justify-content: center; justify-content: center;
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -142,12 +144,33 @@ a {
border-radius: 0 0 10px 10px; border-radius: 0 0 10px 10px;
padding: 10px; padding: 10px;
} }
#remote-video:first-child:nth-last-child(1) {
/* -or- li:only-child { */
max-height: 80vh;
}
/* two items */
#remote-video:first-child:nth-last-child(2),
#remote-video:first-child:nth-last-child(2) ~ #remote-video {
max-height: 40vh;
}
/* three items */
#remote-video:first-child:nth-last-child(3),
#remote-video:first-child:nth-last-child(3) ~ #remote-video {
max-height: 40vh;
}
/* four items */
#remote-video:first-child:nth-last-child(4),
#remote-video:first-child:nth-last-child(4) ~ #remote-video {
max-height: 40vh;
}
#remote-video { #remote-video {
padding: 0; padding: 0;
margin: 0; margin: 10px;
width: 100%; width: auto;
height: auto;
border-radius: 10px; border-radius: 10px;
background-image: url(../images/loader.gif); background-image: url(../images/loader.gif);
background-size: 400px auto; background-size: 400px auto;

View File

@ -26,13 +26,13 @@ const captionButtontext = $("#caption-button-text");
const entireChat = $("#entire-chat"); const entireChat = $("#entire-chat");
const chatZone = $("#chat-zone"); const chatZone = $("#chat-zone");
// Need a Map to keep track of dataChannel connecting with each peer
var dataChannel = new Map(); var dataChannel = new Map();
var VideoChat = { var VideoChat = {
nickname: undefined, nickname: undefined,
videoEnabled: true, videoEnabled: true,
audioEnabled: true, audioEnabled: true,
connected: new Map(), connected: new Map(),
localICECandidates: {}, localICECandidates: {},
socket: io(), socket: io(),
@ -40,7 +40,6 @@ var VideoChat = {
localVideo: document.getElementById("local-video"), localVideo: document.getElementById("local-video"),
peerConnections: new Map(), peerConnections: new Map(),
recognition: undefined, recognition: undefined,
answerCreatedUUIDs: [], // HACKY workaround: currently, onAnswer seems to be triggering twice and we don't know why
// Call to getUserMedia (provided by adapter.js for cross browser compatibility) // Call to getUserMedia (provided by adapter.js for cross browser compatibility)
// asking for access to both the video and audio streams. If the request is // asking for access to both the video and audio streams. If the request is
@ -98,22 +97,26 @@ var VideoChat = {
}, },
}); });
// Used to identify client to other peers in chats, captions etc.
VideoChat.nickname = prompt("Please enter a nickname", ""); VideoChat.nickname = prompt("Please enter a nickname", "");
while (VideoChat.nickname == null || VideoChat.nickname.trim() === "") { while (VideoChat.nickname == null || VideoChat.nickname.trim() === "") {
VideoChat.nickname = prompt("Nickname cannot be empty. Please enter a nickname", ""); VideoChat.nickname = prompt(
"Nickname cannot be empty. Please enter a nickname",
""
);
} }
VideoChat.localVideo.srcObject = stream; VideoChat.localVideo.srcObject = stream;
// Now we're ready to join the chat room. // Now we're ready to join the chat room.
VideoChat.socket.emit("join", roomHash); VideoChat.socket.emit("join", roomHash);
// Receive its own uuid
VideoChat.socket.on("uuid", (uuid) => (VideoChat.uuid = uuid));
// Add listeners to the websocket // Add listeners to the websocket
VideoChat.socket.on("full", chatRoomFull); VideoChat.socket.on("full", chatRoomFull);
VideoChat.socket.on("offer", VideoChat.onOffer); VideoChat.socket.on("offer", VideoChat.onOffer);
VideoChat.socket.on("ready", VideoChat.readyToCall);
VideoChat.socket.on("willInitiateCall", VideoChat.call); VideoChat.socket.on("willInitiateCall", VideoChat.call);
// Set up listeners on the socket // Set up listeners on the socket
VideoChat.socket.on("candidate", VideoChat.onCandidate); VideoChat.socket.on("candidate", VideoChat.onCandidate);
VideoChat.socket.on("answer", VideoChat.onAnswer); VideoChat.socket.on("answer", VideoChat.onAnswer);
@ -123,47 +126,50 @@ var VideoChat = {
); );
}, },
// When we are ready to call, enable the Call button.
readyToCall: function (event) {
logIt("readyToCall");
},
call: function (uuid, room) { call: function (uuid, room) {
logIt("Initiating call"); logIt(`call >>> Initiating call with ${uuid}...`);
VideoChat.startCall(uuid); VideoChat.socket.on(
}, "token",
VideoChat.establishConnection(uuid, function (a) {
// Set up a callback to run when we have the ephemeral token to use Twilio's TURN server. VideoChat.createOffer(a);
startCall: function (uuid) { })
VideoChat.socket.on("token", VideoChat.establishConnection(uuid, function(a) {VideoChat.createOffer(a);})); );
VideoChat.socket.emit("token", roomHash, uuid); VideoChat.socket.emit("token", roomHash, uuid);
}, },
establishConnection: function (correctUuid, callback) { establishConnection: function (correctUuid, callback) {
return function(token, uuid) { return function (token, uuid) {
if (correctUuid != uuid) { if (correctUuid != uuid) {
return; return;
} }
console.log("establishing connection to", uuid); logIt(`<<< Received token, connecting to ${uuid}`);
// Initialise localICEcandidates for peer uuid to empty array
VideoChat.localICECandidates[uuid] = [];
VideoChat.localICECandidates[uuid] = []; // initialise uuid with empty array // Initialise connection status with peer uuid to false
VideoChat.connected.set(uuid, false); VideoChat.connected.set(uuid, false);
// Set up a new RTCPeerConnection using the token's iceServers. // Set up a new RTCPeerConnection using the token's iceServers.
VideoChat.peerConnections.set(uuid, new RTCPeerConnection({ VideoChat.peerConnections.set(
iceServers: token.iceServers, uuid,
})) new RTCPeerConnection({
iceServers: token.iceServers,
})
);
// Add the local video stream to the peerConnection. // Add the local video stream to the peerConnection.
VideoChat.localStream.getTracks().forEach(function (track) { VideoChat.localStream.getTracks().forEach(function (track) {
VideoChat.peerConnections.get(uuid).addTrack(track, VideoChat.localStream); VideoChat.peerConnections
.get(uuid)
.addTrack(track, VideoChat.localStream);
}); });
// Add general purpose data channel to peer connection, // Add general purpose data channel to peer connection,
// used for text chats, captions, and toggling sending captions // used for text chats, captions, and toggling sending captions
dataChannel.set(uuid, VideoChat.peerConnections.get(uuid).createDataChannel("chat", { dataChannel.set(
negotiated: true, uuid,
// both peers must have same id VideoChat.peerConnections.get(uuid).createDataChannel("chat", {
id: 0, negotiated: true,
})); // both peers must have same id
id: 0,
})
);
// Called when dataChannel is successfully opened // Called when dataChannel is successfully opened
dataChannel.get(uuid).onopen = function (event) { dataChannel.get(uuid).onopen = function (event) {
logIt("dataChannel opened"); logIt("dataChannel opened");
@ -182,21 +188,22 @@ var VideoChat = {
toggleSendCaptions(); toggleSendCaptions();
} }
}; };
// Set up callbacks for the connection generating iceCandidates or // Set up callbacks for the connection generating iceCandidates or
// receiving the remote media stream. // receiving the remote media stream. Wrapping callback functions
VideoChat.peerConnections.get(uuid).onicecandidate = function(u) {VideoChat.onIceCandidate(u, uuid)}; // to pass in the peer uuids.
VideoChat.peerConnections.get(uuid).onaddstream = function(u) {VideoChat.onAddStream(u, uuid)}; VideoChat.peerConnections.get(uuid).onicecandidate = function (event) {
VideoChat.onIceCandidate(event, uuid);
};
VideoChat.peerConnections.get(uuid).onaddstream = function (event) {
VideoChat.onAddStream(event, uuid);
};
// Called when there is a change in connection state // Called when there is a change in connection state
VideoChat.peerConnections.get(uuid).oniceconnectionstatechange = function (event) { VideoChat.peerConnections.get(
uuid
).oniceconnectionstatechange = function (event) {
switch (VideoChat.peerConnections.get(uuid).iceConnectionState) { switch (VideoChat.peerConnections.get(uuid).iceConnectionState) {
case "connected": case "connected":
logIt("connected"); logIt("connected");
// Once connected we no longer have a need for the signaling server, so disconnect
// VideoChat.socket.off("token");
// VideoChat.socket.off("offer");
break; break;
case "disconnected": case "disconnected":
logIt("disconnected - UUID " + uuid); logIt("disconnected - UUID " + uuid);
@ -222,7 +229,7 @@ var VideoChat = {
} }
}; };
callback(uuid); callback(uuid);
} };
}, },
// When the peerConnection generates an ice candidate, send it over the socket to the peer. // When the peerConnection generates an ice candidate, send it over the socket to the peer.
@ -230,7 +237,7 @@ var VideoChat = {
logIt("onIceCandidate"); logIt("onIceCandidate");
if (event.candidate) { if (event.candidate) {
logIt( logIt(
`<<< Received local ICE candidate from STUN/TURN server (${event.candidate.address}) associated with UUID (${uuid})` `<<< Received local ICE candidate from STUN/TURN server (${event.candidate.address}) for connection with ${uuid}`
); );
if (VideoChat.connected.get(uuid)) { if (VideoChat.connected.get(uuid)) {
logIt(`>>> Sending local ICE candidate (${event.candidate.address})`); logIt(`>>> Sending local ICE candidate (${event.candidate.address})`);
@ -246,7 +253,6 @@ var VideoChat = {
// The peer may not have created the RTCPeerConnection yet, so we are waiting for the 'answer' // The peer may not have created the RTCPeerConnection yet, so we are waiting for the 'answer'
// to arrive. This will signal that the peer is ready to receive signaling. // to arrive. This will signal that the peer is ready to receive signaling.
VideoChat.localICECandidates[uuid].push(event.candidate); VideoChat.localICECandidates[uuid].push(event.candidate);
} }
} }
}, },
@ -265,7 +271,7 @@ var VideoChat = {
// Create an offer that contains the media capabilities of the browser. // Create an offer that contains the media capabilities of the browser.
createOffer: function (uuid) { createOffer: function (uuid) {
console.log(">>> Creating offer to UUID: ", uuid, ". my UUID is", VideoChat.socket.id); logIt(`createOffer to ${uuid} >>> Creating offer...`);
VideoChat.peerConnections.get(uuid).createOffer( VideoChat.peerConnections.get(uuid).createOffer(
function (offer) { function (offer) {
// If the offer is created successfully, set it as the local description // If the offer is created successfully, set it as the local description
@ -281,7 +287,7 @@ var VideoChat = {
); );
}, },
// Create an answer with the media capabilities that both browsers share. // Create an answer with the media capabilities that the client and peer browsers share.
// This function is called with the offer from the originating browser, which // This function is called with the offer from the originating browser, which
// needs to be parsed into an RTCSessionDescription and added as the remote // needs to be parsed into an RTCSessionDescription and added as the remote
// description to the peerConnection object. Then the answer is created in the // description to the peerConnection object. Then the answer is created in the
@ -289,12 +295,11 @@ var VideoChat = {
createAnswer: function (offer, uuid) { createAnswer: function (offer, uuid) {
logIt("createAnswer"); logIt("createAnswer");
rtcOffer = new RTCSessionDescription(JSON.parse(offer)); rtcOffer = new RTCSessionDescription(JSON.parse(offer));
console.log("createAnswer: setting remote description of " + uuid + " on " + VideoChat.socket.id); logIt(`>>> Creating answer to ${uuid}`);
VideoChat.peerConnections.get(uuid).setRemoteDescription(rtcOffer); VideoChat.peerConnections.get(uuid).setRemoteDescription(rtcOffer);
VideoChat.peerConnections.get(uuid).createAnswer( VideoChat.peerConnections.get(uuid).createAnswer(
function (answer) { function (answer) {
VideoChat.peerConnections.get(uuid).setLocalDescription(answer); VideoChat.peerConnections.get(uuid).setLocalDescription(answer);
console.log(">>> Creating answer to UUID: ", uuid, ". my UUID is", VideoChat.socket.id);
VideoChat.socket.emit("answer", JSON.stringify(answer), roomHash, uuid); VideoChat.socket.emit("answer", JSON.stringify(answer), roomHash, uuid);
}, },
function (err) { function (err) {
@ -308,33 +313,39 @@ var VideoChat = {
// ephemeral token is returned from Twilio. // ephemeral token is returned from Twilio.
onOffer: function (offer, uuid) { onOffer: function (offer, uuid) {
logIt("onOffer <<< Received offer"); logIt("onOffer <<< Received offer");
// VideoChat.socket.on("token", VideoChat.establishConnection(uuid, function(a) {VideoChat.createOffer(a);})); VideoChat.socket.on(
VideoChat.socket.on("token", VideoChat.establishConnection(uuid, function(a) {VideoChat.createAnswer(offer, a);})); "token",
VideoChat.establishConnection(uuid, function (a) {
VideoChat.createAnswer(offer, a);
})
);
VideoChat.socket.emit("token", roomHash, uuid); VideoChat.socket.emit("token", roomHash, uuid);
}, },
// When an answer is received, add it to the peerConnection as the remote description. // When an answer is received, add it to the peerConnection as the remote description.
onAnswer: function (answer, uuid) { onAnswer: function (answer, uuid) {
console.log("onAnswer <<< Received answer", uuid, ". my UUID is", VideoChat.socket.id); logIt(`onAnswer <<< Received answer from ${uuid}`);
var rtcAnswer = new RTCSessionDescription(JSON.parse(answer));
// if (!VideoChat.answerCreatedUUIDs.includes(uuid)) { // Set remote description of RTCSession
VideoChat.answerCreatedUUIDs.push(uuid); VideoChat.peerConnections.get(uuid).setRemoteDescription(rtcAnswer);
// logIt("onAnswer <<< Received answer" + ""); // The caller now knows that the callee is ready to accept new ICE candidates, so sending the buffer over
var rtcAnswer = new RTCSessionDescription(JSON.parse(answer)); VideoChat.localICECandidates[uuid].forEach((candidate) => {
// Set remote description of RTCSession logIt(`>>> Sending local ICE candidate (${candidate.address})`);
console.log("onAnswer: setting remote description of " + uuid + " on " + VideoChat.socket.id); // Send ice candidate over websocket
VideoChat.peerConnections.get(uuid).setRemoteDescription(rtcAnswer); VideoChat.socket.emit(
// The caller now knows that the callee is ready to accept new ICE candidates, so sending the buffer over "candidate",
VideoChat.localICECandidates[uuid].forEach((candidate) => { JSON.stringify(candidate),
logIt(`>>> Sending local ICE candidate (${candidate.address})`); roomHash,
// Send ice candidate over websocket uuid
VideoChat.socket.emit("candidate", JSON.stringify(candidate), roomHash, uuid); );
}); });
// Reset the buffer of local ICE candidates. This is not really needed, but it's good practice
// VideoChat.localICECandidates[uuid] = []; // TESTING
}, },
// Called when a stream is added to the peer connection // Called when a stream is added to the peer connection
onAddStream: function (event, uuid) { onAddStream: function (event, uuid) {
logIt("onAddStream <<< Received new stream from remote. Adding it..." + event); logIt("onAddStream <<< Received new stream from remote. Adding it...");
// Create new remote video source in wrapper // Create new remote video source in wrapper
// Create a <video> node // Create a <video> node
var node = document.createElement("video"); var node = document.createElement("video");
@ -356,14 +367,6 @@ var VideoChat = {
// Reposition local video after a second, as there is often a delay // Reposition local video after a second, as there is often a delay
// between adding a stream and the height of the video div changing // between adding a stream and the height of the video div changing
setTimeout(() => rePositionLocalVideo(), 500); setTimeout(() => rePositionLocalVideo(), 500);
// var timesRun = 0;
// var interval = setInterval(function () {
// timesRun += 1;
// if (timesRun === 10) {
// clearInterval(interval);
// }
// rePositionLocalVideo();
// }, 300);
}, },
}; };
@ -422,7 +425,7 @@ function rePositionCaptions() {
// Get remote video position // Get remote video position
var bounds = remoteVideosWrapper.position(); var bounds = remoteVideosWrapper.position();
bounds.top -= 10; bounds.top -= 10;
bounds.top = bounds.top + remoteVideosWrapper.height() - 1 * captionText.height(); bounds.top += remoteVideosWrapper.height() - 1 * captionText.height();
// Reposition captions // Reposition captions
captionText.css(bounds); captionText.css(bounds);
} }
@ -438,7 +441,7 @@ function isConnected() {
var connected = false; var connected = false;
// No way to 'break' forEach -> we go through all anyway // No way to 'break' forEach -> we go through all anyway
VideoChat.connected.forEach(function(value, key, map) { VideoChat.connected.forEach(function (value, key, map) {
if (value) { if (value) {
connected = true; connected = true;
} }
@ -449,7 +452,7 @@ function isConnected() {
function sendToAllDataChannels(message) { function sendToAllDataChannels(message) {
// key is UUID, value is dataChannel object // key is UUID, value is dataChannel object
dataChannel.forEach(function(value, key, map) { dataChannel.forEach(function (value, key, map) {
value.send(message); value.send(message);
}); });
} }
@ -501,18 +504,19 @@ function sendToAllDataChannels(message) {
// Mute microphone // Mute microphone
function muteMicrophone() { function muteMicrophone() {
VideoChat.audioEnabled = !VideoChat.audioEnabled;
var audioTrack = null; var audioTrack = null;
VideoChat.peerConnections.forEach(function(value, key, map) { VideoChat.peerConnections.forEach(function (value, key, map) {
value.getSenders().find(function (s) { value.getSenders().find(function (s) {
if (s.track.kind === "audio") { if (s.track.kind === "audio") {
audioTrack = s.track; audioTrack = s.track;
} }
}) });
audioTrack.enabled = VideoChat.audioEnabled; audioTrack.enabled = VideoChat.audioEnabled;
}); });
VideoChat.audioEnabled = !VideoChat.audioEnabled;
// select mic button and mic button text // select mic button and mic button text
const micButtonIcon = document.getElementById("mic-icon"); const micButtonIcon = document.getElementById("mic-icon");
const micButtonText = document.getElementById("mic-text"); const micButtonText = document.getElementById("mic-text");
@ -534,14 +538,14 @@ function pauseVideo() {
VideoChat.videoEnabled = !VideoChat.videoEnabled; VideoChat.videoEnabled = !VideoChat.videoEnabled;
// Communicate pause to all the peers' video tracks // Communicate pause to all the peers' video tracks
VideoChat.peerConnections.forEach(function(value, key, map) { VideoChat.peerConnections.forEach(function (value, key, map) {
console.log("pausing video for ", key); console.log("pausing video for ", key);
value.getSenders().find(function (s) { value.getSenders().find(function (s) {
if (s.track.kind === "video") { if (s.track.kind === "video") {
console.log("found video track") console.log("found video track");
videoTrack = s.track; videoTrack = s.track;
} }
}) });
videoTrack.enabled = VideoChat.videoEnabled; videoTrack.enabled = VideoChat.videoEnabled;
}); });
@ -640,18 +644,19 @@ function switchStreamHelper(stream) {
videoTrack.onended = function () { videoTrack.onended = function () {
swap(); swap();
}; };
// Swap video for every peer connection // Swap video for every peer connection
VideoChat.connected.forEach(function(value, key, map) { VideoChat.connected.forEach(function (value, key, map) {
// Just to be safe, check if connected before swapping video channel // Just to be safe, check if connected before swapping video channel
if (VideoChat.connected.get(key)) { if (VideoChat.connected.get(key)) {
const sender = VideoChat.peerConnections.get(key).getSenders().find (function(s) { const sender = VideoChat.peerConnections
return s.track.kind === videoTrack.kind; .get(key)
}) .getSenders()
.find(function (s) {
return s.track.kind === videoTrack.kind;
});
sender.replaceTrack(videoTrack); sender.replaceTrack(videoTrack);
} }
}); });
// Update local video stream // Update local video stream
VideoChat.localStream = videoTrack; VideoChat.localStream = videoTrack;
// Update local video object // Update local video object
@ -737,7 +742,7 @@ function startSpeech() {
sendToAllDataChannels( sendToAllDataChannels(
"cap:" + "cap:" +
interimTranscript.substring(interimTranscript.length - charsToKeep) interimTranscript.substring(interimTranscript.length - charsToKeep)
); );
} }
} }
}; };

26
public/landing.html vendored
View File

@ -67,9 +67,9 @@
class="mt-0 mb-32 reveal-from-bottom" class="mt-0 mb-32 reveal-from-bottom"
data-reveal-delay="300" data-reveal-delay="300"
> >
Simple, Secure, and Fast. Peer to peer video calling Simple, Secure, and Fast. Peer to peer group video
provides quality and latency simply not available with calling provides quality and latency simply not
traditional technology. available with traditional technology.
</p> </p>
<div class="reveal-from-bottom" data-reveal-delay="450"> <div class="reveal-from-bottom" data-reveal-delay="450">
<a <a
@ -191,18 +191,19 @@
<div class="features-tiles-item-header"> <div class="features-tiles-item-header">
<div class="features-tiles-item-image mb-16"> <div class="features-tiles-item-image mb-16">
<img <img
src="images/feature-tile-icon-03.svg" src="images/feature-tile-icon-06.svg"
alt="Feature tile icon 03" alt="Feature tile icon 06"
width="72" width="72"
height="72" height="72"
/> />
</div> </div>
</div> </div>
<div class="features-tiles-item-content"> <div class="features-tiles-item-content">
<h4 class="mt-0 mb-8">Total Privacy</h4> <h4 class="mt-0 mb-8">Decentralized group calls</h4>
<p class="m-0 text-sm"> <p class="m-0 text-sm">
Each chat is single use, data stays between you and your Zipcall lets you talk to up to four friends by
caller. Zipcall is built privacy first. directly connecting to them, completely
decentralized.
</p> </p>
</div> </div>
</div> </div>
@ -236,17 +237,18 @@
<div class="features-tiles-item-header"> <div class="features-tiles-item-header">
<div class="features-tiles-item-image mb-16"> <div class="features-tiles-item-image mb-16">
<img <img
src="images/feature-tile-icon-06.svg" src="images/feature-tile-icon-03.svg"
alt="Feature tile icon 06" alt="Feature tile icon 03"
width="72" width="72"
height="72" height="72"
/> />
</div> </div>
</div> </div>
<div class="features-tiles-item-content"> <div class="features-tiles-item-content">
<h4 class="mt-0 mb-8">Maximum Security</h4> <h4 class="mt-0 mb-8">Total Privacy and Security</h4>
<p class="m-0 text-sm"> <p class="m-0 text-sm">
End to end state of the art encryption means your calls Zipcall is built privacy first. Each chat is single use,
and end to end state of the art encryption means your calls
are exactly that. Your calls. are exactly that. Your calls.
</p> </p>
</div> </div>

View File

@ -8,7 +8,6 @@ var twillioAccountSID =
var twilio = require("twilio")(twillioAccountSID, twillioAuthToken); var twilio = require("twilio")(twillioAccountSID, twillioAuthToken);
var express = require("express"); var express = require("express");
var app = express(); var app = express();
const fs = require('fs');
var http = require("http").createServer(app); var http = require("http").createServer(app);
var io = require("socket.io")(http); var io = require("socket.io")(http);
var path = require("path"); var path = require("path");
@ -79,30 +78,16 @@ io.on("connection", function (socket) {
var numClients = typeof clients !== "undefined" ? clients.length : 0; var numClients = typeof clients !== "undefined" ? clients.length : 0;
if (numClients === 0) { if (numClients === 0) {
socket.join(room); socket.join(room);
twilio.tokens.create(function (err, response) { } else if (numClients < 5) {
if (err) {
logIt(err, room);
} else {
logIt("Token generated. Returning it to the browser client", room);
socket.emit("token", response);
// Existing callers initiates call with user
}
});
} else if (numClients < 4) {
socket.join(room); socket.join(room);
logIt("Connected clients", room)
for (var clientId in clients.sockets) {
logIt('ID: ' + clientId, room);
}
// When the client is not the first to join the room, all clients are ready. // When the client is not the first to join the room, all clients are ready.
logIt("Broadcasting ready message", room); logIt("Broadcasting ready message", room);
socket.broadcast.to(room).emit("willInitiateCall", socket.id, room); socket.broadcast.to(room).emit("willInitiateCall", socket.id, room);
// socket.emit("uuid", socket.id);
socket.emit("ready", room).to(room);
socket.broadcast.to(room).emit("ready", room);
} else { } else {
logIt("room already full with " + numClients + " people in the room.", room); logIt(
"room already full with " + numClients + " people in the room.",
room
);
socket.emit("full", room); socket.emit("full", room);
} }
}); });
@ -129,13 +114,19 @@ io.on("connection", function (socket) {
// Relay offers // Relay offers
socket.on("offer", function (offer, room, uuid) { socket.on("offer", function (offer, room, uuid) {
logIt("Received offer from " + socket.id + " and emitting to " + uuid, room); logIt(
"Received offer from " + socket.id + " and emitting to " + uuid,
room
);
io.to(uuid).emit("offer", offer, socket.id); io.to(uuid).emit("offer", offer, socket.id);
}); });
// Relay answers // Relay answers
socket.on("answer", function (answer, room, uuid) { socket.on("answer", function (answer, room, uuid) {
logIt("Received answer from " + socket.id + " and emitting to " + uuid, room); logIt(
"Received answer from " + socket.id + " and emitting to " + uuid,
room
);
io.to(uuid).emit("answer", answer, socket.id); io.to(uuid).emit("answer", answer, socket.id);
}); });
}); });