From f78030c48224384081afb6c06af68b37b418b5b2 Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Tue, 5 May 2020 17:50:16 +0200 Subject: [PATCH] Initial implementation of mike and camera selection. We're closing and reopening the connection each time, we should be renegotiating instead. --- static/sfu.css | 17 +++++++ static/sfu.html | 14 ++++++ static/sfu.js | 130 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 137 insertions(+), 24 deletions(-) diff --git a/static/sfu.css b/static/sfu.css index 4c1a88c..15433d9 100644 --- a/static/sfu.css +++ b/static/sfu.css @@ -49,6 +49,22 @@ h1 { display: none; } +#optionsdiv { + margin-bottom: 4px; +} + +#videoselect { + width: 8em; + text-align-last: center; + margin-right: 0.4em; +} + +#audioselect { + width: 8em; + text-align-last: center; + margin-right: 0.4em; +} + #main { display: flex; } @@ -111,6 +127,7 @@ h1 { } #input { + margin-left: 0.5em; width: 100%; border: none; resize: none; diff --git a/static/sfu.html b/static/sfu.html index af2a72e..f0c5225 100644 --- a/static/sfu.html +++ b/static/sfu.html @@ -31,8 +31,22 @@
+ + + + + + + +
diff --git a/static/sfu.js b/static/sfu.js index 8067861..35e914e 100644 --- a/static/sfu.js +++ b/static/sfu.js @@ -126,6 +126,16 @@ document.getElementById('presenterbox').onchange = function(e) { setLocalMedia(this.checked); } +document.getElementById('audioselect').onchange = function(e) { + e.preventDefault(); + setLocalMedia(document.getElementById('presenterbox').checked); +} + +document.getElementById('videoselect').onchange = function(e) { + e.preventDefault(); + setLocalMedia(document.getElementById('presenterbox').checked); +} + document.getElementById('sharebox').onchange = function(e) { e.preventDefault(); setShareMedia(this.checked); @@ -193,6 +203,71 @@ function displayStats(id) { setLabel(id); } +function mapMediaOption(value) { + console.assert(typeof(value) === 'string'); + switch(value) { + case 'default': + return true; + case 'off': + return false; + default: + return value; + } +} + +function addSelectOption(select, label, value) { + if(!value) + value = label; + for(let i = 0; i < select.children.length; i++) { + if(select.children[i].value === value) { + return; + } + } + + let option = document.createElement('option'); + option.value = value; + option.textContent = label; + select.appendChild(option); +} + +// media names might not be available before we call getDisplayMedia. So +// we call this lazily. +let mediaChoicesDone = false; + +async function setMediaChoices() { + if(mediaChoicesDone) + return; + + let devices = []; + try { + devices = await navigator.mediaDevices.enumerateDevices(); + } catch(e) { + console.error(e); + return; + } + + let cn = 1, mn = 1; + + devices.forEach(d => { + let label = d.label; + if(d.kind === 'videoinput') { + if(!label) + label = `Camera ${cn}`; + addSelectOption(document.getElementById('videoselect'), + label, d.deviceId); + cn++; + } else if(d.kind === 'audioinput') { + if(!label) + label = `Microphone ${mn}`; + addSelectOption(document.getElementById('audioselect'), + label, d.deviceId); + mn++; + } + }); + + mediaChoicesDone = true; +} + let localMediaId = null; async function setLocalMedia(setup) { @@ -209,32 +284,39 @@ async function setLocalMedia(setup) { return; } - if(!localMediaId) { - let constraints = {audio: true, video: true}; - let stream = null; - try { - stream = await navigator.mediaDevices.getUserMedia(constraints); - } catch(e) { - console.error(e); - document.getElementById('presenterbox').checked = false; - await setLocalMedia(false); - return; - } - localMediaId = await newUpStream(); + let audio = mapMediaOption(document.getElementById('audioselect').value); + let video = mapMediaOption(document.getElementById('videoselect').value); - let c = up[localMediaId]; - c.stream = stream; - stream.getTracks().forEach(t => { - let sender = c.pc.addTrack(t, stream); - c.setInterval(() => { - updateStats(c, sender); - }, 2000); - }); - c.setInterval(() => { - displayStats(localMediaId); - }, 2500); - await setMedia(localMediaId); + setLocalMedia(false); + if(!audio && !video) + return; + + let constraints = {audio: audio, video: video}; + let stream = null; + try { + stream = await navigator.mediaDevices.getUserMedia(constraints); + } catch(e) { + console.error(e); + document.getElementById('presenterbox').checked = false; + await setLocalMedia(false); + return; } + + setMediaChoices(); + + localMediaId = await newUpStream(); + let c = up[localMediaId]; + c.stream = stream; + stream.getTracks().forEach(t => { + let sender = c.pc.addTrack(t, stream); + c.setInterval(() => { + updateStats(c, sender); + }, 2000); + }); + c.setInterval(() => { + displayStats(localMediaId); + }, 2500); + await setMedia(localMediaId); } let shareMediaId = null;