2020-03-30 12:03:11 +08:00
|
|
|
// Vars
|
|
|
|
const browserName = getBrowserName();
|
|
|
|
const url = window.location.href;
|
2020-03-31 02:16:08 +08:00
|
|
|
const roomHash = url.substring(url.lastIndexOf("/") + 1).toLowerCase();
|
2020-03-30 12:03:11 +08:00
|
|
|
var mode = "camera";
|
|
|
|
var isFullscreen = false;
|
|
|
|
var sendingCaptions = false;
|
|
|
|
var receivingCaptions = false;
|
|
|
|
const isWebRTCSupported =
|
2020-03-31 02:16:08 +08:00
|
|
|
navigator.getUserMedia ||
|
|
|
|
navigator.webkitGetUserMedia ||
|
|
|
|
navigator.mozGetUserMedia ||
|
|
|
|
navigator.msGetUserMedia ||
|
|
|
|
window.RTCPeerConnection;
|
2020-03-25 14:02:06 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
// Element vars
|
2020-03-31 12:18:15 +08:00
|
|
|
const chatInput = document.querySelector(".compose input");
|
2020-03-31 02:16:08 +08:00
|
|
|
const pipVideo = document.getElementById("remote-video");
|
2020-03-22 05:23:46 +08:00
|
|
|
|
|
|
|
var VideoChat = {
|
2020-03-31 02:16:08 +08:00
|
|
|
connected: false,
|
|
|
|
willInitiateCall: false,
|
|
|
|
localICECandidates: [],
|
|
|
|
socket: io(),
|
|
|
|
remoteVideo: document.getElementById("remote-video"),
|
|
|
|
localVideo: document.getElementById("local-video"),
|
|
|
|
recognition: undefined,
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// accepted callback to the onMediaStream function, otherwise callback to the
|
|
|
|
// noMediaStream function.
|
|
|
|
requestMediaStream: function (event) {
|
|
|
|
logIt("requestMediaStream");
|
|
|
|
rePositionLocalVideo();
|
|
|
|
navigator.mediaDevices
|
|
|
|
.getUserMedia({
|
|
|
|
video: true,
|
|
|
|
audio: true,
|
|
|
|
})
|
|
|
|
.then((stream) => {
|
|
|
|
VideoChat.onMediaStream(stream);
|
|
|
|
$("#local-video-text").text("Drag Me");
|
|
|
|
setTimeout(() => $("#local-video-text").fadeOut(), 5000);
|
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
logIt(error);
|
|
|
|
logIt(
|
|
|
|
"Failed to get local webcam video, check webcam privacy settings"
|
2020-03-22 05:23:46 +08:00
|
|
|
);
|
2020-03-31 02:16:08 +08:00
|
|
|
setTimeout(VideoChat.requestMediaStream, 1000);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// The onMediaStream function receives the media stream as an argument.
|
|
|
|
onMediaStream: function (stream) {
|
|
|
|
logIt("onMediaStream");
|
|
|
|
VideoChat.localStream = stream;
|
|
|
|
// Add the stream as video's srcObject.
|
|
|
|
Snackbar.show({
|
|
|
|
text: "Share this URL with a friend to get started",
|
|
|
|
actionText: "Copy Link",
|
|
|
|
width: "355px",
|
|
|
|
pos: "top-center",
|
|
|
|
actionTextColor: "#8688ff",
|
|
|
|
duration: 500000,
|
|
|
|
backgroundColor: "#16171a",
|
|
|
|
onActionClick: function (element) {
|
|
|
|
var copyContent = window.location.href;
|
|
|
|
$('<input id="some-element">')
|
|
|
|
.val(copyContent)
|
|
|
|
.appendTo("body")
|
|
|
|
.select();
|
|
|
|
document.execCommand("copy");
|
|
|
|
var toRemove = document.querySelector("#some-element");
|
|
|
|
toRemove.parentNode.removeChild(toRemove);
|
|
|
|
$(element).css("opacity", 0); //Set opacity of element to 0 to close Snackbar
|
|
|
|
},
|
|
|
|
});
|
|
|
|
VideoChat.localVideo.srcObject = stream;
|
|
|
|
// Now we're ready to join the chat room.
|
|
|
|
VideoChat.socket.emit("join", roomHash);
|
|
|
|
VideoChat.socket.on("full", chatRoomFull);
|
|
|
|
VideoChat.socket.on("offer", VideoChat.onOffer);
|
|
|
|
VideoChat.socket.on("ready", VideoChat.readyToCall);
|
|
|
|
VideoChat.socket.on(
|
|
|
|
"willInitiateCall",
|
|
|
|
() => (VideoChat.willInitiateCall = true)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
// When we are ready to call, enable the Call button.
|
|
|
|
readyToCall: function (event) {
|
|
|
|
logIt("readyToCall");
|
|
|
|
if (VideoChat.willInitiateCall) {
|
|
|
|
logIt("Initiating call");
|
|
|
|
VideoChat.startCall();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// Set up a callback to run when we have the ephemeral token to use Twilio's TURN server.
|
|
|
|
startCall: function (event) {
|
|
|
|
logIt("startCall");
|
|
|
|
logIt(">>> Sending token request...");
|
|
|
|
VideoChat.socket.on("token", VideoChat.onToken(VideoChat.createOffer));
|
|
|
|
VideoChat.socket.emit("token", roomHash);
|
|
|
|
},
|
|
|
|
|
|
|
|
// When we receive the ephemeral token back from the server.
|
|
|
|
onToken: function (callback) {
|
|
|
|
logIt("onToken");
|
|
|
|
return function (token) {
|
|
|
|
logIt("<<< Received token");
|
|
|
|
// Set up a new RTCPeerConnection using the token's iceServers.
|
|
|
|
VideoChat.peerConnection = new RTCPeerConnection({
|
|
|
|
iceServers: token.iceServers,
|
|
|
|
});
|
|
|
|
// Add the local video stream to the peerConnection.
|
|
|
|
VideoChat.localStream.getTracks().forEach(function (track) {
|
|
|
|
VideoChat.peerConnection.addTrack(track, VideoChat.localStream);
|
|
|
|
});
|
|
|
|
// Set up callbacks for the connection generating iceCandidates or
|
|
|
|
// receiving the remote media stream.
|
|
|
|
VideoChat.peerConnection.onicecandidate = VideoChat.onIceCandidate;
|
|
|
|
VideoChat.peerConnection.onaddstream = VideoChat.onAddStream;
|
|
|
|
// Set up listeners on the socket for candidates or answers being passed
|
|
|
|
// over the socket connection.
|
|
|
|
VideoChat.socket.on("candidate", VideoChat.onCandidate);
|
|
|
|
VideoChat.socket.on("answer", VideoChat.onAnswer);
|
|
|
|
VideoChat.socket.on("requestToggleCaptions", () => toggleSendCaptions());
|
|
|
|
VideoChat.socket.on("recieveCaptions", (captions) =>
|
|
|
|
recieveCaptions(captions)
|
|
|
|
);
|
|
|
|
|
|
|
|
callback();
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
// When the peerConnection generates an ice candidate, send it over the socket to the peer.
|
|
|
|
onIceCandidate: function (event) {
|
|
|
|
logIt("onIceCandidate");
|
|
|
|
if (event.candidate) {
|
|
|
|
logIt(
|
|
|
|
`<<< Received local ICE candidate from STUN/TURN server (${event.candidate.address})`
|
|
|
|
);
|
|
|
|
if (VideoChat.connected) {
|
|
|
|
logIt(`>>> Sending local ICE candidate (${event.candidate.address})`);
|
|
|
|
VideoChat.socket.emit(
|
|
|
|
"candidate",
|
|
|
|
JSON.stringify(event.candidate),
|
|
|
|
roomHash
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// If we are not 'connected' to the other peer, we are buffering the local ICE candidates.
|
|
|
|
// This most likely is happening on the "caller" side.
|
|
|
|
// 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.
|
|
|
|
VideoChat.localICECandidates.push(event.candidate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// When receiving a candidate over the socket, turn it back into a real
|
|
|
|
// RTCIceCandidate and add it to the peerConnection.
|
|
|
|
onCandidate: function (candidate) {
|
|
|
|
logIt("onCandidate");
|
|
|
|
rtcCandidate = new RTCIceCandidate(JSON.parse(candidate));
|
|
|
|
logIt(
|
|
|
|
`<<< Received remote ICE candidate (${rtcCandidate.address} - ${rtcCandidate.relatedAddress})`
|
|
|
|
);
|
|
|
|
VideoChat.peerConnection.addIceCandidate(rtcCandidate);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Create an offer that contains the media capabilities of the browser.
|
|
|
|
createOffer: function () {
|
|
|
|
logIt("createOffer");
|
|
|
|
logIt(">>> Creating offer...");
|
|
|
|
VideoChat.peerConnection.createOffer(
|
|
|
|
function (offer) {
|
|
|
|
// If the offer is created successfully, set it as the local description
|
|
|
|
// and send it over the socket connection to initiate the peerConnection
|
|
|
|
// on the other side.
|
|
|
|
VideoChat.peerConnection.setLocalDescription(offer);
|
|
|
|
VideoChat.socket.emit("offer", JSON.stringify(offer), roomHash);
|
|
|
|
},
|
|
|
|
function (err) {
|
|
|
|
logIt("failed offer creation");
|
|
|
|
logIt(err, true);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Create an answer with the media capabilities that both browsers share.
|
|
|
|
// This function is called with the offer from the originating browser, which
|
|
|
|
// needs to be parsed into an RTCSessionDescription and added as the remote
|
|
|
|
// description to the peerConnection object. Then the answer is created in the
|
|
|
|
// same manner as the offer and sent over the socket.
|
|
|
|
createAnswer: function (offer) {
|
|
|
|
logIt("createAnswer");
|
|
|
|
return function () {
|
|
|
|
logIt(">>> Creating answer...");
|
|
|
|
rtcOffer = new RTCSessionDescription(JSON.parse(offer));
|
|
|
|
VideoChat.peerConnection.setRemoteDescription(rtcOffer);
|
|
|
|
VideoChat.peerConnection.createAnswer(
|
|
|
|
function (answer) {
|
|
|
|
VideoChat.peerConnection.setLocalDescription(answer);
|
|
|
|
VideoChat.socket.emit("answer", JSON.stringify(answer), roomHash);
|
|
|
|
},
|
|
|
|
function (err) {
|
|
|
|
logIt("Failed answer creation.");
|
|
|
|
logIt(err, true);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
// When a browser receives an offer, set up a callback to be run when the
|
|
|
|
// ephemeral token is returned from Twilio.
|
|
|
|
onOffer: function (offer) {
|
|
|
|
logIt("onOffer");
|
|
|
|
logIt("<<< Received offer");
|
|
|
|
VideoChat.socket.on(
|
|
|
|
"token",
|
|
|
|
VideoChat.onToken(VideoChat.createAnswer(offer))
|
|
|
|
);
|
|
|
|
VideoChat.socket.emit("token", roomHash);
|
|
|
|
},
|
|
|
|
|
|
|
|
// When an answer is received, add it to the peerConnection as the remote description.
|
|
|
|
onAnswer: function (answer) {
|
|
|
|
logIt("onAnswer");
|
|
|
|
logIt("<<< Received answer");
|
|
|
|
|
|
|
|
var rtcAnswer = new RTCSessionDescription(JSON.parse(answer));
|
|
|
|
VideoChat.peerConnection.setRemoteDescription(rtcAnswer);
|
|
|
|
VideoChat.localICECandidates.forEach((candidate) => {
|
|
|
|
// The caller now knows that the callee is ready to accept new ICE candidates, so sending the buffer over
|
|
|
|
logIt(`>>> Sending local ICE candidate (${candidate.address})`);
|
|
|
|
VideoChat.socket.emit("candidate", JSON.stringify(candidate), roomHash);
|
|
|
|
});
|
|
|
|
// Reset the buffer of local ICE candidates. This is not really needed, but it's good practice
|
|
|
|
VideoChat.localICECandidates = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
onAddStream: function (event) {
|
|
|
|
logIt("onAddStream");
|
|
|
|
logIt("<<< Received new stream from remote. Adding it...");
|
|
|
|
VideoChat.remoteVideo.srcObject = event.stream;
|
|
|
|
Snackbar.close();
|
|
|
|
VideoChat.remoteVideo.style.background = "none";
|
|
|
|
VideoChat.connected = true;
|
|
|
|
$("#remote-video-text").fadeOut();
|
|
|
|
$("#local-video-text").fadeOut();
|
|
|
|
|
|
|
|
var timesRun = 0;
|
|
|
|
var interval = setInterval(function () {
|
|
|
|
timesRun += 1;
|
2020-03-31 11:12:25 +08:00
|
|
|
if (timesRun === 10) {
|
2020-03-31 02:16:08 +08:00
|
|
|
clearInterval(interval);
|
|
|
|
}
|
|
|
|
rePositionLocalVideo();
|
|
|
|
}, 300);
|
|
|
|
},
|
2020-03-30 12:03:11 +08:00
|
|
|
};
|
2020-03-29 01:30:38 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
function getBrowserName() {
|
2020-03-31 02:16:08 +08:00
|
|
|
var name = "Unknown";
|
|
|
|
if (window.navigator.userAgent.indexOf("MSIE") !== -1) {
|
|
|
|
} else if (window.navigator.userAgent.indexOf("Firefox") !== -1) {
|
|
|
|
name = "Firefox";
|
|
|
|
} else if (window.navigator.userAgent.indexOf("Opera") !== -1) {
|
|
|
|
name = "Opera";
|
|
|
|
} else if (window.navigator.userAgent.indexOf("Chrome") !== -1) {
|
|
|
|
name = "Chrome";
|
|
|
|
} else if (window.navigator.userAgent.indexOf("Safari") !== -1) {
|
|
|
|
name = "Safari";
|
|
|
|
}
|
|
|
|
return name;
|
2020-03-30 12:03:11 +08:00
|
|
|
}
|
2020-03-22 05:23:46 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
function logIt(message, error) {
|
2020-03-31 02:16:08 +08:00
|
|
|
console.log(message);
|
2020-03-30 12:03:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function chatRoomFull() {
|
2020-03-31 02:16:08 +08:00
|
|
|
alert(
|
|
|
|
"Chat room is full. Check to make sure you don't have multiple open tabs, or try with a new room link"
|
|
|
|
);
|
|
|
|
window.location.href = "/newroom";
|
2020-03-30 12:03:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function rePositionLocalVideo() {
|
2020-03-31 02:16:08 +08:00
|
|
|
var bounds = $("#remote-video").position();
|
|
|
|
bounds.top += 10;
|
|
|
|
bounds.left += 10;
|
|
|
|
$("#moveable").css(bounds);
|
2020-03-30 12:03:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Fullscreen
|
|
|
|
//
|
2020-03-25 06:47:10 +08:00
|
|
|
|
|
|
|
function openFullscreen() {
|
2020-03-31 02:16:08 +08:00
|
|
|
var elem = document.getElementById("remote-video");
|
|
|
|
if (!isFullscreen) {
|
|
|
|
isFullscreen = true;
|
|
|
|
if (elem.requestFullscreen) {
|
|
|
|
elem.requestFullscreen();
|
|
|
|
} else if (elem.mozRequestFullScreen) {
|
|
|
|
/* Firefox */
|
|
|
|
elem.mozRequestFullScreen();
|
|
|
|
} else if (elem.webkitRequestFullscreen) {
|
|
|
|
/* Chrome, Safari and Opera */
|
|
|
|
elem.webkitRequestFullscreen();
|
|
|
|
} else if (elem.msRequestFullscreen) {
|
|
|
|
/* IE/Edge */
|
|
|
|
elem.msRequestFullscreen();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
isFullscreen = false;
|
|
|
|
if (document.exitFullscreen) {
|
|
|
|
document.exitFullscreen();
|
|
|
|
} else if (document.mozCancelFullScreen) {
|
|
|
|
/* Firefox */
|
|
|
|
document.mozCancelFullScreen();
|
|
|
|
} else if (document.webkitExitFullscreen) {
|
|
|
|
/* Chrome, Safari and Opera */
|
|
|
|
document.webkitExitFullscreen();
|
|
|
|
} else if (document.msExitFullscreen) {
|
|
|
|
/* IE/Edge */
|
|
|
|
document.msExitFullscreen();
|
2020-03-25 06:47:10 +08:00
|
|
|
}
|
2020-03-31 02:16:08 +08:00
|
|
|
}
|
2020-03-25 06:47:10 +08:00
|
|
|
}
|
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
// End Fullscreen
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// Mute microphone
|
|
|
|
//
|
2020-03-25 06:47:10 +08:00
|
|
|
function muteMicrophone() {
|
2020-03-31 02:16:08 +08:00
|
|
|
var muted = VideoChat.localStream.getAudioTracks()[0].enabled;
|
|
|
|
VideoChat.localStream.getAudioTracks()[0].enabled = !muted;
|
|
|
|
const micIcon = document.getElementById("mic-icon");
|
|
|
|
const micText = document.getElementById("mic-text");
|
|
|
|
if (muted) {
|
|
|
|
micIcon.classList.remove("fa-microphone");
|
|
|
|
micIcon.classList.add("fa-microphone-slash");
|
|
|
|
micText.innerText = "Unmute";
|
|
|
|
} else {
|
|
|
|
micIcon.classList.add("fa-microphone");
|
|
|
|
micIcon.classList.remove("fa-microphone-slash");
|
|
|
|
micText.innerText = "Mute";
|
|
|
|
}
|
2020-03-25 06:47:10 +08:00
|
|
|
}
|
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
// End Mute microphone
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// Pause Video
|
|
|
|
//
|
2020-03-25 14:02:06 +08:00
|
|
|
function pauseVideo() {
|
2020-03-31 02:16:08 +08:00
|
|
|
var paused = VideoChat.localStream.getVideoTracks()[0].enabled;
|
|
|
|
VideoChat.localStream.getVideoTracks()[0].enabled = !paused;
|
|
|
|
const micIcon = document.getElementById("video-icon");
|
|
|
|
const micText = document.getElementById("video-text");
|
|
|
|
if (paused) {
|
|
|
|
micIcon.classList.remove("fa-video");
|
|
|
|
micIcon.classList.add("fa-video-slash");
|
|
|
|
micText.innerText = "Unpause Video";
|
|
|
|
} else {
|
|
|
|
micIcon.classList.add("fa-video");
|
|
|
|
micIcon.classList.remove("fa-video-slash");
|
|
|
|
micText.innerText = "Pause Video";
|
|
|
|
}
|
2020-03-25 14:02:06 +08:00
|
|
|
}
|
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
// End pause Video
|
|
|
|
//
|
2020-03-28 02:06:07 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
// Swap camera / screen share
|
|
|
|
//
|
2020-03-28 06:05:21 +08:00
|
|
|
|
|
|
|
function swap() {
|
2020-03-31 02:16:08 +08:00
|
|
|
if (!VideoChat.connected) {
|
|
|
|
alert("You must join a call before you can share your screen.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const swapIcon = document.getElementById("swap-icon");
|
|
|
|
const swapText = document.getElementById("swap-text");
|
|
|
|
if (mode === "camera") {
|
|
|
|
navigator.mediaDevices
|
|
|
|
.getDisplayMedia({
|
|
|
|
video: true,
|
|
|
|
audio: true,
|
|
|
|
})
|
|
|
|
.then(function (stream) {
|
|
|
|
mode = "screen";
|
|
|
|
swapIcon.classList.remove("fa-desktop");
|
|
|
|
swapIcon.classList.add("fa-camera");
|
|
|
|
swapText.innerText = "Share Webcam";
|
|
|
|
switchStreamHelper(stream);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
VideoChat.localVideo.srcObject.getTracks().forEach((track) => track.stop());
|
|
|
|
navigator.mediaDevices
|
|
|
|
.getUserMedia({
|
|
|
|
video: true,
|
|
|
|
audio: true,
|
|
|
|
})
|
|
|
|
.then(function (stream) {
|
|
|
|
mode = "camera";
|
|
|
|
swapIcon.classList.remove("fa-camera");
|
|
|
|
swapIcon.classList.add("fa-desktop");
|
|
|
|
swapText.innerText = "Share Screen";
|
|
|
|
switchStreamHelper(stream);
|
|
|
|
});
|
|
|
|
}
|
2020-03-28 06:05:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function switchStreamHelper(stream) {
|
2020-03-31 02:16:08 +08:00
|
|
|
let videoTrack = stream.getVideoTracks()[0];
|
|
|
|
if (VideoChat.connected) {
|
|
|
|
var sender = VideoChat.peerConnection.getSenders().find(function (s) {
|
|
|
|
return s.track.kind === videoTrack.kind;
|
|
|
|
});
|
|
|
|
sender.replaceTrack(videoTrack);
|
|
|
|
}
|
|
|
|
VideoChat.localStream = videoTrack;
|
|
|
|
VideoChat.localVideo.srcObject = stream;
|
2020-03-28 06:05:21 +08:00
|
|
|
}
|
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
// End swap camera / screen share
|
|
|
|
//
|
2020-03-28 06:05:21 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
// Live caption
|
|
|
|
//
|
2020-03-29 10:47:24 +08:00
|
|
|
|
|
|
|
function requestToggleCaptions() {
|
2020-03-31 02:16:08 +08:00
|
|
|
if (!VideoChat.connected) {
|
|
|
|
alert("You must be connected to a peer to use Live Caption");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (receivingCaptions) {
|
|
|
|
$("#remote-video-text").text("").fadeOut();
|
|
|
|
$("#caption-text").text("Start Live Caption");
|
|
|
|
receivingCaptions = false;
|
|
|
|
} else {
|
|
|
|
alert(
|
|
|
|
"This is an expirimental feature. Live transcription requires the other user to have chrome."
|
|
|
|
);
|
|
|
|
$("#caption-text").text("End Live Caption");
|
|
|
|
receivingCaptions = true;
|
|
|
|
}
|
|
|
|
VideoChat.socket.emit("requestToggleCaptions", roomHash);
|
2020-03-29 10:47:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function toggleSendCaptions() {
|
2020-03-31 02:16:08 +08:00
|
|
|
if (sendingCaptions) {
|
|
|
|
sendingCaptions = false;
|
|
|
|
VideoChat.recognition.stop();
|
|
|
|
} else {
|
|
|
|
startSpeech();
|
|
|
|
sendingCaptions = true;
|
|
|
|
}
|
2020-03-29 10:47:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function startSpeech() {
|
2020-03-31 02:16:08 +08:00
|
|
|
try {
|
|
|
|
var SpeechRecognition =
|
|
|
|
window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
|
|
var recognition = new SpeechRecognition();
|
|
|
|
VideoChat.recognition = recognition;
|
|
|
|
} catch (e) {
|
|
|
|
sendingCaptions = false;
|
|
|
|
logIt(e);
|
|
|
|
logIt("error importing speech library");
|
|
|
|
VideoChat.socket.emit("sendCaptions", "notusingchrome", roomHash);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If false, the recording will stop after a few seconds of silence.
|
|
|
|
// When true, the silence period is longer (about 15 seconds),
|
|
|
|
// allowing us to keep recording even when the user pauses.
|
|
|
|
recognition.continuous = true;
|
|
|
|
recognition.interimResults = true;
|
|
|
|
// recognition.maxAlternatives = 3;
|
|
|
|
|
|
|
|
var finalTranscript;
|
|
|
|
recognition.onresult = (event) => {
|
|
|
|
let interimTranscript = "";
|
|
|
|
for (let i = event.resultIndex, len = event.results.length; i < len; i++) {
|
|
|
|
let transcript = event.results[i][0].transcript;
|
|
|
|
if (event.results[i].isFinal) {
|
|
|
|
finalTranscript += transcript;
|
|
|
|
} else {
|
|
|
|
interimTranscript += transcript;
|
|
|
|
VideoChat.socket.emit("sendCaptions", interimTranscript, roomHash);
|
|
|
|
// console.log(interimTranscript);
|
|
|
|
}
|
2020-03-29 10:47:24 +08:00
|
|
|
}
|
2020-03-31 02:16:08 +08:00
|
|
|
};
|
2020-03-29 10:47:24 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
recognition.onstart = function () {
|
|
|
|
console.log("recording on");
|
|
|
|
};
|
2020-03-29 10:47:24 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
recognition.onspeechend = function () {
|
|
|
|
console.log("on speech end");
|
|
|
|
};
|
2020-03-29 10:47:24 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
recognition.onerror = function (event) {
|
|
|
|
if (event.error === "no-speech") {
|
|
|
|
console.log("no speech detected");
|
|
|
|
}
|
|
|
|
};
|
2020-03-29 10:47:24 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
recognition.onend = function () {
|
|
|
|
console.log("on end");
|
|
|
|
console.log(sendingCaptions);
|
|
|
|
if (sendingCaptions) {
|
|
|
|
startSpeech();
|
|
|
|
} else {
|
|
|
|
VideoChat.recognition.stop();
|
|
|
|
}
|
|
|
|
};
|
2020-03-29 10:47:24 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
recognition.start();
|
2020-03-29 10:47:24 +08:00
|
|
|
}
|
2020-03-29 07:12:09 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
function recieveCaptions(captions) {
|
2020-03-31 02:16:08 +08:00
|
|
|
$("#remote-video-text").text("").fadeIn();
|
|
|
|
if (!receivingCaptions) {
|
|
|
|
$("#remote-video-text").text("").fadeOut();
|
|
|
|
}
|
|
|
|
if (captions === "notusingchrome") {
|
|
|
|
alert("Other caller must be using chrome for this feature to work");
|
|
|
|
receivingCaptions = false;
|
|
|
|
$("#remote-video-text").text("").fadeOut();
|
|
|
|
$("#caption-text").text("Start Live Caption");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (captions.length > 100) {
|
|
|
|
$("#remote-video-text").text(captions.substr(captions.length - 199));
|
|
|
|
} else {
|
|
|
|
$("#remote-video-text").text(captions);
|
|
|
|
}
|
2020-03-30 12:03:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// End Live caption
|
|
|
|
//
|
2020-03-29 07:12:09 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
2020-03-30 04:05:22 +08:00
|
|
|
// Chat
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
|
2020-03-30 04:05:22 +08:00
|
|
|
var socket = VideoChat.socket;
|
2020-03-29 07:12:09 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
chatInput.addEventListener("keypress", function (event) {
|
|
|
|
if (event.keyCode === 13) {
|
|
|
|
event.preventDefault();
|
|
|
|
socket.emit("chat message", chatInput.value, roomHash);
|
|
|
|
$(".chat-messages").append(
|
|
|
|
'<div class="message-item customer"><div class="message-bloc"><div class="message">' +
|
|
|
|
chatInput.value +
|
|
|
|
"</div></div></div>"
|
|
|
|
);
|
|
|
|
$("#chat-zone").scrollTop($("#chat-zone")[0].scrollHeight);
|
|
|
|
chatInput.value = "";
|
|
|
|
}
|
2020-03-30 04:05:22 +08:00
|
|
|
});
|
2020-03-29 07:12:09 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
socket.on("chat message", function (msg) {
|
|
|
|
$(".chat-messages").append(
|
|
|
|
'<div class="message-item moderator"><div class="message-bloc"><div class="message">' +
|
|
|
|
msg +
|
|
|
|
"</div></div></div>"
|
|
|
|
);
|
|
|
|
$("#chat-zone").scrollTop($("#chat-zone")[0].scrollHeight);
|
|
|
|
if ($("#entire-chat").is(":hidden")) {
|
|
|
|
toggleChat();
|
|
|
|
}
|
2020-03-30 04:05:22 +08:00
|
|
|
});
|
2020-03-30 10:41:15 +08:00
|
|
|
|
|
|
|
function toggleChat() {
|
2020-03-31 02:16:08 +08:00
|
|
|
var entireChat = $("#entire-chat");
|
|
|
|
var chatIcon = document.getElementById("chat-icon");
|
|
|
|
var chatText = $("#chat-text");
|
|
|
|
if (entireChat.is(":visible")) {
|
|
|
|
entireChat.fadeOut();
|
|
|
|
chatText.text("Show Chat");
|
|
|
|
chatIcon.classList.remove("fa-comment-slash");
|
|
|
|
chatIcon.classList.add("fa-comment");
|
|
|
|
} else {
|
|
|
|
entireChat.fadeIn();
|
|
|
|
chatText.text("Hide Chat");
|
|
|
|
chatIcon.classList.remove("fa-comment");
|
|
|
|
chatIcon.classList.add("fa-comment-slash");
|
|
|
|
}
|
2020-03-30 10:41:15 +08:00
|
|
|
}
|
2020-03-29 07:12:09 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
// End chat
|
|
|
|
//
|
2020-03-30 02:59:59 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
2020-03-30 09:46:29 +08:00
|
|
|
//Picture in picture
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
|
2020-03-30 09:46:29 +08:00
|
|
|
function togglePictureInPicture() {
|
2020-03-31 02:16:08 +08:00
|
|
|
if ("pictureInPictureEnabled" in document) {
|
|
|
|
if (document.pictureInPictureElement) {
|
|
|
|
document.exitPictureInPicture().catch((error) => {
|
|
|
|
logIt("Error exiting pip.");
|
|
|
|
logIt(error);
|
|
|
|
});
|
2020-03-30 09:46:29 +08:00
|
|
|
} else {
|
2020-03-31 02:16:08 +08:00
|
|
|
pipVideo.requestPictureInPicture().catch((error) => {
|
|
|
|
logIt("Error entering pip.");
|
|
|
|
logIt(error);
|
|
|
|
});
|
2020-03-30 09:46:29 +08:00
|
|
|
}
|
2020-03-31 02:16:08 +08:00
|
|
|
} else {
|
|
|
|
alert(
|
|
|
|
"Picture in picture is not supported in your browser. Consider using Chrome."
|
|
|
|
);
|
|
|
|
}
|
2020-03-30 09:46:29 +08:00
|
|
|
}
|
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
pipVideo.addEventListener("enterpictureinpicture", () => {
|
|
|
|
$("#pip-text").text("Exit Picture in Picture");
|
2020-03-30 09:46:29 +08:00
|
|
|
});
|
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
pipVideo.addEventListener("leavepictureinpicture", () => {
|
|
|
|
$("#pip-text").text("Enter Picture in Picture");
|
2020-03-30 09:46:29 +08:00
|
|
|
});
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
2020-03-30 09:46:29 +08:00
|
|
|
//Picture in picture
|
2020-03-30 12:03:11 +08:00
|
|
|
//
|
|
|
|
|
|
|
|
function startUp() {
|
2020-03-31 02:16:08 +08:00
|
|
|
// strip url parameters
|
|
|
|
if (url.indexOf("?") > -1) {
|
|
|
|
window.location.href = url.split("?")[0];
|
|
|
|
}
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
// if no chat room provided go back to newroom
|
|
|
|
if (window.location.pathname === "/room") {
|
|
|
|
window.location.href = "/landing/newroom";
|
|
|
|
}
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
// Redirect unsupported browsers
|
|
|
|
if (
|
|
|
|
!isWebRTCSupported ||
|
|
|
|
browserName === "Safari" ||
|
|
|
|
browserName === "MSIE"
|
|
|
|
) {
|
|
|
|
alert(
|
|
|
|
"Your browser doesn't support Neon Chat. Please use Chrome or Firefox."
|
|
|
|
);
|
|
|
|
window.location.href = "/";
|
|
|
|
}
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
document.title = "Neon Chat - " + url.substring(url.lastIndexOf("/") + 1);
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
// get webcam on load
|
|
|
|
VideoChat.requestMediaStream();
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
$("#remote-video-text").text("").fadeOut();
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
$("#moveable").draggable({ containment: "window" });
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
$(".HoverState").hide();
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
$("#entire-chat").hide();
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
//
|
|
|
|
// Show hide button labels on hover
|
|
|
|
//
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
$(document).ready(function () {
|
|
|
|
$(".hoverButton").mouseover(function () {
|
|
|
|
$(".HoverState").hide();
|
|
|
|
$(this).next().show();
|
2020-03-30 12:03:11 +08:00
|
|
|
});
|
2020-03-31 02:16:08 +08:00
|
|
|
$(".hoverButton").mouseout(function () {
|
|
|
|
$(".HoverState").hide();
|
|
|
|
});
|
|
|
|
});
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
//
|
|
|
|
// End show hide button labels on hover
|
|
|
|
//
|
2020-03-30 09:46:29 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
//
|
|
|
|
// Fade out / show UI on mouse move
|
|
|
|
//
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
var timedelay = 1;
|
2020-03-30 12:03:11 +08:00
|
|
|
|
2020-03-31 02:16:08 +08:00
|
|
|
function delayCheck() {
|
|
|
|
if (timedelay === 5) {
|
|
|
|
$(".multi-button").fadeOut();
|
|
|
|
$("#header").fadeOut();
|
|
|
|
timedelay = 1;
|
2020-03-30 12:03:11 +08:00
|
|
|
}
|
2020-03-31 02:16:08 +08:00
|
|
|
timedelay = timedelay + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
$(document).mousemove(function () {
|
|
|
|
$(".multi-button").fadeIn();
|
|
|
|
$("#header").fadeIn();
|
|
|
|
timedelay = 1;
|
|
|
|
clearInterval(_delay);
|
2020-03-30 12:03:11 +08:00
|
|
|
_delay = setInterval(delayCheck, 500);
|
2020-03-31 02:16:08 +08:00
|
|
|
});
|
|
|
|
_delay = setInterval(delayCheck, 500);
|
|
|
|
|
|
|
|
//
|
|
|
|
// End Fade out / show UI on mouse move
|
|
|
|
//
|
|
|
|
|
|
|
|
// Show accept webcam snackbar
|
|
|
|
Snackbar.show({
|
|
|
|
text: "Please allow microphone and webcam access",
|
|
|
|
actionText: "Show Me How",
|
|
|
|
width: "455px",
|
|
|
|
pos: "top-right",
|
|
|
|
actionTextColor: "#8688ff",
|
|
|
|
duration: 50000,
|
|
|
|
backgroundColor: "#292B32",
|
|
|
|
onActionClick: function (element) {
|
|
|
|
window.open(
|
|
|
|
"https://getacclaim.zendesk.com/hc/en-us/articles/360001547832-Setting-the-default-camera-on-your-browser",
|
|
|
|
"_blank"
|
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
2020-03-30 12:03:11 +08:00
|
|
|
}
|
2020-03-30 02:59:59 +08:00
|
|
|
|
2020-03-30 12:03:11 +08:00
|
|
|
startUp();
|