";
}
}
}
} catch (e) {
errorlog(e);
}
//if (session.optimize){
if (navigator.userAgent.indexOf('Mac OS X') != -1) {
session.codec = "h264"; // default the codec to h264 if OBS and macOS
}
//}
window.addEventListener('obsSceneChanged', function(event) {
log("OBS EVENT");
log(event.detail.name);
window.obsstudio.getCurrentScene(function(scene) {
log("OBS SCENE");
log(scene);
});
window.obsstudio.getStatus(function(status) {
log("OBS STATUS:");
log(status);
});
});
}
window.onload = function winonLoad() { // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending
window.addEventListener("beforeunload", function(e) {
try {
session.ws.close();
if (session.videoElement.recording) {
session.videoElement.recorder.writer.close();
session.videoElement.recording = false;
}
for (var i in session.rpcs) {
if (session.rpcs[i].videoElement) {
if (session.rpcs[i].videoElement.recording) {
session.rpcs[i].videoElement.recorder.writer.close();
session.rpcs[i].videoElement.recording = false;
}
}
}
} catch (e) {}
//setTimeout(function(){session.hangup();},0);
return undefined; // ADDED OCT 29th; get rid of popup. Just close the socket connection if the user is refreshing the page. It's one or the other.
});
};
getById("credits").innerHTML = "Version: " + session.version + " - " + getById("credits").innerHTML;
var lastTouchEnd = 0;
document.addEventListener('touchend', function(event) {
var now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
document.addEventListener('click', function(event) {
if (session.firstPlayTriggered == false) {
playAllVideos();
session.firstPlayTriggered = true;
history.pushState({}, '');
}
});
var Callbacks = [];
var CtrlPressed = false; // global
var AltPressed = false;
document.addEventListener("keydown", event => {
if ((event.ctrlKey) || (event.metaKey)) { // detect if CTRL is pressed
CtrlPressed = true;
} else {
CtrlPressed = false;
}
if (event.altKey) {
AltPressed = true;
} else {
AltPressed = false;
}
if (CtrlPressed && event.keyCode) {
if (event.keyCode == 77) { // m
if (event.metaKey) {
if (AltPressed) {
toggleMute(); // macOS
}
} else {
toggleMute(); // Windows
}
// } else if (event.keyCode == 69) { // e
// hangup();
} else if (event.keyCode == 66) { // b
toggleVideoMute();
}
}
});
document.addEventListener("keyup", event => {
if (!((event.ctrlKey) || (event.metaKey))) {
if (CtrlPressed) {
CtrlPressed = false;
for (var i in Callbacks) {
var cb = Callbacks[i];
log(cb.slice(1));
cb[0](...cb.slice(1)); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#A_better_apply
}
Callbacks = [];
}
}
if (!(event.altKey)) {
AltPressed = false;
}
});
window.onpopstate = function() {
if (session.firstPlayTriggered) {
window.location.reload(true);
}
};
if (typeof session === 'undefined') { // make sure to init the WebRTC if not exists.
var session = WebRTC.Media;
session.streamID = session.generateStreamID();
errorlog("Serious error: WebRTC session didn't load in time");
}
function makeDraggableElement(elmnt) {
try {
elmnt.dragElement = false;
elmnt.style.bottom = "auto";
elmnt.style.cursor = "grab";
elmnt.stashonmouseup = null;
elmnt.stashonmousemove = null;
} catch (e) {
errorlog(e);
return;
}
var pos1 = 0;
var pos2 = 0;
var pos3 = 0;
var pos4 = 0;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.stashonmouseup = document.onmouseup; // I don't want to interfere with other drag events.
elmnt.stashonmousemove = document.onmousemove;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
elmnt.dragElement = true;
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
var topDrag = (elmnt.offsetTop - pos2);
if (topDrag > -3) {
topDrag = -3;
}
elmnt.style.top = topDrag + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
elmnt.onmousedown = dragMouseDown;
function closeDragElement() {
document.onmouseup = elmnt.stashonmouseup;
document.onmousemove = elmnt.stashonmousemove;
}
}
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
if (getCookie("redirect") == "yes") {
setCookie("redirect", "", 0);
session.sticky = true;
} else if (getCookie("settings") != "") {
session.sticky = confirm("Would you like you load your previous session's settings?");
if (!session.sticky) {
setCookie("settings", "", 0);
log("deleting cookie as user said no");
} else {
var cookieSettings = decodeURI(getCookie("settings"));
setCookie("redirect", "yes", 1);
window.location.replace(cookieSettings);
}
}
if (urlParams.has('sticky')) {
if (getCookie("permission") == "") {
session.sticky = confirm("Would you allow us to store a cookie to keep your session settings persistent?");
} else {
session.sticky = true;
}
if (session.sticky) {
setCookie("permission", "yes", 999);
setCookie("settings", encodeURI(window.location.href), 90);
}
}
if (urlParams.has('retrytimeout')) {
session.retryTimeout = parseInt(urlParams.get('retrytimeout'));
}
if (urlParams.has('ptz')){
session.ptz=true;
}
var screensharebutton = true;
var screensharesupport = true;
if (urlParams.has('nosettings') || urlParams.has('ns')) {
screensharebutton = false;
session.showSettings = false;
}
if (urlParams.has('nomicbutton') || urlParams.has('nmb')) {
getById("mutebutton").setAttribute('style', "display: none !important");
}
if (urlParams.has('novideobutton') || urlParams.has('nvb')) {
getById("mutevideobutton").setAttribute('style', "display: none !important");
}
if (urlParams.has('screenshareid') || urlParams.has('ssid')) {
if (urlParams.get('screenshareid') || urlParams.get('ssid')) {
session.screenshareid = urlParams.get('screenshareid') || urlParams.get('ssid');
session.screenshareid = sanitizeStreamID(session.screenshareid);
}
}
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
//session.webcamonly = true;
getById("shareScreenGear").style.display = "none";
screensharebutton = false;
screensharesupport = false;
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
getById("dropButton").style.display = "none";
//session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers?
session.audioEffects = false; // disable audio inbound effects also.
session.audioMeterGuest = false;
} else if ((iOS) || (iPad)) {
getById("shareScreenGear").style.display = "none";
screensharebutton = false;
screensharesupport = false;
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
getById("dropButton").style.display = "none";
//session.audiobitrate = false; // iOS devices seem to get distortion with custom audio bitrates. Disable for now.
//session.maxiosbitrate = 10; // this is 10-kbps by default already.
//session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers?
session.audioEffects = false; // disable audio inbound effects also.
session.audioMeterGuest = false;
} else {
log("MAKE DRAGGABLE");
setTimeout(function(){makeDraggableElement(document.getElementById("subControlButtons"));},100);
if (safariVersion() && !getChromeVersion()){
getById("SafariWarning").style.display = "block";
}
}
if ((iOS) || (iPad)) {
window.addEventListener('resize', function() { // Safari is the new IE.
var msg = {};
msg.requestSceneUpdate = true;
session.sendMessage(msg);
if ( window.matchMedia("(orientation: portrait)").matches ) {
document.getElementsByTagName("html")[0].style.height = "100vh";
setTimeout(function(){
document.getElementsByTagName("html")[0].style.height = "100%";
}, 1000);
} else if ( window.matchMedia("(orientation: landscape)").matches ) {
document.getElementsByTagName("html")[0].style.height = "100vh";
setTimeout(function(){
document.getElementsByTagName("html")[0].style.height = "100%";
}, 1000);
}
});
}
if (/CriOS/i.test(navigator.userAgent) && (iOS || iPad)) {
if (!(session.cleanOutput)) {
try {
navigator.mediaDevices.getUserMedia;
} catch (e) {
warnUser("Chrome on this device does not support the required technology to use this site.\n\nPlease use Safari instead or update your iOS and browser version.");
}
}
}
if (urlParams.has('broadcast') || urlParams.has('bc')) {
log("Broadcast flag set");
session.broadcast = urlParams.get('broadcast') || urlParams.get('bc');
//if ((iOS) || (iPad)) {
// session.nopreview = false;
//} else {
// session.nopreview = true;
//}
session.minipreview = 2; // full screen if nothing else on screen.
session.style = 1;
getById("header").style.display = "none";
getById("header").style.opacity = 0;
}
var directorLanding = false;
if (urlParams.has('director') || urlParams.has('dir')) {
directorLanding = urlParams.get('director') || urlParams.get('dir');
if (directorLanding === null) {
directorLanding = true;
} else if (directorLanding.length === 0) {
directorLanding = true;
} else {
directorLanding = false;
}
} else if (filename === "director") {
directorLanding = true;
filename = false;
}
if (urlParams.has('rooms')) {
session.rooms = urlParams.get('rooms').split(",").map(function(e) {
return sanitizeRoomName(e);
});
getById("rooms").classList.remove('advanced');
}
if (urlParams.has('showdirector')) {
session.showDirector = true;
}
if (urlParams.has('midi') || urlParams.has('hotkeys')) {
session.midiHotkeys = urlParams.get('midi') || urlParams.get ('hotkeys') || 1;
session.midiHotkeys = parseInt(session.midiHotkeys);
}
var loadedQRCode = false;
function loadQR(){
if (loadedQRCode==false){
loadedQRCode=true;
var script = document.createElement('script');
script.src = "./thirdparty/qrcode.min.js"; // dynamically load this only if its needed. Keeps loading time down.
document.head.appendChild(script);
}
}
if (urlParams.has('webcam') || urlParams.has('wc')) {
session.webcamonly = true;
screensharebutton = false;
} else if (urlParams.has('screenshare') || urlParams.has('ss')) {
session.screenshare = true;
} else if (urlParams.has('fileshare') || urlParams.has('fs')) {
getById("container-5").classList.remove('advanced');
getById("container-5").classList.add("skip-animation");
getById("container-5").classList.remove('pointer');
} else if (directorLanding) {
getById("container-1").classList.remove('advanced');
getById("container-1").classList.add("skip-animation");
getById("container-1").classList.remove('pointer');
} else if (urlParams.has('website') || urlParams.has('iframe')) {
getById("container-6").classList.remove('advanced');
getById("container-6").classList.add("skip-animation");
getById("container-6").classList.remove('pointer');
}
if (urlParams.has('ssb')) {
screensharebutton = true;
}
if (urlParams.has('mute') || urlParams.has('muted') || urlParams.has('m')) {
session.muted = true;
}
if (urlParams.has('videomute') || urlParams.has('videomuted') || urlParams.has('vm')) {
session.videoMutedFlag = true;
}
if (urlParams.has('deaf') || urlParams.has('deafen')) {
session.directorSpeakerMuted=true; // false == true in this case.
}
if (urlParams.has('blind')) {
session.directorDisplayMuted=true; // false == true in this case.
}
if (urlParams.has('dpi') || urlParams.has('dpr')) {
session.devicePixelRatio = urlParams.get('dpi') || urlParams.get('dpr') || 2.0;
} //else if (window.devicePixelRatio && window.devicePixelRatio!==1){
// session.devicePixelRatio = window.devicePixelRatio; // this annoys me to no end.
//}
if (urlParams.has('speakermute') || urlParams.has('mutespeaker') || urlParams.has('sm') || urlParams.has('ms')) {
session.speakerMuted = true;
getById("mutespeakertoggle").className = "las la-volume-mute my-float toggleSize";
//getById("mutespeakerbutton").className="advanced float2 red";
getById("mutespeakerbutton").classList.add("red");
getById("mutespeakerbutton").classList.add("float2");
getById("mutespeakerbutton").classList.remove("float");
var sounds = document.getElementsByTagName("video");
for (var i = 0; i < sounds.length; ++i) {
sounds[i].muted = session.speakerMuted;
}
}
if (urlParams.has('chatbutton') || urlParams.has('chat') || urlParams.has('cb')) {
session.chatbutton = urlParams.get('chatbutton') || urlParams.get('chat') || urlParams.get('cb');
if (session.chatbutton === "false") {
session.chatbutton = false;
} else if (session.chatbutton === "0") {
session.chatbutton = false;
} else if (session.chatbutton === "no") {
session.chatbutton = false;
} else if (session.chatbutton === "off") {
session.chatbutton = false;
} else {
session.chatbutton = true;
getById("chatbutton").classList.remove("advanced");
getById("controlButtons").style.display = "inherit";
}
}
if (session.screenshare == true) {
getById("container-3").className = 'column columnfade advanced'; // Hide screen share on mobile
getById("container-2").classList.add("skip-animation");
getById("container-2").classList.remove('pointer');
}
if (urlParams.has('manual')) {
session.manual = true;
}
if (urlParams.has('hands') || urlParams.has('hand')) {
session.raisehands = true;
}
if (urlParams.has('portrait') || urlParams.has('916') || urlParams.has('vertical')) {
session.aspectratio = 1; // 9:16 (default of 0 is 16:9)
} else if (urlParams.has('square') || urlParams.has('11')) {
session.aspectratio = 2; // 9:16 (default of 0 is 16:9)
}
if (urlParams.has('record')) {
if (safariVersion()) {
if (!(session.cleanOutput)) {
warnUser("Your browser or device is not supported. Try Chrome if on macOS.");
}
} else {
session.recordLocal = urlParams.get('record');
if (session.recordLocal !== parseInt(session.recordLocal)) {
session.recordLocal = 6000;
} else {
session.recordLocal = parseInt(session.recordLocal);
}
}
}
if (urlParams.has('pcm')) {
session.pcm = true;
}
if (urlParams.has('bigbutton')) {
session.bigmutebutton = true;
getById("mutebutton").style.width = "min(40vh,40vw)";
getById("mutebutton").style.height = "min(40vh,40vw)";
getById("mutetoggle").style.width = "min(40vh,40vw)";
getById("mutetoggle").style.height = "min(40vh,40vw)";
}
if (urlParams.has('scene')) {
session.scene = parseInt(urlParams.get('scene')) || 0;
session.disableWebAudio = true;
session.audioEffects = false;
session.audioMeterGuest = false;
}
if (session.scene>1){ // scene =0 and 1 should load instantly.
session.hiddenSceneViewBitrate = 0; // By default this is ~ 400kbps, but if you have 10 scenes, i don't want to kill things.
}
if (urlParams.has('scenetype') || urlParams.has('type')) {
session.sceneType = parseInt(urlParams.get('scenetype')) || parseInt(urlParams.get('type')) || false;
}
if (urlParams.has('mediasettings')) {
session.forceMediaSettings = true;
}
if (urlParams.has('transcript') || urlParams.has('transcribe') || urlParams.has('trans')) {
session.transcript = urlParams.get('transcript') || urlParams.get('transcribe') || urlParams.get('trans') || "en-US";
}
if (urlParams.has('cc') || urlParams.has('closedcaptions') || urlParams.has('captions')) {
session.closedCaptions = true;
}
if (session.webcamonly == true) {
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
getById("container-3").classList.add("skip-animation");
getById("container-3").classList.remove('pointer');
setTimeout(function() {
previewWebcam();
}, 100);
}
if (urlParams.has('css')){
var cssURL = urlParams.get('css');
cssURL = decodeURI(cssURL);
log(cssURL);
var cssStylesheet = document.createElement('link');
cssStylesheet.rel = 'stylesheet';
cssStylesheet.type = 'text/css';
cssStylesheet.media = 'screen';
cssStylesheet.href = cssURL;
document.getElementsByTagName('head')[0].appendChild(cssStylesheet);
cssStylesheet.onload = function() {
getById("main").classList.remove('hidden');
log("loaded remote style sheet");
}
cssStylesheet.onerror = function() {
getById("main").classList.remove('hidden');
errorlog("REMOTE STYLE SHEET HAD ERROR");
}
} else {
getById("main").classList.remove('hidden');
}
if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) {
session.password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p');
if (!session.password) {
session.password = prompt("Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)");
} else if (session.password === "false") {
session.password = false;
session.defaultPassword = false;
} else if (session.password === "0") {
session.password = false;
session.defaultPassword = false;
} else if (session.password === "off") {
session.password = false;
session.defaultPassword = false;
}
}
if (session.password) {
session.password = sanitizePassword(session.password);
getById("passwordRoom").value = session.password;
session.defaultPassword = false;
getById("addPasswordBasic").style.display = "none";
}
if (urlParams.has('hash') || urlParams.has('crc') || urlParams.has('check')) { // could be brute forced in theory, so not as safe as just not using a hash check.
session.taintedSession = null; // waiting to see if valid or not.
var hash_input = urlParams.get('hash') || urlParams.get('crc') || urlParams.get('check');
if (session.password === false) {
session.password = prompt("Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)");
session.password = sanitizePassword(session.password);
getById("passwordRoom").value = session.password;
session.defaultPassword = false;
}
session.generateHash(session.password + session.salt, 6).then(function(hash) { // million to one error.
log("hash is " + hash);
if (hash.substring(0, 4) !== hash_input) { // hash crc checks are just first 4 characters.
session.taintedSession = true;
if (!(session.cleanOutput)) {
getById("request_info_prompt").innerHTML = "The password was incorrect.\n\nRefresh and try again.";
getById("request_info_prompt").style.display = "block";
getById("mainmenu").style.display = "none";
getById("head1").style.display = "none";
session.cleanOutput = true;
} else {
getById("request_info_prompt").innerHTML = "";
getById("request_info_prompt").style.display = "block";
getById("mainmenu").style.display = "none";
getById("head1").style.display = "none";
}
} else {
session.taintedSession = false;
session.hash = hash;
}
});
}
if (session.defaultPassword !== false) {
session.password = session.defaultPassword; // no user entered password; let's use the default password if its not disabled.
}
if (urlParams.has('showlabels') || urlParams.has('showlabel') || urlParams.has('sl')) {
session.showlabels = urlParams.get('showlabels') || urlParams.get('showlabel') || urlParams.get('sl') || "";
session.showlabels = sanitizeLabel(session.showlabels.replace(/[\W]+/g, "_").replace(/_+/g, '_'));
if (session.showlabels == "") {
session.showlabels = true;
session.labelstyle = false;
} else {
session.labelstyle = session.showlabels;
session.showlabels = true;
}
}
if (urlParams.has('sizelabel') || urlParams.has('labelsize') || urlParams.has('fontsize')) {
session.labelsize = urlParams.get('sizelabel') || urlParams.get('labelsize') || urlParams.get('fontsize') || 100;
session.labelsize = parseInt(session.labelsize);
}
if (urlParams.has('label') || urlParams.has('l')) {
session.label = urlParams.get('label') || urlParams.get('l');
var updateURLAsNeed = true;
if (session.label == null || session.label.length == 0) {
session.label = prompt("Please enter your display name:");
} else {
var updateURLAsNeed = false;
session.label = decodeURIComponent(session.label);
}
if (session.label != null) {
session.label = sanitizeLabel(session.label); // alphanumeric was too strict.
document.title = session.label; // what the result is.
if (updateURLAsNeed) {
var label = encodeURIComponent(session.label);
if (urlParams.has('l')) {
updateURL("l=" + label, true, false);
} else {
updateURL("label=" + label, true, false);
}
}
}
}
if (urlParams.has('transparent')) { // sets the window to be transparent - useful for IFRAMES?
getById("main").style.backgroundColor = "rgba(0,0,0,0)";
document.documentElement.style.setProperty('--container-color', '#0000');
document.documentElement.style.setProperty('--background-color', '#0000');
document.documentElement.style.setProperty('--regular-margin', '0');
document.documentElement.style.setProperty('--director-margin', '0 25px 0 0');
getById("directorLinksButton").style.color = "black";
session.transparent=true;
}
if (urlParams.has('stereo') || urlParams.has('s') || urlParams.has('proaudio')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono
log("STEREO ENABLED");
session.stereo = urlParams.get('stereo') || urlParams.get('s') || urlParams.get('proaudio');
if (session.stereo) {
session.stereo = session.stereo.toLowerCase();
}
//var supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
//supportedConstraints.channelCount;
if (session.stereo === "false") {
session.stereo = 0;
session.audioInputChannels = 1;
} else if (session.stereo === "0") {
session.stereo = 0;
session.audioInputChannels = 1;
} else if (session.stereo === "no") {
session.stereo = 0;
session.audioInputChannels = 1;
} else if (session.stereo === "off") {
session.stereo = 0;
session.audioInputChannels = 1;
} else if (session.stereo === "1") {
session.stereo = 1;
} else if (session.stereo === "both") {
session.stereo = 1;
} else if (session.stereo === "3") {
session.stereo = 3;
} else if (session.stereo === "out") {
session.stereo = 3;
} else if (session.stereo === "mono") {
session.stereo = 3;
session.audiobitrate = 128;
} else if (session.stereo === "4") {
session.stereo = 4;
} else if (session.stereo === "multi") {
session.stereo = 4;
} else if (session.stereo === "2") {
session.stereo = 2;
} else if (session.stereo === "in") {
session.stereo = 2;
} else {
session.stereo = 5; // guests; no stereo in, no high bitrate in, but otherwise like stereo=1
}
}
if ((session.stereo == 1) || (session.stereo == 3) || (session.stereo == 4) || (session.stereo == 5)) {
session.echoCancellation = false;
session.autoGainControl = false;
session.noiseSuppression = false;
}
if (urlParams.has('mono')) {
session.mono = true;
if ((session.stereo == 1) || (session.stereo == 4)) {
session.stereo = 3;
session.audiobitrate = 128;
} else if (session.stereo == 5) {
session.stereo = 3;
session.audiobitrate = 128;
} else if (session.stereo == 2) {
session.stereo = 0;
session.audiobitrate = 128;
}
}
if (urlParams.has("channelcount") || urlParams.has("ac")) {
session.audioInputChannels = urlParams.get('channelcount') || urlParams.get('ac');
session.audioInputChannels = parseInt(session.audioInputChannels);
if (!session.audioInputChannels) {
session.audioInputChannels = false;
}
}
if (urlParams.has("aec") || urlParams.has("ec")) {
session.echoCancellation = urlParams.get('aec') || urlParams.get('ec');
if (session.echoCancellation) {
session.echoCancellation = session.echoCancellation.toLowerCase();
}
if (session.echoCancellation == "false") {
session.echoCancellation = false;
} else if (session.echoCancellation == "0") {
session.echoCancellation = false;
} else if (session.echoCancellation == "no") {
session.echoCancellation = false;
} else if (session.echoCancellation == "off") {
session.echoCancellation = false;
} else {
session.echoCancellation = true;
}
}
if (urlParams.has("autogain") || urlParams.has("ag")) {
session.autoGainControl = urlParams.get('autogain') || urlParams.get('ag');
if (session.autoGainControl) {
session.autoGainControl = session.autoGainControl.toLowerCase();
}
if (session.autoGainControl == "false") {
session.autoGainControl = false;
} else if (session.autoGainControl == "0") {
session.autoGainControl = false;
} else if (session.autoGainControl == "no") {
session.autoGainControl = false;
} else if (session.autoGainControl == "off") {
session.autoGainControl = false;
} else {
session.autoGainControl = true;
}
}
if (urlParams.has("denoise") || urlParams.has("dn")) {
session.noiseSuppression = urlParams.get('denoise') || urlParams.get('dn');
if (session.noiseSuppression) {
session.noiseSuppression = session.noiseSuppression.toLowerCase();
}
if (session.noiseSuppression == "false") {
session.noiseSuppression = false;
} else if (session.noiseSuppression == "0") {
session.noiseSuppression = false;
} else if (session.noiseSuppression == "no") {
session.noiseSuppression = false;
} else if (session.noiseSuppression == "off") {
session.noiseSuppression = false;
} else {
session.noiseSuppression = true;
}
}
if (urlParams.has('roombitrate') || urlParams.has('roomvideobitrate') || urlParams.has('rbr')) {
log("Room BITRATE SET");
session.roombitrate = urlParams.get('roombitrate') || urlParams.get('rbr') || urlParams.get('roomvideobitrate');
session.roombitrate = parseInt(session.roombitrate);
if (session.roombitrate < 1) {
session.roombitrate = 0;
}
}
if ( urlParams.has('outboundaudiobitrate') || urlParams.has('oab')) {
session.outboundAudioBitrate = parseInt(urlParams.get('outboundaudiobitrate')) || parseInt(urlParams.get('oab')) || false;
}
if (urlParams.has('outboundvideobitrate') || urlParams.has('ovb')) {
session.outboundVideoBitrate = parseInt(urlParams.get('outboundvideobitrate')) || parseInt(urlParams.get('ovb')) || false;
}
if (urlParams.has('webp')){
session.webp = true;
}
if (urlParams.has('webpquality') || urlParams.has('webpq') || urlParams.has('wq')){
session.webPquality = parseInt(urlParams.get('webpquality')) || parseInt(urlParams.get('webpq')) || parseInt(urlParams.get('wq')) || 4;
}
if (urlParams.has('audiobitrate') || urlParams.has('ab')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono
log("AUDIO BITRATE SET");
session.audiobitrate = urlParams.get('audiobitrate') || urlParams.get('ab');
session.audiobitrate = parseInt(session.audiobitrate);
if (session.audiobitrate < 1) {
session.audiobitrate = false;
} else if (session.audiobitrate > 510) {
session.audiobitrate = 510;
} // this is to just prevent abuse
}
if ((iOS) || (iPad)) {
session.audiobitrate = false; // iOS devices seem to get distortion with custom audio bitrates. Disable for now.
}
/* if (urlParams.has('whitebalance') || urlParams.has('temp')){ // Need to be applied after the camera is selected. bleh. not enforcible. remove for now.
var temperature = urlParams.get('whitebalance') || urlParams.get('temp');
try{
updateCameraConstraints('colorTemperature', parseFloat(temperature));
} catch (e){errorlog(e);}
} */
if (urlParams.has('streamid') || urlParams.has('view') || urlParams.has('v') || urlParams.has('pull')) { // the streams we want to view; if set, but let blank, we will request no streams to watch.
session.view = urlParams.get('streamid') || urlParams.get('view') || urlParams.get('v') || urlParams.get('pull'); // this value can be comma seperated for multiple streams to pull
getById("headphonesDiv2").style.display = "inline-block";
getById("headphonesDiv").style.display = "inline-block";
getById("addPasswordBasic").style.display = "none";
if (session.view == null) {
session.view = "";
}
if (session.view) {
if (session.view.split(",").length > 1) {
session.view_set = session.view.split(",");
}
}
}
if (urlParams.has('nopreview') || urlParams.has('np')) {
log("preview OFF");
session.nopreview = true;
} else if ((urlParams.has('preview')) || (urlParams.has('showpreview'))) {
log("preview ON");
session.nopreview = false;
} else if ((urlParams.has('minipreview')) || (urlParams.has('mini'))) {
var mini = urlParams.has('minipreview') || urlParams.has('mini') || true; // 2 is a valid option.
log("preview ON");
session.nopreview = false;
session.minipreview = mini;
}
if (urlParams.has('obsfix')) {
session.obsfix = urlParams.get('obsfix');
if (session.obsfix) {
session.obsfix = session.obsfix.toLowerCase();
}
if (session.obsfix == "false") {
session.obsfix = false;
} else if (session.obsfix == "0") {
session.obsfix = false;
} else if (session.obsfix == "no") {
session.obsfix = false;
} else if (session.obsfix == "off") {
session.obsfix = false;
} else if (parseInt(session.obsfix) > 0) {
session.obsfix = parseInt(session.obsfix);
} else {
session.obsfix = 1; // aggressive.
}
}
if (urlParams.has('controlroombitrate') || urlParams.has('crb')) {
session.controlRoomBitrate = true;
}
if (urlParams.has('remote') || urlParams.has('rem')) {
log("remote ENABLED");
session.remote = urlParams.get('remote') || urlParams.get('rem') || "nosecurity";
session.remote = session.remote.trim();
}
if (urlParams.has('latency') || urlParams.has('al') || urlParams.has('audiolatency')) {
log("latency ENABLED");
session.audioLatency = urlParams.get('latency') || urlParams.get('al') || urlParams.get('audiolatency');
session.audioLatency = parseInt(session.audioLatency) || 0;
session.disableWebAudio = false;
}
if (urlParams.has('micdelay') || urlParams.has('delay') || urlParams.has('md')) {
log("audio gain ENABLED");
session.micDelay = urlParams.get('micdelay') || urlParams.get('delay') || urlParams.get('md');
session.micDelay = parseInt(session.micDelay) || 0;
session.disableWebAudio = false;
}
if (urlParams.has('audiogain') || urlParams.has('gain') || urlParams.has('g')) {
log("audio gain ENABLED");
session.audioGain = urlParams.get('audiogain') || urlParams.get('gain') || urlParams.get('g');
session.audioGain = parseInt(session.audioGain) || 0;
session.disableWebAudio = false;
}
if (urlParams.has('compressor') || urlParams.has('comp')) {
log("audio gain ENABLED");
session.compressor = 1;
session.disableWebAudio = false;
} else if (urlParams.has('limiter')) {
log("audio gain ENABLED");
session.compressor = 2;
session.disableWebAudio = false;
}
if ((urlParams.has('equalizer')) || (urlParams.has('eq'))) {
session.equalizer = true;
session.disableWebAudio = false;
}
if ((urlParams.has('lowcut')) || (urlParams.has('lc')) || (urlParams.has('higpass'))) {
session.lowcut = urlParams.get('lowcut') || urlParams.get('lc') || urlParams.get('higpass') || 100;
session.lowcut = parseInt(session.lowcut);
session.disableWebAudio = false;
}
if (urlParams.has('pip')) {
session.pip = true; // togglePip
//session.manual=true;
//innerHTML =
}
if (urlParams.has('keyframeinterval') || urlParams.has('keyframerate') || urlParams.has('keyframe') || urlParams.has('fki')) {
log("keyframerate ENABLED");
session.keyframerate = parseInt(urlParams.get('keyframeinterval') || urlParams.get('keyframerate') || urlParams.get('keyframe') || urlParams.get('fki')) || 0;
}
if (urlParams.has('tallyoff') || urlParams.has('obsoff') || urlParams.has('oo')) {
log("OBS feedback disabled");
session.disableOBS = true;
}
if (urlParams.has('chroma')) {
log("Chroma ENABLED");
getById("main").style.backgroundColor = "#" + (urlParams.get('chroma') || "000");
}
if (urlParams.has("videodevice") || urlParams.has("vdevice") || urlParams.has('vd') || urlParams.has('device') || urlParams.has('d')) {
session.videoDevice = urlParams.get("videodevice") || urlParams.get("vdevice") || urlParams.get("vd") || urlParams.get("device") || urlParams.get("d");
if (session.videoDevice === null) {
session.videoDevice = "1";
} else if (session.videoDevice) {
session.videoDevice = session.videoDevice.toLowerCase().replace(/[\W]+/g, "_");
}
if (session.videoDevice == "false") {
session.videoDevice = 0;
} else if (session.videoDevice == "0") {
session.videoDevice = 0;
} else if (session.videoDevice == "no") {
session.videoDevice = 0;
} else if (session.videoDevice == "off") {
session.videoDevice = 0;
} else if (session.videoDevice == "snapcam") {
session.videoDevice = "snap_camera";
} else if (session.videoDevice == "canon") {
session.videoDevice = "eos";
} else if (session.videoDevice == "camlink") {
session.videoDevice = "cam_link";
} else if (session.videoDevice == "ndi") {
session.videoDevice = "newtek_ndi_video";
} else if (session.videoDevice == "") {
session.videoDevice = 1;
} else if (session.videoDevice == "1") {
session.videoDevice = 1;
} else if (session.videoDevice == "default") {
session.videoDevice = 1;
} else {
// whatever the user entered I guess, santitized.
session.videoDevice = session.videoDevice.replace(/[\W]+/g, "_").toLowerCase();
}
if (session.videoDevice === 0) {
getById("add_camera").innerHTML = "Share your Microphone";
miniTranslate(getById("add_camera"), "share-your-mic");
}
getById("videoMenu").style.display = "none";
log("session.videoDevice:" + session.videoDevice);
}
// audioDevice
if (urlParams.has("audiodevice") || urlParams.has("adevice") || urlParams.has("ad") || urlParams.has("device") || urlParams.has("d")) {
session.audioDevice = urlParams.get("audiodevice") || urlParams.get("adevice") || urlParams.get("ad") || urlParams.get("device") || urlParams.get("d");
if (session.audioDevice === null) {
session.audioDevice = "1";
} else if (session.audioDevice) {
session.audioDevice = session.audioDevice.toLowerCase().replace(/[\W]+/g, "_");
}
if (session.audioDevice == "false") {
session.audioDevice = 0;
} else if (session.audioDevice == "0") {
session.audioDevice = 0;
} else if (session.audioDevice == "no") {
session.audioDevice = 0;
} else if (session.audioDevice == "off") {
session.audioDevice = 0;
} else if (session.audioDevice == "") {
session.audioDevice = 1;
} else if (session.audioDevice == "1") {
session.audioDevice = 1;
} else if (session.audioDevice == "default") {
session.audioDevice = 1;
} else if (session.audioDevice == "ndi") {
session.audioDevice = "line_newtek_ndi_audio";
} else {
// whatever the user entered I guess
session.audioDevice = session.audioDevice.replace(/[\W]+/g, "_").toLowerCase();
}
if (session.videoDevice === 0) {
if (session.audioDevice === 0) {
getById("add_camera").innerHTML = "Click Start to Join";
miniTranslate(getById("add_camera"), "click-start-to-join");
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
getById("container-3").classList.add("skip-animation");
getById("container-3").classList.remove('pointer');
setTimeout(function() {
previewWebcam();
}, 100);
session.webcamonly = true;
}
}
log("session.audioDevice:" + session.audioDevice);
getById("audioMenu").style.display = "none";
getById("headphonesDiv").style.display = "none";
getById("headphonesDiv2").style.display = "none";
getById("audioScreenShare1").style.display = "none";
}
if (urlParams.has("autojoin") || urlParams.has("autostart") || urlParams.has('aj') || urlParams.has('as')) {
session.autostart = true;
if (session.screenshare) {
setTimeout(function() {
publishScreen();
}, 200);
}
}
if (urlParams.has('noiframe') || urlParams.has('noiframes') || urlParams.has('nif')) {
session.noiframe = urlParams.get('noiframe') || urlParams.get('noiframes') || urlParams.get('nif');
if (!(session.noiframe)) {
session.noiframe = [];
} else {
session.noiframe = session.noiframe.split(",");
}
log("disable iframe playback");
log(session.noiframe);
}
if (urlParams.has('exclude') || urlParams.has('ex')) {
session.exclude = urlParams.get('exclude') || urlParams.get('ex');
if (!(session.exclude)) {
session.exclude = false;
} else {
session.exclude = session.exclude.split(",");
}
log("exclude video playback");
log(session.exclude);
}
if (urlParams.has('novideo') || urlParams.has('nv') || urlParams.has('hidevideo') || urlParams.has('showonly')) {
session.novideo = urlParams.get('novideo') || urlParams.get('nv') || urlParams.get('hidevideo') || urlParams.get('showonly');
if (!(session.novideo)) {
session.novideo = [];
} else {
session.novideo = session.novideo.split(",");
}
log("disable video playback");
log(session.novideo);
}
if (urlParams.has('noaudio') || urlParams.has('na') || urlParams.has('hideaudio')) {
session.noaudio = urlParams.get('noaudio') || urlParams.get('na') || urlParams.get('hideaudio');
if (!(session.noaudio)) {
session.noaudio = [];
} else {
session.noaudio = session.noaudio.split(",");
}
log("disable audio playback");
}
if (urlParams.has('forceios')) {
log("allow iOS to work in video group chat; for this user at least");
session.forceios = true;
}
if (urlParams.has('nocursor')) {
session.nocursor = true;
log("DISABLE CURSOR");
var style = document.createElement('style');
style.innerHTML = `
video {
margin: 0;
padding: 0;
overflow: hidden;
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=), none;
user-select: none;
}
`;
document.head.appendChild(style);
}
if (urlParams.has('vbr')) {
session.cbr = 0;
}
if (urlParams.has('order')) {
session.order = parseInt(urlParams.get('order')) || 0;
}
if (urlParams.has('sensors') || urlParams.has('sensor') || urlParams.has('gyro') || urlParams.has('gyros') || urlParams.has('accelerometer')) {
session.sensorData = urlParams.get('sensors') || urlParams.get('sensor') || urlParams.get('gyro') || urlParams.get('gyros') || urlParams.get('accelerometer') || 30;
session.sensorData = parseInt(session.sensorData);
}
if (urlParams.has('minptime')) {
session.minptime = parseInt(urlParams.get('minptime')) || 10;
if (session.minptime < 10) {
session.minptime = 10;
}
if (session.minptime > 300) {
session.minptime = 300;
}
}
if (urlParams.has('maxptime')) {
session.maxptime = parseInt(urlParams.get('maxptime')) || 60;
if (session.maxptime < 10) {
session.maxptime = 10;
}
if (session.maxptime > 300) {
session.maxptime = 300;
}
}
if (urlParams.has('ptime')) {
session.ptime = parseInt(urlParams.get('ptime')) || 20;
if (session.minptime < 10) {
session.ptime = 10;
}
if (session.minptime > 300) {
session.ptime = 300;
}
}
if (urlParams.has('codec')) {
log("CODEC CHANGED");
session.codec = urlParams.get('codec').toLowerCase();
if (session.codec=="webp"){
session.webp = true;
session.codec = false;
}
} //else if (window.obsstudio){
//if (session.obsfix===false){
// session.codec = "h264"; // H264 --- It's too laggy for many!!!
//}
//}
if (urlParams.has('nonacks')){ // disables error control / throttling.
session.noNacks = true;
}
if (urlParams.has('nopli')){ // disables error control / throttling.
session.noPLIs = true;
}
if (urlParams.has('noremb')){ // disables error control / throttling.
session.noREMB = true;
}
if (urlParams.has('scale')) {
if (urlParams.get('scale') == "false") {} else if (urlParams.get('scale') == "0") {} else if (urlParams.get('scale') == "no") {} else if (urlParams.get('scale') == "off") {} else {
log("Resolution scale requested");
session.scale = parseInt(urlParams.get('scale')) || 100;
}
session.dynamicScale = false; // default true
}
var ConfigSettings = getById("main-js");
var ln_template = false;
var translation = false;
try {
if (ConfigSettings) {
ln_template = ConfigSettings.getAttribute('data-translation'); // Translations
if (typeof ln_template === "undefined") {
ln_template = false;
} else if (ln_template === null) {
ln_template = false;
}
}
if (urlParams.has('ln')) {
ln_template = urlParams.get('ln');
}
} catch (e) {
errorlog(e);
}
if (ln_template) { // checking if manual lanuage override enabled
try {
log("Template: " + ln_template);
fetch("./translations/" + ln_template + '.json').then(function(response) {
if (response.status !== 200) {
log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
response.json().then(function(data) {
log(data);
translation = data;
var trans = data.innerHTML;
var allItems = document.querySelectorAll('[data-translate]');
allItems.forEach(function(ele) {
if (ele.dataset.translate in trans) {
ele.innerHTML = trans[ele.dataset.translate];
}
});
trans = data.titles;
var allTitles = document.querySelectorAll('[title]');
allTitles.forEach(function(ele) {
var key = ele.title.replace(/[\W]+/g, "-").toLowerCase();
if (key in trans) {
ele.title = trans[key];
}
});
trans = data.placeholders;
var allPlaceholders = document.querySelectorAll('[placeholder]');
allPlaceholders.forEach(function(ele) {
var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase();
if (key in trans) {
ele.placeholder = trans[key];
}
});
getById("mainmenu").style.opacity = 1;
}).catch(function(err) {
errorlog(err);
getById("mainmenu").style.opacity = 1;
});
}).catch(function(err) {
errorlog(err);
getById("mainmenu").style.opacity = 1;
});
} catch (error) {
errorlog(error);
getById("mainmenu").style.opacity = 1;
}
} else if (location.hostname !== "obs.ninja") {
if (location.hostname === "rtc.ninja") {
try {
if (session.label === false) {
document.title = "";
}
getById("qos").innerHTML = "";
getById("logoname").innerHTML = "";
getById("helpbutton").style.display = "none";
getById("helpbutton").style.opacity = 0;
getById("reportbutton").style.display = "none";
getById("reportbutton").style.opacity = 0;
getById("mainmenu").style.opacity = 1;
getById("mainmenu").style.margin = "30px 0";
getById("translateButton").style.display = "none";
getById("translateButton").style.opacity = 0;
getById("info").style.display = "none";
getById("info").style.opacity = 0;
getById("chatBody").innerHTML = "";
} catch (e) {}
}
try {
fetch("./translations/blank.json").then(function(response) {
if (response.status !== 200) {
log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
response.json().then(function(data) {
log(data);
var trans = data.innerHTML;
var allItems = document.querySelectorAll('[data-translate]');
allItems.forEach(function(ele) {
if (ele.dataset.translate in trans) {
ele.innerHTML = trans[ele.dataset.translate];
}
});
trans = data.titles;
var allTitles = document.querySelectorAll('[title]');
allTitles.forEach(function(ele) {
var key = ele.title.replace(/[\W]+/g, "-").toLowerCase();
if (key in trans) {
ele.title = trans[key];
}
});
trans = data.placeholders;
var allPlaceholders = document.querySelectorAll('[placeholder]');
allPlaceholders.forEach(function(ele) {
var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase();
if (key in trans) {
ele.placeholder = trans[key];
}
});
if (session.label === false) {
document.title = location.hostname;
}
getById("qos").innerHTML = location.hostname;
getById("logoname").innerHTML = getById("qos").outerHTML;
getById("helpbutton").style.display = "none";
getById("reportbutton").style.display = "none";
getById("mainmenu").style.opacity = 1;
}).catch(function(err) {
errorlog(err);
getById("mainmenu").style.opacity = 1;
});
}).catch(function(err) {
errorlog(err);
getById("mainmenu").style.opacity = 1;
});
if (session.label === false) {
document.title = location.hostname;
}
getById("qos").innerHTML = location.hostname;
getById("logoname").innerHTML = getById("qos").outerHTML;
getById("helpbutton").style.display = "none";
getById("reportbutton").style.display = "none";
getById("chatBody").innerHTML = "";
} catch (error) {
errorlog(error);
}
} else { // check if automatic language translation is available
getById("mainmenu").style.opacity = 1;
}
try {
if (location.hostname === "rtc.ninja") { // an extra-brand-free version of OBS.Ninja
if (session.label === false) {
document.title = "";
}
getById("qos").innerHTML = "";
getById("logoname").innerHTML = "";
getById("helpbutton").style.display = "none";
getById("helpbutton").style.opacity = 0;
getById("reportbutton").style.display = "none";
getById("reportbutton").style.opacity = 0;
getById("mainmenu").style.opacity = 1;
getById("mainmenu").style.margin = "30px 0";
getById("translateButton").style.display = "none";
getById("translateButton").style.opacity = 0;
getById("info").style.display = "none";
getById("info").style.opacity = 0;
getById("chatBody").innerHTML = "";
} else if (location.hostname !== "obs.ninja") {
if (session.label === false) {
document.title = location.hostname;
}
getById("qos").innerHTML = sanitizeLabel(location.hostname);
getById("logoname").innerHTML = getById("qos").outerHTML;
getById("helpbutton").style.display = "none";
getById("reportbutton").style.display = "none";
}
} catch (e) {}
if (isIFrame) {
getById("helpbutton").style.display = "none";
getById("helpbutton").style.opacity = 0;
getById("reportbutton").style.display = "none";
getById("reportbutton").style.opacity = 0;
getById("chatBody").innerHTML = "";
}
function miniTranslate(ele, ident = false) {
if (ident) {
ele.dataset.translate = ident;
} else {
ident = ele.dataset.translate;
}
try {
if (ident in translation.innerHTML) {
ele.innerHTML = translation.innerHTML[ident];
}
} catch (e) {}
}
function changeLg(lang) {
fetch("./translations/" + lang + '.json').then(function(response) {
if (response.status !== 200) {
logerror('Language translation file not found.' + response.status);
return;
}
response.json().then(function(data) {
log(data);
translation = data; // translation.innerHTML[ele.dataset.translate]
var trans = data.innerHTML;
var allItems = document.querySelectorAll('[data-translate]');
allItems.forEach(function(ele) {
if (ele.dataset.translate in trans) {
ele.innerHTML = trans[ele.dataset.translate];
}
});
trans = data.titles;
var allTitles = document.querySelectorAll('[title]');
allTitles.forEach(function(ele) {
var key = ele.title.replace(/[\W]+/g, "-").toLowerCase();
if (key in trans) {
ele.title = trans[key];
}
});
trans = data.placeholders;
var allPlaceholders = document.querySelectorAll('[placeholder]');
allPlaceholders.forEach(function(ele) {
var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase();
if (key in trans) {
ele.placeholder = trans[key];
}
});
});
}).catch(function(err) {
errorlog(err);
});
}
if (urlParams.has('beep') || urlParams.has('notify') || urlParams.has('tone')) {
session.beepToNotify = true;
}
if (urlParams.has('r2d2')) {
getById("testtone").innerHTML = "";
getById("testtone").src = "./media/robot.mp3";
session.beepToNotify = true;
}
if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')) {
session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb');
if (session.bitrate) {
if ((session.view_set) && (session.bitrate.split(",").length > 1)) {
session.bitrate_set = session.bitrate.split(",");
session.bitrate = parseInt(session.bitrate_set[0]);
} else {
session.bitrate = parseInt(session.bitrate);
}
if (session.bitrate < 1) {
session.bitrate = false;
}
log("BITRATE ENABLED");
log(session.bitrate);
}
}
if (urlParams.has('maxvideobitrate') || urlParams.has('maxbitrate') || urlParams.has('mvb')) {
session.maxvideobitrate = urlParams.get('maxvideobitrate') || urlParams.get('maxbitrate') || urlParams.get('mvb');
session.maxvideobitrate = parseInt(session.maxvideobitrate);
if (session.maxvideobitrate < 1) {
session.maxvideobitrate = false;
}
log("maxvideobitrate ENABLED");
log(session.maxvideobitrate);
}
if (urlParams.has('totalroombitrate') || urlParams.has('totalroomvideobitrate') || urlParams.has('trb')) {
session.totalRoomBitrate = urlParams.get('totalroombitrate') || urlParams.get('totalroomvideobitrate') || urlParams.get('trb');
session.totalRoomBitrate = parseInt(session.totalRoomBitrate);
if (session.totalRoomBitrate < 1) {
session.totalRoomBitrate = false;
}
log("totalRoomBitrate ENABLED");
log(session.totalRoomBitrate);
}
if (urlParams.has('height') || urlParams.has('h')) {
session.height = urlParams.get('height') || urlParams.get('h');
session.height = parseInt(session.height);
}
if (urlParams.has('width') || urlParams.has('w')) {
session.width = urlParams.get('width') || urlParams.get('w');
session.width = parseInt(session.width);
}
if (urlParams.has('quality') || urlParams.has('q')) {
try {
session.quality = urlParams.get('quality') || urlParams.get('q') || 0;
session.quality = parseInt(session.quality);
getById("gear_screen").parentNode.removeChild(getById("gear_screen"));
getById("gear_webcam").parentNode.removeChild(getById("gear_webcam"));
} catch (e) {
errorlog(e);
}
}
if (urlParams.has('sink')) {
session.sink = urlParams.get('sink');
} else if (urlParams.has('outputdevice') || urlParams.has('od') || urlParams.has('audiooutput')) {
session.outputDevice = urlParams.get('outputdevice') || urlParams.get('od') || urlParams.get('audiooutput');
if (session.outputDevice) {
session.outputDevice = session.outputDevice.toLowerCase().replace(/[\W]+/g, "_");
} else {
session.outputDevice = false;
}
if (session.outputDevice) {
try {
enumerateDevices().then(function(deviceInfos) {
for (let i = 0; i !== deviceInfos.length; ++i) {
if (deviceInfos[i].kind === 'audiooutput') {
if (deviceInfos[i].label.replace(/[\W]+/g, "_").toLowerCase().includes(session.outputDevice)) {
session.sink = deviceInfos[i].deviceId;
log("AUDIO OUT DEVICE: " + deviceInfos[i].deviceId);
break;
}
}
}
});
} catch (e) {}
}
}
if (urlParams.has('fullscreen')) {
session.fullscreen = true;
}
if (urlParams.has('stats')) {
session.statsMenu = true;
}
if (urlParams.has('cleandirector') || urlParams.has('cdv')) {
session.cleanDirector = true;
}
if (urlParams.has('cleanoutput') || urlParams.has('clean') || urlParams.has('cleanish')) {
session.cleanOutput = true;
getById("translateButton").style.display = "none";
getById("credits").style.display = "none";
getById("header").style.display = "none";
getById("controlButtons").style.display = "none";
var style = document.createElement('style');
style.innerHTML = `
video {
background-image: none;
}
`;
document.head.appendChild(style);
}
if (urlParams.has('cleanish')) {
session.cleanish = true;
}
if (urlParams.has('channels')) { // must be loaded before channelOffset
session.audioChannels = parseInt(urlParams.get('channels'));
session.offsetChannel = 0;
log("max channels is 32; channels offset");
session.audioEffects = true;
}
if (urlParams.has('channeloffset')) {
session.offsetChannel = parseInt(urlParams.get('channeloffset'));
log("max channels is 32; channels offset");
session.audioEffects = true;
}
if (urlParams.has('enhance')) {
//if (parseInt(urlParams.get('enhance')>0){
session.enhance = true; //parseInt(urlParams.get('enhance'));
//}
}
if (urlParams.has('maxviewers') || urlParams.has('mv')) {
session.maxviewers = urlParams.get('maxviewers') || urlParams.get('mv');
if (session.maxviewers.length == 0) {
session.maxviewers = 1;
} else {
session.maxviewers = parseInt(session.maxviewers);
}
log("maxviewers set");
}
if (urlParams.has('maxpublishers') || urlParams.has('mp')) {
session.maxpublishers = urlParams.get('maxpublishers') || urlParams.get('mp');
if (session.maxpublishers.length == 0) {
session.maxpublishers = 1;
} else {
session.maxpublishers = parseInt(session.maxpublishers);
}
log("maxpublishers set");
}
if (urlParams.has('maxconnections') || urlParams.has('mc')) {
session.maxconnections = urlParams.get('maxconnections') || urlParams.get('maxconnections');
if (session.maxconnections.length == 0) {
session.maxconnections = 1;
} else {
session.maxconnections = parseInt(session.maxconnections);
}
log("maxconnections set");
}
if (urlParams.has('secure')) {
session.security = true;
if (!(session.cleanOutput)) {
setTimeout(function() {
warnUser("Enhanced Security Mode Enabled.");
}, 100);
}
}
if (urlParams.has('random') || urlParams.has('randomize')) {
session.randomize = true;
}
if (urlParams.has('framerate') || urlParams.has('fr') || urlParams.has('fps')) {
session.framerate = urlParams.get('framerate') || urlParams.get('fr') || urlParams.get('fps');
session.framerate = parseInt(session.framerate);
log("framerate Changed");
log(session.framerate);
}
if (urlParams.has('maxframerate') || urlParams.has('mfr') || urlParams.has('mfps')) {
session.maxframerate = urlParams.get('maxframerate') || urlParams.get('mfr') || urlParams.get('mfps');
session.maxframerate = parseInt(session.maxframerate);
log("max framerate assigned");
log(session.maxframerate);
}
if (urlParams.has('buffer')) { // needs to be before sync
session.buffer = parseFloat(urlParams.get('buffer')) || 0;
log("buffer Changed: " + session.buffer);
session.sync = 0;
session.audioEffects = true;
}
if (urlParams.has('sync')) {
session.sync = parseFloat(urlParams.get('sync'));
log("sync Changed; in milliseconds. If not set, defaults to auto.");
log(session.sync);
session.audioEffects = true;
if (session.buffer === false) {
session.buffer = 0;
}
}
if (urlParams.has('mirror')) {
if (urlParams.get('mirror') == "3") {
getById("main").classList.add("mirror");
} else if (urlParams.get('mirror') == "2") {
session.mirrored = 2;
} else if (urlParams.get('mirror') == "0") {
session.mirrored = 0;
} else if (urlParams.get('mirror') == "false") {
session.mirrored = 0;
} else if (urlParams.get('mirror') == "off") {
session.mirrored = 0;
} else {
session.mirrored = 1;
}
}
if (urlParams.has('flip')) {
if (urlParams.get('flip') == "0") {
session.flipped = false;
} else if (urlParams.get('flip') == "false") {
session.flipped = false;
} else if (urlParams.get('flip') == "off") {
session.flipped = false;
} else {
session.flipped = true;
}
}
if ((session.mirrored) && (session.flipped)) {
try {
log("Mirror all videos");
var mirrorStyle = document.createElement('style');
mirrorStyle.innerHTML = "video {transform: scaleX(-1) scaleY(-1); }";
document.getElementsByTagName("head")[0].appendChild(mirrorStyle);
} catch (e) {
errorlog(e);
}
} else if (session.mirrored) { // mirror the video horizontally
try {
log("Mirror all videos");
var mirrorStyle = document.createElement('style');
mirrorStyle.innerHTML = "video {transform: scaleX(-1);}";
document.getElementsByTagName("head")[0].appendChild(mirrorStyle);
} catch (e) {
errorlog(e);
}
} else if (session.flipped) { // mirror the video vertically
try {
log("Mirror all videos");
var mirrorStyle = document.createElement('style');
mirrorStyle.innerHTML = "video {transform: scaleY(-1);}";
document.getElementsByTagName("head")[0].appendChild(mirrorStyle);
} catch (e) {
errorlog(e);
}
}
if (urlParams.has('icefilter')) {
log("ICE FILTER ENABLED");
session.icefilter = urlParams.get('icefilter');
}
if (urlParams.has('effects') || urlParams.has('effect')) {
session.effects = urlParams.get('effects') || urlParams.get('effect') || 0;
session.effects = parseInt(session.effects);
if (session.effects === 0){
getById("effectsDiv").style.display = "block";
}
// mirror == 2
// face == 1
// blur = 3
// green = 4
// image = 5
// avatar = 6
}
if (urlParams.has('activespeaker') || urlParams.has('speakerview') || urlParams.has('sas')){
session.activeSpeaker = true;
//if (session.audioEffects === null) {
session.audioEffects = true;
session.audioMeterGuest = true;
//}
setInterval(function(){activeSpeaker(false)},100);
}// else {
// if (session.scene!==false){
// setInterval(function(){activeSpeaker(true)},100);
// }
//}
if (urlParams.has('style') || urlParams.has('st')) {
session.style = urlParams.get('style') || urlParams.get('st') || 1;
if ((parseInt(session.style) == 1) || (session.style == "justvideo")) { // no audio only
session.style = 1;
} else if ((parseInt(session.style) == 2) || (session.style == "waveform")) { // audio waveform
session.style = 2;
session.audioEffects = true; ////!!!!!!! Do I want to enable the audioEffects myself? or do it here?
} else if ((parseInt(session.style) == 3) || (session.style == "volume")) { // photo is taken? upload option? canvas?
session.style = 3;
session.audioEffects = true;
} else {
session.style = 1;
}
}
if (urlParams.has('samplerate') || urlParams.has('sr')) {
session.sampleRate = parseInt(urlParams.get('samplerate')) || parseInt(urlParams.get('samplerate')) || 48000;
if (session.audioCtx) {
session.audioCtx.close(); // close the default audio context.
}
session.audioCtx = new AudioContext({ // create a new audio context with a higher sample rate.
sampleRate: session.sampleRate
});
session.audioEffects = true;
}
if (urlParams.has('noaudioprocessing') || urlParams.has('noap')) {
session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers?
session.audioEffects = false; // disable audio inbound effects also.
session.audioMeterGuest = false;
}
if (urlParams.has('tcp')){ // forces the TURN servers to use TCP mode; still need to add &private to force TURN also tho
session.forceTcpMode = true;
}
if (urlParams.has('speedtest')){ // forces essentially UDP mode, unless TCP is specified, and some other stuff
session.speedtest = true;
}
if (urlParams.has('turn')) {
var turnstring = urlParams.get('turn');
if (turnstring == "twilio") { // a sample function on loading remote credentials for TURN servers.
try {
session.ws = false; // prevents connection
var twillioRequest = new XMLHttpRequest();
twillioRequest.onreadystatechange = function() {
if (twillioRequest.status === 200) {
try{
var res = JSON.parse(twillioRequest.responseText);
} catch(e){return;}
session.configuration = {
iceServers: [{
"username": res["1"],
"credential": res["2"],
"url": "turn:global.turn.twilio.com:3478?transport=tcp",
"urls": "turn:global.turn.twilio.com:3478?transport=tcp"
},
{
"username": res["1"],
"credential": res["2"],
"url": "turn:global.turn.twilio.com:443?transport=tcp",
"urls": "turn:global.turn.twilio.com:443?transport=tcp"
}
],
sdpSemantics: 'unified-plan' // future-proofing
};
if (session.ws===false){
session.ws=null; // allows connection (clears state)
session.connect(); // connect if not already connected.
}
}
// system does not connect if twilio API does not respond.
};
twillioRequest.open('GET', 'https://api.obs.ninja:1443/twilio', true); // `false` makes the request synchronous
twillioRequest.send();
} catch (e) {
errorlog("Twilio Failed");
}
} else if ((turnstring == "false") || (turnstring == "off") || (turnstring == "0")) { // disable TURN servers
session.configuration = {
iceServers: [
{ urls: ["stun:stun.l.google.com:19302", "stun:stun4.l.google.com:19302"]} // more than 4 stun+turn servers will cause firefox issues? (2 + 2 for now then)
],
sdpSemantics: 'unified-plan' // future-proofing
};
} else {
try {
turnstring = turnstring.split(";");
if (turnstring !== "false") { // false disables the TURN server. Useful for debuggin
var turn = {};
turn.username = turnstring[0]; // myusername
turn.credential = turnstring[1]; //mypassword
turn.urls = [turnstring[2]]; // ["turn:turn.obs.ninja:443"];
session.configuration.iceServers = [{
urls: ["stun:stun.l.google.com:19302", "stun:stun4.l.google.com:19302"]
}];
session.configuration.iceServers.push(turn);
}
} catch (e) {
if (!(session.cleanOutput)) {
warnUser("TURN server parameters were wrong.");
}
errorlog(e);
}
}
} else {
chooseBestTURN(); // obs.ninja turn servers, if needed.
}
if (urlParams.has('privacy') || urlParams.has('private') || urlParams.has('relay')) { // please only use if you are also using your own TURN service.
session.privacy = true;
try {
session.configuration.iceTransportPolicy = "relay"; // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/address
} catch (e) {
if (!(session.cleanOutput)) {
warnUser("Privacy mode failed to configure.");
}
errorlog(e);
}
if (session.speedtest){
if (session.maxvideobitrate !== false) {
if (session.maxvideobitrate > 6000) {
session.maxvideobitrate = 6000; // Please feel free to get rid of this if using your own TURN servers...
}
} else {
session.maxvideobitrate = 6000; // don't let people pull more than 2500 from you
}
if (session.bitrate !== false) {
if (session.bitrate > 6000) {
session.bitrate = 6000; // Please feel free to get rid of this if using your own TURN servers...
}
}
} else {
if (session.maxvideobitrate !== false) {
if (session.maxvideobitrate > 2500) {
session.maxvideobitrate = 2500; // Please feel free to get rid of this if using your own TURN servers...
}
} else {
session.maxvideobitrate = 2500; // don't let people pull more than 2500 from you
}
if (session.bitrate !== false) {
if (session.bitrate > 2500) {
session.bitrate = 2500; // Please feel free to get rid of this if using your own TURN servers...
}
}
}
}
if (urlParams.has('wss')) {
if (urlParams.get('wss')) {
session.wss = "wss://" + urlParams.get('wss');
}
}
if (urlParams.has('queue')) {
session.queue = true;
}
if (isIFrame) { // reduce CPU load if not needed.
window.onmessage = function(e) { // iFRAME support
log(e);
try {
if ("function" in e.data) { // these are calling in-app functions, with perhaps a callback -- TODO: add callbacks
var ret = null;
if (e.data.function === "previewWebcam") {
ret = previewWebcam();
} else if (e.data.function === "changeHTML") {
ret = getById(e.data.target);
ret.innerHTML = e.data.value;
} else if (e.data.function === "publishScreen") {
ret = publishScreen();
} else if (e.data.function === "eval") {
eval(e.data.value); // eval == evil ; feedback welcomed
}
}
} catch (err) {
errorlog(err);
}
if ("sendChat" in e.data) {
sendChat(e.data.sendChat); // sends to all peers; more options down the road
}
// Chat out gets called via getChatMessage function
// Related code: parent.postMessage({"chat": {"msg":-----,"type":----,"time":---} }, "*");
if ("mic" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho.
if (e.data.mic === true) { // unmute
session.muted = false; // set
log(session.muted);
toggleMute(true); // apply
} else if (e.data.mic === false) { // mute
session.muted = true; // set
log(session.muted);
toggleMute(true); // apply
} else if (e.data.mic === "toggle") { // toggle
toggleMute();
}
}
if ("camera" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho.
if (e.data.camera === true) { // unmute
session.videoMuted = false; // set
log(session.videoMuted);
toggleVideoMute(true); // apply
} else if (e.data.camera === false) { // mute
session.videoMuted = true; // set
log(session.videoMuted);
toggleVideoMute(true); // apply
} else if (e.data.camera === "toggle") { // toggle
toggleVideoMute();
}
}
if ("mute" in e.data) {
if (e.data.mute === true) { // unmute
session.speakerMuted = true; // set
toggleSpeakerMute(true); // apply
} else if (e.data.mute === false) { // mute
session.speakerMuted = false; // set
toggleSpeakerMute(true); // apply
} else if (e.data.mute === "toggle") { // toggle
toggleSpeakerMute();
}
} else if ("speaker" in e.data) { // same thing as mute.
if (e.data.speaker === true) { // unmute
session.speakerMuted = false; // set
toggleSpeakerMute(true); // apply
} else if (e.data.speaker === false) { // mute
session.speakerMuted = true; // set
toggleSpeakerMute(true); // apply
} else if (e.data.speaker === "toggle") { // toggle
toggleSpeakerMute();
}
}
if ("record" in e.data) {
if (e.data.record == false) { // mute
if ("recording" in session.videoElement) {
recordLocalVideo("stop");
}
} else if (e.data.record == true){
if ("recording" in session.videoElement) {
// already recording
} else {
recordLocalVideo("start");
}
}
}
if ("volume" in e.data) {
for (var i in session.rpcs) {
try {
session.rpcs[i].videoElement.volume = parseFloat(e.data.volume);
} catch (e) {
errorlog(e);
}
}
}
if ("bitrate" in e.data) {
for (var i in session.rpcs) {
try {
session.requestRateLimit(parseInt(e.data.bitrate), i);
} catch (e) {
errorlog(e);
}
}
}
if ("audiobitrate" in e.data) {
for (var i in session.rpcs) {
try {
session.requestAudioRateLimit(parseInt(e.data.audiobitrate), i);
} catch (e) {
errorlog(e);
}
}
}
if ("sceneState" in e.data) { // TRUE OR FALSE - tells the connected peers if they are live or not via a tally light change.
var visibility = e.data.sceneState;
var bundle = {};
bundle.sceneUpdate = [];
for (var UUID in session.rpcs) {
if (session.rpcs[UUID].visibility !== visibility) { // only move forward if there is a change; the event likes to double fire you see.
session.rpcs[UUID].visibility = visibility;
var msg = {};
msg.visibility = visibility;
if (session.rpcs[UUID].videoElement.style.display == "none") { // Flag will be left alone, but message will say its disabled.
msg.visibility = false;
}
msg.UUID = UUID;
session.sendRequest(msg, UUID);
bundle.sceneUpdate.push(msg);
}
}
session.sendRequest(bundle); // we want all publishing peers to know the state
}
if ("sendMessage" in e.data) { // webrtc send to viewers
session.sendMessage(e.data);
}
if ("sendRequest" in e.data) { // webrtc send to publishers
session.sendRequest(e.data);
}
if ("sendPeers" in e.data) { // webrtc send message to every connected peer; like send and request; a hammer vs a knife.
session.sendPeers(e.data);
}
if ("reload" in e.data) {
location.reload();
}
if ("getStats" in e.data) {
var stats = {};
stats.total_outbound_connections = Object.keys(session.pcs).length;
stats.total_inbound_connections = Object.keys(session.rpcs).length;
stats.inbound_stats = {};
for (var i in session.rpcs) {
stats.inbound_stats[session.rpcs[i].streamID] = session.rpcs[i].stats;
}
for (var uuid in session.pcs) {
setTimeout(function(UUID) {
session.pcs[UUID].getStats().then(function(stats) {
stats.forEach(stat => {
if (stat.type == "outbound-rtp") {
if (stat.kind == "video") {
if ("qualityLimitationReason" in stat) {
session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason;
}
if ("framesPerSecond" in stat) {
session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond;
}
if ("encoderImplementation" in stat) {
session.pcs[UUID].stats.encoder = stat.encoderImplementation;
}
}
} else if (stat.type == "remote-candidate") {
if ("relayProtocol" in stat) {
if ("ip" in stat) {
session.pcs[UUID].stats.remote_relay_IP = stat.ip;
}
session.pcs[UUID].stats.remote_relayProtocol = stat.relayProtocol;
}
if ("candidateType" in stat) {
session.pcs[UUID].stats.remote_candidateType = stat.candidateType;
}
} else if (stat.type == "local-candidate") {
if ("relayProtocol" in stat) {
if ("ip" in stat) {
session.pcs[UUID].stats.local_relayIP = stat.ip;
}
session.pcs[UUID].stats.local_relayProtocol = stat.relayProtocol;
}
if ("candidateType" in stat) {
session.pcs[UUID].stats.local_candidateType = stat.candidateType;
}
} else if ((stat.type == "candidate-pair" ) && (stat.nominated)) {
if ("availableOutgoingBitrate" in stat){
session.pcs[UUID].stats.available_outgoing_bitrate_kbps = parseInt(stat.availableOutgoingBitrate/1024);
}
if ("totalRoundTripTime" in stat){
session.pcs[UUID].stats.total_roundTripTime_ms = stat.totalRoundTripTime*1000;
}
}
return;
});
return;
});
}, 0, uuid);
}
setTimeout(function() {
stats.outbound_stats = {};
for (var i in session.pcs) {
stats.outbound_stats[i] = session.pcs[i].stats;
}
parent.postMessage({
"stats": stats
}, "*");
}, 1000);
}
if ("getRemoteStats" in e.data) {
session.sendRequest({"requestStats":true, "remote":session.remote});
}
if ("getLoudness" in e.data) {
log("GOT LOUDNESS REQUEST");
if (e.data.getLoudness == true) {
session.pushLoudness = true;
var loudness = {};
for (var i in session.rpcs) {
loudness[session.rpcs[i].streamID] = session.rpcs[i].stats.Audio_Loudness;
}
parent.postMessage({
"loudness": loudness
}, "*");
} else {
session.pushLoudness = false;
}
}
if ("getStreamIDs" in e.data) {
if (e.data.getStreamIDs == true) {
var streamIDs = {};
for (var i in session.rpcs) {
streamIDs[session.rpcs[i].streamID] = session.rpcs[i].label;
}
parent.postMessage({
"streamIDs": streamIDs
}, "*");
}
}
if ("close" in e.data) {
for (var i in session.rpcs) {
try {
session.rpcs[i].close();
} catch (e) {
errorlog(e);
}
}
}
if ("style" in e.data) {
try {
const style = document.createElement('style');
style.textContent = e.data.style;
document.head.append(style);
log(style);
} catch (e) {
errorlog(e);
}
}
if ("automixer" in e.data) {
if (e.data.automixer == true) {
session.manual = false;
try {
updateMixer();
} catch (e) {}
} else if (e.data.automixer == false) {
session.manual = true;
}
}
if ("target" in e.data) {
log(e.data);
for (var i in session.rpcs) {
try {
if ("streamID" in session.rpcs[i]) {
if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) {
try {
if ("settings" in e.data) {
for (const property in e.data.settings) {
session.rpcs[i].videoElement[property] = e.data.settings[property];
}
}
if ("add" in e.data) {
getById("gridlayout").appendChild(session.rpcs[i].videoElement);
} else if ("remove" in e.data) {
try {
session.rpcs[i].videoElement.parentNode.removeChild(session.rpcs[i].videoElement);
} catch (e) {
try {
session.rpcs[i].videoElement.parentNode.parentNode.removeChild(session.rpcs[i].videoElement.parentNode);
} catch (e) {}
}
}
} catch (e) {
errorlog(e);
}
}
}
} catch (e) {
errorlog(e);
}
}
}
};
}
function setupIncomingVideoTracking(v, UUID){ // video element.
if (session.directorUUID===UUID){
v.muted=false;
} else {
v.muted = session.speakerMuted;
}
v.onpause = (event) => { // prevent things from pausing; human or other
if (!((event.ctrlKey) || (event.metaKey) )){
warnlog("Video paused; force it to play again");
//return;
//session.audioCtx.resume();
//log("ctx resume");
event.currentTarget.play().then(_ => {
log("playing");
}).catch(error => {
warnlog("didnt play 1");
});
}
}
v.onplay = function(){
try {
var bigPlayButton = document.getElementById("bigPlayButton");
if (bigPlayButton){
bigPlayButton.parentNode.removeChild(bigPlayButton);
}
} catch(e){}
if (session.pip){
if (v.readyState >= 3){
if (!(v.pip)){
v.pip=true;
toggleSystemPip(v, true);
}
}
}
}
if (session.pip){
v.onloadedmetadata = function(){
if (!v.paused){
if (!(v.pip)){
v.pip=true;
toggleSystemPip(v, true);
}
}
}
}
v.addEventListener('resize', (e) => {
log("resize event");
var aspectRatio = parseFloat(e.target.videoWidth/e.target.videoHeight);
if (v.dataset.aspectRatio){
if (aspectRatio != v.dataset.aspectRatio){
setTimeout(function(){updateMixer();},1); // We don't want to run this on the first resize? just subsequent ones.
}
} else {
log("ASPECT RATIO CHANGED");
setTimeout(function(){updateMixer();},1);
}
v.dataset.aspectRatio = parseFloat(e.target.videoWidth/e.target.videoHeight);
});
v.volume = 1.0; // play audio automatically
v.autoplay = true;
v.controls = false;
v.className += "tile";
v.setAttribute("playsinline","");
v.controlTimer = null;
changeAudioOutputDevice(v); // if enabled, changes to desired output audio device.
if (document.getElementById("mainmenu")){
var m = getById("mainmenu");
m.remove();
}
if (session.director){
v.controls = true;
var container = getById("videoContainer_"+UUID);
v.disablePictureInPicture = false
v.setAttribute("controls","controls")
container.appendChild(v);
session.requestRateLimit(session.directorViewBitrate,UUID); /// limit resolution for director
if (session.beepToNotify) {
playtone();
}
} else if (session.scene!==false){
v.controls = false;
if (session.view){ // specific video to be played
v.style.display="block";
} else if (session.scene===0){
v.style.display="block";
} else { // group scene I guess; needs to be added manually
v.style.display="none";
v.muted= true;
}
setTimeout(function(){updateMixer();},1);
} else if (session.roomid!==false){
if (session.cleanOutput){
v.controls = false;
} else if (window.obsstudio) {
v.controls = false;
} else {
v.controls = true;
}
if ((session.roomid==="") && (session.bitrate)){
// let's keep the default bitrates, since this isn't a real room and bitrates are specified.
} else if (session.novideo !== false){
if (session.novideo.includes(session.rpcs[UUID].streamID)){
session.requestRateLimit(0,UUID);// limit resolution for guests see ln: 1804 in main.js also
}
} else {
session.requestRateLimit(0,UUID);// limit resolution for guests see ln: 1804 in main.js also
}
setTimeout(function(){updateMixer();},1);
} else {
v.style.display="block";
if (window.obsstudio) {
v.controls = false;
}
setTimeout(function(){updateMixer();},1);
}
v.addEventListener('click', function(e) { // show stats of video if double clicked
log("clicked");
try {
if ((e.ctrlKey)||(e.metaKey)){
e.preventDefault();
var uid = e.currentTarget.dataset.UUID;
if ("stats" in session.rpcs[uid]){
var [menu, innerMenu] = statsMenuCreator();
printViewStats(innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID );
menu.interval = setInterval(printViewStats,3000, innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID);
}
e.stopPropagation();
return false;
}
} catch(e){errorlog(e);}
});
if (session.statsMenu){
if ("stats" in session.rpcs[UUID]){
if (getById("menuStatsBox")){
clearInterval(getById("menuStatsBox").interval);
getById("menuStatsBox").remove();
}
var [menu, innerMenu] = statsMenuCreator();
printViewStats(innerMenu, session.rpcs[UUID].stats, session.rpcs[UUID].streamID );
menu.interval = setInterval(printViewStats,3000, innerMenu, session.rpcs[UUID].stats, session.rpcs[UUID].streamID);
}
}
v.touchTimeOut = null;
v.touchLastTap = 0;
v.touchCount = 0;
v.addEventListener('touchend', function(event) {
log("touched");
document.ontouchup = null;
document.onmouseup = null;
document.onmousemove = null;
document.ontouchmove = null;
var currentTime = new Date().getTime();
var tapLength = currentTime - v.touchLastTap;
clearTimeout(v.touchTimeOut);
if (tapLength < 500 && tapLength > 0) {
///
log("double touched");
v.touchCount+=1;
event.preventDefault();
if (v.touchCount<5){
v.touchLastTap = currentTime;
return false;
}
v.touchLastTap = 0;
v.touchCount=0;
log("double touched");
var uid = event.currentTarget.dataset.UUID;
if ("stats" in session.rpcs[uid]){
var [menu, innerMenu] = statsMenuCreator();
printViewStats(innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID );
menu.interval = setInterval(printViewStats,3000, innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID);
}
event.stopPropagation();
return false;
//////
} else {
v.touchCount=1;
v.touchTimeOut = setTimeout(function(vv) {
clearTimeout(vv.touchTimeOut);
vv.touchLastTap = 0;
vv.touchCount=0;
}, 5000, v);
v.touchLastTap = currentTime;
}
});
if (session.remote){
v.addEventListener("wheel", session.remoteControl);
}
if (v.controls == false){
v.addEventListener("click", function () {
if (v.paused){
log("PLAYING MANUALLY?");
v.play().then(_ => {
log("playing");
}).catch(warnlog);
}
});
if (session.nocursor==false){ // we do not want to show the controls. This is because MacOS + OBS does not work; so electron app needs this.
if (!(session.cleanOutput)){
if (!(window.obsstudio)){
if (v.controlTimer){
clearInterval(v.controlTimer);
}
v.controlTimer = setTimeout(showControlBar.bind(null,v),3000);
//v.controlTimer = setTimeout(function (){v.controls=true;},3000); // 3 seconds before I enable the controls automatically. This way it doesn't auto appear during loading. 3s enough, right?
}
}
}
}
setTimeout(session.processStats, 1000, UUID);
}
function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a giant function that runs when there are changes to screensize, video track statuses, etc.
if (getById("subControlButtons").dragElement){
if (parseInt(getById("subControlButtons").style.top) > 0){
getById("subControlButtons").style.top = "0px";
} else if (parseInt(getById("subControlButtons").style.top) < parseInt(50 - window.innerHeight) ){
getById("subControlButtons").style.top = parseInt( 50 - window.innerHeight)+"px";
}
if (parseInt(getById("subControlButtons").style.left) < 0){
getById("subControlButtons").style.left = "0px";
} else if (parseInt(getById("subControlButtons").style.left) > parseInt( window.innerWidth - getById("subControlButtons").offsetWidth) ){
getById("subControlButtons").style.left = parseInt( window.innerWidth -getById("subControlButtons").offsetWidth )+"px";
}
}
if (session.director){return;}
if (session.manual === true){return;}
var playarea = getById("gridlayout");
var header = getById("header");
var hi = header.offsetHeight ;
var w = window.innerWidth;
var h = window.innerHeight - hi;
if ( window.innerHeight<=700 ){
if (document.getElementById("controlButtons")){
var h = window.innerHeight - hi - document.getElementById("controlButtons").offsetHeight;
} else {
var h = window.innerHeight - hi;
}
}
if (session.aspectratio){
if (session.aspectratio==1){
var arW = 9.0;
var arH = 16.0;
} else if (session.aspectratio==2){
var arW = 1.0;
var arH = 1.0;
}
} else {
var arW = 16.0;
var arH = 9.0;
}
var ww = w/arW;
var hh = h/arH;
var mediaPool = [];
var mediaPool_invisible = [];
if (session.videoElement){ // I, myself, exist
if (session.videoElement.style.display!="none"){ // local feed
if (session.minipreview && (session.infocus!==true)){
session.videoElement.onclick = function(){
if (session.infocus === true){
session.infocus = false;
} else {
session.infocus = true;
log("session: myself");
}
setTimeout(()=>updateMixer(),10);
};
} else {
if (session.order!==false){
session.videoElement.order=session.order;
} else {
session.videoElement.order=0;
}
if (session.activeSpeaker && (!session.activelySpeaking)){
mediaPool_invisible.push(session.videoElement);
} else {
mediaPool.push(session.videoElement);
}
}
}
}
if (session.screenShareElement){ // I, myself, exist
if (session.screenShareElement.style.display!="none"){ // local feed
if (session.order!==false){
session.screenShareElement.order=session.order;
} else {
session.screenShareElement.order=0;
}
if (session.infocus!==false){
mediaPool_invisible.push(session.screenShareElement);
} else if (session.activeSpeaker && (!session.activelySpeaking)){
mediaPool_invisible.push(session.screenShareElement);
} else {
mediaPool.push(session.screenShareElement);
}
}
}
if (session.iframeEle){ // I, myself, exist
if (session.iframeEle.style.display!="none"){ // local feed
if (session.order!==false){
session.iframeEle.order=session.order;
} else {
session.iframeEle.order=0;
}
if (session.activeSpeaker && (!session.activelySpeaking)){
mediaPool_invisible.push(session.iframeEle);
} else {
mediaPool.push(session.iframeEle);
}
}
}
if ((session.infocus) && (session.infocus in session.rpcs)){ // remote guest being full screened; infocus == UUID
log(session.infocus+" set fullscreen");
mediaPool = []; // remove myself from fullscreen
for (var j in session.rpcs){
if (j != session.infocus){
session.requestRateLimit(0, j); // disable the video of non-fullscreen videos
try {
if (session.rpcs[j].videoElement.style.display!="none"){ // Add it if not hidden
mediaPool_invisible.push(session.rpcs[j].videoElement);
}
} catch(e){}
} else { // in focus video
////////
try {
if (session.rpcs[j].order!==false){
session.rpcs[j].videoElement.order=session.rpcs[j].order;
} else {
session.rpcs[j].videoElement.order=0;
}
///////////
if (session.activeSpeaker && (!session.rpcs[j].activelySpeaking)){
mediaPool_invisible.push(session.rpcs[j].videoElement);
} else {
mediaPool.push(session.rpcs[j].videoElement);
}
session.rpcs[j].videoElement.style.visibility = "visible";
if ((session.rpcs[j].targetBandwidth!==-1) && (session.rpcs[j].targetBandwidth0){
if (session.nopreview!==false){
mediaPool = []; // we don't want to show our self-preview if in broadcast mode and there is a director.
}
}
}
if (roomQuality === 0){roomQuality=1;}
roomQuality = parseInt(session.totalRoomBitrate/roomQuality);
//if (roomQuality<20){
// roomQuality=20;
//}
for (var i in session.rpcs){
if (session.rpcs[i]===null){continue;}
if (session.rpcs[i].iframeEle){
if (session.rpcs[i].order!==false){
session.rpcs[i].iframeEle.order=session.rpcs[i].order;
} else {
session.rpcs[i].iframeEle.order=0;
}
if (session.activeSpeaker && (!session.rpcs[i].activelySpeaking)){
mediaPool_invisible.push(session.rpcs[i].iframeEle);
} else {
mediaPool.push(session.rpcs[i].iframeEle);
}
continue;
}
if (session.rpcs[i].imageElement){
//if (session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0){ // video shouldn't be on if images are on.
if (session.rpcs[i].videoElement){ // is there audio?
mediaPool_invisible.push(session.rpcs[i].videoElement); // include audio as hidden track;
}
if (session.rpcs[i].videoMuted){
continue;
} else if (session.rpcs[i].directorVideoMuted){
continue;
}
if (session.rpcs[i].order!==false){
session.rpcs[i].imageElement.order=session.rpcs[i].order;
} else {
session.rpcs[i].imageElement.order=0;
}
if (session.activeSpeaker && (!session.rpcs[i].activelySpeaking)){
// mediaPool_invisible.push(session.rpcs[i].imageElement);
} else {
mediaPool.push(session.rpcs[i].imageElement);
}
continue;
// } else {
// errorlog("Video track added while in webp streaming mode? wasted bandwidth / cpu ..")
// }
}
if (session.rpcs[i].videoElement){ // remote feeds
//session.rpcs[i].targetBandwidth = -1;
try{
session.rpcs[i].videoElement.style.visibility = "visible";
} catch(e){}
if (session.directorUUID ===session.rpcs[i].UUID){ // director is never audio-only. Video if need, yes, but not visualized-audio.
if (session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0){
//if (session.style==1){ // avatars and waveforms might be better done elsewhere? as a canvas effect even?
continue;
//}
}
} else if (session.style==2){
log("style = 2");
if (session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0){
if (session.rpcs[i].canvas){
if (session.rpcs[i].order!==false){
session.rpcs[i].canvas.order=session.rpcs[i].order;
} else {
session.rpcs[i].canvas.order=0;
}
if (session.activeSpeaker && (!session.rpcs[i].activelySpeaking)){
mediaPool_invisible.push(session.rpcs[i].canvas);
} else {
mediaPool.push(session.rpcs[i].canvas);
}
}
continue;
}
} else if (session.style==1){
if (session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0){
//if (session.style==1){ // avatars and waveforms might be better done elsewhere? as a canvas effect even?
continue;
//}
}
} else if (session.scene!==false){
if (session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0){ // unless style==2, then scenes don't show audio.
//if (session.style==1){ // avatars and waveforms might be better done elsewhere? as a canvas effect even?
continue;
//}
}
}
if (session.rpcs[i].videoMuted){
continue;
} else if (session.rpcs[i].directorVideoMuted){
continue;
}
if (session.director){ // director video should be low-bitrate, although this should never fire.
warnlog("Update should not be called on DIRECTORs view? sorta at least"); // the director should never be called.
return;
} else if (session.rpcs[i].videoElement.style.display=="none"){ // Video is disabled; run at lowest
if (session.scene!==false){
session.requestRateLimit(session.hiddenSceneViewBitrate, i); // hidden. I dont want it to be super low, for video quality reasons.
if (!session.hiddenSceneViewBitrate){
session.rpcs[i].videoElement.classList.add("nogb");
}
} else {
session.requestRateLimit(0, i); // w/e This is not in OBS, so we just set it as low as possible. Shoudln't exist really unless loading?
}
} else if (session.scene!==false){ // max
session.requestRateLimit(-1, i); // unlock.
if (session.rpcs[i].order!==false){
session.rpcs[i].videoElement.order=session.rpcs[i].order;
} else {
session.rpcs[i].videoElement.order=0;
}
if (session.activeSpeaker && (!session.rpcs[i].activelySpeaking)){
if (!(session.rpcs[i].videoElement in mediaPool_invisible)){
mediaPool_invisible.push(session.rpcs[i].videoElement);
} else {
errorlog("THIS SHOULD NOT HAPPEN; 650");
}
} else {
mediaPool.push(session.rpcs[i].videoElement);
}
} else if (session.roomid!==false){ // guests should see video at low bitrate, ie: 100kbps (not 35kbps like if disabled)
if (session.rpcs[i].order!==false){
session.rpcs[i].videoElement.order=session.rpcs[i].order;
} else {
session.rpcs[i].videoElement.order=0;
}
if (session.activeSpeaker && (!session.rpcs[i].activelySpeaking)){
if (!(session.rpcs[i].videoElement in mediaPool_invisible)){
mediaPool_invisible.push(session.rpcs[i].videoElement);
} else {
errorlog("THIS SHOULD NOT HAPPEN; 665");
}
} else {
mediaPool.push(session.rpcs[i].videoElement);
}
if ((session.roomid==="") && (session.bitrate)){
// we will let the URL specified bitrate hold, since this isn't a real room.
session.requestRateLimit(-1, i);
} else {
session.requestRateLimit(roomQuality, i); // well, screw that. Setting it to room quality.
}
} else { // view=xx,yy or whatever. This should be highest quality.
if (session.rpcs[i].order!==false){
session.rpcs[i].videoElement.order=session.rpcs[i].order;
} else {
session.rpcs[i].videoElement.order=0;
}
if (session.activeSpeaker && (!session.rpcs[i].activelySpeaking)){
if (!(session.rpcs[i].videoElement in mediaPool_invisible)){
mediaPool_invisible.push(session.rpcs[i].videoElement);
} else {
errorlog("THIS SHOULD NOT HAPPEN; 684");
}
} else {
mediaPool.push(session.rpcs[i].videoElement);
}
session.requestRateLimit(-1, i);
}
}
}
}
if (session.director){return;} // director view says go no further :)
if (document.fullscreenElement) {
log("FULL SCREEN: "+document.fullscreenElement.id);
return; // This is FULL SCREEN, so let's not continue.
}
var mpl = mediaPool.length;
if (mpl>1){
var BB = 0;
var rw = 1;
var rh = 1;
var NW;
var NH;
var current;
for (NW=1; NW <= mpl; NW++){
NH = Math.ceil(mpl/NW);
var www = ww/NW;
var hhh = hh/NH;
if (www>hhh){
current = hhh * hhh * (mpl/(NW*NH));
} else {
current = www * www * (mpl/(NW*NH));
}
if (current>=BB){
BB = current;
rw = NW;
rh = NH;
}
}
} else { var rw=1; var rh=1;}
if (playarea.getElementsByTagName("iframe").length){
var childNodes = playarea.childNodes;
for (var n=0;n{
vid.style.width = "0px";
vid.style.height = "0px";
vid.style.top = "0px";
vid.style.left = "0px";
//vid.alreadyAdded=false; // what am I doing here? wtf? TODO: this is a typo, right?
if (vid.alreadyAdded && vid.alreadyAdded==true){
vid.alreadyAdded=false;
return;
} else if (vid.dataset.doNotMove){
return;
}
playarea.appendChild(vid);
});
mediaPool.sort(compare_vids);
mediaPool.forEach(vid=>{
var container = document.createElement("div");
container.style.position = "absolute";
container.style.display = "flex";
container.style.alignItems = "center";
offsetx=0;
if (i!==0){
if (Math.ceil((i+0.01)/rw)==rh){
if (mediaPool.length%rw){
offsetx = ((rw - mediaPool.length%rw)*(window.innerWidth/rw))/2;
}
}
}
offsety = (h- Math.ceil(mediaPool.length/rw)*Math.ceil(h/rh))/2;
if (vid.alreadyAdded && vid.alreadyAdded==true){
container = vid.parentNode;
}
container.style.left = offsetx+Math.floor(((i%rw)+0)*w/rw)+"px";
container.style.top = offsety+Math.floor((Math.floor(i/rw)+0)*h/rh + hi)+"px";
container.style.width = Math.ceil(w/rw)+"px";
container.style.height = Math.ceil(h/rh)+"px";
if (vid.alreadyAdded && vid.alreadyAdded==true){
vid.alreadyAdded=false;
i+=1;
return;
} else if (vid.dataset.doNotMove){
vid.style.position = "absolute";
vid.style.left = container.style.left;
vid.style.top = container.style.top;
vid.style.width = container.style.width;
vid.style.height = container.style.height;
vid.style.display = "flex";
i+=1;
return;
}
playarea.appendChild(container);
var wrw = Math.ceil(w/rw);
var hrh = Math.ceil(h/rh);
if (session.dynamicScale){
if (vid.dataset.UUID){
if (wrw && hrh){
if (session.devicePixelRatio){
session.requestResolution(vid.dataset.UUID, wrw * session.devicePixelRatio, hrh * session.devicePixelRatio);
} else if (window.devicePixelRatio && parseInt(window.devicePixelRatio) > 1 ){
session.requestResolution(vid.dataset.UUID, wrw*window.devicePixelRatio, hrh*window.devicePixelRatio);
} else {
session.requestResolution(vid.dataset.UUID, wrw, hrh);
}
}
}
}
vid.style.objectFit = "contain";
//vid.classList="";
vid.style.maxWidth = "100%";
vid.style.maxHeight = "100%";
vid.style.margin = "auto";
// Creates relative positioned element, important for label pos
var holder = document.createElement("div");
holder.className = "holder";
//holder.appendChild(vid);
container.appendChild(vid);
container.appendChild(holder);
if (vid.videoWidth && vid.videoHeight){
var asw = wrw/vid.videoWidth;
var ash = hrh/vid.videoHeight;
if (asw < ash){
holder.style.width = Math.ceil(vid.videoWidth*asw)+"px";
holder.style.height = Math.ceil(vid.videoHeight*asw)+"px";
holder.style.left = Math.ceil((Math.ceil(w/rw) - Math.ceil(vid.videoWidth*asw))/2);
holder.style.top = Math.ceil(( Math.ceil(h/rh) - Math.ceil(vid.videoHeight*asw))/2);
} else {
holder.style.width = Math.ceil(vid.videoWidth*ash)+"px";
holder.style.height = Math.ceil(vid.videoHeight*ash)+"px";
holder.style.left = Math.ceil((Math.ceil(w/rw) - Math.ceil(vid.videoWidth*ash))/2);
holder.style.top = Math.ceil((Math.ceil(h/rh) - Math.ceil(vid.videoHeight*ash))/2);
}
} else if (vid.width && vid.height){
var asw = wrw/vid.width;
var ash = hrh/vid.height;
if (asw < ash){
holder.style.width = Math.ceil(vid.width*asw)+"px";
holder.style.height = Math.ceil(vid.height*asw)+"px";
holder.style.left = Math.ceil((Math.ceil(w/rw) - Math.ceil(vid.width*asw))/2);
holder.style.top = Math.ceil(( Math.ceil(h/rh) - Math.ceil(vid.height*asw))/2);
} else {
holder.style.width = Math.ceil(vid.width*ash)+"px";
holder.style.height = Math.ceil(vid.height*ash)+"px";
holder.style.left = Math.ceil((Math.ceil(w/rw) - Math.ceil(vid.width*ash))/2);
holder.style.top = Math.ceil((Math.ceil(h/rh) - Math.ceil(vid.height*ash))/2);
}
} else if (wrw/hrh < arW/arH){
holder.style.width = "100%";
holder.style.height = "100%";
if (vid.tagName.toLowerCase()=="iframe"){
holder.style.height = "100%";
}
holder.style.left = 0;
holder.style.top = 0;
} else {
holder.style.width = "100%";
holder.style.height = "100%";
if (vid.tagName.toLowerCase()=="iframe"){
holder.style.width = "100%";
}
holder.style.left = 0;
holder.style.top = 0;
}
holder.style.position = "absolute";
vid.style.width = "100%";
vid.style.height = "100%";
var fontsize = (vid.offsetWidth + vid.offsetHeight)*0.03;
log("fontsize " +fontsize + " - " +vid.offsetWidth + " - " + vid.offsetHeight);
if (!fontsize){
if (vid.videoWidth/holder.offsetWidth > vid.videoHeight/holder.offsetHeight){
fontsize = (holder.offsetHeight + vid.videoHeight/holder.offsetHeight*vid.offsetWidth)*0.03;
} else {
fontsize = (holder.offsetWidth + vid.videoWidth/holder.offsetWidth*vid.offsetHeight)*0.03;
}
}
if (!fontsize){
fontsize = (holder.offsetWidth + holder.offsetHeight)*0.03;
}
log("fontsize final:" +fontsize);
if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID].label !== false && session.showlabels===true){ // remote source
// creates a video label holder inside the recently created label holder
var label = document.createElement("span");
if (session.labelstyle){
label.className = 'video-label '+session.labelstyle;
} else {
label.className = 'video-label';
}
if (fontsize){
if (session.labelsize){
fontsize = fontsize*session.labelsize/100;
}
fontsize = parseInt(fontsize);
fontsize = fontsize+"px";
label.style.fontSize = fontsize;
}
label.innerText = session.rpcs[vid.dataset.UUID].label;
holder.appendChild(label);
} else if ((session.showlabels===true) && (vid.id === "videosource") && (session.label)){ // local source
// creates a label holder that's the same size of the vid element.
// creates a video label holder inside the recently created label holder
var label = document.createElement("span");
if (session.labelstyle){
label.className = 'video-label '+session.labelstyle;
} else {
label.className = 'video-label';
}
if (fontsize){
if (session.labelsize){
fontsize = fontsize*session.labelsize/100;
}
fontsize = parseInt(fontsize);
fontsize = fontsize+"px";
label.style.fontSize = fontsize;
}
label.innerText = sanitizeLabel(session.label);//.replace(/[\W]+/g,"_").replace(/_+/g, ' ');
holder.appendChild(label);
}
if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID] && session.rpcs[vid.dataset.UUID].voiceMeter){
holder.appendChild(session.rpcs[vid.dataset.UUID].voiceMeter);
}
if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID] && session.rpcs[vid.dataset.UUID].remoteMuteElement){
holder.appendChild(session.rpcs[vid.dataset.UUID].remoteMuteElement);
}
try {
if (!(session.cleanOutput) || (session.cleanish==true)){
if (session.firstPlayTriggered===false){ // don't play unless needed; might cause clicking or who knows what else.
warnlog("VIDEO IS NOT PLAYING");
if (vid.tagName.toLowerCase()=="video"){ // we don't want to try playing an Iframe or Canvas.
var playPromise = vid.play();
if (playPromise !== undefined){
playPromise.then(_ => {
// playing
session.firstPlayTriggered=true; // global tracking. "user gesture obtained", so no longer needed if playing.
}).catch((err)=>{
var bigPlayButton = document.getElementById("bigPlayButton");
if (bigPlayButton){
bigPlayButton.innerHTML = '';
bigPlayButton.style.display="block";
}
});
} else {
session.firstPlayTriggered=true; // well, I don't know if it's playing, and so whatever. fail gracefully.
}
}
}
}
} catch(e) {
log("VIDEO IS PLAYING");
var bigPlayButton = document.getElementById("bigPlayButton");
if (bigPlayButton){
bigPlayButton.parentNode.removeChild(bigPlayButton);
}
}
if (vid.tagName.toLowerCase()=="iframe"){ // I need to add this back in at some point.
i+=1;
return;
}
if (!session.cleanOutput && !session.nocursor){
if ((session.roomid!==false) && (session.scene===false)){
var button = document.createElement("div");
button.id = "button_"+vid.id;
if (session.infocus){
button.innerHTML = "";
button.title = "Show all active videos togethers";
} else if (mediaPool.length>1){
button.innerHTML = "";
button.title = "Enlarge video and increase its clarity";
} else {
button.style.visibility = "hidden";
}
button.style.transition = "opacity 0.3s"
button.style.width ="4vh";
button.style.height = "4vh";
button.style.maxWidth ="30px";
button.style.maxHeight = "30px";
button.style.minWidth ="15px";
button.style.minHeight = "15px";
button.style.position = "absolute";
//button.style.display="none";
//button.style.opacity="10%";
button.style.zIndex="3";
button.style.right = "4vh";//(Math.ceil(w/rw) -30 - 30 + offsetx+Math.floor(((i%rw)+0)*w/rw))+"px";
button.style.top = "4vh";//( offsety + 30 + Math.floor((Math.floor(i/rw)+0)*h/rh + hi))+"px";
button.style.color = "white";
button.style.cursor = "pointer";
container.appendChild(button);
if (vid.id == "videosource"){
button.onclick = function(){
var target = event.currentTarget;
log(target);
if (session.infocus === true){
session.infocus = false;
//target.childNodes[0].className = 'las la-arrows-alt';
} else {
session.infocus = true;
log("session: myself");
//target.childNodes[0].className = 'las la-compress';
}
setTimeout(()=>updateMixer(),10);
};
} else {
button.dataset.UUID = vid.dataset.UUID;
button.onclick = function(event){
var target = event.currentTarget;
log("fullscreen");
log(target);
if (session.infocus === target.dataset.UUID){
//target.childNodes[0].className = 'las la-arrows-alt';
session.infocus = false;
} else {
//target.childNodes[0].className = 'las la-compress';
session.infocus = target.dataset.UUID;
//log("session:"+target.dataset.UUID);
}
setTimeout(()=>updateMixer(),10);
};
}
vid.onclick = function(){
button.style.display="block";
container.style.backgroundColor= "#4444";
button.style.opacity="100%";
};
button.onmouseenter = function(){
button.style.display="block";
container.style.backgroundColor= "#4444";
setTimeout(function(button){button.style.opacity="100%";},0,button);
};
container.onmouseenter = function(){
button.style.display="block";
container.style.backgroundColor= "#4444";
setTimeout(function(button){button.style.opacity="100%";},0,button);
};
container.onmouseleave = function(){
button.style.display="none";
container.style.backgroundColor= null;
button.style.opacity="10%";
};
}
}
i+=1;
});
}
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function(e) { // this listens for child IFRAMES.
if ("action" in e.data) {
if (e.data.action == "screen-share-ended") {
if (session.screenShareElement) {
if (e.source == session.screenShareElement.contentWindow) { // reject messages send from other iframes
warnlog(e);
session.screenShareElement.contentWindow.postMessage({
"close": true
}, '*');
session.screenShareElement.parentNode.removeChild(session.screenShareElement);
session.screenShareElement = false;
updateMixer();
getById("screenshare2button").classList.add("float");
getById("screenshare2button").classList.remove("float2");
}
}
}
}
});
function requestKeyframeScene(ele) {
var UUID = ele.dataset.UUID;
if (ele.dataset.value == 1) {
} else {
ele.dataset.value = 1;
ele.classList.add("pressed");
session.requestKeyframe(UUID, true);
setTimeout(function(el){
el.dataset.value = 0;
el.classList.remove("pressed");
}, 1000, ele)
}
}
function pokeIframeAPI(action, value = null, UUID = null) {
if (!isIFrame){return;}
try {
var data = {};
data.action = action;
if (value !== null) {
data.value = value;
}
if (UUID !== null) {
data.UUID = UUID;
}
if (isIFrame) {
parent.postMessage(data, "*");
}
} catch (e) {
errorlog(e);
}
}
function jumptoroom(event = null) {
if (event) {
if (event.which !== 13) {
return;
}
}
var arr = window.location.href.split('?');
var roomname = getById("joinroomID").value;
roomname = sanitizeRoomName(roomname);
if (roomname.length) {
var passStr = "";
var pass = prompt("Enter a password if provided, otherwise just click cancel"); //sanitizePassword(session.password);
if (pass && pass.length) {
session.password = sanitizePassword(pass);
passStr = "&password=" + session.password;
} else {
session.password = false;
}
if (arr.length > 1 && arr[1] !== '') {
window.location += "&room=" + roomname + passStr;
} else {
window.location += "?room=" + roomname + passStr;
}
}
}
function sleep(ms = 0) {
return new Promise(r => setTimeout(r, ms)); // LOLz!
}
////////// Canvas Effects ///////////////
function drawFrameMirrored() {
session.canvasCtx.save();
session.canvasCtx.scale(-1, 1);
session.canvasCtx.drawImage(session.canvasSource, 0, 0, session.canvas.width * -1, session.canvas.height);
session.canvasCtx.restore();
}
function setupCanvas() {
if (session.canvas === null) {
warnlog("SETUP CANVAS");
session.canvas = document.createElement("canvas");
session.canvas.width = 512;
session.canvas.height = 288;
session.canvasCtx = session.canvas.getContext('2d', {alpha: false, desynchronized: true});
//session.canvasCtx.width=288;
//session.canvasCtx.height=720;
session.canvasCtx.fillStyle = "blue";
session.canvasCtx.fillRect(0, 0, 512, 288);
session.canvasSource = document.createElement("video");
session.canvasSource.width=512;
session.canvasSource.height=288;
session.canvasSource.autoplay = true;
session.canvasSource.srcObject = new MediaStream();
}
}
function applyEffects(track, stream) {
setupCanvas();
session.canvasSource.srcObject.addTrack(track, stream);
session.canvasSource.width = track.getSettings().width || 1280;
session.canvasSource.height = track.getSettings().height || 720;
session.canvas.width = track.getSettings().width || 1280;
session.canvas.height = track.getSettings().height || 720;
var audioTracks = session.streamSrc.getAudioTracks();
session.streamSrc = session.canvas.captureStream();
audioTracks.forEach(function(trk) {
session.streamSrc.addTrack(trk);
});
session.videoElement.srcObject = session.streamSrc;
if (session.effects == 1) { // auto align face
setTimeout(function() {
drawFace();
}, 100);
} else if (session.effects == 2) { // mirror video at a canvas level
var drawRate = parseInt(1000 / track.getSettings().frameRate) + 1;
if (session.canvasInterval !== null) {
clearInterval(session.canvasInterval);
}
session.canvasInterval = setInterval(function() {
drawFrameMirrored();
}, drawRate);
} else if ((session.effects == 3) || (session.effects == 4) || (session.effects == 5)){ // blur & greenscreen (low and high)
TFLiteWorker();
} else if (session.effects == 6){
session.canvasSource.onloadeddata = mainMeshMask;
}
return session.streamSrc.getVideoTracks()[0];
}
function dataURItoArraybuffer(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return ab;
}
var makeImagesActive = null;
var makeImagesTimeout = null;
async function makeImages(startup=false){
if (makeImagesActive===true){return;}
if (!session.videoElement){return;}
if (session.videoMuted){return;}
if (!session.videoElement.srcObject.getVideoTracks().length){return;}
if (makeImagesActive===null){
makeImagesActive=true;
session.webPcanvas = document.createElement("canvas");
var width = 480;
var height = 270;
var timeout = 60;
if (session.webPquality===0){
width = 1920;
height = 1080;
timeout = 15;
} else if (session.webPquality===1){
width = 1280;
height = 720;
timeout = 15;
} else if (session.webPquality===2){
width = 640;
height = 360;
timeout = 15;
} else if (session.webPquality===3){
width = 480;
height = 270;
timeout = 15;
} else if (session.webPquality===4){
width = 480;
height = 270;
timeout = 30;
} else {
width = 480;
height = 270;
timeout = 60;
}
session.webPcanvas.width = width;
session.webPcanvas.height = height;
session.webPcanvas.timeout = timeout;
session.webPcanvasCtx = session.webPcanvas.getContext('2d', {alpha: false, desynchronized: true});
session.webPcanvasCtx.fillStyle = "black";
session.webPcanvasCtx.fillRect(0, 0, width, height);
} else {
makeImagesActive=true;
}
if (startup){
var exit = true;
for (var i in session.pcs){
if (session.pcs[i].allowBroadcast){ // just for safety, to avoid a race condition, double check that it's still not active.
exit = false;
}
}
if (exit){
makeImagesActive=false;
return;
}
}
try{
session.webPcanvasCtx.drawImage(session.videoElement, 0, 0, session.webPcanvas.width, session.webPcanvas.height);
const webp = session.webPcanvas.toDataURL("image/webp",0.6); // seems like a good balance here
const arrayBuffer = dataURItoArraybuffer(webp);
var broadcasting = false;
for (var i in session.pcs){
try{
if (session.pcs[i].allowBroadcast){ // only publish to those seeking this stream
broadcasting = true;
if (!session.pcs[i].sendChannel.bufferedAmount){ // only send when the buffer is free. don't clog it up or wait.
session.pcs[i].sendChannel.send(arrayBuffer);
}
}
} catch(e){}
}
} catch(e){
warnlog(e);
makeImagesActive=false;
return;
}
makeImagesActive=false;
if (broadcasting){ // cancel broadcasting since now one is active.
clearTimeout(makeImagesTimeout);
makeImagesTimeout = setTimeout(function(){
window.requestAnimationFrame(makeImages);
},session.webPcanvas.timeout);
} else {
for (var i in session.pcs){
if (session.pcs[i].allowBroadcast){ // just for safety, to avoid a race condition, double check that it's still not active.
clearTimeout(makeImagesTimeout);
makeImagesTimeout = setTimeout(function(){
window.requestAnimationFrame(makeImages);
},session.webPcanvas.timeout);
return;
}
}
log("Stopping webP broadcast.");
}
}
var updateUserListTimeout=null
var updateUserListActive = false;
function updateUserList(){
if (session.cleanOutput || session.scene!==false || !session.roomid || session.director){return;}
clearInterval(updateUserListTimeout);
updateUserListTimeout = setTimeout(function(){
if (updateUserListActive){return;}
updateUserListActive=true;
try {
var added = false;
getById("userList").innerHTML = "";
for (var UUID in session.rpcs){
var track = false;
if (session.rpcs[UUID].streamSrc && session.rpcs[UUID].streamSrc.getVideoTracks().length){
var tracks = session.rpcs[UUID].streamSrc.getVideoTracks().forEach(function(trk){
//if (!trk.muted){
track=true;
//}
});
}
if (((session.rpcs[UUID].videoMuted || (track==false && !session.rpcs[UUID].imageElement)) && !session.rpcs[UUID].canvas) || ( session.infocus && session.infocus!==UUID )){
if (UUID === session.directorUUID){
if (!session.rpcs[UUID].streamSrc){ // director not active yet, so we won't bother showing it.
continue;
}
}
var insert = document.createElement("div");
if (session.rpcs[UUID].label){
insert.innerText = session.rpcs[UUID].label + "";
} else if (session.directorUUID === UUID){
insert.innerText = "Director";
} else {
insert.innerText = "Unknown User";
}
getById("userList").appendChild(insert);
if (session.rpcs[UUID].remoteMuteState || !(session.rpcs[UUID].streamSrc)){
var muteInsert = document.createElement("div");
muteInsert.className = "video-mute-state-userlist";
muteInsert.innerHTML = '';
insert.appendChild(muteInsert);
} else if (session.rpcs[UUID].voiceMeter){
insert.appendChild(session.rpcs[UUID].voiceMeter);
}
//getById("userList").innerHTML += " ";
added=true;
}
}
if (!added){
getById("connectUsers").style.display = "none";
} else {
getById("connectUsers").style.display = "block";
}
} catch(e){}
updateUserListActive=false;
},200);
}
var LaunchTFWorkerCallback = false;
function TFLiteWorker(){
if (session.tfliteModule==false){
LaunchTFWorkerCallback=true
return;
}
if (TFLITELOADING){LaunchTFWorkerCallback=true;return;}
LaunchTFWorkerCallback=false;
const segmentationWidth = 256;
const segmentationHeight = 144;
const segmentationPixelCount = segmentationWidth * segmentationHeight;
const inputMemoryOffset = session.tfliteModule._getInputMemoryOffset() / 4;
const outputMemoryOffset = session.tfliteModule._getOutputMemoryOffset() / 4;
const segmentationMask = new ImageData(segmentationWidth, segmentationHeight);
const segmentationMaskCanvas = document.createElement('canvas');
segmentationMaskCanvas.width = segmentationWidth;
segmentationMaskCanvas.height = segmentationHeight;
const segmentationMaskCtx = segmentationMaskCanvas.getContext('2d');
session.tfliteModule.img = document.createElement("img");
session.tfliteModule.img.onload = function(){
URL.revokeObjectURL(session.tfliteModule.img.src); // no longer needed, free memory
session.tfliteModule.img.ready = true;
}
session.tfliteModule.img.src = "./media/bg_sample.jpg";
session.tfliteModule.img.ready = false;
console.log('Starting Loop');
function process(){
if (session.tfliteModule.activelyProcessing){return;}
session.tfliteModule.activelyProcessing=true;
try{
segmentationMaskCtx.drawImage(
session.canvasSource,
0,
0,
session.canvasSource.width,
session.canvasSource.height,
0,
0,
segmentationWidth,
segmentationHeight
)
const imageData = segmentationMaskCtx.getImageData(
0,
0,
segmentationWidth,
segmentationHeight
);
for (let i = 0; i < segmentationPixelCount; i++) {
session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3] = imageData.data[i * 4] / 255;
session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3 + 1] = imageData.data[i * 4 + 1] / 255;
session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3 + 2] = imageData.data[i * 4 + 2] / 255;
}
session.tfliteModule._runInference();
for (let i = 0; i < segmentationPixelCount; i++) {
const background = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2];
const person = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2 + 1];
const shift = Math.max(background, person);
const backgroundExp = Math.exp(background - shift);
const personExp = Math.exp(person - shift);
segmentationMask.data[i * 4 + 3] = (255 * personExp) / (backgroundExp + personExp); // softmax
}
segmentationMaskCtx.putImageData(segmentationMask, 0, 0);
session.canvasCtx.globalCompositeOperation = 'copy';
session.canvasCtx.filter = 'blur(4px)';
session.canvasCtx.drawImage(
segmentationMaskCanvas,
0,
0,
segmentationWidth,
segmentationHeight,
0,
0,
session.canvasSource.width,
session.canvasSource.height
)
session.canvasCtx.globalCompositeOperation = 'source-in';
session.canvasCtx.filter = 'none';
session.canvasCtx.drawImage(session.canvasSource, 0, 0);
session.canvasCtx.globalCompositeOperation = 'destination-over';
if (session.effects=="4"){ // greenscreen
session.canvasCtx.filter = 'none';
session.canvasCtx.fillStyle = "#0F0";
session.canvasCtx.fillRect(0, 0, session.canvas.width, session.canvas.height);
} else if (session.effects=="5"){
session.canvasCtx.filter = 'none';
if (session.tfliteModule.img.ready){
session.canvasCtx.drawImage(session.tfliteModule.img, 0, 0, session.canvas.width, session.canvas.height);
}
} else if (session.effects=="3"){ // BLUR
session.canvasCtx.filter = 'blur(4px)'; // Does not work on Safari
session.canvasCtx.drawImage(session.canvasSource, 0, 0);
} else {
session.tfliteModule.activelyProcessing=false;
return;
}
} catch (e){
errorlog(e);
session.tfliteModule.activelyProcessing=false;
return;
}
session.tfliteModule.activelyProcessing=false;
window.requestAnimationFrame(process);
}
process();
}
function mainMeshMask() {
if (model == false){
setTimeout(function(){mainMeshMask();},1000);
return;
}
function heatMapColorforValue(value){
var h = parseInt((1.0 - value) * 240);
if (h<0){h=0;}
if (h>240){h=240;}
return "hsl(" + h + ", 100%, 50%)";
}
async function innerLoop(){
const predictions = await model.estimateFaces({
input: session.canvasSource
});
if (predictions.length > 0) {
for (let j = 0; j < predictions.length; j++) {
const fp = predictions[j].annotations;
session.canvasCtx.fillStyle = "#000000";
session.canvasCtx.fillRect(0, 0, session.canvas.width, session.canvas.height);
const keypoints = predictions[j].scaledMesh
for (let i = 0; i < keypoints.length; i++) {
const [x, y, z] = keypoints[i];
session.canvasCtx.fillStyle = heatMapColorforValue((z+40)/60);
session.canvasCtx.fillRect(parseInt(x), parseInt(y), 5, 5);
}
}
}
window.requestAnimationFrame(innerLoop);
}
innerLoop();
}
function drawFace() {
var faceAlignment = (function() {
var vid = session.canvasSource;
var canvas = session.canvas;
var ctx = session.canvasCtx;
var canvas_tmp = document.createElement("canvas");
var ctx_tmp = canvas_tmp.getContext('2d');
//var stream = canvas.captureStream(30);
var image = new Image();
var zoom = 10;
var scale = 1;
var lastFace = {};
var w = vid.videoWidth;
var h = vid.videoHeight;
var x = vid.videoWidth / 2;
var y = vid.videoHeight / 2;
lastFace.x = vid.videoWidth / 2;
lastFace.y = vid.videoHeight / 2;
lastFace.w = vid.videoWidth;
lastFace.h = vid.videoHeight;
var yoffset = 0;
if (window.FaceDetector == undefined) {
//console.error('Face Detection not supported');
var faceDetector = false;
} else {
var faceDetector = new FaceDetector();
//console.log('FaceD Loaded');
setTimeout(function() {
detect();
}, 300);
setTimeout(function() {
draw();
}, 33);
}
canvas.height = vid.videoHeight;
canvas.width = vid.videoWidth;
canvas_tmp.height = vid.videoHeight;
canvas_tmp.width = vid.videoWidth;
image.src = canvas_tmp.toDataURL();
scale = canvas.width / image.width;
lastFace.x = 0;
lastFace.y = 0;
lastFace.w = 1280 / 3 / 16 * zoom;
lastFace.h = 720 / 3 / 9 * zoom;
w = 1280 / 5;
h = 720 / 5;
x = 1280 / 2;
y = 720 / 2 - w * 9 / zoom / 2;
async function detect() {
ctx_tmp.drawImage(vid, 0, 0, vid.videoWidth, vid.videoHeight);
image.src = canvas_tmp.toDataURL();
await faceDetector.detect(image).then(faces => {
if (faces.length === 0) {
log("NO FACES");
setTimeout(function() {
detect();
}, 10);
return;
}
for (let face of faces) {
lastFace.x = (face.boundingBox.x + lastFace.x) / 2 || face.boundingBox.x;
lastFace.y = (face.boundingBox.y + lastFace.y) / 2 || face.boundingBox.y;
lastFace.w = (face.boundingBox.width + lastFace.w) / 2 || face.boundingBox.width;
lastFace.h = (face.boundingBox.height + lastFace.h) / 2 || face.boundingBox.height;
}
setTimeout(function() {
detect();
}, 300);
}).catch((e) => {
console.error("Boo, Face Detection failed: " + e);
});
}
function draw() {
canvas.height = vid.videoHeight;
canvas.width = vid.videoWidth;
if (lastFace.w - w < 0.15 * lastFace.w) {
w = w * 0.999 + 0.001 * lastFace.w;
}
if (lastFace.h - h < 0.15 * lastFace.h) {
h = h * 0.999 + 0.001 * lastFace.h;
}
if (Math.abs(x - (lastFace.x + lastFace.w / 2)) > 0.15 * (lastFace.x + lastFace.w / 2.0)) {
x = x * 0.999 + 0.001 * (lastFace.x + lastFace.w / 2.0);
}
if (Math.abs(y - (lastFace.y + lastFace.h / 2)) > 0.15 * (lastFace.y + lastFace.h / 2.0)) {
y = y * 0.999 + 0.001 * (lastFace.y + lastFace.h / 2.0);
}
yoffset = w * 9 / zoom / 2;
var yyy = y - w * 9 / zoom - yoffset;
var hhh = w * 3 * 9 / zoom;
var www = w * 3 * 16 / zoom;
var xxx = x - w * 16 / zoom * 1.5;
if (www + xxx < 1280) {
xxx = 1280 - www;
}
if (hhh + yyy < 720) {
yyy = 720 - hhh;
}
if (www + xxx > 1280) {
xxx = 1280 - www;
}
if (hhh + yyy > 720) {
yyy = 720 - hhh;
}
if (yyy < 0) {
yyy = 0;
}
if (xxx < 0) {
xxx = 0;
}
ctx.drawImage(vid, xxx, yyy, www, hhh, 0, 0, vid.videoWidth, vid.videoHeight);
setTimeout(function() {
draw();
}, 30);
}
})();
}
//////// END CANVAS EFFECTS ///////////////////
if (urlParams.has('permaid') || urlParams.has('push')) {
session.permaid = urlParams.get('push') || urlParams.get('permaid');
if (session.permaid) {
session.streamID = sanitizeStreamID(session.permaid);
} else {
session.permaid = null;
}
if (urlParams.has('permaid')) {
updateURL("permaid=" + session.streamID, true, false); // I'm not deleting the permaID first tho...
} else {
updateURL("push=" + session.streamID, true, false); // I'm not deleting the permaID first tho...
}
if (urlParams.has('director') || urlParams.has('dir')) { // if I do a short form of this, it will cause duplications in the code elsewhere.
//var director_room_input = urlParams.get('director');
//director_room_input = sanitizeRoomName(director_room_input);
//createRoom(director_room_input);
session.permaid = false; // used to avoid a trigger later on.
} else {
getById("container-1").className = 'column columnfade advanced';
getById("container-4").className = 'column columnfade advanced';
getById("dropButton").className = 'column columnfade advanced';
getById("info").innerHTML = "";
if (session.videoDevice === 0) {
getById("add_camera").innerHTML = "Share your Microphone";
miniTranslate(getById("add_camera"), "share-your-mic");
} else {
getById("add_camera").innerHTML = "Share your Camera";
miniTranslate(getById("add_camera"), "share-your-camera");
}
getById("add_screen").innerHTML = "Share your Screen";
miniTranslate(getById("add_screen"), "share-your-screen");
getById("passwordRoom").value = "";
getById("videoname1").value = "";
getById("dirroomid").innerHTML = "";
getById("roomid").innerHTML = "";
getById("mainmenu").style.alignSelf = "center";
getById("mainmenu").classList.add("mainmenuclass");
getById("header").style.alignSelf = "center";
if ((iOS) || (iPad)) {
getById("header").style.display = "none"; // just trying to free up space.
}
if (session.webcamonly == true) { // mobile or manual flag 'webcam' pflag set
getById("head1").innerHTML = '- Please accept any camera permissions';
} else {
getById("head1").innerHTML = ' - Please select which you wish to share';
}
}
}
if ((session.roomid) || (urlParams.has('roomid')) || (urlParams.has('r')) || (urlParams.has('room')) || (filename) || (session.permaid !== false)) {
var roomid = "";
if (filename) {
roomid = filename;
} else if (urlParams.has('room')) {
roomid = urlParams.get('room');
} else if (urlParams.has('roomid')) {
roomid = urlParams.get('roomid');
} else if (urlParams.has('r')) {
roomid = urlParams.get('r');
} else if (session.roomid) {
roomid = session.roomid;
}
session.roomid = sanitizeRoomName(roomid);
if (!(session.cleanOutput)) {
if (session.roomid === "test") {
if (session.password === session.defaultPassword) {
var testRoomResponse = confirm("The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?");
if (testRoomResponse == false) {
hangup();
throw new Error("User requested to not enter room 'room'.");
}
}
}
}
if (session.audioDevice === false) {
getById("headphonesDiv2").style.display = "inline-block";
getById("headphonesDiv").style.display = "inline-block";
}
getById("addPasswordBasic").style.display = "none";
getById("info").innerHTML = "";
getById("info").style.color = "#CCC";
getById("videoname1").value = session.roomid;
getById("dirroomid").innerText = session.roomid;
getById("roomid").innerText = session.roomid;
getById("container-1").className = 'column columnfade advanced';
getById("container-4").className = 'column columnfade advanced';
getById("container-7").style.display = 'none';
getById("container-8").style.display = 'none';
getById("mainmenu").style.alignSelf = "center";
getById("mainmenu").classList.add("mainmenuclass");
getById("header").style.alignSelf = "center";
if (session.webcamonly == true) { // mobile or manual flag 'webcam' pflag set
getById("head1").innerHTML = '';
} else {
getById("head1").innerHTML = 'Please select an option to join.';
}
if (session.roomid.length > 0) {
if (session.videoDevice === 0) {
if (session.audioDevice === 0) {
getById("add_camera").innerHTML = "Join room";
miniTranslate(getById("add_camera"), "join-room");
} else {
getById("add_camera").innerHTML = "Join room with Microphone";
miniTranslate(getById("add_camera"), "join-room-with-mic");
}
} else {
getById("add_camera").innerHTML = "Join Room with Camera";
miniTranslate(getById("add_camera"), "join-room-with-camera");
}
getById("add_screen").innerHTML = "Screenshare with Room";
miniTranslate(getById("add_screen"), "share-screen-with-room");
} else {
if (session.videoDevice === 0) {
getById("add_camera").innerHTML = "Share your Microphone";
miniTranslate(getById("add_camera"), "share-your-mic");
} else {
getById("add_camera").innerHTML = "Share your Camera";
miniTranslate(getById("add_camera"), "share-your-camera");
}
getById("add_screen").innerHTML = "Share your Screen";
miniTranslate(getById("add_screen"), "share-your-screen");
}
getById("head3").className = 'advanced';
if (session.scene !== false) {
getById("container-4").className = 'column columnfade';
getById("container-3").className = 'column columnfade';
getById("container-2").className = 'column columnfade';
getById("container-1").className = 'column columnfade';
getById("header").className = 'advanced';
getById("info").className = 'advanced';
getById("head1").className = 'advanced';
getById("head2").className = 'advanced';
getById("mainmenu").style.display = "none";
getById("translateButton").style.display = "none";
log("Update Mixer Event on REsize SET");
window.addEventListener("resize", updateMixer);
window.addEventListener("orientationchange", function() {
setTimeout(updateMixer, 200);
});
joinRoom(session.roomid); // this is a scene, so we want high resolutions
getById("main").style.overflow = "hidden";
} else {
if ((session.permaid === null) && (session.roomid == "")) {
if (!(session.cleanOutput)) {
getById("head3").className = '';
}
}
}
} else if (urlParams.has('director') || urlParams.has('dir')) { // if I do a short form of this, it will cause duplications in the code elsewhere.
if (directorLanding == false) {
var director_room_input = urlParams.get('director') || urlParams.get('dir');
director_room_input = sanitizeRoomName(director_room_input);
log("director_room_input:" + director_room_input);
createRoom(director_room_input);
}
} else if ((session.view) && (session.permaid === false)) {
session.audioMeterGuest = false;
if (session.audioEffects === null) {
session.audioEffects = false;
}
log("Update Mixer Event on REsize SET");
getById("translateButton").style.display = "none";
window.addEventListener("resize", updateMixer);
window.addEventListener("orientationchange", function() {
setTimeout(updateMixer, 200);
});
getById("main").style.overflow = "hidden";
}
if (session.audioEffects === null) {
session.audioEffects = true;
}
if (session.audioEffects) {
getById("channelGroup1").style.display = "block";
getById("channelGroup2").style.display = "block";
}
if (urlParams.has('hidemenu') || urlParams.has('hm')) { // needs to happen the room and permaid applications
getById("mainmenu").style.display = "none";
getById("header").style.display = "none";
getById("mainmenu").style.opacity = 0;
getById("header").style.opacity = 0;
}
if (urlParams.has('hideheader') || urlParams.has('noheader') || urlParams.has('hh')) { // needs to happen the room and permaid applications
getById("header").style.display = "none";
getById("header").style.opacity = 0;
}
function checkConnection() {
if (session.ws === null) {
return;
}
if (document.getElementById("qos")) { // true or false; null might cause problems?
if ((session.ws) && (session.ws.readyState === WebSocket.OPEN)) {
getById("qos").style.color = "white";
} else {
getById("qos").style.color = "red";
}
}
}
setInterval(function() {
checkConnection();
}, 5000);
function remoteStats(msg){
if (session.director){
var output = "";
var size = 0;
for (var key in msg.remoteStats) {
if (msg.remoteStats.hasOwnProperty(key)) size++;
}
output += "Total Viewers: "+size;
for (var uuid in msg.remoteStats){
if ("scene" in msg.remoteStats[uuid] && msg.remoteStats[uuid].scene !== false){
output+="scene: "+msg.remoteStats[uuid].scene;
if ("video_bitrate_kbps" in msg.remoteStats[uuid]){
output+=" video_bitrate_kbps: "+msg.remoteStats[uuid].video_bitrate_kbps;
}
if ("audio_bitrate_kbps" in msg.remoteStats[uuid]){
output+=" audio_bitrate_kbps: "+msg.remoteStats[uuid].audio_bitrate_kbps;
}
if (msg.remoteStats[uuid].resolution){
output+=" resolution: "+msg.remoteStats[uuid].resolution;
}
if (msg.remoteStats[uuid].video_encoder){
output+=" video_encoder: "+msg.remoteStats[uuid].video_encoder;
}
if ("scaleFactor" in msg.remoteStats[uuid]){
output+=" scaleFactor: "+msg.remoteStats[uuid].scaleFactor;
}
if ("nacks_per_second" in msg.remoteStats[uuid]){
output+=" nacks_per_second: "+msg.remoteStats[uuid].nacks_per_second;
}
if (msg.remoteStats[uuid].retransmitted_kbps){
output+=" quality_limitation_reason: "+msg.remoteStats[uuid].retransmitted_kbps;
}
if (msg.remoteStats[uuid].quality_limitation_reason){
output+=" quality_limitation_reason: "+msg.remoteStats[uuid].quality_limitation_reason;
}
}
}
warnUser(output);
};
if (isIFrame){
parent.postMessage({"remoteStats": msg.remoteStats }, "*");
}
}
function printViewStats(menu, statsObj, streamID) { // Stats for viewing a remote video
var scrollLeft = menu.scrollLeft;
var scrollTop = menu.scrollTop;
//menu.innerHTML="rae:"+session.audioEffects+ ", lae:"+ !session.disableWebAudio;
menu.innerHTML = "StreamID: " + streamID + " ";
menu.innerHTML += printValues(statsObj);
menu.scrollTop = scrollTop;
menu.scrollLeft = scrollLeft;
}
function printValues(obj) { // see: printViewStats
var out = "";
for (var key in obj) {
if (typeof obj[key] === "object") {
if (obj[key] != null) {
var tmp = key;
tmp = sanitizeChat((tmp));
out += "
" + tmp + "
"
out += printValues(obj[key]);
}
} else {
if (key.startsWith("_")) {
// if it starts with _, we don't want to show it.
} else {
try {
var unit = '';
var value = obj[key];
var stat = sanitizeChat(key);
if (typeof obj[key] == "string") {
value = sanitizeChat((value));
}
//
if (key == 'useragent') {
value = ""+value+""
}
if (key == 'Bitrate_in_kbps') {
var unit = " kbps";
stat = "Bitrate";
}
else if (key == 'type') {
var unit = "";
stat = 'Type';
if (value == "Audio Track") {
value = "🔊 " + value;
//out += "";
}
if (value == "Video Track") {
value = "📺 " + value;
}
}
else if (key == 'packetLoss_in_percentage') {
var unit = " %";
stat = 'Packet Loss 📶';
value = parseInt(parseFloat(value) * 10000) / 10000.0;
}
else if (key == 'local_relayIP') {
value = "" + value + "";
}
else if (key == 'remote_relay_IP') {
value = "" + value + "";
}
else if (key == 'local_candidateType') {
if (value == "relay") {
value = "💸 relay server";
}
}
else if (key == 'remote_candidateType') {
if (value == "relay") {
value = "💸 relay server";
}
}
else if (key == 'height_url') {
if (value == false) {
continue;
}
}
else if (key == 'width_url') {
if (value == false) {
continue;
}
}
else if (key == 'height_url') {
if (value == false) {
continue;
}
}
else if (key == 'version') {
stat = "OBS.Ninja Version";
} else if (key == 'platform') {
stat = "Platform (OS)";
}
else if (key == 'aec_url') {
stat = "Echo-Cancellation";
}
else if (key == 'agc_url') {
stat = "Auto-Gain (agc)";
}
else if (key == 'denoise_url') {
stat = "De-noising ";
}
else if (key == 'audio_level') {
stat = "Audio Level";
}
else if (key == 'Buffer_Delay_in_ms') {
var unit = " ms";
stat = 'Buffer Delay';
}
else if (value === null) {
value = "null";
}
else if (key == "stereo_url") {
stat = "Pro-Audio (Stereo-mode)";
if (value == 3) {
value = "3 (outbound hi-fi) Use Headphones";
} else if (value == 1) {
value = "1 (in & out hi-fi) Use Headphones";
} else if (value == 2) {
value = "3 (inbound hi-fi)";
} else if (value == 4) {
value = "3 (multichannel) Use Headphones";
} else if (value == 5) {
value = "5 (auto-mode) Use Headphones";
}
}
else if (value === false) {
continue
}
else if (value === "false") {
continue
}
out += "
" + stat + "" + value + unit + "
";
} catch (e) {
warnlog(e);
}
}
}
}
return out;
}
function printMyStats(menu) { // see: setupStatsMenu
var scrollLeft = getById("menuStatsBox").scrollLeft;
var scrollTop = getById("menuStatsBox").scrollTop;
menu.innerHTML = "";
session.stats.outbound_connections = Object.keys(session.pcs).length;
session.stats.inbound_connections = Object.keys(session.rpcs).length;
function printViewValues(obj) {
if (!(document.getElementById("menuStatsBox"))){
return;
}
for (var key in obj) {
if (typeof obj[key] === "object") {
printViewValues(obj[key]);
} else {
if (key.startsWith("_")){continue;}
var stat = sanitizeChat(key);
var value = obj[key];
if (typeof value == "string") {
value = sanitizeChat((value));
}
if (value === false){continue;}
if (key == 'useragent') {
value = ""+value+""
}
if (key == 'local_relayIP') {
value = "" + value + "";
}
if (key == 'remote_relay_IP') {
value = "" + value + "";
}
if (key == 'local_candidateType') {
if (value == "relay") {
value = "💸 relay server";
}
}
if (key == 'remote_candidateType') {
if (value == "relay") {
value = "💸 relay server";
}
}
menu.innerHTML += "
" + stat + "" + value + "
";
}
}
}
printViewValues(session.stats);
menu.innerHTML += "";
for (var uuid in session.pcs) {
printViewValues(session.pcs[uuid].stats);
menu.innerHTML += "";
}
try {
getById("menuStatsBox").scrollLeft = scrollLeft;
getById("menuStatsBox").scrollTop = scrollTop;
} catch (e) {}
}
function updateLocalStats(){
var totalBitrate = 0;
var totalBitrate2 = 0;
var cpuLimited = false;
for (var uuid in session.pcs) {
if ("video_bitrate_kbps" in session.pcs[uuid].stats){
totalBitrate+=session.pcs[uuid].stats.video_bitrate_kbps || 0;
}
if ("audio_bitrate_kbps" in session.pcs[uuid].stats){
totalBitrate+=session.pcs[uuid].stats.audio_bitrate_kbps || 0;
}
if ("total_sending_bitrate_kbps" in session.pcs[uuid].stats){
totalBitrate2+=session.pcs[uuid].stats.total_sending_bitrate_kbps || 0;
}
if ("quality_limitation_reason" in session.pcs[uuid].stats){
if (session.pcs[uuid].stats.quality_limitation_reason == "cpu"){
cpuLimited=true;
}
}
if (uuid in session.rpcs){
if (session.pcs[uuid].stats.label){
session.pcs[uuid].stats.label = session.rpcs[uuid].label;
}
if (session.pcs[uuid].stats.streamID){
session.pcs[uuid].stats.streamID = session.rpcs[uuid].streamID;
}
}
setTimeout(function(UUID) {
if (!( session.pcs[UUID])){return;}
session.pcs[UUID].getStats().then(function(stats) {
if ("audio_bitrate_kbps" in session.pcs[UUID].stats){
session.pcs[UUID].stats.audio_bitrate_kbps=0;
}
stats.forEach(stat => {
if (stat.type == "transport"){
if ("bytesSent" in stat) {
if ("_bytesSent" in session.pcs[UUID].stats){
if (session.pcs[UUID].stats._timestamp){
if (stat.timestamp){
session.pcs[UUID].stats.total_sending_bitrate_kbps = parseInt(8*(stat.bytesSent - session.pcs[UUID].stats._bytesSent)/(stat.timestamp - session.pcs[UUID].stats._timestamp));
}
}
}
session.pcs[UUID].stats._bytesSent = stat.bytesSent;
}
if ("timestamp" in stat) {
session.pcs[UUID].stats._timestamp = stat.timestamp;
}
} else if (stat.type == "outbound-rtp") {
if (stat.kind == "video") {
if ("qualityLimitationReason" in stat) {
session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason;
}
if ("framesPerSecond" in stat) {
session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond;
}
if ("encoderImplementation" in stat) {
session.pcs[UUID].stats.video_encoder = stat.encoderImplementation;
}
if ("bytesSent" in stat) {
if ("_bytesSentVideo" in session.pcs[UUID].stats){
if (session.pcs[UUID].stats._timestamp1){
session.pcs[UUID].stats.video_bitrate_kbps = parseInt(8*(stat.bytesSent - session.pcs[UUID].stats._bytesSentVideo)/(stat.timestamp - session.pcs[UUID].stats._timestamp1));
if (stat.timestamp){
}
}
}
session.pcs[UUID].stats._bytesSentVideo = stat.bytesSent;
}
if ("nackCount" in stat) {
if ("_nackCount" in session.pcs[UUID].stats){
if (session.pcs[UUID].stats._timestamp1){
if (stat.timestamp){
session.pcs[UUID].stats.nacks_per_second = parseInt(10000*(stat.nackCount - session.pcs[UUID].stats._nackCount)/(stat.timestamp - session.pcs[UUID].stats._timestamp1))/10;
}
}
}
}
if ("retransmittedBytesSent" in stat) {
if ("_retransmittedBytesSent" in session.pcs[UUID].stats){
if (session.pcs[UUID].stats._timestamp1){
if (stat.timestamp){
session.pcs[UUID].stats.retransmitted_kbps = parseInt(8*(stat.retransmittedBytesSent - session.pcs[UUID].stats._retransmittedBytesSent)/(stat.timestamp - session.pcs[UUID].stats._timestamp1));
}
}
}
}
if ("nackCount" in stat) {
session.pcs[UUID].stats._nackCount = stat.nackCount;
}
if ("retransmittedBytesSent" in stat) {
session.pcs[UUID].stats._retransmittedBytesSent = stat.retransmittedBytesSent;
}
if ("timestamp" in stat) {
session.pcs[UUID].stats._timestamp1 = stat.timestamp;
}
if ("pliCount" in stat) {
session.pcs[UUID].stats.total_pli_count = stat.pliCount;
}
if ("keyFramesEncoded" in stat) {
session.pcs[UUID].stats.total_key_frames_encoded = stat.keyFramesEncoded;
}
} else if (stat.kind == "audio") {
if ("bytesSent" in stat) {
if (session.pcs[UUID].stats._bytesSentAudio){
if (session.pcs[UUID].stats._timestamp2){
if (stat.timestamp){
if ("audio_bitrate_kbps" in session.pcs[UUID].stats){
session.pcs[UUID].stats.audio_bitrate_kbps += parseInt(8*(stat.bytesSent - session.pcs[UUID].stats._bytesSentAudio)/(stat.timestamp - session.pcs[UUID].stats._timestamp2));
} else {
session.pcs[UUID].stats.audio_bitrate_kbps=0;
}
}
}
}
}
if ("timestamp" in stat) {
session.pcs[UUID].stats._timestamp2 = stat.timestamp;
}
if ("bytesSent" in stat) {
session.pcs[UUID].stats._bytesSentAudio = stat.bytesSent;
}
}
} else if (stat.type == "remote-candidate") {
if ("relayProtocol" in stat) {
if ("ip" in stat) {
session.pcs[UUID].stats.remote_relay_IP = stat.ip;
}
session.pcs[UUID].stats.remote_relayProtocol = stat.relayProtocol;
}
if ("candidateType" in stat) {
session.pcs[UUID].stats.remote_candidateType = stat.candidateType;
}
} else if (stat.type == "local-candidate") {
if ("relayProtocol" in stat) {
if ("ip" in stat) {
session.pcs[UUID].stats.local_relayIP = stat.ip;
}
session.pcs[UUID].stats.local_relayProtocol = stat.relayProtocol;
}
if ("candidateType" in stat) {
session.pcs[UUID].stats.local_candidateType = stat.candidateType;
}
} else if ((stat.type == "candidate-pair" ) && (stat.nominated)) {
if ("availableOutgoingBitrate" in stat){
session.pcs[UUID].stats.available_outgoing_bitrate_kbps = parseInt(stat.availableOutgoingBitrate/1024);
}
if ("totalRoundTripTime" in stat){
session.pcs[UUID].stats.total_roundTripTime_ms = stat.totalRoundTripTime*1000;
}
}
return;
});
return;
});
}, 0, uuid);
}
var headerStats = "Viewers: ";
headerStats += Object.keys(session.pcs).length || 0;
headerStats += ", Upload (kbps): "+totalBitrate2; // + " / "+totalBitrate;
if (cpuLimited){
headerStats += ", CPU Overloaded";
}
if (Object.keys(session.pcs).length){
getById("head5").classList.remove("advanced");
}
getById("head5").innerHTML = headerStats;
}
function updateStats(obsvc = false) {
log('updateStats - resolution found');
if (document.getElementById('previewWebcam')) {
var ele = document.getElementById('previewWebcam');
var wcs = "webcamstats";
} else if (document.getElementById('videosource')) {
var ele = document.getElementById('videosource');
var wcs = "webcamstats3";
} else {
return;
}
try {
getById(wcs).innerHTML = "";
ele.srcObject.getVideoTracks().forEach(
function(track) {
if ((obsvc) && (parseInt(track.getSettings().frameRate) == 30)) {
getById(wcs).innerHTML = "Video Settings: " + (track.getSettings().width || 0) + "x" + (track.getSettings().height || 0) + " @ up to 60fps";
} else {
getById(wcs).innerHTML = "Current Video Settings: " + (track.getSettings().width || 0) + "x" + (track.getSettings().height || 0) + "@" + (parseInt(track.getSettings().frameRate * 10) / 10) + "fps";
}
}
);
} catch (e) {
errorlog(e);
}
}
function toggleMute(apply = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched.
log("muting");
if (session.director) {
if (!session.directorEnabledPPT) {
log("Director doesn't have PPT enabled yet");
// director has not enabled PTT yet.
return;
}
}
if (apply) {
session.muted = !session.muted; // we flip here as we are going to flip again in a second.
}
//try{var ptt = getById("press2talk");} catch(e){var ptt=false;}
if (session.muted == false) {
session.muted = true;
getById("mutetoggle").className = "las la-microphone-slash my-float toggleSize";
if (!(session.cleanOutput)){
getById("mutebutton").className = "float2 red puslate";
getById("header").classList.add('red');
if (session.localMuteElement){
session.localMuteElement.style.display = "block";
}
}
if (session.streamSrc) {
session.streamSrc.getAudioTracks().forEach((track) => {
track.enabled = false;
});
}
//if (ptt){
// ptt.innerHTML = "🔇 Push to Talk";
//}
} else {
session.muted = false;
getById("mutetoggle").className = "las la-microphone my-float toggleSize";
if (!(session.cleanOutput)){
getById("mutebutton").className = "float";
getById("header").classList.remove('red');
if (session.localMuteElement){
session.localMuteElement.style.display = "none";
}
}
if (session.streamSrc) {
session.streamSrc.getAudioTracks().forEach((track) => {
track.enabled = true;
});
}
//if (ptt){
// ptt.innerHTML = "🔴 Push to Mute";
//}
}
if (document.getElementById("screensharesource")){
document.getElementById("screensharesource").contentWindow.postMessage({"mic":!session.muted}, '*');
}
if (!apply) { // only if they are changing states do we bother to spam.
data = {};
data.muteState = session.muted;
session.sendMessage(data);
log("SEND DATA");
pokeIframeAPI('mic-mute-state', session.muted);
}
}
function toggleSpeakerMute(apply = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (CtrlPressed) {
resetupAudioOut();
}
if (apply) {
session.speakerMuted = !session.speakerMuted;
}
if (session.speakerMuted == false) {
session.speakerMuted = true;
getById("mutespeakertoggle").className = "las la-volume-mute my-float toggleSize";
if (!(session.cleanOutput)){
getById("mutespeakerbutton").className = "float2 red";
}
var sounds = document.getElementsByTagName("video");
for (var i = 0; i < sounds.length; ++i) {
sounds[i].muted = session.speakerMuted;
}
} else {
session.speakerMuted = false;
getById("mutespeakertoggle").className = "las la-volume-up my-float toggleSize";
if (!(session.cleanOutput)){
getById("mutespeakerbutton").className = "float";
}
var sounds = document.getElementsByTagName("video");
for (var i = 0; i < sounds.length; ++i) {
if (sounds[i].id === "videosource") { // don't unmute ourselves. feedback galore if so.
continue;
} else if (sounds[i].id === "previewWebcam") {
continue;
} else if (sounds[i].id === "screenshare") {
continue;
} else {
sounds[i].muted = session.speakerMuted;
}
}
}
for (var UUID in session.rpcs) {
if (session.rpcs[UUID].videoElement) {
//if (UUID === session.directorUUID) {
// session.rpcs[UUID].videoElement.muted = false; // unmute director
// log("MAKE SURE DIRECTOR ISN'T MUTED");
//} else {
session.rpcs[UUID].videoElement.muted = session.speakerMuted;
// }
}
}
if ((iOS) || (iPad)) {
resetupAudioOut();
}
}
function toggleChat(event = null) { // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (session.chat == false) {
setTimeout(function() {
document.addEventListener("click", toggleChat);
}, 10);
getById("chatModule").addEventListener("click", function(e) {
e.stopPropagation();
return false;
});
session.chat = true;
getById("chattoggle").className = "las la-comment-dots my-float toggleSize";
getById("chatbutton").className = "float2";
getById("chatModule").style.display = "block";
getById("chatInput").focus(); // give it keyboard focus
} else {
session.chat = false;
getById("chattoggle").className = "las la-comment-alt my-float toggleSize";
getById("chatbutton").className = "float";
getById("chatModule").style.display = "none";
document.removeEventListener("click", toggleChat);
getById("chatModule").removeEventListener("click", function(e) {
e.stopPropagation();
return false;
});
}
if (getById("chatNotification").value) {
getById("chatNotification").value = 0;
}
getById("chatNotification").classList.remove("notification");
}
function directorAdvanced(ele) {
var target = document.createElement("div");
target.style = "position:absolute;float:left;width:270px;height:222px;background-color:#7E7E7E;";
var closeButton = document.createElement("button");
closeButton.innerHTML = " close";
closeButton.style.left = "5px";
closeButton.style.position = "relative";
closeButton.onclick = function() {
target.parentNode.removeChild(target);
};
target.appendChild(closeButton);
var someButton = document.createElement("button");
someButton.innerHTML = " some action ";
someButton.style.left = "5px";
someButton.style.position = "relative";
someButton.onclick = function() {
var actionMsg = {};
session.sendRequest(actionMsg, ele.dataset.UUID);
};
target.appendChild(someButton);
ele.parentNode.appendChild(target);
}
function directorSendMessage(ele) {
var target = document.createElement("div");
target.style = "position:absolute;float:left;width:270px;height:222px;background-color:#7E7E7E;";
var inputField = document.createElement("textarea");
inputField.placeholder = "Enter your message here";
inputField.style.width = "255px";
inputField.style.height = "170px";
inputField.style.margin = "5px 10px 5px 10px";
inputField.style.padding = "5px";
target.appendChild(inputField);
var sendButton = document.createElement("button");
sendButton.innerHTML = " send message ";
sendButton.style.left = "5px";
sendButton.style.position = "relative";
sendButton.onclick = function() {
var chatMsg = {};
chatMsg.chat = inputField.value;
if (sendButton.parentNode.overlay) {
chatMsg.overlay = sendButton.parentNode.overlay;
}
session.sendRequest(chatMsg, ele.dataset.UUID);
inputField.value = "";
//target.parentNode.removeChild(target);
};
var closeButton = document.createElement("button");
closeButton.innerHTML = " close";
closeButton.style.left = "5px";
closeButton.style.position = "relative";
closeButton.onclick = function() {
inputField.value = "";
target.parentNode.removeChild(target);
};
var overlayMsg = document.createElement("span");
overlayMsg.style.left = "16px";
overlayMsg.style.top = "6px";
overlayMsg.style.position = "relative";
overlayMsg.innerHTML = "";
target.overlay = true;
overlayMsg.onclick = function(e) {
log(e.target.parentNode.parentNode);
if (e.target.parentNode.parentNode.overlay === true) {
e.target.parentNode.parentNode.overlay = false;
e.target.parentNode.innerHTML = "";
} else {
e.target.parentNode.parentNode.overlay = true;
e.target.parentNode.innerHTML = "";
}
}
inputField.addEventListener("keydown", function(e) {
if (e.keyCode == 13) {
e.preventDefault();
sendButton.click();
} else if (e.keyCode == 27) {
e.preventDefault();
inputField.value = "";
target.parentNode.removeChild(target);
}
});
target.appendChild(closeButton);
target.appendChild(sendButton);
target.appendChild(overlayMsg);
ele.parentNode.appendChild(target);
inputField.focus();
inputField.select();
}
function toggleVideoMute(apply = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (apply) {
session.videoMuted = !session.videoMuted;
}
if (session.videoMuted == false) {
session.videoMuted = true;
getById("mutevideotoggle").className = "las la-video-slash my-float toggleSize";
if (!(session.cleanOutput)){
getById("mutevideobutton").className = "float2 red";
}
if (session.streamSrc) {
session.streamSrc.getVideoTracks().forEach((track) => {
track.enabled = false;
});
}
} else {
session.videoMuted = false;
getById("mutevideotoggle").className = "las la-video my-float toggleSize";
if (!(session.cleanOutput)){
getById("mutevideobutton").className = "float";
}
if (session.streamSrc) {
session.streamSrc.getVideoTracks().forEach((track) => {
// try {
// if (document.querySelector("select#videoSource3").value == "ZZZ"){
// return;
// }
// } catch(e){}
track.enabled = true;
});
}
}
if (!apply) {
var msg = {};
msg.videoMuted = session.videoMuted;
session.sendMessage(msg);
pokeIframeAPI('video-mute-state', session.videoMuted);
if (!session.videoMuted){makeImages();}
}
}
var toggleSettingsState = false;
function toggleSettings(forceShow = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched.
getById("multiselect-trigger3").dataset.state = "0";
getById("multiselect-trigger3").classList.add('closed');
getById("multiselect-trigger3").classList.remove('open');
getById("chevarrow2").classList.add('bottom');
if (toggleSettingsState == true) {
if (forceShow == true) {
enumerateDevices().then(gotDevices2);
return;
}
} // don't close if already open
if (getById("popupSelector").style.display == "none") {
updateConstraintSliders();
setTimeout(function() {
document.addEventListener("click", toggleSettings);
}, 10);
getById("popupSelector").addEventListener("click", function(e) {
e.stopPropagation();
return false;
});
if (navigator.userAgent.indexOf('Chrome') != -1) {
try {
navigator.permissions.query({
name: "camera"
}).then(function(promise) {
if (promise && promise.state) {
if (promise.state == "prompt") {
navigator.mediaDevices.getUserMedia({
video: true
, audio: false
}).then(function(stream) {
enumerateDevices().then(gotDevices2).then(function() {
stream.getTracks().forEach(function(track) {
//stream.removeTrack(track);
track.stop(); // clean up?
});
});
}).catch(function(err) {
enumerateDevices().then(gotDevices2).then(function() {});
});
} else {
enumerateDevices().then(gotDevices2).then(function() {});
}
// console.log(promise.state); //"granted", "prompt" or "rejected"
} else {
enumerateDevices().then(gotDevices2).then(function() {});
}
});
} catch (e) {
enumerateDevices().then(gotDevices2).then(function() {});
}
} else {
enumerateDevices().then(gotDevices2).then(function() {});
}
getById("popupSelector").style.display = "inline-block"
getById("settingsbutton").classList.add("float2");
getById("settingsbutton").classList.remove("float");
setTimeout(function() {
getById("popupSelector").style.right = "0px";
}, 1);
toggleSettingsState = true;
} else {
document.removeEventListener("click", toggleSettings);
getById("popupSelector").removeEventListener("click", function(e) {
e.stopPropagation();
return false;
});
getById("popupSelector").style.right = "-400px";
getById("settingsbutton").classList.add("float");
getById("settingsbutton").classList.remove("float2");
setTimeout(function() {
getById("popupSelector").style.display = "none";
}, 200);
toggleSettingsState = false;
}
}
function hangup() { // TODO: I need to have this be MUTE, toggle, with volume not touched.
getById("main").innerHTML = "👋";
setTimeout(function() {
session.hangup();
}, 0);
}
function hangup2() {
session.hangupDirector();
getById("miniPerformer").innerHTML = "";
getById("press2talk").dataset.value = 0;
getById("screensharebutton").classList.add("advanced");
getById("settingsbutton").classList.add("advanced");
getById("mutebutton").classList.add("advanced");
getById("hangupbutton2").classList.add("advanced");
//getById("chatbutton").classList.remove("advanced");
getById("controlButtons").style.display = "inherit";
//getById("mutespeakerbutton").classList.add("advanced");
getById("mutevideobutton").classList.add("advanced");
getById("screenshare2button").classList.add("advanced");
getById("screensharebutton").classList.add("float");
getById("screensharebutton").classList.remove("float2");
if (session.showDirector == false) {
getById("miniPerformer").innerHTML = '';
} else {
getById("miniPerformer").innerHTML = '';
}
getById("miniPerformer").className = "";
}
function hangupComplete() {
getById("main").innerHTML = "👋";
}
function raisehand() {
if (session.directorUUID == false) {
log("no director in room yet");
return;
}
var data = {};
data.UUID = session.directorUUID;
log(data);
if (getById("raisehandbutton").dataset.raised == "0") {
getById("raisehandbutton").dataset.raised = "1";
getById("raisehandbutton").classList.add("raisedHand");
data.chat = "Raised hand";
log("hand raised");
} else {
log("hand lowered");
getById("raisehandbutton").dataset.raised = "0";
getById("raisehandbutton").classList.remove("raisedHand");
data.chat = "Lowered hand";
}
session.sendMessage(data, data.UUID);
}
function lowerhand() {
log("hand lowered");
getById("raisehandbutton").dataset.raised = "0";
getById("raisehandbutton").classList.remove("raisedHand");
}
var previousRoom = "";
var stillNeedRoom = true;
var transferCancelled = false;
var armedTransfer = false;
function directMigrate(ele, event) { // everyone in the room will hangup this guest also? I like that idea. What about the STREAM ID? I suppose we don't kick out if the viewID matches.
log("directMigrate");
if (event === false) {
if (previousRoom === null) { // user cancelled in previous callback
ele.innerHTML = ' Transfer';
ele.style.backgroundColor = null;
return;
}
if (transferCancelled === true) {
ele.innerHTML = ' Transfer';
ele.style.backgroundColor = null;
return;
}
var migrateRoom = previousRoom
} else if ((event.ctrlKey) || (event.metaKey)) {
ele.innerHTML = ' Armed';
ele.style.backgroundColor = "#BF3F3F";
transferCancelled = false;
//armedTransfer=true;
Callbacks.push([directMigrate, ele, stillNeedRoom]);
stillNeedRoom = false;
log("Migrate queued");
return;
// } else if (armedTransfer){
//migrateRoom = sanitizeRoomName(previousRoom);
} else {
if (armedTransfer!==false && previousRoom!==""){
var migrateRoom = sanitizeRoomName(previousRoom);
} else {
var migrateRoom = prompt("Transfer guests to room:\n\n(Please note rooms must share the same password)", previousRoom);
}
stillNeedRoom = true;
if (migrateRoom === null) { // user cancelled
ele.innerHTML = ' Transfer';
ele.style.backgroundColor = null;
transferCancelled = true;
return;
}
try {
migrateRoom = sanitizeRoomName(migrateRoom);
previousRoom = migrateRoom;
} catch (e) {}
}
ele.innerHTML = ' Transfer';
ele.style.backgroundColor = null;
if (migrateRoom) {
previousRoom = migrateRoom;
var msg = {};
msg.request = "migrate";
if (session.password) {
return session.generateHash(migrateRoom + session.password + session.salt, 16).then(function(rid) {
var msg = {};
msg.request = "migrate";
msg.roomid = rid;
msg.target = ele.dataset.UUID;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
});
} else {
var msg = {};
msg.request = "migrate";
msg.roomid = migrateRoom;
msg.target = ele.dataset.UUID;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
}
}
var stillNeedHangupTarget = 1;
function directHangup(ele, event) { // everyone in the room will hangup this guest? I like that idea.
if (event == false) {
if (stillNeedHangupTarget === 1) {
var confirmHangup = confirm("Are you sure you wish to disconnect these users?");
stillNeedHangupTarget = confirmHangup;
} else {
confirmHangup = stillNeedHangupTarget;
}
} else if ((event.ctrlKey) || (event.metaKey)) {
ele.innerHTML = ' ARMED';
ele.style.backgroundColor = "#BF3F3F";
stillNeedHangupTarget = 1;
Callbacks.push([directHangup, ele, false]);
log("Hangup queued");
return;
} else {
var confirmHangup = confirm("Are you sure you wish to disconnect this user?");
}
if (confirmHangup) {
var msg = {};
msg.hangup = true;
log(msg);
log(ele.dataset.UUID);
session.sendRequest(msg, ele.dataset.UUID);
//session.anysend(msg); // send to everyone in the room, so they know if they are on air or not.
} else {
ele.innerHTML = ' Hangup';
ele.style.backgroundColor = null;
}
}
function directEnable(ele, event, scene=1, director=false) { // A directing room only is controlled by the Director, with the exception of MUTE.
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
if (ele.children[1]){
ele.children[1].innerHTML = "Add to Scene "+scene;
if (director){
getById("container_director").style.backgroundColor = null;
} else {
getById("container_" + ele.dataset.UUID).style.backgroundColor = null;
}
}
} else {
ele.dataset.value = 1;
ele.className = "pressed";
if (ele.children[1]){
ele.children[1].innerHTML = "Remove";
if (director){
getById("container_director").style.backgroundColor = "#649166";
} else {
getById("container_" + ele.dataset.UUID).style.backgroundColor = "#649166";
}
}
}
}
var msg = {};
msg.request = "sendroom";
msg.scene = scene;
msg.action = "display";
msg.value = ele.dataset.value;
msg.target = ele.dataset.sid;
//session.anysend(msg);
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
var previousURL = "";
var stillNeedURL = true;
var reloadCancelled = false;
var armedReload = false;
function directPageReload(ele, event) {
log("URL Page reload");
if (event === false) {
if (previousURL === null) { // user cancelled in previous callback
ele.innerHTML = ' Change URL';
ele.style.backgroundColor = null;
return;
}
if (reloadCancelled === true) {
ele.innerHTML = ' Change URL';
ele.style.backgroundColor = null;
return;
}
reloadURL = previousURL
} else if ((event.ctrlKey) || (event.metaKey)) {
ele.innerHTML = ' Armed';
ele.style.backgroundColor = "#BF3F3F";
reloadCancelled = false;
armedReload=true;
Callbacks.push([directPageReload, ele, stillNeedURL]);
stillNeedURL = false;
log("URL update queued");
return;
} else if (armedReload){
reloadURL = previousURL;
} else {
var reloadURL = prompt("Transfer guests to new website URL.\n\n(Guests will be prompted to accept)", previousURL);
stillNeedURL = true;
if (reloadURL === null) { // user cancelled
ele.innerHTML = ' Change URL';
ele.style.backgroundColor = null;
reloadCancelled = true;
return;
}
try {
previousURL = reloadURL;
} catch (e) {}
}
ele.innerHTML = ' Change URL';
ele.style.backgroundColor = null;
if (reloadURL) {
previousURL = reloadURL;
var msg = {};
msg.changeURL = reloadURL;
if (ele.dataset.UUID in session.rpcs){
session.rpcs[ele.dataset.UUID].receiveChannel.send(JSON.stringify(msg));
}
}
}
function directMute(ele, event) { // A directing room only is controlled by the Director, with the exception of MUTE.
log("mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.value == 0) {
ele.dataset.value = 1;
ele.className = "";
ele.children[1].innerHTML = "Mute in scene";
} else {
ele.dataset.value = 0;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-mute";
}
}
var msg = {};
msg.request = "sendroom";
msg.scene = "1";
msg.action = "mute";
msg.value = ele.dataset.value;
msg.target = ele.dataset.sid;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
function remoteSpeakerMute(ele, event) {
log("speaker mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "deafen guest";
} else {
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-deafen";
}
}
var msg = {};
if (ele.dataset.value == 0) {
msg.speakerMute = ele.dataset.value;
} else {
msg.speakerMute = ele.dataset.value;
}
msg.UUID = ele.dataset.UUID;
session.sendRequest(msg, ele.dataset.UUID);
}
function updateRemoteSpeakerMute(UUID) {
var ele = document.querySelectorAll('[data-action-type="toggle-remote-speaker"][data--u-u-i-d="' + UUID + '"]');
if (ele[0]) {
ele[0].classList.add("pressed");
ele[0].dataset.value = 1;
ele[0].className = "pressed";
ele[0].children[1].innerHTML = "Un-deafen";
}
}
function updateRemoteDisplayMute(UUID) {
var ele = document.querySelectorAll('[data-action-type="toggle-remote-display"][data--u-u-i-d="' + UUID + '"]');
if (ele[0]) {
ele[0].classList.add("pressed");
ele[0].dataset.value = 1;
ele[0].className = "pressed";
ele[0].children[1].innerHTML = "Un-blind";
}
}
function remoteDisplayMute(ele, event) {
log("display mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "blind guest";
} else {
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-blind";
}
}
var msg = {};
if (ele.dataset.value == 0) {
msg.displayMute = false;
} else {
msg.displayMute = true;
}
msg.UUID = ele.dataset.UUID;
session.sendRequest(msg, ele.dataset.UUID);
}
function remoteLowerhands(UUID) {
var msg = {};
msg.lowerhand = true;
msg.UUID = UUID;
session.sendRequest(msg, UUID);
}
function remoteMute(ele, event) {
log("mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "mute guest";
} else {
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-mute guest";
}
}
try {
session.rpcs[ele.dataset.UUID].directorMutedState = ele.dataset.mute;
var volume = session.rpcs[ele.dataset.UUID].directorVolumeState;
} catch (e) {
errorlog(e);
var volume = 100;
}
var msg = {};
if (ele.dataset.value == 0) {
msg.volume = volume;
} else {
msg.volume = 0;
}
msg.UUID = ele.dataset.UUID;
session.sendRequest(msg, ele.dataset.UUID);
}
function remoteMuteVideo(ele, event) {
log("video mute");
if ((event.ctrlKey) || (event.metaKey)) {
ele.children[1].innerHTML = "ARMED";
ele.style.backgroundColor = "#BF3F3F";
Callbacks.push([remoteMuteVideo, ele, false]);
log("video queued");
return;
} else {
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "hide guest";
} else {
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Unhide guest";
}
ele.style.backgroundColor = null;
}
var msg = {};
if (ele.dataset.value == 0) {
msg.directVideoMuted = false;
} else {
msg.directVideoMuted = true;
}
for (var i in session.pcs){
msg.target = ele.dataset.UUID;
if (i === msg.target){
msg.target = true;
}
try{
session.pcs[i].sendChannel.send(JSON.stringify(msg));
} catch(e){}
}
}
function updateDirectorVideoMute(UUID) {
var ele = document.querySelectorAll('[data-action-type="hide-guest"][data--u-u-i-d="' + UUID + '"]');
if (ele[0]) {
ele[0].dataset.value = 1;
ele[0].className = "pressed";
ele[0].children[1].innerHTML = "Unhide guest";
}
}
function directVolume(ele) { // NOT USED ANYMORE
log("volume");
var msg = {};
msg.request = "sendroom";
msg.scene = "1";
msg.action = "volume";
msg.target = ele.dataset.sid; // i want to focus on the STREAM ID, not the UUID...
msg.value = ele.value;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
function remoteVolumeUI(ele){
ele.nextSibling.innerHTML = ele.value;
}
function remoteVolume(ele) { // A directing room only is controlled by the Director, with the exception of MUTE.
log("volume");
var msg = {};
var muted = session.rpcs[ele.dataset.UUID].directorMutedState;
if (muted == 1) { // 1 is a string, not an int, so == and not ===. this happens in a few places :/
session.rpcs[ele.dataset.UUID].directorVolumeState = ele.value;
} else {
session.rpcs[ele.dataset.UUID].directorVolumeState = ele.value;
msg.volume = ele.value;
msg.UUID = ele.dataset.UUID;
session.sendRequest(msg, ele.dataset.UUID);
}
}
function sendChat(chatmessage = "hi", UUID=false) { // A directing room only is controlled by the Director, with the exception of MUTE.
log("Chat message");
var msg = {};
msg.chat = chatmessage;
session.sendPeers(msg, UUID);
}
var activatedStream = false;
function publishScreen() {
if (activatedStream == true) {
return;
}
activatedStream = true;
setTimeout(function() {
activatedStream = false;
}, 1000);
var title = "ScreenShare"; //getById("videoname2").value;
formSubmitting = false;
var quality = parseInt(getById("webcamquality2").elements.namedItem("resolution2").value);
if (session.quality !== false) {
quality = session.quality; // override the user's setting
}
if (quality == 0) {
var width = {
ideal: 1920
};
var height = {
ideal: 1080
};
} else if (quality == 1) {
var width = {
ideal: 1280
};
var height = {
ideal: 720
};
} else if (quality == 2) {
var width = {
ideal: 640
};
var height = {
ideal: 360
};
} else if (quality >= 3) { // lowest
var width = {
ideal: 320
};
var height = {
ideal: 180
};
}
if (session.width) {
width = {
ideal: session.width
};
}
if (session.height) {
height = {
ideal: session.height
};
}
var constraints = window.constraints = {
audio: {
echoCancellation: false
, autoGainControl: false
, noiseSuppression: false
}
, video: {
width: width
, height: height
, mediaSource: "screen"
}
};
if (session.noiseSuppression === true) {
constraints.audio.noiseSuppression = true;; // the defaults for screen publishing should be off.
}
if (session.autoGainControl === true) {
constraints.audio.autoGainControl = true; // the defaults for screen publishing should be off.
}
if (session.echoCancellation === true) {
constraints.audio.echoCancellation = true; // the defaults for screen publishing should be off.
}
if (session.nocursor) {
constraints.video.cursor = {
exact: "none"
}; // Not sure this does anything, but whatever.
}
if (session.framerate !== false) {
constraints.video.frameRate = session.framerate;
} else if (session.maxframerate != false){
constraints.video.frameRate = {
ideal: session.maxframerate,
max: session.maxframerate
};
} else {
constraints.video.frameRate = {
ideal: 60
};
}
var audioSelect = document.querySelector('select#audioSourceScreenshare');
var outputSelect = document.querySelector('select#outputSourceScreenshare');
session.sink = outputSelect.options[outputSelect.selectedIndex].value;
log("Session SInk: " + session.sink);
if (session.sink == "default") {
session.sink = false;
}
/* if (session.sink ===false){
if (session.outputDevice){
try {
for (var i in outputSelect.options){
log(outputSelect.options[i].label.replace(/[\W]+/g,"_").toLowerCase());
log(session.outputDevice);
if (outputSelect.options[i].label.replace(/[\W]+/g,"_").toLowerCase().includes(session.outputDevice)){
session.sink = outputSelect.options[i].value;
break;
}
}
} catch (e){}
}
} */
log("*");
session.publishScreen(constraints, title, audioSelect).then((res) => {
if (res == false) {
return;
} // no screen selected
log("streamID is: " + session.streamID);
if (session.transcript) {
setTimeout(function() {
setupClosedCaptions();
}, 0);
}
//session.screenShareState=true;
if (!(session.cleanOutput)) {
getById("mutebutton").className = "float";
getById("mutespeakerbutton").classList.remove("advanced");
//getById("mutespeakerbutton").className="float";
getById("chatbutton").className = "float";
getById("mutevideobutton").className = "float";
getById("hangupbutton").className = "float";
if (session.showSettings) {
getById("settingsbutton").className = "float";
}
if (session.raisehands) {
getById("raisehandbutton").className = "float";
}
if (session.recordLocal !== false) {
getById("recordLocalbutton").className = "float";
}
if (screensharebutton) {
getById("screensharebutton").className = "float2";
}
getById("controlButtons").style.display = "flex";
getById("helpbutton").style.display = "inherit";
getById("reportbutton").style.display = "";
} else if (session.cleanish && session.recordLocal!==false){
getById("recordLocalbutton").className = "float";
getById("mutebutton").classList.add("advanced");
getById("mutespeakerbutton").classList.add("advanced");
getById("chatbutton").classList.add("advanced");
getById("mutevideobutton").classList.add("advanced");
getById("hangupbutton").classList.add("advanced");
getById("hangupbutton2").classList.add("advanced");
getById("controlButtons").style.display = "flex";
getById("settingsbutton").classList.add("advanced");
getById("screenshare2button").classList.add("advanced");
getById("screensharebutton").classList.add("advanced");
getById("queuebutton").classList.add("advanced");
} else {
getById("controlButtons").style.display = "none";
}
if (session.chatbutton === true) {
getById("chatbutton").classList.remove("advanced");
getById("controlButtons").style.display = "inherit";
} else if (session.chatbutton === false) {
getById("chatbutton").classList.add("advanced");
}
getById("head1").className = 'advanced';
getById("head2").className = 'advanced';
}).catch(() => {});
}
function publishWebcam(btn = false) {
if (btn) {
if (btn.dataset.ready == "false") {
warnlog("Clicked too quickly; button not enabled yet");
return;
}
if (getById("passwordBasicInput").value.length){
session.password = getById("passwordBasicInput").value;
session.password = sanitizePassword(session.password);
if (session.password.length==0){
session.password = false;
} else {
session.defaultPassword = false;
if (urlParams.has('pass')) {
updateURL("pass=" + session.password);
} else if (urlParams.has('pw')) {
updateURL("pw=" + session.password);
} else if (urlParams.has('p')) {
updateURL("p=" + session.password);
} else {
updateURL("password=" + session.password);
}
}
}
}
if (activatedStream == true) {
return;
}
activatedStream = true;
log("PRESSED PUBLISH WEBCAM!!");
var title = "Webcam"; // getById("videoname3").value;
var ele = getById("previewWebcam");
formSubmitting = false;
window.scrollTo(0, 0); // iOS has a nasty habit of overriding the CSS when changing camaera selections, so this addresses that.
getById("head2").className = 'advanced';
if (session.roomid !== false) {
if ((session.roomid === "") && ((!(session.view)) || (session.view === ""))) {
// no room, no viewing, viewing disabled
session.manual = true;
window.addEventListener("resize", updateMixer);
window.addEventListener("orientationchange", function() {
setTimeout(updateMixer, 200);
});
} else {
log("ROOM ID ENABLED");
log("Update Mixer Event on REsize SET");
window.addEventListener("resize", updateMixer);
window.addEventListener("orientationchange", function() {
setTimeout(updateMixer, 200);
});
getById("main").style.overflow = "hidden";
//session.cbr=0; // we're just going to override it
if (session.stereo == 5) {
if (session.roomid === "") {
session.stereo = 1;
} else {
session.stereo = 3;
}
}
joinRoom(session.roomid);
if (session.roomid !== "") {
if (!(session.cleanOutput)) {
getById("head2").className = '';
}
}
getById("head3").className = 'advanced';
}
} else {
getById("head3").className = '';
getById("logoname").style.display = 'none';
}
log("streamID is: " + session.streamID);
getById("head1").className = 'advanced';
if (!(session.cleanOutput)) {
getById("mutebutton").className = "float";
getById("mutespeakerbutton").classList.remove("advanced");
//getById("mutespeakerbutton").className="float";
getById("chatbutton").className = "float";
getById("mutevideobutton").className = "float";
getById("hangupbutton").className = "float";
if (session.showSettings) {
getById("settingsbutton").className = "float";
}
if (session.raisehands) {
getById("raisehandbutton").className = "float";
}
if (session.recordLocal !== false) {
getById("recordLocalbutton").className = "float";
}
if (screensharebutton) {
if (session.roomid) {
getById("screenshare2button").className = "float";
getById("screensharebutton").className = "float advanced";
} else {
getById("screensharebutton").className = "float";
getById("screenshare2button").className = "float advanced";
}
}
getById("controlButtons").style.display = "flex";
getById("helpbutton").style.display = "inherit";
getById("reportbutton").style.display = "";
} else if (session.cleanish && session.recordLocal!==false){
getById("recordLocalbutton").className = "float";
getById("mutebutton").classList.add("advanced");
getById("mutespeakerbutton").classList.add("advanced");
getById("chatbutton").classList.add("advanced");
getById("mutevideobutton").classList.add("advanced");
getById("hangupbutton").classList.add("advanced");
getById("hangupbutton2").classList.add("advanced");
getById("controlButtons").style.display = "flex";
getById("settingsbutton").classList.add("advanced");
getById("screenshare2button").classList.add("advanced");
getById("screensharebutton").classList.add("advanced");
getById("queuebutton").classList.add("advanced");
} else {
getById("controlButtons").style.display = "none";
}
if (session.chatbutton === true) {
getById("chatbutton").classList.remove("advanced");
getById("controlButtons").style.display = "inherit";
} else if (session.chatbutton === false) {
getById("chatbutton").classList.add("advanced");
}
if (urlParams.has('permaid')) {
updateURL("permaid=" + session.streamID);
} else {
updateURL("push=" + session.streamID);
}
session.publishStream(ele, title);
}
session.publishIFrame = function(iframeURL){
if (session.transcript){
setTimeout(function(){setupClosedCaptions();},0);
}
if (iframeURL==""){
iframeURL="./";
}
if (iframeURL === session.iframeSrc){return;}
if (iframeURL.startsWith("https://") || iframeURL.startsWith("http://")){
var domain = (new URL(iframeURL));
domain = domain.hostname;
log(domain);
if ((domain=="www.youtube.com") || (domain=="youtube.com")){
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
var match = iframeURL.match(regExp);
var vidid = (match&&match[7].length==11)? match[7] : false;
if(vidid){
iframeURL = "https://www.youtube.com/embed/"+vidid+"?autoplay=1&modestbranding=1";
log(iframeURL);
}
} else if (domain=="www.twitch.tv"){
var vidid = iframeURL.split('/').pop().split('#')[0].split('?')[0];
if (vidid){
iframeURL = "https://player.twitch.tv/?channel="+vidid+"&parent="+location.hostname;
log(iframeURL);
}
} else if (domain=="twitch.tv"){
var vidid = iframeURL.split('/').pop().split('#')[0].split('?')[0];
if (vidid){
iframeURL = "https://player.twitch.tv/?channel="+vidid+"&parent="+location.hostname;
log(iframeURL);
}
}
}
// https://player.twitch.tv/?channel=twitchpresents&parent=obs.ninja
session.iframeSrc = iframeURL;
var container = document.createElement("div");
container.id = "container";
var iframe = document.createElement("iframe");
iframe.allow="autoplay;camera;microphone";
iframe.allowtransparency="true";
iframe.allowfullscreen ="true";
iframe.style.width="100%";
iframe.style.height="100%";
iframe.style.margin="auto";
iframe.style.border = "10px dashed rgb(64 65 62)";
iframe.src = session.iframeSrc;
iframe.id = "iframe_source"
session.iframeEle = iframe;
if (session.roomid!==false){
window.addEventListener("resize", updateMixer);
if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){
} else {
log("ROOMID EANBLED");
getById("head3").className = 'advanced';
joinRoom(session.roomid);
}
} else {
getById("head3").className = '';
getById("logoname").style.display = 'none';
}
getById("head1").className = 'advanced';
if (urlParams.has('permaid')){
updateURL("permaid="+session.streamID);
} else {
updateURL("push="+session.streamID);
}
getById("head1").className = 'advanced';
getById("head2").className = 'advanced';
if (!(session.cleanOutput)){
getById("chatbutton").className="float";
getById("hangupbutton").className="float";
getById("controlButtons").style.display="flex";
getById("helpbutton").style.display = "inherit";
getById("reportbutton").style.display = "";
} else {
getById("controlButtons").style.display="none";
}
if (session.director){
} else if (session.scene!==false){
updateMixer();
} else if (session.roomid!==false){
if (session.roomid===""){
if (!(session.view) || (session.view==="")){
getById("mutespeakerbutton").classList.add("advanced");
container.style.width="100%";
container.style.height="100%";
container.style.alignItems = "center";
container.style.maxWidth= "1280px";
container.style.maxHeight= "720px";
container.style.verticalAlign= "middle";
container.style.margin= "auto";
container.style.backgroundColor = "#666";
container.style.border = "2px solid";
} else {
session.windowed = false;
updateMixer();
}
} else {
session.windowed = false;
updateMixer();
}
} else {
container.style.maxHeight= "1280px";
container.style.maxWidth= "720px";
container.style.verticalAlign= "middle";
container.style.height="100%";
container.style.width= "100%";
container.style.margin= "auto";
container.style.alignItems = "center";
container.style.backgroundColor = "#666";
}
getById("mainmenu").parentElement.removeChild(getById("mainmenu"));
getById("gridlayout").innerHTML = "";
container.appendChild(iframe);
getById("gridlayout").appendChild(container);
session.seeding=true;
getById("reshare").href = "https://"+location.host+location.pathname+"?view="+session.streamID;
getById("reshare").text = "https://"+location.host+location.pathname+"?view="+session.streamID;
getById("reshare").style.width = ((getById("reshare").text.length + 1)*1.15 * 8) + 'px';
pokeIframeAPI('started-iframe-share');
session.seedStream();
}
function outboundAudioPipeline(stream) {
if (session.disableWebAudio) {
return stream;
}
//if ((iOS) || (iPad)){
// return stream
//} else {
try {
log("Web Audio");
var tracks = stream.getAudioTracks();
if (tracks.length) {
for (var waid in session.webAudios) { // TODO: EXCLUDE CURRENT TRACK IF ALREADY EXISTS ... if (track.id === wa.id){..
session.webAudios[waid].stop();
delete session.webAudios[waid];
}
var webAudio = {};
webAudio.micDelay = false;
webAudio.compressor = false;
webAudio.analyser = false;
webAudio.gainNode = false;
webAudio.lowEQ = false;
webAudio.midEQ = false;
webAudio.highEQ = false;
webAudio.id = tracks[0].id; // first track is used.
if (session.audioLatency !== false) { // session.audioLatency could be useful for fixing clicking issues?
var audioContext = new AudioContext({
latencyHint: session.audioLatency / 1000.0 //, // needs to be in seconds, but OBSN user input is via milliseconds
// sampleRate: 48000 // not sure this is a great idea, but might as well add this here, versus later on since it is needed anyways.
});
} else {
var audioContext = new AudioContext();
}
webAudio.audioContext = audioContext;
webAudio.mediaStreamSource = audioContext.createMediaStreamSource(stream); // clone to fix iOS issue
webAudio.destination = audioContext.createMediaStreamDestination();
webAudio.gainNode = audioGainNode(webAudio.mediaStreamSource, audioContext);
var anonNode = webAudio.gainNode;
if (session.micDelay) {
webAudio.micDelay = micDelayNode(anonNode, audioContext);
anonNode = webAudio.micDelay;
}
if (session.audioInputChannels == 1) {
webAudio.splitter = audioContext.createChannelSplitter(6);
anonNode.connect(webAudio.splitter);
webAudio.merger = audioContext.createChannelMerger(6);
webAudio.splitter.connect(webAudio.merger, 0, 0);
webAudio.splitter.connect(webAudio.merger, 0, 1);
webAudio.splitter.connect(webAudio.merger, 0, 2);
webAudio.splitter.connect(webAudio.merger, 0, 3);
webAudio.splitter.connect(webAudio.merger, 0, 4);
webAudio.splitter.connect(webAudio.merger, 0, 5);
anonNode = webAudio.merger;
}
if (session.lowcut) { // https://webaudioapi.com/samples/frequency-response/ for a tool to help set values
webAudio.lowcut1 = audioContext.createBiquadFilter();
webAudio.lowcut1.type = "highpass";
webAudio.lowcut1.frequency.value = session.lowcut;
webAudio.lowcut2 = audioContext.createBiquadFilter();
webAudio.lowcut2.type = "highpass";
webAudio.lowcut2.frequency.value = session.lowcut;
webAudio.lowcut3 = audioContext.createBiquadFilter();
webAudio.lowcut3.type = "highpass";
webAudio.lowcut3.frequency.value = session.lowcut;
anonNode.connect(webAudio.lowcut1);
webAudio.lowcut1.connect(webAudio.lowcut2);
webAudio.lowcut2.connect(webAudio.lowcut3);
anonNode = webAudio.lowcut3;
}
if (session.equalizer) { // https://webaudioapi.com/samples/frequency-response/ for a tool to help set values
webAudio.lowEQ = audioContext.createBiquadFilter();
webAudio.lowEQ.type = "lowshelf";
webAudio.lowEQ.frequency.value = 100;
webAudio.lowEQ.gain.value = 0;
webAudio.midEQ = audioContext.createBiquadFilter();
webAudio.midEQ.type = "peaking";
webAudio.midEQ.frequency.value = 1000;
webAudio.midEQ.Q.value = 0.5;
webAudio.midEQ.gain.value = 0;
webAudio.highEQ = audioContext.createBiquadFilter();
webAudio.highEQ.type = "highshelf";
webAudio.highEQ.frequency.value = 10000;
webAudio.highEQ.gain.value = 0;
anonNode.connect(webAudio.lowEQ);
webAudio.lowEQ.connect(webAudio.midEQ);
webAudio.midEQ.connect(webAudio.highEQ);
anonNode = webAudio.highEQ;
}
if (session.compressor === 1) {
webAudio.compressor = audioCompressor(anonNode, audioContext);
anonNode = webAudio.compressor;
} else if (session.compressor === 2) {
webAudio.compressor = audioLimiter(anonNode, audioContext);
anonNode = webAudio.compressor;
}
webAudio.analyser = audioMeter(anonNode, audioContext);
webAudio.analyser.connect(webAudio.destination);
webAudio.stop = function() {
try {
webAudio.destination.disconnect();
} catch (e) {}
try {
clearInterval(webAudio.analyser.interval);
} catch (e) {}
try {
webAudio.analyser.disconnect();
} catch (e) {}
try {
webAudio.splitter.disconnect();
} catch (e) {}
try {
webAudio.merger.disconnect();
} catch (e) {}
try {
webAudio.lowcut1.disconnect();
webAudio.lowcut2.disconnect();
webAudio.lowcut3.disconnect();
} catch (e) {}
try {
webAudio.lowEQ.disconnect();
} catch (e) {}
try {
webAudio.midEQ.disconnect();
} catch (e) {}
try {
webAudio.highEQ.disconnect();
} catch (e) {}
try {
webAudio.gainNode.disconnect();
} catch (e) {}
try {
webAudio.micDelay.disconnect();
} catch (e) {}
try {
webAudio.compressor.disconnect();
} catch (e) {}
try {
webAudio.mediaStreamSource.context.close();
} catch (e) {}
}
webAudio.mediaStreamSource.onended = function() {
webAudio.stop();
};
session.webAudios[webAudio.id] = webAudio;
stream.getTracks().forEach(function(track) {
if (webAudio.id != track.id) {
webAudio.destination.stream.addTrack(track, stream);
}
});
return webAudio.destination.stream;
} else {
return stream; // no audio track
}
} catch (e) {
errorlog(e);
return stream;
}
}
function changeLowCut(freq, trackid = 0) {
if (trackid != 0) {
errorlog("EQ Doesn't work for anything but track 0. yet");
}
log("LOW EQ");
for (var webAudio in session.webAudios) {
if (!session.webAudios[webAudio].lowcut1) {
errorlog("EQ not setup");
return;
}
if (!session.webAudios[webAudio].lowcut2) {
errorlog("EQ not setup");
return;
}
if (!session.webAudios[webAudio].lowcut3) {
errorlog("EQ not setup");
return;
}
session.webAudios[webAudio].lowcut1.frequency.setValueAtTime(freq, session.webAudios[webAudio].audioContext.currentTime);
session.webAudios[webAudio].lowcut2.frequency.setValueAtTime(freq, session.webAudios[webAudio].audioContext.currentTime);
session.webAudios[webAudio].lowcut3.frequency.setValueAtTime(freq, session.webAudios[webAudio].audioContext.currentTime);
}
}
function changeLowEQ(lowEQ, trackid = 0) {
if (trackid != 0) {
errorlog("EQ Doesn't work for anything but track 0. yet");
}
log("LOW EQ");
for (var webAudio in session.webAudios) {
if (!session.webAudios[webAudio].lowEQ) {
errorlog("EQ not setup");
return;
}
session.webAudios[webAudio].lowEQ.gain.setValueAtTime(lowEQ, session.webAudios[webAudio].audioContext.currentTime);
}
}
function changeMidEQ(midEQ, trackid = 0) {
if (trackid != 0) {
errorlog("EQ Doesn't work for anything but track 0. yet");
}
for (var webAudio in session.webAudios) {
if (!session.webAudios[webAudio].midEQ) {
errorlog("EQ not setup");
return;
}
session.webAudios[webAudio].midEQ.gain.setValueAtTime(midEQ, session.webAudios[webAudio].audioContext.currentTime);
}
}
function changeHighEQ(highEQ, trackid = 0) {
if (trackid != 0) {
errorlog("EQ Doesn't work for anything but track 0. yet");
}
for (var webAudio in session.webAudios) {
if (!session.webAudios[webAudio].highEQ) {
errorlog("EQ not setup");
return;
}
session.webAudios[webAudio].highEQ.gain.setValueAtTime(highEQ, session.webAudios[webAudio].audioContext.currentTime);
}
}
function micDelayNode(mediaStreamSource, audioContext) {
var delayNode = audioContext.createDelay();
if (session.micDelay !== false) {
var delay = parseFloat(session.micDelay/1000) || 0;
} else {
var delay = 0;
}
delayNode.delayTime.value = delay;
mediaStreamSource.connect(delayNode);
return delayNode;
}
function audioGainNode(mediaStreamSource, audioContext) {
var gainNode = audioContext.createGain();
if (session.audioGain !== false) {
var gain = parseFloat(session.audioGain / 100.0) || 0;
} else {
var gain = 1.0;
}
gainNode.gain.value = gain;
mediaStreamSource.connect(gainNode);
return gainNode;
}
function audioMeter(mediaStreamSource, audioContext) {
var analyser = audioContext.createAnalyser();
mediaStreamSource.connect(analyser);
analyser.fftSize = 256;
analyser.smoothingTimeConstant = 0.05;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
function draw() {
analyser.getByteFrequencyData(dataArray);
var total = 0;
for (var i = 0; i < dataArray.length; i++) {
total += dataArray[i];
}
total = total / 100;
if (session.cleanOutput){
return;
}
if (document.getElementById("meter1")) {
if (total == 0) {
getById("meter1").style.width = "1px";
getById("meter2").style.width = "0px";
} else if (total <= 1) {
getById("meter1").style.width = "1px";
getById("meter2").style.width = "0px";
} else if (total <= 150) {
getById("meter1").style.width = total + "px";
getById("meter2").style.width = "0px";
} else if (total > 150) {
if (total > 200) {
total = 200;
}
getById("meter1").style.width = "150px";
getById("meter2").style.width = (total - 150) + "px";
}
} else if (document.getElementById("mutetoggle")) {
if (total > 200) {
total = 200;
}
total = parseInt(total);
document.getElementById("mutetoggle").style.color = "rgb(" + (255 - total) + ",255," + (255 - total) + ")";
} else {
clearInterval(analyser.interval);
warnlog("METERS NOT FOUND");
return;
}
};
analyser.interval = setInterval(function() {
draw();
}, 100);
return analyser;
}
function audioCompressor(mediaStreamSource, audioContext) {
var compressor = audioContext.createDynamicsCompressor();
compressor.threshold.value = -50;
compressor.knee.value = 40;
compressor.ratio.value = 12;
compressor.attack.value = 0;
compressor.release.value = 0.25;
mediaStreamSource.connect(compressor);
return compressor;
}
function audioLimiter(mediaStreamSource, audioContext) {
var compressor = audioContext.createDynamicsCompressor();
compressor.threshold.value = -5;
compressor.knee.value = 0;
compressor.ratio.value = 20.0; // 1 to 20
compressor.attack.value = 0.001;
compressor.release.value = 0.1;
mediaStreamSource.connect(compressor);
return compressor;
}
function activeSpeaker(border=false) {
var lastActiveSpeaker = null;
var changed = false;
for (var UUID in session.rpcs) {
if (session.rpcs[UUID].stats._Audio_Loudness_average) {
session.rpcs[UUID].stats._Audio_Loudness_average = parseFloat(session.rpcs[UUID].stats.Audio_Loudness*0.2 + session.rpcs[UUID].stats._Audio_Loudness_average*0.8);
} else {
session.rpcs[UUID].stats._Audio_Loudness_average = 1;
}
//log(session.rpcs[UUID].stats._Audio_Loudness_average);
if (session.rpcs[UUID].stats._Audio_Loudness_average > 13) {
if (border) {
if (session.rpcs[UUID].videoElement) {
session.rpcs[UUID].videoElement.style.border = "green solid 1px";
session.rpcs[UUID].videoElement.style.padding = "0";
}
} else if (!session.rpcs[UUID].activelySpeaking){
session.rpcs[UUID].activelySpeaking = true;
changed = true;
lastActiveSpeaker = UUID;
session.rpcs[UUID].stats._Audio_Loudness_average+=2000000;
}
} else if (session.rpcs[UUID].stats._Audio_Loudness_average > 6) {
} else {
if (border){
if (session.rpcs[UUID].videoElement) {
session.rpcs[UUID].videoElement.style.border = "";
session.rpcs[UUID].videoElement.style.padding = "1px";
}
} else if (session.rpcs[UUID].activelySpeaking) {
changed = true;
session.rpcs[UUID].activelySpeaking=false;
lastActiveSpeaker = UUID;
}
}
}
var speaker = false;
for (var UUID in session.rpcs) {
if (session.rpcs[UUID].activelySpeaking){speaker=true;}
}
if (!speaker && lastActiveSpeaker && (session.nopreview || session.minipreview)){
session.rpcs[lastActiveSpeaker].activelySpeaking=true;
} else if (changed) {
setTimeout(function(){updateMixer();},1);
}
}
function randomizeArray(unshuffled) {
var arr = unshuffled.map((a) => ({
sort: Math.random(), value: a
})).sort((a, b) => a.sort - b.sort).map((a) => a.value); // shuffle once
for (var i = arr.length - 1; i > 0; i--) { // shuffle twice
var j = Math.floor(Math.random() * (i + 1));
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr
}
function joinRoom(roomname) {
if (roomname.length) {
roomname = sanitizeRoomName(roomname);
log("Join room");
log(roomname);
session.joinRoom(roomname).then(function(response) { // callback from server; we've joined the room. Just the listing is returned
if (session.joiningRoom === "seedPlz") { // allow us to seed, now that we have joined the room.
session.joiningRoom = false; // joined
session.seedStream();
} else {
session.joiningRoom = false; // no seeding callback
}
log("Members in Room");
log(response);
if (session.randomize === true) {
response = randomizeArray(response);
log("Randomized List of Viewers");
log(response);
for (var i in response) {
if ("UUID" in response[i]) {
if ("streamID" in response[i]) {
if (response[i].UUID in session.rpcs) {
log("RTC already connected"); /// lets just say instead of Stream, we have
} else {
log(response[i].streamID);
var streamID = session.desaltStreamID(response[i].streamID);
if (session.queue){
if (session.directorUUID === response[i].UUID){
play(streamID);
} else if (session.queueList.length<5000){
if (!session.queueList.includes(streamID)){
session.queueList.push(streamID);
}
}
} else {
log("STREAM ID DESALTED 3: " + streamID);
setTimeout(function(sid) {
play(sid);
}, (Math.floor(Math.random() * 100)), streamID); // add some furtherchance with up to 100ms added latency
}
}
}
}
}
} else {
for (var i in response) {
if ("UUID" in response[i]) {
if ("streamID" in response[i]) {
if (response[i].UUID in session.rpcs) {
log("RTC already connected"); /// lets just say instead of Stream, we have
} else {
log(response[i].streamID);
var streamID = session.desaltStreamID(response[i].streamID);
if (session.queue){
if (session.directorUUID === response[i].UUID){
play(streamID);
} else if (session.queueList.length<5000){
if (!session.queueList.includes(streamID)){
session.queueList.push(streamID);
}
}
} else {
log("STREAM ID DESALTED 3: " + streamID);
play(streamID); // play handles the group room mechanics here
}
}
}
}
}
}
session.updateQueue();
}, function(error) {
return {};
});
} else {
log("Room name not long enough or contained all bad characaters");
}
}
function createRoom(roomname = false) {
if (roomname == false) {
roomname = getById("videoname1").value;
roomname = sanitizeRoomName(roomname);
if (roomname.length != 0) {
if (urlParams.has('dir')){
updateURL("dir=" + roomname, true, false); // make the link reloadable.
} else {
updateURL("director=" + roomname, true, false); // make the link reloadable.
}
}
}
if (roomname.length == 0) {
if (!(session.cleanOutput)) {
warnUser("Please enter a room name before continuing");
}
return;
}
log(roomname);
session.roomid = roomname;
getById("dirroomid").innerHTML = decodeURIComponent(session.roomid);
getById("roomid").innerHTML = session.roomid;
var passwordRoom = getById("passwordRoom").value;
passwordRoom = sanitizePassword(passwordRoom);
if (passwordRoom.length) {
session.password = passwordRoom;
session.defaultPassword = false;
if (urlParams.has('pass')) {
updateURL("pass=" + session.password);
} else if (urlParams.has('pw')) {
updateURL("pw=" + session.password);
} else if (urlParams.has('p')) {
updateURL("p=" + session.password);
} else {
updateURL("password=" + session.password);
}
}
var passAdd = "";
var passAdd2 = "";
if ((session.defaultPassword === false) && (session.password)) {
passAdd2 = "&password=" + session.password;
return session.generateHash(session.password + session.salt, 4).then(function(hash) {
passAdd = "&hash=" + hash;
createRoomCallback(passAdd, passAdd2);
});
} else {
createRoomCallback(passAdd, passAdd2);
}
}
function hideDirectorinvites(ele) {
if (getById("directorLinks2").style.display == "none") {
ele.innerHTML = ' LINKS (GUEST INVITES & SCENES)';
getById("directorLinks2").style.display = "inline-block";
getById("customizeLinks").classList.remove("advanced");
} else {
ele.innerHTML = ' LINKS (GUEST INVITES & SCENES)'
getById("directorLinks2").style.display = "none";
getById("help_directors_room").style.display = "none";
getById("roomnotes2").style.display = "none";
getById("customizeLinks").classList.add("advanced");
}
if (getById("directorLinks1").style.display == "none") {
getById("directorLinks1").style.display = "inline-block";
getById("customizeLinks").classList.remove("advanced");
} else {
getById("directorLinks1").style.display = "none";
getById("help_directors_room").style.display = "none";
getById("roomnotes2").style.display = "none";
getById("customizeLinks").classList.add("advanced");
}
//document.querySelector(".directorContainer.half").style.display="none";
//document.querySelector(".directorContainer").style.display="none";
}
function createRoomCallback(passAdd, passAdd2) {
var gridlayout = getById("gridlayout");
gridlayout.classList.add("directorsgrid");
var broadcastFlag = getById("broadcastFlag");
try {
if (broadcastFlag.checked) {
broadcastFlag = true;
} else {
broadcastFlag = false;
}
} catch (e) {
broadcastFlag = false;
}
var broadcastString = "";
if (broadcastFlag) {
broadcastString = "&broadcast";
getById("broadcastSlider").checked = true;
}
var showdirectorFlag = getById("showdirectorFlag");
try {
if (showdirectorFlag.checked) {
showdirectorFlag = true;
} else {
showdirectorFlag = false;
}
} catch (e) {
showdirectorFlag = false;
}
if (showdirectorFlag) {
updateURL("showdirector", true, false);
session.showDirector = true;
//getById("broadcastSlider").checked=true;
}
var codecGroupFlag = getById("codecGroupFlag");
if (codecGroupFlag.value) {
if (codecGroupFlag.value === "vp9") {
codecGroupFlag = "&codec=vp9";
getById("codech264toggle").disabled=true;
} else if (codecGroupFlag.value === "h264") {
codecGroupFlag = "&codec=h264";
getById("codech264toggle").checked=true;
} else if (codecGroupFlag.value === "vp8") {
codecGroupFlag = "&codec=vp8";
getById("codech264toggle").disabled=true;
} else if (codecGroupFlag.value === "av1") {
codecGroupFlag = "&codec=av1";
getById("codech264toggle").disabled=true;
} else {
codecGroupFlag = "";
}
} else {
codecGroupFlag = "";
}
if (codecGroupFlag) {
session.codecGroupFlag = codecGroupFlag;
}
formSubmitting = false;
var m = getById("mainmenu");
m.remove();
getById("head1").className = 'advanced';
getById("head2").className = 'advanced';
//getById("head3").className = 'advanced';
getById("head4").className = '';
try {
if (session.label === false) {
document.title = "Control Room";
}
} catch (e) {
errorlog(e);
};
session.director = true;
screensharesupport = false;
window.addEventListener("resize", updateMixer);
window.addEventListener("orientationchange", updateMixer);
getById("reshare").parentNode.removeChild(getById("reshare"));
//getById("mutespeakerbutton").style.display = null;
session.speakerMuted = true; // the director will start with audio playback muted.
toggleSpeakerMute(true);
if (session.cleanDirector == false) {
getById("roomHeader").style.display = "";
//getById("directorLinks").style.display = "";
getById("directorLinks1").style.display = "inline-block";
getById("directorLinks2").style.display = "inline-block";
getById("director_block_1").dataset.raw = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd;
getById("director_block_1").href = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd;
getById("director_block_1").innerText = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd;
getById("director_block_3").dataset.raw = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2;
getById("director_block_3").href = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2;
getById("director_block_3").innerText = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2;
getById("calendarButton").style.display = "inline-block";
} else {
getById("guestFeeds").innerHTML = '';
}
getById("guestFeeds").style.display = "";
if (!(session.cleanOutput)) {
if (session.queue){
getById("queuebutton").classList.remove("advanced");
}
getById("chatbutton").classList.remove("advanced");
getById("controlButtons").style.display = "inherit";
getById("mutespeakerbutton").classList.remove("advanced");
if (session.showDirector == false) {
getById("miniPerformer").innerHTML = '';
} else {
getById("miniPerformer").innerHTML = '';
}
getById("miniPerformer").className = "";
var tabindex = 26;
if (session.rooms && session.rooms.length > 0){
var container = getById("rooms");
container.innerHTML += 'Arm Transfer: ';
session.rooms.forEach(function (r) {
if(session.roomid == r) return; //don't include self
container.innerHTML += '';
tabindex++;
});
}
} else {
getById("miniPerformer").style.display = "none";
getById("controlButtons").style.display = "none";
}
if (session.chatbutton === true) {
getById("chatbutton").classList.remove("advanced");
getById("controlButtons").style.display = "inherit";
} else if (session.chatbutton === false) {
getById("chatbutton").classList.add("advanced");
}
clearInterval(session.updateLocalStatsInterval);
session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},3000);
if (session.autostart){
press2talk(true);
} else {
session.seeding=true;
session.seedStream();
}
joinRoom(session.roomid);
}
/**
* Handles click actions on the room selection buttons in #controlButtons
* @param {string} room - Room name to select/deselect for the next transfer call
*/
function handleRoomSelect(room) {
var elems = document.querySelectorAll(".btnArmTransferRoom");
[].forEach.call(elems, function(el) {
el.classList.remove("selected");
});
if (previousRoom == room) {
previousRoom = "";
armedTransfer = false;
stillNeedRoom = true;
} else {
previousRoom = room;
stillNeedRoom = false;
armedTransfer = true;
getById("roomselect_" + room).classList.add('selected');
}
}
function getDirectorSettings(scene){
var settings = {};
var eles = document.querySelectorAll('[data-action-type="solo-video"]');
settings.soloVideo = false;
for (var i=0;i\
\
Add a label\
";
controls.innerHTML += "
\
\
" + soloLink + "\
\
This is you, the director. You are also a performer.
";
controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference
ele.dataset.sid = session.streamID;
});
container.innerHTML = buttons;
container.appendChild(controls);
var labelID = document.getElementById("label_director");
labelID.onclick = function(ee){
var oldlabel = ee.target.innerText;
if (session.label===false){
oldlabel = "";
}
var newlabel = prompt("Enter a new Display Name for this stream", oldlabel);
if (newlabel!==null){
if (newlabel == ""){
newlabel = false;
ee.target.innerText = "Add a label";
} else {
ee.target.innerText = newlabel;
}
session.label = newlabel;
var data = {};
data.changeLabel = true;
data.value = session.label;
session.sendMessage(data);
}
}
labelID.style.float = "left";
labelID.style.top = "2px";
labelID.style.marginLeft = "5px";
labelID.style.position = "relative";
labelID.style.cursor="pointer";
if (session.label){
labelID.innerText = session.label;
}
}
function createControlBox(UUID, soloLink, streamID) {
if (document.getElementById("deleteme")) {
getById("deleteme").parentNode.removeChild(getById("deleteme"));
}
var controls = getById("controls_blank").cloneNode(true);
var container = document.createElement("div");
container.id = "container_" + UUID; // needed to delete on user disconnect
container.className = "vidcon directorMargins";
controls.style.display = "block";
controls.id = "controls_" + UUID;
getById("guestFeeds").appendChild(container);
var buttons = "
";
controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference
ele.dataset.UUID = UUID;
ele.dataset.sid = streamID;
});
container.innerHTML = buttons;
container.appendChild(videoContainer);
videoContainer.appendChild(session.rpcs[UUID].voiceMeter);
videoContainer.appendChild(session.rpcs[UUID].remoteMuteElement);
container.appendChild(controls);
}
function createDirectorCam(vid) {
getById("press2talk").innerHTML = "";
getById("press2talk").outerHTML = "";
if (document.getElementById("videoContainer_director")){
getById("videoContainer_director").appendChild(vid);
} else {
getById("miniPerformer").appendChild(vid);
}
vid.title = "This is the preview of the Director's audio and video output.";
getById("press2talk").dataset.value = 1;
session.muted = false;
toggleMute(true);
getById("screensharebutton").classList.remove("advanced");
getById("hangupbutton2").classList.remove("advanced");
setTimeout(function() {
toggleSettings();
}, 200);
if (urlParams.has('permaid')) {
updateURL("permaid=" + session.streamID);
} else {
updateURL("push=" + session.streamID);
}
}
function press2talk(clean = false) {
var ele = getById("press2talk");
ele.style.minWidth = "127px";
ele.style.padding = "7px";
getById("settingsbutton").classList.remove("advanced");
if (!document.getElementById("controls_director") && session.showDirector){createDirectorOnlyBox();}
if (session.videoDevice || (session.audioDevice && session.audioDevice!==1)){
if ((session.videoDevice === 1) && (session.audioDevice===false || session.audioDevice==1)){
session.publishDirector(clean, true);
session.muted = false;
toggleMute(true);
return;
} else {
enumerateDevices().then(function(deviceInfos) {
var vdevice = false;
var adevice = true;
if (session.audioDevice==0){
adevice=false;
}
if (session.videoDevice && (session.videoDevice!=1)){
for (let i = 0; i !== deviceInfos.length; ++i) {
var deviceInfo = deviceInfos[i];
if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.videoDevice))) {
vdevice = {deviceId: {exact: deviceInfo.deviceId}};
break;
} else if (deviceInfo.deviceId === session.videoDevice){
vdevice = {deviceId: {exact: deviceInfo.deviceId}};
break;
}
}
}
if (session.audioDevice && (session.audioDevice!=1)){
for (let i = 0; i !== deviceInfos.length; ++i) {
var deviceInfo = deviceInfos[i];
if ((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.audioDevice))) {
adevice = {deviceId: {exact: deviceInfo.deviceId}};
break;
} else if (deviceInfo.deviceId === session.audioDevice){
adevice = {deviceId: {exact: deviceInfo.deviceId}};
break;
}
}
}
session.publishDirector(clean, vdevice, adevice);
session.muted = false;
toggleMute(true);
});
return;
}
}
session.publishDirector(clean);
session.muted = false;
toggleMute(true);
}
function addToGoogleCalendar(){
var title = "Live Stream";
//var dates = "20180512T230000Z/20180513T030000Z";
var linkout = getById("director_block_1").innerText;
var details = "Join the live stream as a performer at the following link:
===> "+linkout+"
To test your connection and camera ahead of time, please visit https://obs.ninja/speedtest
Do not share the details of this invite with others, unless explicitly told to.";
details = details.split(' ').join('+');
details = details.split('&').join('%26');
var linkToOpen = "https://calendar.google.com/calendar/r/eventedit?text="+title+"&details="+details;
//https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134
window.open(linkToOpen);
}
function addToOutlookCalendar(){
var title = "Live Stream";
var linkout = getById("director_block_1").innerText;
var details = "Join the live stream as a performer at the following link:
===> "+linkout+"
To test your connection and camera ahead of time, please visit https://obs.ninja/speedtest
Do not share the details of this invite with others, unless explicitly told to.";
details = details.split(' ').join('%20');
details = details.split('&').join('%26');
var linkToOpen = "https://outlook.live.com/owa/?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&subject="+title+"&body="+details;
//https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134
window.open(linkToOpen);
}
function addToYahooCalendar(){
var title = "Live Stream";
var linkout = getById("director_block_1").innerText;
var details = "Join the live stream as a performer at the following link:
===> "+linkout+"
To test your connection and camera ahead of time, please visit https://obs.ninja/speedtest
Do not share the details of this invite with others, unless explicitly told to.";
details = details.split(' ').join('%20');
details = details.split('&').join('%26');
var linkToOpen = "https://calendar.yahoo.com?v60&title="+title+"&desc="+details;
//https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134
window.open(linkToOpen);
}
function toggle(ele, tog = false, inline = true) {
var x = ele;
if (x.style.display === "none") {
if (inline) {
x.style.display = "inline-block";
} else {
x.style.display = "block";
}
} else {
x.style.display = "none";
}
if (tog) {
if (tog.dataset.saved) {
tog.innerHTML = tog.dataset.saved;
delete(tog.dataset.saved);
} else {
tog.dataset.saved = tog.innerHTML;
tog.innerHTML = "Hide This";
}
}
}
function toggleByDataset(filter) {
var elements = document.querySelectorAll('[data-cluster="'+filter+'"]'); // ie: .cluster1
for (var i = 0; i < elements.length; i++) {
elements[i].classList.toggle('hidden');
}
}
var SelectedAudioOutputDevices = []; // order matters.
var SelectedAudioInputDevices = []; // ..
var SelectedVideoInputDevices = []; // ..
function enumerateDevices() {
log("enumerated start");
if (typeof navigator.enumerateDevices === "function") {
log("enumerated failed 1");
return navigator.enumerateDevices();
} else if (typeof navigator.mediaDevices === "object" && typeof navigator.mediaDevices.enumerateDevices === "function") {
return navigator.mediaDevices.enumerateDevices();
} else {
return new Promise((resolve, reject) => {
try {
if (window.MediaStreamTrack == null || window.MediaStreamTrack.getSources == null) {
throw new Error();
}
window.MediaStreamTrack.getSources((devices) => {
resolve(devices
.filter(device => {
return device.kind.toLowerCase() === "video" || device.kind.toLowerCase() === "videoinput";
})
.map(device => {
return {
deviceId: device.deviceId != null ? device.deviceId : ""
, groupId: device.groupId
, kind: "videoinput"
, label: device.label
, toJSON: /* istanbul ignore next */ function() {
return this;
}
};
}));
});
} catch (e) {
errorlog(e);
}
});
}
}
function requestOutputAudioStream() {
try {
//warnlog("GET USER MEDIA");
return navigator.mediaDevices.getUserMedia({
audio: true
, video: false
}).then(function(stream1) { // Apple needs thi to happen before I can access EnumerateDevices.
log("get media sources; request audio stream");
return enumerateDevices().then(function(deviceInfos) {
stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels
});
const audioOutputSelect = document.querySelector('#outputSourceScreenshare');
audioOutputSelect.remove(0);
audioOutputSelect.removeAttribute("onclick");
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
if (deviceInfo == null) {
continue;
}
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audiooutput') {
const option = document.createElement('option');
if (audioOutputSelect.length === 0) {
option.dataset.default = true;
} else {
option.dataset.default = false;
}
option.value = deviceInfo.deviceId || "default";
if (option.value == session.sink) {
option.selected = true;
}
option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`;
audioOutputSelect.appendChild(option);
} else {
log('Some other kind of source/device: ', deviceInfo);
}
}
});
});
} catch (e) {
if (!(session.cleanOutput)) {
if (window.isSecureContext) {
warnUser("An error has occured when trying to access the default audio device. The reason is not known.");
} else if ((iOS) || (iPad)) {
warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported.");
} else {
warnUser("Error acessing the default audio device.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia");
}
}
}
}
function requestAudioStream() {
try {
//warnlog("GET USER MEDIA");
return navigator.mediaDevices.getUserMedia({
audio: true
, video: false
}).then(function(stream1) { // Apple needs thi to happen before I can access EnumerateDevices.
log("get media sources; request audio stream");
return enumerateDevices().then(function(deviceInfos) {
stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels
});
log("updating audio");
const audioInputSelect = document.querySelector('select#audioSourceScreenshare');
audioInputSelect.remove(1);
audioInputSelect.removeAttribute("onchange");
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
if (deviceInfo == null) {
continue;
}
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audioinput') {
option.text = deviceInfo.label || `Microphone ${audioInputSelect.length + 1}`;
audioInputSelect.appendChild(option);
} else {
log('Some other kind of source/device: ', deviceInfo);
}
}
audioInputSelect.style.minHeight = ((audioInputSelect.childElementCount + 1) * 1.15 * 16) + 'px';
audioInputSelect.style.minWidth = "342px";
});
});
} catch (e) {
if (!(session.cleanOutput)) {
if (window.isSecureContext) {
warnUser("An error has occured when trying to access the default audio device. The reason is not known.");
} else if ((iOS) || (iPad)) {
warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported.");
} else {
warnUser("Error acessing the default audio device.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia");
}
}
}
}
function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-pages/src/content/devices/input-output/js/main.js#L19
log("got devices!");
log(deviceInfos);
try {
const audioInputSelect = document.querySelector('#audioSource');
audioInputSelect.innerHTML = "";
var option = document.createElement('input');
option.type = "checkbox";
option.value = "ZZZ";
option.name = "multiselect1";
option.id = "multiselect1";
option.style.display = "none";
option.checked = true;
var label = document.createElement('label');
label.for = option.name;
label.innerHTML = 'No Audio';
var listele = document.createElement('li');
listele.appendChild(option);
listele.appendChild(label);
audioInputSelect.appendChild(listele);
option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected
if (!(getById("multiselect1").checked)) {
getById("multiselect1").checked = true;
if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {} else {
SelectedAudioInputDevices.push(event.currentTarget.value);
}
log("CHECKED 1");
} else {
var list = document.querySelectorAll("#audioSource>li>input");
for (var i = 0; i < list.length; i++) {
if (list[i].id !== "multiselect1") {
list[i].checked = false;
}
}
while (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {
SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(event.currentTarget.value), 1);
}
}
};
getById('multiselect-trigger').dataset.state = '0';
getById('multiselect-trigger').classList.add('closed');
getById('multiselect-trigger').classList.remove('open');
getById('chevarrow1').classList.add('bottom');
const videoSelect = document.querySelector('select#videoSourceSelect');
const audioOutputSelect = document.querySelector('#outputSource');
const selectors = [videoSelect];
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
function comp(a, b) {
if (a.kind === 'audioinput') {
return 0;
} else if (a.kind === 'audiooutput') {
return 0;
}
const labelA = a.label.toUpperCase();
const labelB = b.label.toUpperCase();
if (labelA > labelB) {
return 1;
} else if (labelA < labelB) {
return -1;
}
return 0;
}
//deviceInfos.sort(comp); // I like this idea, but it messes with the defaults. I just don't know what it will do.
// This is to hide NDI from default device. NDI Tools fucks up.
var tmp = [];
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if (!((deviceInfo.kind === 'videoinput') && (deviceInfo.label.toLowerCase().startsWith("ndi") || deviceInfo.label.toLowerCase().startsWith("newtek")))) {
tmp.push(deviceInfo);
}
}
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.toLowerCase().startsWith("ndi") || deviceInfo.label.toLowerCase().startsWith("newtek"))) {
tmp.push(deviceInfo);
log("V DEVICE FOUND = " + deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase());
}
}
deviceInfos = tmp;
log(deviceInfos);
if ((session.audioDevice) && (session.audioDevice !== 1)) { // this sorts according to users's manual selection
var tmp = [];
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if ((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.audioDevice))) {
tmp.push(deviceInfo);
log("A DEVICE FOUND = " + deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase());
} else if (deviceInfo.deviceId === session.audioDevice){
tmp.push(deviceInfo);
log("EXACT A DEVICE FOUND");
}
}
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if (!((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.audioDevice)))) {
if (deviceInfo.deviceId !== session.audioDevice){
tmp.push(deviceInfo);
}
}
}
deviceInfos = tmp;
log(session.audioDevice);
log(deviceInfos);
}
if ((session.videoDevice) && (session.videoDevice !== 1)) { // this sorts according to users's manual selection
var tmp = [];
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.videoDevice))) {
tmp.push(deviceInfo);
log("V DEVICE FOUND = " + deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase());
} else if (deviceInfo.deviceId === session.videoDevice){
tmp.push(deviceInfo);
log("EXACT V DEVICE FOUND");
}
}
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if (!((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.videoDevice)))) {
if (deviceInfo.deviceId !== session.videoDevice){
tmp.push(deviceInfo);
}
}
}
deviceInfos = tmp;
log("VDECICE:" + session.videoDevice);
log(deviceInfos);
}
var counter = 1;
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
if (deviceInfo == null) {
continue;
}
if (deviceInfo.kind === 'audioinput') {
option = document.createElement('input');
option.type = "checkbox";
counter++;
listele = document.createElement('li');
if (counter == 2) {
option.checked = true;
listele.style.display = "block";
option.style.display = "none";
getById("multiselect1").checked = false;
getById("multiselect1").parentNode.style.display = "none";
} else {
listele.style.display = "none";
}
option.value = deviceInfo.deviceId || "default";
option.name = "multiselect" + counter;
option.id = "multiselect" + counter;
label = document.createElement('label');
label.for = option.name;
label.innerHTML = " " + (deviceInfo.label || ("microphone " + ((audioInputSelect.length || 0) + 1)));
listele.appendChild(option);
listele.appendChild(label);
audioInputSelect.appendChild(listele);
option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected
getById("multiselect1").checked = false;
log("UNCHECKED");
if (!(CtrlPressed)) {
document.querySelectorAll("#audioSource input[type='checkbox']").forEach(function(item) {
if (event.currentTarget.id !== item.id) {
item.checked = false;
while (SelectedAudioInputDevices.indexOf(item.value) > -1) {
SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1);
}
} else {
item.checked = true;
if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {} else {
SelectedAudioInputDevices.push(event.currentTarget.value);
}
}
});
}
};
} else if (deviceInfo.kind === 'videoinput') {
option = document.createElement('option');
option.value = deviceInfo.deviceId || "default";
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
} else if (deviceInfo.kind === 'audiooutput') {
option = document.createElement('option');
if (audioOutputSelect.length === 0) {
option.dataset.default = true;
} else {
option.dataset.default = false;
}
option.value = deviceInfo.deviceId || "default";
if (option.value == session.sink) {
option.selected = true;
}
option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`;
audioOutputSelect.appendChild(option);
} else {
log('Some other kind of source/device: ', deviceInfo);
}
}
if (audioOutputSelect.childNodes.length == 0) {
option = document.createElement('option');
option.value = "default";
option.text = "System Default";
audioOutputSelect.appendChild(option);
}
option = document.createElement('option');
option.text = "Disable Video";
option.value = "ZZZ";
videoSelect.appendChild(option); // NO AUDIO OPTION
selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
} catch (e) {
errorlog(e);
}
}
if (location.protocol !== 'https:') {
if (!(session.cleanOutput)) {
warnUser("SSL (https) is not enabled. This site will not work without it!");
}
}
function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
switch (resolutionFallbackLevel) {
case 0:
if (isSafariBrowser) {
return {
width: {
min: 360
, ideal: 1920
, max: 1920
}
, height: {
min: 360
, ideal: 1080
, max: 1080
}
};
} else {
return {
width: {
min: 720
, ideal: 1920
, max: 1920
}
, height: {
min: 720
, ideal: 1080
, max: 1920
}
};
}
case 1:
if (isSafariBrowser) {
return {
width: {
min: 360
, ideal: 1280
, max: 1280
}
, height: {
min: 360
, ideal: 720
, max: 720
}
};
} else {
return {
width: {
min: 720
, ideal: 1280
, max: 1280
}
, height: {
min: 720
, ideal: 720
, max: 1280
}
};
}
case 2:
if (isSafariBrowser) {
return {
width: {
min: 640
}
, height: {
min: 360
}
};
} else {
return {
width: {
min: 240
, ideal: 640
, max: 1280
}
, height: {
min: 240
, ideal: 360
, max: 1280
}
};
}
case 3:
if (isSafariBrowser) {
return {
width: {
min: 360
, ideal: 1280
, max: 1440
}
};
} else {
return {
width: {
min: 360
, ideal: 1280
, max: 1440
}
};
}
case 4:
if (isSafariBrowser) {
return {
height: {
min: 360
, ideal: 720
, max: 960
}
};
} else {
return {
height: {
ideal: 720
, max: 960
}
};
}
case 5:
if (isSafariBrowser) {
return {
width: {
min: 360
, ideal: 640
, max: 1440
}
, height: {
min: 360
, ideal: 360
, max: 720
}
};
} else {
return {
width: {
ideal: 640
, max: 1920
}
, height: {
ideal: 360
, max: 1920
}
}; // same as default, but I didn't want to mess with framerates until I gave it all a try first
}
case 6:
if (isSafariBrowser) {
return {}; // iphone users probably don't need to wait any longer, so let them just get to it
} else {
return {
width: {
min: 360
, ideal: 640
, max: 3840
}
, height: {
min: 360
, ideal: 360
, max: 2160
}
};
}
case 7:
return { // If the camera is recording in low-light, it may have a low framerate. It coudl also be recording at a very high resolution.
width: {
min: 360
, ideal: 640
}
, height: {
min: 360
, ideal: 360
}
, };
case 8:
return {
width: {
min: 360
}
, height: {
min: 360
}
, frameRate: 10
}; // same as default, but I didn't want to mess with framerates until I gave it all a try first
case 9:
return {
frameRate: 0
}; // Some Samsung Devices report they can only support a framerate of 0.
case 10:
return {}
default:
return {};
}
}
function addScreenDevices(device) {
if (device.kind == "audio") {
const audioInputSelect = document.querySelector('#audioSource3');
const listele = document.createElement('li');
listele.style.display = "block";
const option = document.createElement('input');
option.type = "checkbox";
option.checked = true;
if (getById('multiselect-trigger3').dataset.state == 0) {
option.style.display = "none";
}
option.value = device.id;
option.name = device.label;
option.dataset.type = "screen";
option.label = device.label;
const label = document.createElement('label');
label.for = option.name;
label.innerHTML = " " + device.label;
listele.appendChild(option);
listele.appendChild(label);
option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected
log("change 4644");
if (!(CtrlPressed)) {
document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) {
if (event.currentTarget.value !== item.value) { // this shoulnd't happen, but if it does.
item.checked = false;
if (item.dataset.type == "screen") {
item.parentElement.parentElement.removeChild(item.parentElement);
}
while (SelectedAudioInputDevices.indexOf(item.value) > -1) {
SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1);
}
activatedPreview = false;
grabAudio("videosource", "#audioSource3"); // exclude item.id
} else {
if (SelectedAudioInputDevices.indexOf(item.value) > -1) {} else {
SelectedAudioInputDevices.push(item.value);
}
item.checked = true;
activatedPreview = false;
grabAudio("videosource", "#audioSource3", item.value); // exclude item.id. we will reconnect, even if already connected, as a way to 'reset' a device if it isn't working.
}
});
}
event.stopPropagation();
return false;
};
audioInputSelect.appendChild(listele);
getById("audioSourceNoAudio2").checked = false;
} else if (device.kind == "video") {
const videoSelect = document.querySelector('select#videoSource3');
//const selectors = [ videoSelect];
//const values = selectors.map(select => select.value);
const option = document.createElement('option');
option.value = device.id;
option.text = device.label;
option.selected = true;
option.label = device.label;
videoSelect.appendChild(option);
}
}
function gotDevices2(deviceInfos) {
log("got devices!");
log(deviceInfos);
getById("multiselect-trigger3").dataset.state = "0";
getById("multiselect-trigger3").classList.add('closed');
getById("multiselect-trigger3").classList.remove('open');
getById("chevarrow2").classList.add('bottom');
var knownTrack = false;
try {
const audioInputSelect = document.querySelector('#audioSource3');
const videoSelect = document.querySelector('select#videoSource3');
const audioOutputSelect = document.querySelector('#outputSource3');
const selectors = [videoSelect];
[audioInputSelect].forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
[audioOutputSelect].forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
var counter = 0;
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
if (deviceInfo == null) {
continue;
}
if (deviceInfo.kind === 'audioinput') {
const option = document.createElement('input');
option.type = "checkbox";
counter++;
const listele = document.createElement('li');
listele.style.display = "none";
try {
session.streamSrc.getAudioTracks().forEach(function(track) {
if (deviceInfo.label == track.label) {
option.checked = true;
listele.style.display = "inherit";
}
});
} catch (e) {
errorlog(e);
}
option.style.display = "none"
option.value = deviceInfo.deviceId || "default";
option.name = "multiselecta" + counter;
option.id = "multiselecta" + counter;
option.dataset.label = deviceInfo.label || ("microphone " + ((audioInputSelect.length || 0) + 1));
const label = document.createElement('label');
label.for = option.name;
label.innerHTML = " " + (deviceInfo.label || ("microphone " + ((audioInputSelect.length || 0) + 1)));
listele.appendChild(option);
listele.appendChild(label);
audioInputSelect.appendChild(listele);
option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected
log("change 4768");
if (!(CtrlPressed)) {
document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) {
if (event.currentTarget.value !== item.value) {
item.checked = false;
if (item.dataset.type == "screen") {
item.parentElement.parentElement.removeChild(item.parentElement);
}
while (SelectedAudioInputDevices.indexOf(item.value) > -1) {
SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1);
}
} else {
item.checked = true;
if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {} else {
SelectedAudioInputDevices.push(event.currentTarget.value);
}
}
});
} else {
if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {} else {
SelectedAudioInputDevices.push(event.currentTarget.value);
}
getById("audioSourceNoAudio2").checked = false;
}
};
} else if (deviceInfo.kind === 'videoinput') {
const option = document.createElement('option');
option.value = deviceInfo.deviceId || "default";
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
try {
if (!knownTrack){
if (session.canvasSource){
session.canvasSource.srcObject.getVideoTracks().forEach(function(track) {
if (option.text == track.label) {
option.selected = true;
knownTrack = true;
}
});
}
}
if (!knownTrack){
session.streamSrc.getVideoTracks().forEach(function(track) {
if (option.text == track.label) {
option.selected = true;
knownTrack = true;
}
});
}
} catch (e) {
errorlog(e);
}
videoSelect.appendChild(option);
} else if (deviceInfo.kind === 'audiooutput') {
const option = document.createElement('option');
if (audioOutputSelect.length === 0) {
option.dataset.default = true;
} else {
option.dataset.default = false;
}
option.value = deviceInfo.deviceId || "default";
if (option.value == session.sink) {
option.selected = true;
}
option.text = deviceInfo.label || `Speaker ${outputSelect.length + 1}`;
audioOutputSelect.appendChild(option);
} else {
log('Some other kind of source/device: ', deviceInfo);
}
}
if (audioOutputSelect.childNodes.length == 0) {
const option = document.createElement('option');
option.value = "default";
option.text = "System Default";
audioOutputSelect.appendChild(option);
}
////////////
session.streamSrc.getAudioTracks().forEach(function(track) { // add active ScreenShare audio tracks to the list
log("Checking for screenshare audio");
var matched = false;
for (var i = 0; i !== deviceInfos.length; ++i) {
var deviceInfo = deviceInfos[i];
if (deviceInfo == null) {
continue;
}
log("---");
if (track.label == deviceInfo.label) {
matched = true;
continue;
}
}
if (matched == false) { // Not a gUM device
var listele = document.createElement('li');
listele.style.display = "block";
var option = document.createElement('input');
option.type = "checkbox";
option.value = track.id;
option.checked = true;
option.style.display = "none";
option.name = track.label;
option.label = track.label;
option.dataset.type = "screen";
const label = document.createElement('label');
label.for = option.name;
label.innerHTML = " " + track.label;
listele.appendChild(option);
listele.appendChild(label);
option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected
log("change 4873");
var trackid = null;
if (!(CtrlPressed)) {
document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) {
if (event.currentTarget.value !== item.value) { // this shoulnd't happen, but if it does.
item.checked = false;
if (item.dataset.type == "screen") {
item.parentElement.parentElement.removeChild(item.parentElement);
}
} else {
event.currentTarget.checked = true;
trackid = item.value;
}
});
} else {
//getById("audioSourceNoAudio2").checked=false;
if (event.currentTarget.dataset.type == "screen") {
event.currentTarget.parentElement.parentElement.removeChild(event.currentTarget.parentElement);
}
}
activatedPreview = false;
grabAudio("videosource", "#audioSource3", trackid); // exclude item.id.
event.stopPropagation();
return false;
};
audioInputSelect.appendChild(listele);
}
});
/////////// no video option
var optionss = false;
if (screensharesupport) {
optionss = document.createElement('option');
optionss.text = "New Screen Share";
optionss.value = "XXX";
optionss.previous =
videoSelect.appendChild(optionss); // NO AUDIO OPTION
}
option = document.createElement('option'); // no video
option.text = "Disable Video";
option.value = "ZZZ";
videoSelect.appendChild(option);
if (session.streamSrc.getVideoTracks().length == 0) {
option.selected = true;
} else if (knownTrack == false) {
option = document.createElement('option'); // no video
option.text = session.streamSrc.getVideoTracks()[0].label;
option.value = "YYY";
videoSelect.appendChild(option);
option.selected = true;
}
if (optionss) {
optionss.lastSelected = videoSelect.selectedIndex;
}
///////////// /// NO AUDIO appended option
var option = document.createElement('input');
option.type = "checkbox";
option.value = "ZZZ";
option.style.display = "none"
option.id = "audioSourceNoAudio2";
var label = document.createElement('label');
label.for = option.name;
label.innerHTML = " No Audio";
var listele = document.createElement('li');
if (session.streamSrc.getAudioTracks().length == 0) {
option.checked = true;
} else {
listele.style.display = "none";
option.checked = false;
}
option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected
log("change 4938");
if (!(CtrlPressed)) {
document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) {
if (event.currentTarget.value !== item.value) {
item.checked = false;
if (item.dataset.type == "screen") {
item.parentElement.parentElement.removeChild(item.parentElement);
}
while (SelectedAudioInputDevices.indexOf(item.value) > -1) {
SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1);
}
} else {
item.checked = true;
if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {
//
} else {
SelectedAudioInputDevices.push(event.currentTarget.value);
}
}
});
} else {
document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) {
if (event.currentTarget.value === item.value) {
event.currentTarget.checked = true;
if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {} else {
SelectedAudioInputDevices.push(event.currentTarget.value);
}
} else {
item.checked = false;
if (item.dataset.type == "screen") {
item.parentElement.parentElement.removeChild(item.parentElement);
}
while (SelectedAudioInputDevices.indexOf(item.value) > -1) {
SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1);
}
}
});
}
};
listele.appendChild(option);
listele.appendChild(label);
audioInputSelect.appendChild(listele);
////////////
selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
audioInputSelect.onchange = function() {
log("Audio OPTION HAS CHANGED? 2");
activatedPreview = false;
setTimeout(function(){
grabAudio("videosource", "#audioSource3");
},10)
};
videoSelect.onchange = function(event) {
try {
if (event.target.options[event.target.options.selectedIndex].value === "XXX") {
videoSelect.selectedIndex = event.target.options[event.target.options.selectedIndex].lastSelected;
if (session.screenShareState == false) {
toggleScreenShare();
} else {
toggleScreenShare(true);
}
return;
}
} catch (e) {}
log("video source changed");
activatedPreview = false;
grabVideo(session.quality, "videosource", "select#videoSource3");
enumerateDevices().then(gotDevices2).then(function() {
session.screenShareState = false;
pokeIframeAPI("screen-share-ended");
getById("screensharebutton").classList.add("float");
getById("screensharebutton").classList.remove("float2");
});
};
getById("refreshVideoButton").onclick = function() {
if (session.screenShareState) {
log("can't refresh a screenshare");
return;
}
log("video source changed");
activatedPreview = false;
grabVideo(session.quality, "videosource", "select#videoSource3");
};
audioOutputSelect.onchange = function() {
if ((iOS) || (iPad)) {
return;
}
var outputSelect = document.querySelector('select#outputSource3');
session.sink = outputSelect.options[outputSelect.selectedIndex].value;
//if (session.sink=="default"){session.sink=false;} else {
try {
getById("videosource").setSinkId(session.sink).then(() => {
log("New Output Device:" + session.sink);
}).catch(error => {
errorlog(error);
});
} catch (e) {
errorlog(e);
}
for (UUID in session.rpcs) {
session.rpcs[UUID].videoElement.setSinkId(session.sink).then(() => {
log("New Output Device for: " + UUID);
}).catch(error => {
errorlog(error);
});
}
}
} catch (e) {
errorlog(e);
}
}
function playtone(screen = false) {
if ((iOS) || (iPad)) {
// try{
// session.audioContext.resume();
// } catch(e){errorlog(e);}
var testtone = document.getElementById("testtone");
if (testtone) {
testtone.mute
testtone.play();
}
return;
}
if (screen) {
var outputSelect = document.querySelector('select#outputSourceScreenshare');
session.sink = outputSelect.options[outputSelect.selectedIndex].value;
}
var testtone = document.getElementById("testtone");
if (testtone) {
if (session.sink) {
try {
testtone.setSinkId(session.sink).then(() => { // TODO: iOS doens't support sink. Needs to bypass if IOS
log("changing audio sink:" + session.sink);
testtone.play();
}).catch(error => {
errolog("couldn't set sink");
errorlog(error);
});
} catch (e) {
warnlog(e); // firefox?
testtone.play();
}
} else {
testtone.play();
}
}
}
async function getAudioOnly(selector, trackid = null, override = false) {
var audioSelect = document.querySelector(selector).querySelectorAll("input");
var audioList = [];
var streams = [];
log("getAudioOnly()");
for (var i = 0; i < audioSelect.length; i++) {
if (audioSelect[i].value == "ZZZ") {
continue;
} else if (trackid == audioSelect[i].value) { // skip already excluded
continue;
} else if ("screen" == audioSelect[i].dataset.type) { // skip already excluded ---------- !!!!!! DOES THIS MAKE SENSE? TODO: CHECK
continue;
} else if (audioSelect[i].checked) {
log(audioSelect[i]);
audioList.push(audioSelect[i]);
}
}
for (var i = 0; i < audioList.length; i++) {
if ((audioList[i].value == "default") && (session.echoCancellation !== false) && (session.autoGainControl !== false) && (session.noiseSuppression !== false)) {
var constraint = {
audio: true
};
} else { // Just trying to avoid problems with some systems that don't support these features
var constraint = {
audio: {
deviceId: {
exact: audioList[i].value
}
}
};
if (session.echoCancellation === false) {
constraint.audio.echoCancellation = false;
}
if (session.autoGainControl === false) {
constraint.audio.autoGainControl = false;
}
if (session.noiseSuppression === false) {
constraint.audio.noiseSuppression = false;
}
}
constraint.video = false;
if (override !== false) {
try {
if (override.audio.deviceId == audioList[i].value) {
constraint = override;
}
} catch (e) {}
}
if (session.audioInputChannels) {
if (constraint.audio === true) {
constraint.audio = {};
constraint.audio.channelCount = session.audioInputChannels;
} else if (constraint.audio) {
constraint.audio.channelCount = session.audioInputChannels;
}
}
log("CONSTRAINT");
log(constraint);
var stream = await navigator.mediaDevices.getUserMedia(constraint).then(function(stream2) {
return stream2;
}).catch(function(err) {
errorlog(err);
if (!(session.cleanOutput)) {
if (override !== false) {
if (err.name) {
if (err.constraint) {
warnUser(err['name'] + ": " + err['constraint']);
}
}
}
}
}); // More error reporting maybe?
if (stream) {
streams.push(stream);
}
}
return streams;
}
function applyMirror(mirror, eleName = 'previewWebcam') { // true unmirrors as its already mirrored
var transFlip = "";
var transNorm = "";
if ((eleName == 'videosource') && (session.windowed)) {
transFlip = " translate(0, 50%)";
transNorm = " translate(0, -50%)";
}
if (session.mirrored == 2) {
mirror = true;
} else if (session.mirrored === 0) {
mirror = true;
}
if (mirror) {
if (session.mirrored && session.flipped) {
getById(eleName).style.transform = " scaleX(-1) scaleY(-1)" + transFlip;
getById(eleName).classList.add("mirrorControl");
} else if (session.mirrored) {
getById(eleName).style.transform = "scaleX(-1)" + transNorm;
getById(eleName).classList.add("mirrorControl");
} else if (session.flipped) {
getById(eleName).style.transform = "scaleY(-1) scaleX(1)" + transFlip;
getById(eleName).classList.remove("mirrorControl");
} else {
getById(eleName).style.transform = "scaleX(1)" + transNorm;
getById(eleName).classList.remove("mirrorControl");
}
} else {
if (session.mirrored && session.flipped) {
getById(eleName).style.transform = " scaleX(1) scaleY(-1)" + transFlip;
getById(eleName).classList.remove("mirrorControl");
} else if (session.mirrored) {
getById(eleName).style.transform = "scaleX(1)" + transNorm;
getById(eleName).classList.remove("mirrorControl");
} else if (session.flipped) {
getById(eleName).style.transform = "scaleY(-1) scaleX(-1)" + transFlip;
getById(eleName).classList.add("mirrorControl");
} else {
getById(eleName).style.transform = "scaleX(-1)" + transNorm;
getById(eleName).classList.add("mirrorControl");
}
}
}
function cleanupMediaTracks() {
try {
if (session.streamSrc) {
session.streamSrc.getTracks().forEach(function(track) {
session.streamSrc.removeTrack(track);
track.stop();
log("stopping old track");
});
}
if (session.videoElement) {
session.videoElement.srcObject.getTracks().forEach(function(track) {
session.videoElement.srcObject.removeTrack(track);
track.stop();
log("stopping old track");
});
}
activatedPreview = false;
} catch (e) {
errorlog(e);
}
}
/// Detect system changes; handle change or use for debugging
var lastAudioDevice = null;
var lastVideoDevice = null;
var lastPlaybackDevice = null;
var audioReconnectTimeout = null;
var videoReconnectTimeout = null;
var grabDevicesTimeout = null;
var playbackReconnectTimeout = null;
function reconnectDevices(event) { /// TODO: Perhaps change this to only if there is a DISCONNECT; rather than ON NEW DEVICE?
if ((iOS) || (iPad)) {
// try{
// session.audioContext.resume();
// } catch(e){errorlog(e);}
resetupAudioOut();
return;
}
warnlog("A media device has changed");
if (document.getElementById("previewWebcam")) {
var outputSelect = document.getElementById("outputSource");
if (!outputSelect) {
errorlog("resetup audio failed");
return;
}
try {
session.sink = outputSelect.options[outputSelect.selectedIndex].value;
getById("previewWebcam").setSinkId(session.sink).then(() => {}).catch(error => {
warnlog(error);
});
} catch (e) {
errorlog(e);
}
return;
}
if (session.streamSrc === null) {
return;
}
if (document.getElementById("videosource") === null) {
return;
}
try {
session.streamSrc.getTracks().forEach(function(track) {
if (track.readyState == "ended") {
if (track.kind == "audio") {
lastAudioDevice = track.label;
} else if (track.kind == "video") {
lastVideoDevice = track.label;
}
session.streamSrc.removeTrack(track);
log("remove ended old track");
}
});
session.videoElement.srcObject.getTracks().forEach(function(track) {
if (track.readyState == "ended") {
session.videoElement.srcObject.removeTrack(track);
log("remove ended old track");
}
});
} catch (e) {
errorlog(e);
}
clearTimeout(audioReconnectTimeout);
audioReconnectTimeout = null;
if (lastAudioDevice) {
audioReconnectTimeout = setTimeout(function() { // only reconnect same audio device. If reconnected, clear the disconnected flag.
enumerateDevices().then(gotDevices2).then(function() {
// TODO: check to see if any audio is connected?
var streamConnected = false;
var audioSelect = document.querySelector("#audioSource3").querySelectorAll("input");
for (var i = 0; i < audioSelect.length; i++) {
if (audioSelect[i].value == "ZZZ") {
continue;
} else if (audioSelect[i].checked) {
log("checked");
streamConnected = true;
break;
}
}
if (!streamConnected) {
for (var i = 0; i < audioSelect.length; i++) {
if (audioSelect[i].value == "ZZZ") {
continue;
}
//errorlog(lastAudioDevice + " : " + audioSelect[i].dataset.label);
if (lastAudioDevice == audioSelect[i].dataset.label) { // if the last disconnected device matches.
audioSelect[i].checked = true;
streamConnected = true;
lastAudioDevice = null;
warnlog("DISCONNECTED AUDIO DEVICE RECONNECTED");
//for (var j=0; j check if session.sink still exists -> if not, select default default (track past last sink) -> if last disconnected devices comes back, reconnect it.
// lastPlaybackDevice
//if (session.sink){ // Let Chrome handle the audio automatically, since not manually specified.
clearTimeout(playbackReconnectTimeout);
playbackReconnectTimeout = setTimeout(function() {
enumerateDevices().then(gotDevices2).then(function() {
resetupAudioOut();
});
}, 500);
}
function resetupAudioOut() {
if ((iOS) || (iPad)) {
for (var UUID in session.rpcs) {
if (session.rpcs[UUID].videoElement) {
session.rpcs[UUID].videoElement.pause().then(() => {
setTimeout(function(uuid) {
session.rpcs[uuid].videoElement.play().then(() => {
log("toggle pause/play");
});
}, 0, UUID);
});
}
}
return;
}
var outputSelect = document.getElementById("outputSource3");
if (!outputSelect) {
errorlog("resetup audio failed");
return;
}
log("Resetting Audio Output");
var sinkSet = false;
for (var i = 0; i < outputSelect.options.length; i++) {
if (outputSelect.options[i].value == session.sink) {
outputSelect.options[i].selected = true;
sinkSet = true;
}
}
if (sinkSet == false) {
if (outputSelect.options[0]) {
outputSelect.options[0].selected = true;
sinkSet = outputSelect.value;
}
} else {
sinkSet = session.sink;
}
if (sinkSet) {
session.videoElement.setSinkId(sinkSet).then(() => {}).catch(error => {
errorlog(error);
});
for (UUID in session.rpcs) {
try{
session.rpcs[UUID].videoElement.setSinkId(sinkSet).then(() => {
log("New Output Device for: " + UUID);
}).catch(error => {
errorlog(error);
});
} catch(e){warnlog(e);}
}
}
}
function obfuscateURL(input) {
if (input.startsWith("https://obs.ninja/")) {
input = input.replace('https://obs.ninja/', '');
} else if (input.startsWith("http://obs.ninja/")) {
input = input.replace('http://obs.ninja/', '');
} else if (input.startsWith("obs.ninja/")) {
input = input.replace('obs.ninja/', '');
}
input = input.replace('&view=', '&v=');
input = input.replace('&view&', '&v&');
input = input.replace('?view&', '?v&');
input = input.replace('?view=', '?v=');
input = input.replace('&videobitrate=', '&vb=');
input = input.replace('?videobitrate=', '?vb=');
input = input.replace('&bitrate=', '&vb=');
input = input.replace('?bitrate=', '?vb=');
input = input.replace('?audiodevice=', '?ad=');
input = input.replace('&audiodevice=', '&ad=');
input = input.replace('?label=', '?l=');
input = input.replace('&label=', '&l=');
input = input.replace('?stereo=', '?s=');
input = input.replace('&stereo=', '&s=');
input = input.replace('&stereo&', '&s&');
input = input.replace('?stereo&', '?s&');
input = input.replace('?webcam&', '?wc&');
input = input.replace('&webcam&', '&wc&');
input = input.replace('?remote=', '?rm=');
input = input.replace('&remote=', '&rm=');
input = input.replace('?password=', '?p=');
input = input.replace('&password=', '&p=');
input = input.replace('&maxvideobitrate=', '&mvb=');
input = input.replace('?maxvideobitrate=', '?mvb=');
input = input.replace('&maxbitrate=', '&mvb=');
input = input.replace('?maxbitrate=', '?mvb=');
input = input.replace('&height=', '&h=');
input = input.replace('?height=', '?h=');
input = input.replace('&width=', '&w=');
input = input.replace('?width=', '?w=');
input = input.replace('&quality=', '&q=');
input = input.replace('?quality=', '?q=');
input = input.replace('&cleanoutput=', '&clean=');
input = input.replace('?cleanoutput=', '?clean=');
input = input.replace('&maxviewers=', '&clean=');
input = input.replace('?maxviewers=', '?clean=');
input = input.replace('&framerate=', '&fr=');
input = input.replace('?framerate=', '?fr=');
input = input.replace('&fps=', '&fr=');
input = input.replace('?fps=', '?fr=');
input = input.replace('&permaid=', '&push=');
input = input.replace('?permaid=', '?push=');
input = input.replace('&roomid=', '&r=');
input = input.replace('?roomid=', '?r=');
input = input.replace('&room=', '&r=');
input = input.replace('?room=', '?r=');
log(input);
var key = "OBSNINJAFORLIFE";
var encrypted = CryptoJS.AES.encrypt(input, key);
var output = "https://invite.cam/" + encrypted.toString();
return output;
}
document.addEventListener("visibilitychange", function() {
log(document.hidden, document.visibilityState);
if ((iOS) || (iPad)) { // fixes a bug on iOS devices. Not need with other devices?
if (document.visibilityState === 'visible') {
setTimeout(function() {
resetupAudioOut();
}, 500);
}
}
});
try {
navigator.mediaDevices.ondevicechange = reconnectDevices;
} catch (e) {
errorlog(e);
}
function updateConnectionStatus() {
warnlog("Connection type changed from " + session.stats.network_type + " to " + Connection.effectiveType);
session.stats.network_type = Connection.effectiveType + " / " + Connection.type;
session.ping();
}
try {
var Connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
session.stats.network_type = Connection.effectiveType + " / " + Connection.type;
Connection.addEventListener('change', updateConnectionStatus);
} catch (e) {}
var beforeScreenShare = null; // video
var screenShareAudioTrack = null;
async function toggleScreenShare(reload = false) { ////////////////////////////
if (reload) {
await grabScreen(quality = 0, audio = true, videoOnEnd = true).then(res => {
if (res != false) {
session.screenShareState = true;
getById("screensharebutton").classList.add("float2");
getById("screensharebutton").classList.remove("float");
enumerateDevices().then(gotDevices2).then(function() {});
}
});
return;
}
if (session.screenShareState == false) { // adding a screen
await grabScreen(quality = 0, audio = true, videoOnEnd = true).then(res => {
if (res != false) {
session.screenShareState = true;
getById("screensharebutton").classList.add("float2");
getById("screensharebutton").classList.remove("float");
enumerateDevices().then(gotDevices2).then(function() {});
}
});
} else { // removing a screen . session.screenShareState already true true /////////////////////////////////
session.screenShareState = false;
pokeIframeAPI("screen-share-ended");
if (beforeScreenShare) {
session.streamSrc.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point.
if (screenShareAudioTrack == track) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share.
session.streamSrc.removeTrack(track);
//session.videoElement.srcObject = outboundAudioPipeline(session.streamSrc);
track.stop();
}
});
session.streamSrc.getVideoTracks().forEach(function(track) {
//errorlog(track);
session.streamSrc.removeTrack(track);
track.stop();
});
session.videoElement.srcObject.getVideoTracks().forEach(function(track) {
//errorlog(track);
session.videoElement.srcObject.removeTrack(track);
track.stop();
});
getById("screensharebutton").classList.add("float");
getById("screensharebutton").classList.remove("float2");
session.streamSrc.addTrack(beforeScreenShare); // add back in the video track we had before we started screen sharing. It should be NULL if we changed the video track else where (such as via the settings). #TODO:
session.videoElement.srcObject.addTrack(beforeScreenShare);
toggleVideoMute(true);
for (UUID in session.pcs) {
try {
if ((session.pcs[UUID].guest == true) && (session.roombitrate === 0)) {
log("room rate restriction detected. No videos will be published to other guests");
} else if (session.pcs[UUID].allowVideo == true) { // allow
var senders = session.pcs[UUID].getSenders(); // for any connected peer, update the video they have if connected with a video already.
var added = false;
senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams?
if (sender.track) {
if (sender.track && sender.track.kind == "video") {
sender.replaceTrack(beforeScreenShare); // replace may not be supported by all browsers. eek.
sender.track.enabled = true;
added = true;
}
}
});
if (added == false) {
session.pcs[UUID].addTrack(beforeScreenShare, stream);
}
}
} catch (e) {
errorlog(e);
}
}
session.refreshScale();
beforeScreenShare = null;
}
toggleSettings(forceShow = true);
//enumerateDevices().then(gotDevices2).then(function(){
//grabVideo();
//grabAudio();
// toggleSettings(forceShow=true);
//});
}
}
var ElectronDesktopCapture = false;
if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { // this enables Screen Capture in Electron
try{
const { desktopCapturer} = require('electron'); // This is definitely Electron specific. Requires Node Integration to be on, which is a potential security hazzard
window.navigator.mediaDevices.getDisplayMedia = () => {
return new Promise(async (resolve, reject) => {
try {
const sources = await desktopCapturer.getSources({ types: ['screen', 'window'] });
const selectionElem = document.createElement('div');
selectionElem.classList = 'desktop-capturer-selection';
selectionElem.innerHTML = `