From a95cc4bc4ef4cf009fe5794a6d5dff95694d9d61 Mon Sep 17 00:00:00 2001 From: steveseguin Date: Mon, 14 Nov 2022 17:24:40 -0500 Subject: [PATCH] buffer audio fix --- index.html | 6 +- lib.js | 101 +++++++++-- main.js | 33 +++- mixer.html | 486 ++++++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 500 insertions(+), 126 deletions(-) diff --git a/index.html b/index.html index 4734e89..3723b5f 100644 --- a/index.html +++ b/index.html @@ -949,7 +949,7 @@ Guests hear others @@ -2296,11 +2296,11 @@ // session.darkmode = false; // enable or disable the dark style theme as the default // session.defaultBackgroundImages = ["./media/bg_sample1.webp", "./media/bg_sample2.webp"]; // for &effects=5 (virtual backgrounds) - + - + diff --git a/lib.js b/lib.js index ea721de..be27e44 100644 --- a/lib.js +++ b/lib.js @@ -1571,7 +1571,7 @@ function manageSceneState(data, UUID){ // incoming obs details } else { controlButton.onclick = async function(){ var msg = {}; - msg.obsCommand = {} + msg.obsCommand = {}; msg.obsCommand.action = this.dataset.obsAction; msg.UUID = this.dataset.UUID; if (document.querySelector("#obsRemotePassword>input").value){ @@ -2122,6 +2122,17 @@ function getStorage(cname) { function play(streamid=null, UUID=false){ // play whatever is in the URL params; or filter by a streamID option log("play stream: "+session.view+ " " +streamid); + + if (session.viewDirectorOnly){ + if (!(UUID || streamid)){ + warnlog("No UUID and StreamID"); + return; + } else if (session.directorList.indexOf(UUID)==-1){ + warnlog("Not a director"); + return; + } + } + if (session.view_set){ var played = false; for (var j in session.view_set){ @@ -4298,11 +4309,11 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a // ANIMATED - CONTAINER ; width/height/z-index/cover/////////////// if (layout){ - var left = (w/100*layout[vid.dataset.sid].x) || 0; - var top = (h/100*layout[vid.dataset.sid].y) || 0; + var left = (w/100*layout[vid.dataset.sid].x) || layout[vid.dataset.sid].xp || 0; + var top = (h/100*layout[vid.dataset.sid].y) || layout[vid.dataset.sid].yp || 0; top+=hi; - var width = (w/100*layout[vid.dataset.sid].w) || 0; - var height = (h/100*layout[vid.dataset.sid].h) || 0; + var width = (w/100*layout[vid.dataset.sid].w) || layout[vid.dataset.sid].wp || 0; + var height = (h/100*layout[vid.dataset.sid].h) || layout[vid.dataset.sid].hp || 0; if (layout[vid.dataset.sid].cover || layout[vid.dataset.sid].c){ // this should be true/false vid.style.objectFit = "cover"; cover = true; @@ -8060,11 +8071,13 @@ function playoutdelay(UUID){ // applies a delay to all videos receiver.playoutDelayHint = parseFloat(sync_offset/1000); var audio_delay = session.sync || 0; // video is typically showing greater delay than video - if (receiver.track.id in session.rpcs[UUID].delayNode){ - log("session.sync audio delay"); - if (audio_delay<0){audio_delay=0;} - session.rpcs[UUID].delayNode[receiver.track.id].delayTime.setValueAtTime(parseFloat(audio_delay/1000.0), session.audioCtx.currentTime+1); - session.rpcs[UUID].stats[tid].Audio_Sync_Delay_ms = audio_delay; + audio_delay += target_buffer - session.rpcs[UUID].stats[tid].Buffer_Delay_in_ms + if (receiver.track.id in session.rpcs[UUID].inboundAudioPipeline){ + if (session.rpcs[UUID].inboundAudioPipeline[receiver.track.id] && session.rpcs[UUID].inboundAudioPipeline[receiver.track.id].delayNode){ + if (audio_delay<0){audio_delay=0;} + session.rpcs[UUID].inboundAudioPipeline[receiver.track.id].delayNode.delayTime.setValueAtTime(parseFloat(audio_delay/1000.0), session.audioCtx.currentTime+1); + session.rpcs[UUID].stats[tid].Audio_Sync_Delay_ms = audio_delay; + } } } else if (session.rpcs[UUID].stats[tid]._type=="video"){ if(sync_offset<0){sync_offset=0;} @@ -10607,6 +10620,60 @@ function issueLayout(layout=false, scene=false, UUID=false) { // A directing roo } } +async function issueLayoutOBS(data) { // A directing room only is controlled by the Director, with the exception of MUTE. + + var layout = data.layout || false; + var scene = data.scene || false; + var UUID = data.UUID || false; + var obsCommand = data.obsCommand || false; + + log("issueLayoutOBS() called"); + var msg = {}; + msg.layout = layout; + msg.obsCommand = obsCommand; + + if (data.remote){ + msg.remote = data.remote; + } else { + msg.remote = session.remote || true; + } + msg = await session.encodeRemote(msg); + + if (UUID){ + + try { + log("CONTROL STATE" + session.pcs[UUID].obsState.details.controlLevel); + } catch(e){ + } + + if (session.pcs[UUID] && (scene!==false) && (session.pcs[UUID].scene===(scene+""))){ + if (!session.pcs[UUID].solo){ + session.sendMessage(msg, UUID); + } + } else if (session.pcs[UUID] && session.pcs[UUID].layout){ + session.sendMessage(msg, UUID); + log("broadcast"); + } + } else { + for (var uuid in session.pcs){ + + try { + log("CONTROL STATE" + session.pcs[UUID].obsState.details.controlLevel); + } catch(e){ + } + + if ((scene!==false) && (session.pcs[uuid].scene===(scene+""))){ + if (!session.pcs[uuid].solo){ + session.sendMessage(msg, uuid); + } + } else if (session.pcs[uuid].layout){ + session.sendMessage(msg, uuid); + log("broadcast"); + } + } + } +} + var previousURL = ""; var stillNeedURL = true; var reloadCancelled = false; @@ -11238,7 +11305,10 @@ function loadDirectorSettings(){ directorLinks1.querySelector('[data-param="'+key+'"]').checked = settings.directorLinks1[key]; directorLinks1.querySelector('[data-param="'+key+'"]').onchange(); } - } catch(e){errorlog(e);} + } catch(e){ + errorlog("key :"+key); + errorlog(e); + } }); } @@ -11250,7 +11320,10 @@ function loadDirectorSettings(){ directorLinks2.querySelector('[data-param="'+key+'"]').checked = settings.directorLinks2[key]; directorLinks2.querySelector('[data-param="'+key+'"]').onchange(); } - } catch(e){errorlog(e);} + } catch(e){ + errorlog("key :"+key); + errorlog(e); + } }); } } @@ -27578,12 +27651,13 @@ function addAudioPipeline(UUID, track){ // INBOUND AUDIO EFFECTS delete session.rpcs[UUID].inboundAudioPipeline[tid]; // get rid of old nodes. } var trackid = track.id; + session.rpcs[UUID].inboundAudioPipeline[trackid] = {}; session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream = createMediaStream(); session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream.addTrack(track); - if (ChromeVersion && (ChromeVersion<106)){ // I'm going to deprecate this. + if (ChromeVersion){ // I'm going to deprecate this. -> re investigate this session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio = createAudioElement(); // TODO: I don't know if this mutedAudio thing matters any more, in recent versions of Chrome, since it won't play even if muted. session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.muted = true; session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.playsinline = true; // ## Added Oct 9th 2022. Not sure it's does anything, but might help with iPhones? @@ -27659,6 +27733,7 @@ function addAudioPipeline(UUID, track){ // INBOUND AUDIO EFFECTS } if (screwedUp){ + if (session.rpcs[UUID].inboundAudioPipeline[trackid].destination===false){ session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); } diff --git a/main.js b/main.js index a189a17..f075e11 100644 --- a/main.js +++ b/main.js @@ -1177,6 +1177,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } else { session.password = decodeURIComponent(session.password); // will be re-encoded in a moment. } + } else if (urlParams.has('nopassword') || urlParams.has('nopass') || urlParams.has('nopw')) { + session.password = false; + session.defaultPassword = false; } if (session.password) { @@ -1646,6 +1649,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } } + if (urlParams.has('directoronly') || urlParams.has('do')){ + session.viewDirectorOnly = true; + } + + if (session.view!==false) { session.view_set = session.view.split(","); } @@ -4653,6 +4661,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } // don't use if the stream is in your room (as not needed) } // you can load a stream ID from inside a room that exists outside any room + if ("previewMode" in e.data){ if ("layout" in e.data){ session.layout = e.data.layout; @@ -4663,20 +4672,28 @@ async function main(){ // main asyncronous thread; mostly initializes the user s warnlog("changing layout request via IFRAME API"); session.layout = e.data.layout; pokeIframeAPI("layout-updated", session.layout); - if (session.director){ - if ("scene" in e.data){ - if ("UUID" in e.data){ - issueLayout(e.data.layout, e.data.scene, e.data.UUID); - } else { - issueLayout(e.data.layout, e.data.scene); + + if (e.data.obsCommand){ + issueLayoutOBS(e.data); + } else { + if (session.director){ + if ("scene" in e.data){ + if ("UUID" in e.data){ + issueLayout(e.data.layout, e.data.scene, e.data.UUID); + } else { + issueLayout(e.data.layout, e.data.scene); + } + } else if ("UUID" in e.data){ + issueLayout(e.data.layout, false, e.data.UUID); } - } else if ("UUID" in e.data){ - issueLayout(e.data.layout, false, e.data.UUID); } } updateMixer(); + } else if (e.data.obsCommand){ + errorlog("obsCommand via iframe API currently needs a layout.."); } + if ("slotmode" in e.data){ if (session.slotmode){ session.slotmode = parseInt(e.data.slotmode); diff --git a/mixer.html b/mixer.html index 487eb61..9acadb9 100644 --- a/mixer.html +++ b/mixer.html @@ -18,6 +18,8 @@ :root{ --aspect-ratio: 1.7777777777; --chat-width: 450px; + --iframe-width: 1280px; + --iframe-height: 720px; } body { @@ -38,7 +40,7 @@ display: block; height: 100%; - width: 1280px; + width: var(--iframe-width); max-height: calc(100vh - 80px); @@ -51,10 +53,10 @@ } iframe.aspectRatio{ - max-height: min(calc(100vh - 80px), calc(100vw - 160px - var(--chat-width)) / var(--aspect-ratio))) !important; + max-height: min(calc(100vh - 80px), calc(100vw - 160px - var(--chat-width)) / var(--aspect-ratio)) !important; max-width: min(calc((100vh - 80px) * var(--aspect-ratio)), calc(100vw - 160px - var(--chat-width))) !important; - height: 720px; - width: 1280px; + height: var(--iframe-height) !important; + width: var(--iframe-width) !important; } .gone { position:absolute; @@ -121,6 +123,9 @@ font-family: Verdana; line-height: 14px; } + #sceneSettings{ + font-size: 80%; + } #chatModule { bottom: 0; position: fixed; @@ -423,15 +428,17 @@ } #canvas{ background-color: #000; - width: 1280px; - height: 720px; + width: var(--iframe-width); + height: var(--iframe-height); margin:0; padding:0; border:0; display: inline-block; } - + h3 { + margin-bottom: 6px; + } .settings { display: block; @@ -844,7 +851,8 @@ - + + @@ -913,27 +921,37 @@ @@ -993,12 +1011,12 @@ })(window); + function errorlog(e,a=null,b=null){ + console.error(e); + } function warnlog(msg){ console.warn(msg); } - function errorlog(msg){ - console.error(msg); - } function log(msg){ console.log(msg); } @@ -1042,7 +1060,42 @@ return roomid; } - + var CtrlPressed = false; + document.addEventListener("keydown", event => { + if ((event.ctrlKey) || (event.metaKey)) { // detect if CTRL is pressed + if (!CtrlPressed){ + CtrlPressed = true; + $(function(){ + // $(".draggable").unbind(); + $(".draggable").draggable({ snap: false , grid: [ 1,1 ] }); + }); + $(function(){ + //$(".resizable").unbind(); + $(".resizable").resizable({ snap: false , grid: [ 1,1 ] }); + }); + //errorlog("CtrlPressed :"+CtrlPressed); + } + } + }); + + document.addEventListener("keyup", event => { + if ((event.ctrlKey) || (event.metaKey)) { // detect if CTRL is pressed + // + } else if (CtrlPressed){ + CtrlPressed = false; + $(function(){ + // $(".draggable").unbind(); + $(".draggable").draggable({ snap: true , grid: [ 10,10 ] }); + }); + $(function(){ + //$(".resizable").unbind(); + $(".resizable").resizable({ snap: true , grid: [ 10, 10] }); + }); + //errorlog("CtrlPressed :"+CtrlPressed); + } + }); + + var urlEdited = window.location.search.replace(/\?\?/g, "?"); urlEdited = urlEdited.replace(/\?/g, "&"); urlEdited = urlEdited.replace(/\&/, "?"); @@ -1070,13 +1123,24 @@ var toggleBroadcast = true; var messageList = []; var password = false; + var syncOBS = false; if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) { password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p'); } var aspectRatio = 16/9.0; + var pixelDensity = 720; document.documentElement.style.setProperty('--aspect-ratio', aspectRatio); + var absolutePixel = false; + var advancedMode = false; + var hh = pixelDensity; + var ww = parseInt((pixelDensity*16/9) * (aspectRatio/(16/9))); + + document.documentElement.style.setProperty('--iframe-width', ww); + document.documentElement.style.setProperty('--iframe-height',hh); + + var roomname = false; if (urlParams.has("room") || urlParams.has("r") ||urlParams.has("dir") || urlParams.has("director")){ roomname = urlParams.get("room") || urlParams.get("r") ||urlParams.get("dir") || urlParams.get("director"); @@ -1154,7 +1218,6 @@ var msg = document.getElementById('chatInput').value; msg = sanitize(msg); if (msg==""){return;} - console.log(msg); iframe.contentWindow.postMessage({ sendChat: msg }, "*"); document.getElementById('chatInput').value = ""; @@ -1263,9 +1326,7 @@ //chatUpdateTimeout = setTimeout(function(){updateMessages()},60000); } - function errorlog(e,a=null,b=null){ - console.error(e); - } + var currentLayout = {}; @@ -1332,9 +1393,6 @@ ev.preventDefault(); var data = ev.dataTransfer.getData("text"); var origThing = document.getElementById(data); - console.log(origThing); - console.log(data); - console.log(ev); ev.target.style.border = ""; origThing.style.border = ""; @@ -1356,8 +1414,6 @@ ev.target.parentNode.insertBefore(origThing, ev.target.nextSibling); - console.warn(origThing); - if (origThing.dataset.slot && (origThing.dataset.slot!=="undefined")){ document.querySelectorAll("[data-slot='"+origThing.dataset.slot+"']").forEach(ele=>{ele.style.display = "block";}) document.querySelectorAll("[data-slot='"+origThing.dataset.slot+"']").forEach(ele=>{ele.classList.remove("hidden");}) @@ -1455,12 +1511,9 @@ thing.classList.add("thing"); thing.ondblclick = function(ev){ var origThing = ev.target; - console.log(ev); if (origThing.parentNode.id == "col1"){return;} ev.preventDefault(); var target = document.querySelector("[data-slot][class='empty']:not([class='hidden'])"); - console.log(origThing); - console.log(target); origThing.style.border = ""; var eles = document.querySelectorAll(".thing"); @@ -1489,8 +1542,6 @@ if (target.dataset.slot=="undefined"){return;} target.parentNode.insertBefore(origThing, target.nextSibling); - console.warn(origThing); - if (origThing.dataset.slot && (origThing.dataset.slot!=="undefined")){ document.querySelector("[data-slot='"+origThing.dataset.slot+"']").style.display = "block"; document.querySelector("[data-slot='"+origThing.dataset.slot+"']").classList.remove("hidden"); @@ -1654,6 +1705,103 @@ getById("toggleBroadcast").checked = true; } } + if (savedSession.settings && ("syncOBS" in savedSession.settings)){ + syncOBS = savedSession.settings.syncOBS; + if (!syncOBS){ + getById("syncOBS").value = "off"; + getById("syncOBS").checked = false; + getById("syncOBS").removeAttribute('checked'); + } else { + getById("syncOBS").value = "on"; + getById("syncOBS").checked = true; + } + } + if (savedSession.settings && ("aspectRatio" in savedSession.settings)){ + aspectRatio = savedSession.settings.aspectRatio; + document.documentElement.style.setProperty('--aspect-ratio', aspectRatio); + + document.querySelectorAll(".aspectbutton").forEach(ele=>{ + if ((ele.dataset.value == "169") && (aspectRatio == 16/9.0)){ + ele.checked = true; + ele.value = true; + } else if (ele.dataset.value == aspectRatio){ + ele.checked = true; + ele.value = true; + } else { + ele.checked = false; + ele.value = false; + } + }); + } + if (savedSession.settings && ("advancedMode" in savedSession.settings)){ + advancedMode = savedSession.settings.advancedMode; + if (!advancedMode){ + getById("advancedMode").value = "off"; + getById("advancedMode").checked = false; + getById("advancedMode").removeAttribute('checked'); + } else { + getById("advancedMode").value = "on"; + getById("advancedMode").checked = true; + } + if (iframe && iframe.contentWindow){ + iframe.contentWindow.postMessage({"advancedMode":advancedMode}, '*'); + } + } + if (savedSession.settings && ("absolutePixel" in savedSession.settings)){ + absolutePixel = savedSession.settings.absolutePixel; + + document.querySelectorAll(".absolutePosition").forEach(ele=>{ + + if (ele.dataset.value == "true"){ + if (true == absolutePixel){ + ele.checked = true; + ele.value = true; + } else { + ele.checked = false; + ele.value = false; + } + } else { + if (false == absolutePixel){ + ele.checked = true; + ele.value = true; + } else { + ele.checked = false; + ele.value = false; + } + } + + }); + } + + + if (savedSession.settings && ("pixelDensity" in savedSession.settings)){ + pixelDensity = savedSession.settings.pixelDensity; + if (pixelDensity==1080){ + + document.querySelectorAll(".pixeldensity").forEach(ele=>{ + if (ele.dataset.value == pixelDensity){ + ele.checked = true; + ele.value = true; + } else { + ele.checked = false; + ele.value = false; + } + }); + + var hh = pixelDensity; + var ww = parseInt((pixelDensity*16/9) * (aspectRatio/(16/9))); + + document.documentElement.style.setProperty('--aspect-ratio', aspectRatio); + document.documentElement.style.setProperty('--iframe-width', ww+"px"); + document.documentElement.style.setProperty('--iframe-height', hh+"px"); + + + + // getById("syncOBS").value = "off"; + // getById("syncOBS").checked = false; + // getById("syncOBS").removeAttribute('checked'); + } + } } var savedSession = getStorage("savedSession"); @@ -1690,21 +1838,33 @@ assignSlotToGuest=false iframe.contentWindow.postMessage({"slotmode":2}, '*');; } - + saveSession(); + } + + function submitChange3(element){ + if (element.checked){ + syncOBS=true; + } else { // do not assign guests to slots automatically + element.removeAttribute('checked'); + syncOBS=false + } saveSession(); } function toggleAdvanced(element){ if (element.checked){ iframe.contentWindow.postMessage({"advancedMode":true}, '*'); + advancedMode = true; } else { element.removeAttribute('checked'); iframe.contentWindow.postMessage({"advancedMode":false}, '*'); + advancedMode = false; } + saveSession(); } function exportSession() { - var content = JSON.stringify(savedSession); + var content = JSON.stringify(savedSession,null,2); var fileName = roomname + ".json"; var a = document.createElement("a"); var file = new Blob([content], {type: 'text/plain'}); @@ -1714,10 +1874,9 @@ } function importSession(event){ - var reader = new FileReader(); reader.onload = function(event){ - console.log(event.target.result); + log(event.target.result); try { var obj = JSON.parse(event.target.result); } catch(e){ @@ -1731,8 +1890,13 @@ document.getElementById("containermenu").innerHTML = ""; drawAddNewLayout2(); drawAddNewLayout3(); + for (var i in savedSession.layouts){ - drawLayout(savedSession.layouts[i]); + if (savedSession.obsScenes && savedSession.obsScenes[i]){ + drawLayout(savedSession.layouts[i], false, savedSession.obsScenes[i]); + } else { + drawLayout(savedSession.layouts[i]); + } } alert("The saved session has been imported and loaded."); } else { @@ -1752,6 +1916,7 @@ drawAddNewLayout2(); drawAddNewLayout3(); + savedSession.obsScenes = []; savedSession.layouts = initialLayouts.layouts; for (var i in savedSession.layouts){ @@ -1760,6 +1925,29 @@ saveSession(); } + function updateLayouts(){ + document.getElementById("containermenu").innerHTML = ""; + + var slots = document.getElementById("col1").children; + for (var i=0;i{ @@ -3134,7 +3369,10 @@ } catch(e){} } else { - drawLayout(scene, sceneName); + var sceneName = document.getElementById("canvas").sceneName || parseInt(Math.random()*1000000000); + var obsSceneName = document.getElementById("canvas").obsSceneName || ""; + console.log("'sceneName: "+sceneName); + drawLayout(scene, sceneName, obsSceneName); popupMessage(event, "Scene saved"); } @@ -3166,12 +3404,22 @@ savedSession.settings.assignSlotToGuest = assignSlotToGuest; savedSession.settings.toggleLabel = toggleLabel; savedSession.settings.toggleBroadcast = toggleBroadcast; + savedSession.settings.syncOBS = syncOBS; + savedSession.settings.aspectRatio = aspectRatio; + savedSession.settings.pixelDensity = pixelDensity; + savedSession.settings.absolutePixel = absolutePixel; + + savedSession.settings.advancedMode = advancedMode; + savedSession.version = 1; + savedSession.obsScenes = []; for (var i=0;i{ + if (ele == button){return;} + ele.checked = false; + ele.value = false; + }); + absolutePixel = value; + saveSession(); + updateLayouts(); + } + + function changePixelDensity(pd, button){ + document.querySelectorAll(".pixeldensity").forEach(ele=>{ + if (ele == button){return;} + ele.checked = false; + ele.value = false; + }); + pixelDensity = pd; + saveSession(); + + var hh = pixelDensity; + var ww = parseInt((pixelDensity*16/9) * (aspectRatio/(16/9))); + + document.documentElement.style.setProperty('--aspect-ratio', aspectRatio); + document.documentElement.style.setProperty('--iframe-width', ww+"px"); + document.documentElement.style.setProperty('--iframe-height', hh+"px"); + + updateLayouts(); + //document.getElementById('canvas').style.width = ww+"px"; + //document.getElementById('canvas').style.height = hh+"px"; + + } + //let modal = document.querySelector("#modal"); document.querySelectorAll(".close-btn").forEach(ele2=>{ ele2.onclick = function(){