WIP: restructured onToken to prevent it from firing multiple times

This commit is contained in:
Taichi Kato 2020-05-29 19:15:29 +08:00
parent da732735ae
commit 7eb2aab2b5
2 changed files with 195 additions and 107 deletions

View File

@ -38,7 +38,7 @@ var VideoChat = {
peerConnections: {}, peerConnections: {},
recognition: undefined, recognition: undefined,
uuid: undefined, uuid: undefined,
token: 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
// accepted callback to the onMediaStream function, otherwise callback to the // accepted callback to the onMediaStream function, otherwise callback to the
@ -99,6 +99,7 @@ var VideoChat = {
VideoChat.socket.emit("join", roomHash); VideoChat.socket.emit("join", roomHash);
// Receive its own uuid // Receive its own uuid
VideoChat.socket.on("uuid", (uuid) => (VideoChat.uuid = uuid)); VideoChat.socket.on("uuid", (uuid) => (VideoChat.uuid = uuid));
VideoChat.socket.on("token", (token) => (VideoChat.token = token));
// 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);
@ -117,85 +118,161 @@ var VideoChat = {
// 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 (uuid) { startCall: function (uuid) {
logIt("startCall >>> Sending token request..."); VideoChat.establishConnection(uuid);
VideoChat.socket.on("token", VideoChat.onToken(function() {VideoChat.createOffer(uuid)}, uuid)); VideoChat.createOffer(uuid);
VideoChat.socket.emit("token", roomHash);
},
establishConnection: function (uuid) {
console.log("establishing connection to", uuid);
// Set up a new RTCPeerConnection using the token's iceServers.
VideoChat.peerConnections[uuid] = new RTCPeerConnection({
iceServers: VideoChat.token.iceServers,
});
// Add the local video stream to the peerConnection.
VideoChat.localStream.getTracks().forEach(function (track) {
console.log("local stream: ", track);
VideoChat.peerConnections[uuid].addTrack(track, VideoChat.localStream);
});
// Add general purpose data channel to peer connection,
// used for text chats, captions, and toggling sending captions
dataChanel = VideoChat.peerConnections[uuid].createDataChannel("chat", {
negotiated: true,
// both peers must have same id
id: 0,
});
// Called when dataChannel is successfully opened
dataChanel.onopen = function (event) {
logIt("dataChannel opened");
};
// Handle different dataChannel types
dataChanel.onmessage = function (event) {
const receivedData = event.data;
// First 4 chars represent data type
const dataType = receivedData.substring(0, 4);
const cleanedMessage = receivedData.slice(4);
if (dataType === "mes:") {
handleRecieveMessage(cleanedMessage);
} else if (dataType === "cap:") {
recieveCaptions(cleanedMessage);
} else if (dataType === "tog:") {
toggleSendCaptions();
}
};
// Set up callbacks for the connection generating iceCandidates or
// receiving the remote media stream.
VideoChat.peerConnections[uuid].onicecandidate = VideoChat.onIceCandidate;
VideoChat.peerConnections[uuid].onaddstream = VideoChat.onAddStream;
// Set up listeners on the socket
VideoChat.socket.on("candidate", VideoChat.onCandidate);
VideoChat.socket.on("answer", VideoChat.onAnswer);
VideoChat.socket.on("requestToggleCaptions", () => toggleSendCaptions());
VideoChat.socket.on("recieveCaptions", (captions) =>
recieveCaptions(captions)
);
// Called when there is a change in connection state
VideoChat.peerConnections[uuid].oniceconnectionstatechange = function (event) {
switch (VideoChat.peerConnections[uuid].iceConnectionState) {
case "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;
case "disconnected":
logIt("disconnected");
case "failed":
logIt("failed");
// VideoChat.socket.connect
// VideoChat.createOffer();
// Refresh page if connection has failed
location.reload();
break;
case "closed":
logIt("closed");
break;
}
};
}, },
// When we receive the ephemeral token back from the server. // When we receive the ephemeral token back from the server.
onToken: function (callback, uuid) { // onToken: function (callback) {
logIt("onToken"); // logIt("onToken");
return function (token) { // return function (token, uuid) {
logIt("<<< Received token"); // logIt("<<< Received token");
// Set up a new RTCPeerConnection using the token's iceServers. // console.log(token, uuid);
VideoChat.peerConnections[uuid] = new RTCPeerConnection({ // // Set up a new RTCPeerConnection using the token's iceServers.
iceServers: token.iceServers, // VideoChat.peerConnections[uuid] = new RTCPeerConnection({
}); // iceServers: token.iceServers,
// Add the local video stream to the peerConnection. // });
VideoChat.localStream.getTracks().forEach(function (track) { // // Add the local video stream to the peerConnection.
VideoChat.peerConnections[uuid].addTrack(track, VideoChat.localStream); // VideoChat.localStream.getTracks().forEach(function (track) {
}); // VideoChat.peerConnections[uuid].addTrack(track, VideoChat.localStream);
// Add general purpose data channel to peer connection, // });
// used for text chats, captions, and toggling sending captions // // Add general purpose data channel to peer connection,
dataChanel = VideoChat.peerConnections[uuid].createDataChannel("chat", { // // used for text chats, captions, and toggling sending captions
negotiated: true, // dataChanel = VideoChat.peerConnections[uuid].createDataChannel("chat", {
// both peers must have same id // negotiated: true,
id: 0, // // both peers must have same id
}); // id: 0,
// Called when dataChannel is successfully opened // });
dataChanel.onopen = function (event) { // // Called when dataChannel is successfully opened
logIt("dataChannel opened"); // dataChanel.onopen = function (event) {
}; // logIt("dataChannel opened");
// Handle different dataChannel types // };
dataChanel.onmessage = function (event) { // // Handle different dataChannel types
const receivedData = event.data; // dataChanel.onmessage = function (event) {
// First 4 chars represent data type // const receivedData = event.data;
const dataType = receivedData.substring(0, 4); // // First 4 chars represent data type
const cleanedMessage = receivedData.slice(4); // const dataType = receivedData.substring(0, 4);
if (dataType === "mes:") { // const cleanedMessage = receivedData.slice(4);
handleRecieveMessage(cleanedMessage); // if (dataType === "mes:") {
} else if (dataType === "cap:") { // handleRecieveMessage(cleanedMessage);
recieveCaptions(cleanedMessage); // } else if (dataType === "cap:") {
} else if (dataType === "tog:") { // recieveCaptions(cleanedMessage);
toggleSendCaptions(); // } else if (dataType === "tog:") {
} // toggleSendCaptions();
}; // }
// Set up callbacks for the connection generating iceCandidates or // };
// receiving the remote media stream. // // Set up callbacks for the connection generating iceCandidates or
VideoChat.peerConnections[uuid].onicecandidate = VideoChat.onIceCandidate; // // receiving the remote media stream.
VideoChat.peerConnections[uuid].onaddstream = VideoChat.onAddStream; // VideoChat.peerConnections[uuid].onicecandidate = VideoChat.onIceCandidate;
// Set up listeners on the socket // VideoChat.peerConnections[uuid].onaddstream = VideoChat.onAddStream;
VideoChat.socket.on("candidate", VideoChat.onCandidate); // // Set up listeners on the socket
VideoChat.socket.on("answer", VideoChat.onAnswer); // VideoChat.socket.on("candidate", VideoChat.onCandidate);
VideoChat.socket.on("requestToggleCaptions", () => toggleSendCaptions()); // VideoChat.socket.on("answer", VideoChat.onAnswer);
VideoChat.socket.on("recieveCaptions", (captions) => // VideoChat.socket.on("requestToggleCaptions", () => toggleSendCaptions());
recieveCaptions(captions) // VideoChat.socket.on("recieveCaptions", (captions) =>
); // recieveCaptions(captions)
// Called when there is a change in connection state // );
VideoChat.peerConnections[uuid].oniceconnectionstatechange = function (event) { // // Called when there is a change in connection state
switch (VideoChat.peerConnections[uuid].iceConnectionState) { // VideoChat.peerConnections[uuid].oniceconnectionstatechange = function (event) {
case "connected": // switch (VideoChat.peerConnections[uuid].iceConnectionState) {
logIt("connected"); // case "connected":
// Once connected we no longer have a need for the signaling server, so disconnect // logIt("connected");
// VideoChat.socket.disconnect(); // // Once connected we no longer have a need for the signaling server, so disconnect
break; // VideoChat.socket.off("token");
case "disconnected": // VideoChat.socket.off("offer");
logIt("disconnected"); // break;
case "failed": // case "disconnected":
logIt("failed"); // logIt("disconnected");
// VideoChat.socket.connect // case "failed":
// VideoChat.createOffer(); // logIt("failed");
// Refresh page if connection has failed // // VideoChat.socket.connect
location.reload(); // // VideoChat.createOffer();
break; // // Refresh page if connection has failed
case "closed": // location.reload();
logIt("closed"); // break;
break; // case "closed":
} // logIt("closed");
}; // break;
callback(); // }
}; // };
}, // callback();
// };
// },
// 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.
onIceCandidate: function (event) { onIceCandidate: function (event) {
@ -258,32 +335,27 @@ var VideoChat = {
// same manner as the offer and sent over the socket. // same manner as the offer and sent over the socket.
createAnswer: function (offer, uuid) { createAnswer: function (offer, uuid) {
logIt("createAnswer"); logIt("createAnswer");
return function () { rtcOffer = new RTCSessionDescription(JSON.parse(offer));
rtcOffer = new RTCSessionDescription(JSON.parse(offer)); VideoChat.peerConnections[uuid].setRemoteDescription(rtcOffer);
VideoChat.peerConnections[uuid].setRemoteDescription(rtcOffer); VideoChat.peerConnections[uuid].createAnswer(
VideoChat.peerConnections[uuid].createAnswer( function (answer) {
function (answer) { VideoChat.peerConnections[uuid].setLocalDescription(answer);
VideoChat.peerConnections[uuid].setLocalDescription(answer); console.log(">>> Creating answer to UUID: ", uuid, ". my UUID is", VideoChat.socket.id);
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) { logIt("Failed answer creation.");
logIt("Failed answer creation."); logIt(err, true);
logIt(err, true); }
} );
);
};
}, },
// 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, uuid) { onOffer: function (offer, uuid) {
logIt("onOffer <<< Received offer"); logIt("onOffer <<< Received offer");
VideoChat.socket.on( VideoChat.establishConnection(uuid);
"token", VideoChat.createAnswer(offer, uuid);
VideoChat.onToken(VideoChat.createAnswer(offer, uuid), uuid)
);
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.
@ -305,11 +377,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) { // if(VideoChat.counter > 4) {
VideoChat.socket.disconnect(); // VideoChat.socket.disconnect();
}else{ // }else{
VideoChat.counter++; // 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
@ -319,7 +391,7 @@ var VideoChat = {
node.setAttribute("id", "remote-video"); node.setAttribute("id", "remote-video");
VideoChat.remoteVideoWrapper.appendChild(node); VideoChat.remoteVideoWrapper.appendChild(node);
// Update remote video source // Update remote video source
console.log(VideoChat.remoteVideoWrapper.children); console.log(VideoChat.remoteVideoWrapper.children.length, event.stream);
VideoChat.remoteVideoWrapper.lastChild.srcObject = event.stream; VideoChat.remoteVideoWrapper.lastChild.srcObject = event.stream;
// Close the initial share url snackbar // Close the initial share url snackbar
Snackbar.close(); Snackbar.close();

View File

@ -78,6 +78,15 @@ 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) {
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 < 3) { } else if (numClients < 3) {
socket.join(room); socket.join(room);
logIt("Connected clients", room) logIt("Connected clients", room)
@ -87,10 +96,17 @@ io.on("connection", function (socket) {
// 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);
// Existing callers initiates call with user
socket.broadcast.to(room).emit("willInitiateCall", socket.id, room);
// Send its uui twilio.tokens.create(function (err, response) {
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
socket.broadcast.to(room).emit("willInitiateCall", socket.id, room);
}
});
// socket.emit("uuid", socket.id); // 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);
@ -109,7 +125,7 @@ io.on("connection", function (socket) {
logIt(err, room); logIt(err, room);
} else { } else {
logIt("Token generated. Returning it to the browser client", room); logIt("Token generated. Returning it to the browser client", room);
socket.emit("token", response).to(room); socket.emit("token", response);
} }
}); });
}); });