diff --git a/public/css/chat.css b/public/css/chat.css index 1010186..efb74ab 100644 --- a/public/css/chat.css +++ b/public/css/chat.css @@ -147,8 +147,8 @@ a { padding: 10px; } #remote-video:first-child:nth-last-child(1) { -/* -or- li:only-child { */ -width: 80vw; + width: min(calc(80vh * 4/3), 80vw); + max-height: 80vh; } /* two items */ diff --git a/public/js/chat.js b/public/js/chat.js index 145d310..3404d46 100644 --- a/public/js/chat.js +++ b/public/js/chat.js @@ -204,7 +204,9 @@ var VideoChat = { break; case "disconnected": logIt("disconnected - UUID " + uuid); - VideoChat.remoteVideoWrapper.removeChild(document.querySelectorAll(`[uuid="${uuid}"]`)[0]); + VideoChat.remoteVideoWrapper.removeChild( + document.querySelectorAll(`[uuid="${uuid}"]`)[0] + ); VideoChat.connected.delete(uuid); VideoChat.peerConnections.delete(uuid); dataChannel.delete(uuid); @@ -361,6 +363,12 @@ 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); @@ -499,6 +507,32 @@ 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() { var audioTrack = null; @@ -511,7 +545,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"); @@ -863,15 +897,16 @@ function uuidToColor(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); - console.log(hue); // 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: 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) { for (var i = 0; i < availColors.length; i++) { if (availColors[i] != null) { hue = (hue % 20) + availColors[i]; @@ -886,7 +921,9 @@ function uuidToColor(uuid) { // 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}`; + document.querySelectorAll( + `[uuid="${uuid}"]` + )[0].style.border = `3px solid ${color}`; VideoChat.peerColors[uuid] = color; } @@ -914,25 +951,34 @@ function toggleChat() { function togglePictureInPicture() { if ( "pictureInPictureEnabled" in document || - remoteVideoVanilla.webkitSetPresentationMode + VideoChat.remoteVideoWrapper.lastChild.webkitSetPresentationMode ) { if (document.pictureInPictureElement) { document.exitPictureInPicture().catch((error) => { logIt("Error exiting pip."); logIt(error); }); - } else if (remoteVideoVanilla.webkitPresentationMode === "inline") { - remoteVideoVanilla.webkitSetPresentationMode("picture-in-picture"); } else if ( - remoteVideoVanilla.webkitPresentationMode === "picture-in-picture" + VideoChat.remoteVideoWrapper.lastChild.webkitPresentationMode === "inline" ) { - remoteVideoVanilla.webkitSetPresentationMode("inline"); + VideoChat.remoteVideoWrapper.lastChild.webkitSetPresentationMode( + "picture-in-picture" + ); + } else if ( + VideoChat.remoteVideoWrapper.lastChild.webkitPresentationMode === + "picture-in-picture" + ) { + VideoChat.remoteVideoWrapper.lastChild.webkitSetPresentationMode( + "inline" + ); } else { - remoteVideoVanilla.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( diff --git a/public/landing.html b/public/landing.html index 7e1b62d..693a094 100755 --- a/public/landing.html +++ b/public/landing.html @@ -67,9 +67,9 @@ class="mt-0 mb-32 reveal-from-bottom" data-reveal-delay="300" > - Simple, Secure, and Fast. Peer to peer group video - calling provides quality and latency simply not - available with traditional technology. + Simple, Secure, and Fast. Peer to peer group video calling + provides quality and latency simply not available with + traditional technology.
@@ -248,8 +247,8 @@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. + and end to end state of the art encryption means your + calls are exactly that. Your calls.