working with peerconnection dict for 2 ppl

This commit is contained in:
Taichi Kato 2020-05-29 16:49:03 +08:00
parent 79e6a860cd
commit da732735ae
2 changed files with 67 additions and 52 deletions

View File

@ -29,12 +29,15 @@ const chatZone = $("#chat-zone");
var VideoChat = { var VideoChat = {
connected: false, connected: false,
counter: 0,
willInitiateCall: false, willInitiateCall: false,
localICECandidates: [], localICECandidates: [],
socket: io(), socket: io(),
remoteVideoWrapper: document.getElementById("wrapper"), remoteVideoWrapper: document.getElementById("wrapper"),
localVideo: document.getElementById("local-video"), localVideo: document.getElementById("local-video"),
peerConnections: {},
recognition: undefined, recognition: undefined,
uuid: undefined,
// 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
@ -94,49 +97,47 @@ var VideoChat = {
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("ready", VideoChat.readyToCall);
VideoChat.socket.on( VideoChat.socket.on("willInitiateCall", VideoChat.call)
"willInitiateCall",
() => (VideoChat.willInitiateCall = true)
);
}, },
// When we are ready to call, enable the Call button. // When we are ready to call, enable the Call button.
readyToCall: function (event) { readyToCall: function (event) {
logIt("readyToCall"); logIt("readyToCall");
// First to join call will most likely initiate call },
if (VideoChat.willInitiateCall) { call: function (uuid, room) {
logIt("Initiating call"); logIt("Initiating call");
VideoChat.startCall(); VideoChat.startCall(uuid);
}
}, },
// Set up a callback to run when we have the ephemeral token to use Twilio's TURN server. // Set up a callback to run when we have the ephemeral token to use Twilio's TURN server.
startCall: function (event) { startCall: function (uuid) {
logIt("startCall >>> Sending token request..."); logIt("startCall >>> Sending token request...");
VideoChat.socket.on("token", VideoChat.onToken(VideoChat.createOffer)); VideoChat.socket.on("token", VideoChat.onToken(function() {VideoChat.createOffer(uuid)}, uuid));
VideoChat.socket.emit("token", roomHash); VideoChat.socket.emit("token", roomHash);
}, },
// When we receive the ephemeral token back from the server. // When we receive the ephemeral token back from the server.
onToken: function (callback) { onToken: function (callback, uuid) {
logIt("onToken"); logIt("onToken");
return function (token) { return function (token) {
logIt("<<< Received token"); logIt("<<< Received token");
// Set up a new RTCPeerConnection using the token's iceServers. // Set up a new RTCPeerConnection using the token's iceServers.
VideoChat.peerConnection = new RTCPeerConnection({ VideoChat.peerConnections[uuid] = new RTCPeerConnection({
iceServers: token.iceServers, 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.peerConnection.addTrack(track, VideoChat.localStream); VideoChat.peerConnections[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
dataChanel = VideoChat.peerConnection.createDataChannel("chat", { dataChanel = VideoChat.peerConnections[uuid].createDataChannel("chat", {
negotiated: true, negotiated: true,
// both peers must have same id // both peers must have same id
id: 0, id: 0,
@ -161,8 +162,8 @@ var VideoChat = {
}; };
// 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.
VideoChat.peerConnection.onicecandidate = VideoChat.onIceCandidate; VideoChat.peerConnections[uuid].onicecandidate = VideoChat.onIceCandidate;
VideoChat.peerConnection.onaddstream = VideoChat.onAddStream; VideoChat.peerConnections[uuid].onaddstream = VideoChat.onAddStream;
// 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);
@ -171,12 +172,12 @@ var VideoChat = {
recieveCaptions(captions) recieveCaptions(captions)
); );
// Called when there is a change in connection state // Called when there is a change in connection state
VideoChat.peerConnection.oniceconnectionstatechange = function (event) { VideoChat.peerConnections[uuid].oniceconnectionstatechange = function (event) {
switch (VideoChat.peerConnection.iceConnectionState) { switch (VideoChat.peerConnections[uuid].iceConnectionState) {
case "connected": case "connected":
logIt("connected"); logIt("connected");
// Once connected we no longer have a need for the signaling server, so disconnect // Once connected we no longer have a need for the signaling server, so disconnect
VideoChat.socket.disconnect(); // VideoChat.socket.disconnect();
break; break;
case "disconnected": case "disconnected":
logIt("disconnected"); logIt("disconnected");
@ -222,26 +223,26 @@ var VideoChat = {
// When receiving a candidate over the socket, turn it back into a real // When receiving a candidate over the socket, turn it back into a real
// RTCIceCandidate and add it to the peerConnection. // RTCIceCandidate and add it to the peerConnection.
onCandidate: function (candidate) { onCandidate: function (candidate, uuid) {
// Update caption text // Update caption text
captionText.text("Found other user... connecting"); captionText.text("Found other user... connecting");
rtcCandidate = new RTCIceCandidate(JSON.parse(candidate)); rtcCandidate = new RTCIceCandidate(JSON.parse(candidate));
logIt( logIt(
`onCandidate <<< Received remote ICE candidate (${rtcCandidate.address} - ${rtcCandidate.relatedAddress})` `onCandidate <<< Received remote ICE candidate (${rtcCandidate.address} - ${rtcCandidate.relatedAddress})`
); );
VideoChat.peerConnection.addIceCandidate(rtcCandidate); VideoChat.peerConnections[uuid].addIceCandidate(rtcCandidate);
}, },
// Create an offer that contains the media capabilities of the browser. // Create an offer that contains the media capabilities of the browser.
createOffer: function () { createOffer: function (uuid) {
logIt("createOffer >>> Creating offer..."); console.log(">>> Creating offer to UUID: ", uuid, ". my UUID is", VideoChat.socket.id);
VideoChat.peerConnection.createOffer( VideoChat.peerConnections[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
// and send it over the socket connection to initiate the peerConnection // and send it over the socket connection to initiate the peerConnection
// on the other side. // on the other side.
VideoChat.peerConnection.setLocalDescription(offer); VideoChat.peerConnections[uuid].setLocalDescription(offer);
VideoChat.socket.emit("offer", JSON.stringify(offer), roomHash); VideoChat.socket.emit("offer", JSON.stringify(offer), roomHash, uuid);
}, },
function (err) { function (err) {
logIt("failed offer creation"); logIt("failed offer creation");
@ -255,16 +256,16 @@ var VideoChat = {
// 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
// same manner as the offer and sent over the socket. // same manner as the offer and sent over the socket.
createAnswer: function (offer) { createAnswer: function (offer, uuid) {
logIt("createAnswer"); logIt("createAnswer");
return function () { return function () {
logIt(">>> Creating answer...");
rtcOffer = new RTCSessionDescription(JSON.parse(offer)); rtcOffer = new RTCSessionDescription(JSON.parse(offer));
VideoChat.peerConnection.setRemoteDescription(rtcOffer); VideoChat.peerConnections[uuid].setRemoteDescription(rtcOffer);
VideoChat.peerConnection.createAnswer( VideoChat.peerConnections[uuid].createAnswer(
function (answer) { function (answer) {
VideoChat.peerConnection.setLocalDescription(answer); VideoChat.peerConnections[uuid].setLocalDescription(answer);
VideoChat.socket.emit("answer", JSON.stringify(answer), roomHash); console.log(">>> Creating answer to UUID: ", uuid, ". my UUID is", VideoChat.socket.id);
VideoChat.socket.emit("answer", JSON.stringify(answer), roomHash, uuid);
}, },
function (err) { function (err) {
logIt("Failed answer creation."); logIt("Failed answer creation.");
@ -276,26 +277,27 @@ var VideoChat = {
// When a browser receives an offer, set up a callback to be run when the // When a browser receives an offer, set up a callback to be run when the
// ephemeral token is returned from Twilio. // ephemeral token is returned from Twilio.
onOffer: function (offer) { onOffer: function (offer, uuid) {
logIt("onOffer <<< Received offer"); logIt("onOffer <<< Received offer");
VideoChat.socket.on( VideoChat.socket.on(
"token", "token",
VideoChat.onToken(VideoChat.createAnswer(offer)) VideoChat.onToken(VideoChat.createAnswer(offer, uuid), uuid)
); );
VideoChat.socket.emit("token", roomHash); VideoChat.socket.emit("token", roomHash);
}, },
// 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) { onAnswer: function (answer, uuid) {
logIt("onAnswer <<< Received answer"); console.log("onAnswer <<< Received answer", uuid, ". my UUID is", VideoChat.socket.id);
// logIt("onAnswer <<< Received answer" + "");
var rtcAnswer = new RTCSessionDescription(JSON.parse(answer)); var rtcAnswer = new RTCSessionDescription(JSON.parse(answer));
// Set remote description of RTCSession // Set remote description of RTCSession
VideoChat.peerConnection.setRemoteDescription(rtcAnswer); VideoChat.peerConnections[uuid].setRemoteDescription(rtcAnswer);
// The caller now knows that the callee is ready to accept new ICE candidates, so sending the buffer over // The caller now knows that the callee is ready to accept new ICE candidates, so sending the buffer over
VideoChat.localICECandidates.forEach((candidate) => { VideoChat.localICECandidates.forEach((candidate) => {
logIt(`>>> Sending local ICE candidate (${candidate.address})`); logIt(`>>> Sending local ICE candidate (${candidate.address})`);
// Send ice candidate over websocket // Send ice candidate over websocket
VideoChat.socket.emit("candidate", JSON.stringify(candidate), roomHash); 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 // Reset the buffer of local ICE candidates. This is not really needed, but it's good practice
VideoChat.localICECandidates = []; VideoChat.localICECandidates = [];
@ -303,6 +305,11 @@ var VideoChat = {
// Called when a stream is added to the peer connection // Called when a stream is added to the peer connection
onAddStream: function (event) { onAddStream: function (event) {
if(VideoChat.counter > 4) {
VideoChat.socket.disconnect();
}else{
VideoChat.counter++;
}
logIt("onAddStream <<< Received new stream from remote. Adding it..."); 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
@ -451,7 +458,7 @@ function windowResized() {
function muteMicrophone() { function muteMicrophone() {
var audioTrack = null; var audioTrack = null;
// Get audio track to mute // Get audio track to mute
VideoChat.peerConnection.getSenders().find(function (s) { VideoChat.peerConnections.first.getSenders().find(function (s) {
if (s.track.kind === "audio") { if (s.track.kind === "audio") {
audioTrack = s.track; audioTrack = s.track;
} }

View File

@ -78,16 +78,24 @@ 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);
} else if (numClients === 1) { } else if (numClients < 3) {
socket.join(room); socket.join(room);
// When the client is second to join the room, both clients are ready. 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.
logIt("Broadcasting ready message", room); logIt("Broadcasting ready message", room);
// First to join call initiates call // Existing callers initiates call with user
socket.broadcast.to(room).emit("willInitiateCall", room); socket.broadcast.to(room).emit("willInitiateCall", socket.id, room);
// Send its uui
// socket.emit("uuid", socket.id);
socket.emit("ready", room).to(room); socket.emit("ready", room).to(room);
socket.broadcast.to(room).emit("ready", room); socket.broadcast.to(room).emit("ready", room);
} else { } else {
logIt("room already full", room); logIt("room already full with " + numClients + " people in the room.", room);
socket.emit("full", room); socket.emit("full", room);
} }
}); });
@ -107,21 +115,21 @@ io.on("connection", function (socket) {
}); });
// Relay candidate messages // Relay candidate messages
socket.on("candidate", function (candidate, room) { socket.on("candidate", function (candidate, room, uuid) {
logIt("Received candidate. Broadcasting...", room); logIt("Received candidate. Broadcasting...", room);
socket.broadcast.to(room).emit("candidate", candidate); io.to(uuid).emit("candidate", candidate, socket.id);
}); });
// Relay offers // Relay offers
socket.on("offer", function (offer, room) { socket.on("offer", function (offer, room, uuid) {
logIt("Received offer. Broadcasting...", room); logIt("Received offer from " + socket.id + " and emitting to " + uuid, room);
socket.broadcast.to(room).emit("offer", offer); io.to(uuid).emit("offer", offer, socket.id);
}); });
// Relay answers // Relay answers
socket.on("answer", function (answer, room) { socket.on("answer", function (answer, room, uuid) {
logIt("Received answer. Broadcasting...", room); logIt("Received answer from " + socket.id + " and emitting to " + uuid, room);
socket.broadcast.to(room).emit("answer", answer); io.to(uuid).emit("answer", answer, socket.id);
}); });
}); });