Merge pull request #9 from questo-ai/fixes

Merge all the Fixes into Master
This commit is contained in:
Khush Jammu 2020-06-06 19:28:19 +08:00 committed by GitHub
commit 4ddf8b0f25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 125 deletions

27
public/css/chat.css vendored
View File

@ -111,7 +111,9 @@ a {
#wrapper {
display: flex;
flex-direction: column;
/* flex: 1 1 20em; */
/* flex-basis: 30%; */
flex-direction: row;
align-items: center;
flex-wrap: wrap;
justify-content: center;
@ -122,8 +124,8 @@ a {
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
width: 65%;
max-height: 100%;
width: 100%;
max-height: 90vh;
max-width: 100%;
}
@ -145,7 +147,7 @@ a {
padding: 10px;
}
#remote-video:first-child:nth-last-child(1) {
/* -or- li:only-child { */
width: min(calc(80vh * 4/3), 80vw);
max-height: 80vh;
}
@ -421,7 +423,22 @@ button:hover {
margin: 0;
line-height: 2rem;
}
#wrapper {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
justify-content: center;
padding: 0;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, calc(-50% - 3rem));
max-height: 90%;
max-width: 100%;
}
#remote-video {
/* width: 75vw;
height: calc((16/9) * 75vw); */

View File

@ -32,7 +32,6 @@ var dataChannel = new Map();
var VideoChat = {
videoEnabled: true,
audioEnabled: true,
borderColor: `hsl(${Math.floor(Math.random() * 360)}, 70%, 80%)`,
connected: new Map(),
localICECandidates: {},
socket: io(),
@ -74,6 +73,14 @@ var VideoChat = {
onMediaStream: function (stream) {
logIt("onMediaStream");
VideoChat.localStream = stream;
// We need to store the local audio track, because
// the screen sharing MediaStream doesn't have audio
// by default, which is problematic for peer C who joins
// while another peer A/B is screen sharing (C won't receive
// A/Bs audio).
VideoChat.localAudio = stream.getAudioTracks()[0];
// Add the stream as video's srcObject.
// Now that we have webcam video sorted, prompt user to share URL
Snackbar.show({
@ -99,14 +106,16 @@ var VideoChat = {
},
});
VideoChat.borderColor = uuidToColor(VideoChat.socket.id);
VideoChat.localVideo.srcObject = stream;
VideoChat.localVideo.style.border = `3px solid ${VideoChat.borderColor}`;
// Now we're ready to join the chat room.
VideoChat.socket.emit("join", roomHash);
VideoChat.socket.emit("join", roomHash, function() {
VideoChat.borderColor = hueToColor(uuidToHue(VideoChat.socket.id));
VideoChat.localVideo.style.border = `3px solid ${VideoChat.borderColor}`;
});
// Add listeners to the websocket
VideoChat.socket.on("leave", VideoChat.onLeave);
VideoChat.socket.on("full", chatRoomFull);
VideoChat.socket.on("offer", VideoChat.onOffer);
VideoChat.socket.on("willInitiateCall", VideoChat.call);
@ -131,6 +140,22 @@ var VideoChat = {
VideoChat.socket.emit("token", roomHash, uuid);
},
onLeave: function(uuid) {
logIt("disconnected - UUID " + uuid);
// Remove video element
VideoChat.remoteVideoWrapper.removeChild(
document.querySelectorAll(`[uuid="${uuid}"]`)[0]
);
// Delete connection & metadata
VideoChat.connected.delete(uuid);
VideoChat.peerConnections.get(uuid).close(); // This is necessary, because otherwise the RTC connection isn't closed
VideoChat.peerConnections.delete(uuid);
dataChannel.delete(uuid);
if (VideoChat.peerConnections.size === 0) {
displayWaitingCaption();
}
},
establishConnection: function (correctUuid, callback) {
return function (token, uuid) {
if (correctUuid != uuid) {
@ -171,7 +196,7 @@ var VideoChat = {
const dataType = receivedData.substring(0, 4);
const cleanedMessage = receivedData.slice(4);
if (dataType === "mes:") {
handleRecieveMessage(cleanedMessage, VideoChat.peerColors[uuid]);
handleRecieveMessage(cleanedMessage, hueToColor(VideoChat.peerColors.get(uuid)));
} else if (dataType === "cap:") {
recieveCaptions(cleanedMessage);
} else if (dataType === "tog:") {
@ -203,22 +228,11 @@ var VideoChat = {
logIt("connected");
break;
case "disconnected":
// Disconnects are handled server-side
logIt("disconnected - UUID " + uuid);
VideoChat.remoteVideoWrapper.removeChild(
document.querySelectorAll(`[uuid="${uuid}"]`)[0]
);
VideoChat.connected.delete(uuid);
VideoChat.peerConnections.delete(uuid);
dataChannel.delete(uuid);
if (VideoChat.peerConnections.size === 0) {
displayWaitingCaption();
}
break;
case "failed":
logIt("failed");
// VideoChat.socket.connect
// VideoChat.createOffer();
// Refresh page if connection has failed
location.reload();
break;
@ -363,12 +377,6 @@ var VideoChat = {
VideoChat.connected.set(uuid, true);
// Hide caption status text
captionText.fadeOut();
// Downscale send resolution and bitrate if num in room > 4
// if (VideoChat.peerConnections.size > 3) {
// VideoChat.peerConnections.forEach(function (value, key, map) {
// downscaleStream(value);
// });
// }
// Reposition local video after a second, as there is often a delay
// between adding a stream and the height of the video div changing
setTimeout(() => rePositionLocalVideo(), 500);
@ -507,31 +515,6 @@ function sendToAllDataChannels(message) {
// }
// End Fullscreen
// Downscale single stream
// async function downscaleStream(pc, applying = false) {
// height = 240;
// rate = 800000;
// if (applying) return;
// try {
// applying = true;
// do {
// h = height;
// const sender = pc.getSenders().find(function (s) {
// return s.track.kind === "video";
// });
// const ratio = sender.track.getSettings().height / height;
// const params = sender.getParameters();
// if (!params.encodings) params.encodings = [{}]; // Firefox workaround!
// params.encodings[0].scaleResolutionDownBy = Math.max(ratio, 1);
// params.encodings[0].maxBitrate = rate;
// await sender.setParameters(params);
// } while (h != height);
// } catch (e) {
// logIt(e);
// } finally {
// applying = false;
// }
// }
// Mute microphone
function muteMicrophone() {
@ -545,7 +528,7 @@ function muteMicrophone() {
});
audioTrack.enabled = VideoChat.audioEnabled;
});
// select mic button and mic button text
const micButtonIcon = document.getElementById("mic-icon");
const micButtonText = document.getElementById("mic-text");
@ -636,6 +619,12 @@ function swap() {
swapIcon.classList.remove("fa-desktop");
swapIcon.classList.add("fa-camera");
swapText.innerText = "Share Webcam";
// Add audio track to screen sharing MediaStream,
// in case another peer joins while screen is being
// shared.
stream.addTrack(VideoChat.localAudio);
console.log(stream);
switchStreamHelper(stream);
})
.catch(function (err) {
@ -645,8 +634,9 @@ function swap() {
});
// If mode is screenshare then switch to webcam
} else {
// Stop the screen share track
VideoChat.localVideo.srcObject.getTracks().forEach((track) => track.stop());
// Stop the screen share video track. (We don't want to
// stop the audio track obviously.)
VideoChat.localVideo.srcObject.getVideoTracks().forEach((track) => track.stop());
// Get webcam input
navigator.mediaDevices
.getUserMedia({
@ -687,7 +677,7 @@ function switchStreamHelper(stream) {
}
});
// Update local video stream
VideoChat.localStream = videoTrack;
VideoChat.localStream = stream;
// Update local video object
VideoChat.localVideo.srcObject = stream;
// Unpause video on swap
@ -809,39 +799,6 @@ function recieveCaptions(captions) {
}
// End Live caption
// Translation
// function translate(text) {
// let fromLang = "en";
// let toLang = "zh";
// // let text = "hello how are you?";
// const API_KEY = "APIKEYHERE";
// let gurl = `https://translation.googleapis.com/language/translate/v2?key=${API_KEY}`;
// gurl += "&q=" + encodeURI(text);
// gurl += `&source=${fromLang}`;
// gurl += `&target=${toLang}`;
// fetch(gurl, {
// method: "GET",
// headers: {
// "Content-Type": "application/json",
// Accept: "application/json",
// },
// })
// .then((res) => res.json())
// .then((response) => {
// // console.log("response from google: ", response);
// // return response["data"]["translations"][0]["translatedText"];
// logIt(response);
// var translatedText =
// response["data"]["translations"][0]["translatedText"];
// console.log(translatedText);
// dataChanel.send("cap:" + translatedText);
// })
// .catch((error) => {
// console.log("There was an error with the translation request: ", error);
// });
// }
// End Translation
// Text Chat
// Add text message to chat screen on page
function addMessageToScreen(msg, border, isOwnMessage) {
@ -893,38 +850,38 @@ function handleRecieveMessage(msg, color) {
}
}
function uuidToColor(uuid) {
// Using uuid to generate random. unique pastel color
function uuidToHue(uuid) {
// Using uuid to generate random, unique pastel color
var hash = 0;
for (var i = 0; i < uuid.length; i++) {
hash = uuid.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
hash = uuid.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
}
var hue = Math.abs(hash % 360);
// Ensure color is not similar to other colors
var availColors = Array.from({ length: 18 }, (x, i) => i * 20);
VideoChat.peerColors.forEach(function (value, key, map) {
availColors[Math.floor(value / 20)] = null;
});
if (availColors[Math.floor(hue / 20)] == null) {
var availColors = Array.from({length: 6}, (x,i) => i*60);
VideoChat.peerColors.forEach(function(value, key, map) {availColors[Math.floor(value/60)] = null});
if (availColors[Math.floor(hue/60)] == null) {
for (var i = 0; i < availColors.length; i++) {
if (availColors[i] != null) {
hue = (hue % 20) + availColors[i];
hue = (hue % 60) + availColors[i];
availColors[i] = null;
break;
}
}
}
return `hsl(${hue},100%,70%)`;
VideoChat.peerColors.set(uuid, hue);
return hue;
}
function hueToColor(hue) {
return `hsl(${hue},100%,70%)`
}
// Sets the border color of uuid's stream
function setStreamColor(uuid) {
const color = uuidToColor(uuid);
document.querySelectorAll(
`[uuid="${uuid}"]`
)[0].style.border = `3px solid ${color}`;
VideoChat.peerColors[uuid] = color;
const hue = uuidToHue(uuid);
document.querySelectorAll(`[uuid="${uuid}"]`)[0].style.border = `3px solid ${hueToColor(hue)}`;
}
// Show and hide chat
@ -958,27 +915,18 @@ function togglePictureInPicture() {
logIt("Error exiting pip.");
logIt(error);
});
} else if (VideoChat.remoteVideoWrapper.lastChild.webkitPresentationMode === "inline") {
VideoChat.remoteVideoWrapper.lastChild.webkitSetPresentationMode("picture-in-picture");
} else if (
VideoChat.remoteVideoWrapper.lastChild.webkitPresentationMode === "inline"
VideoChat.remoteVideoWrapper.lastChild.webkitPresentationMode === "picture-in-picture"
) {
VideoChat.remoteVideoWrapper.lastChild.webkitSetPresentationMode(
"picture-in-picture"
);
} else if (
VideoChat.remoteVideoWrapper.lastChild.webkitPresentationMode ===
"picture-in-picture"
) {
VideoChat.remoteVideoWrapper.lastChild.webkitSetPresentationMode(
"inline"
);
VideoChat.remoteVideoWrapper.lastChild.webkitSetPresentationMode("inline");
} else {
VideoChat.remoteVideoWrapper.lastChild
.requestPictureInPicture()
.catch((error) => {
alert(
"You must be connected to another person to enter picture in picture."
);
});
VideoChat.remoteVideoWrapper.lastChild.requestPictureInPicture().catch((error) => {
alert(
"You must be connected to another person to enter picture in picture."
);
});
}
} else {
alert(
@ -996,6 +944,12 @@ function displayWaitingCaption() {
rePositionCaptions();
}
window.onbeforeunload = function () {
VideoChat.socket.emit("leave", roomHash);
return null;
};
function startUp() {
// Try and detect in-app browsers and redirect
var ua = navigator.userAgent || navigator.vendor || window.opera;

View File

@ -72,8 +72,10 @@ function logIt(msg, room) {
io.on("connection", function (socket) {
// When a client tries to join a room, only allow them if they are first or
// second in the room. Otherwise it is full.
socket.on("join", function (room) {
socket.on("join", function (room, acknowledgement) {
logIt("A client joined the room", room);
acknowledgement();
var clients = io.sockets.adapter.rooms[room];
var numClients = typeof clients !== "undefined" ? clients.length : 0;
if (numClients === 0) {
@ -92,6 +94,13 @@ io.on("connection", function (socket) {
}
});
// Client is disconnecting from the server
socket.on('disconnecting', () => {
var room = Object.keys(socket.rooms).filter(item => item != socket.id); // Socket joins a room of itself, remove that
logIt("A client has disconnected from the room", room);
socket.broadcast.to(room).emit("leave", socket.id);
});
// When receiving the token message, use the Twilio REST API to request an
// token to get ephemeral credentials to use the TURN server.
socket.on("token", function (room, uuid) {