/*
* Copyright (c) 2020 Steve Seguin. All Rights Reserved.
*
* Use of this source code is governed by the APGLv3 open-source license
* that can be found in the LICENSE file in the root of the source
* tree. Alternative licencing options can be made available on request.
*
*/
/*jshint esversion: 6 */
var formSubmitting = true;
var activatedPreview = false;
function getById(id) {
var el = document.getElementById(id);
if (!el) {
errorlog(id + " is not defined; skipping.");
el = document.createElement("span"); // create a fake element
}
return el;
}
function updateURL(param, force=false) {
var para = param.split('=')[0];
if (!(urlParams.has(para)) || (force)){
if (history.pushState){
var arr = window.location.href.split('?');
var newurl;
if (arr.length > 1 && arr[1] !== '') {
newurl = window.location.href + '&' +param;
} else {
newurl = window.location.href + '?' +param;
}
window.history.pushState({path:newurl},'',newurl);
}
}
if (session.sticky){
setCookie("settings", encodeURI(window.location.href), 90)
}
}
(function (w) {
w.URLSearchParams = w.URLSearchParams || function (searchString) {
var self = this;
self.searchString = searchString;
self.get = function (name) {
var results = new RegExp('[\?&]' + name + '=([^]*)').exec(self.searchString);
if (results == null) {
return null;
}
else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlParams = new URLSearchParams(window.location.search);
if (window.obsstudio){
log("OBS VERSION:"+window.obsstudio.pluginVersion);
log("macOS: "+navigator.userAgent.indexOf('Mac OS X') != -1);
log(window.obsstudio);
if (!(urlParams.has('streamlabs'))){
var ver1 = window.obsstudio.pluginVersion;
ver1 = ver1.split(".");
updateURL("streamlabs");
if (ver1.length == 3){ // Should be 3, but disabled3
if ((ver1.length == 3) && (parseInt(ver1[0])==2) && (parseInt(ver1[1])>4) && (navigator.userAgent.indexOf('Mac OS X') != -1)){
getById("main").innerHTML = "
On macOS, Please use OBS v23, as OBS v24 and v25 are not supported currently.
\
You can bypass this error message by refreshing, Clicking Here, or by adding &streamlabs to the URL.\
Please report this problem to steve@seguin.email if you feel it is an error.\
";
}
}
}
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() { // 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) {
if (formSubmitting) {
return undefined;
}
var confirmationMessage = 'Leaving the page now will terminate your stream ';
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
};
var lastTouchEnd = 0;
document.addEventListener('touchend', function (event) {
var now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
var interacted=false;
document.addEventListener('click', function (event) {
if (interacted==false){
interacted=true;
history.pushState({}, '');
}
});
var Callbacks = [];
var CtrlPressed = false; // global
document.addEventListener("keydown", event => {
if ((event.ctrlKey) || (event.metaKey) ){ // detect if CTRL is pressed
CtrlPressed = true;
}
if (CtrlPressed && event.keyCode){
if (event.keyCode == 77) { // m
toggleMute();
} 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=[];
}
}
});
window.onpopstate = function() {
if (interacted){
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 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( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
session.webcamonly = true;
}
if (urlParams.has('webcam') || urlParams.has('wc')){
session.webcamonly = true;
}
if (urlParams.has('screenshare') || urlParams.has('ss')){
session.screenshare = true;
}
if (session.screenshare==true){
getById("container-3").className = 'column columnfade advanced'; // Hide screen share on mobile
}
if (session.webcamonly==true){
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
}
if (urlParams.has('password')){
session.password = urlParams.get('password');
if (session.password.length==0){
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.)");
}
getById("passwordRoom").value = session.password;
}
if (urlParams.has('stereo') || urlParams.has('s')){ // 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');
session.stereo = session.stereo;
if (session.stereo){
session.stereo = session.stereo.toLowerCase();
}
if (session.stereo=="false"){
session.stereo = 0;
} else if (session.stereo=="0"){
session.stereo = 0;
} else if (session.stereo=="no"){
session.stereo = 0;
} else if (session.stereo=="off"){
session.stereo = 0;
} else if (session.stereo=="1"){
session.stereo = 1;
} else if (session.stereo=="3"){
session.stereo = 3;
} else if (session.stereo=="2"){
session.stereo = 2;
} else {
session.stereo = 1;
}
}
if ((session.stereo==1) || (session.stereo==3)){
session.echoCancellation = false;
session.autoGainControl = false;
session.noiseSuppression = 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.echoCancellation){
session.autoGainControl.autoGainControl();
}
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('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>1024){
session.audiobitrate=1024;
} // this is to just prevent abuse
}
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
}
if (urlParams.has('icefilter')){
log("ICE FILTER ENABLED");
session.icefilter = urlParams.get('icefilter');
}
if (urlParams.has('remote') || urlParams.has('rem')){
log("remote ENABLED");
session.remote = urlParams.get('remote') || urlParams.get('rem')
session.remote = session.remote.trim();
}
if (urlParams.has('optimize')){
session.optimize = true;
}
if (urlParams.has('obsoff') || urlParams.has('oo')){
log("OBS feedback disabled");
session.disableOBS = true;
}
if (urlParams.has('noaudio') || urlParams.has('na')){
log("disable audio playback");
session.audio = false;
}
if (urlParams.has('novideo') || urlParams.has('nv')){
log("disable video playback");
session.video = false;
}
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('codec')){
log("CODEC CHANGED");
session.codec = urlParams.get('codec');
}
if (urlParams.has('scale')){
log("Resolution scale requested");
session.scale = urlParams.get('scale');
}
var ConfigSettings = getById("main-js");
var ln_template = 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);
document.querySelectorAll('[data-translate]').forEach(function(ele){
//log(ele.dataset.translate);
//log(translations[ele.dataset.translate]);
if (ele.dataset.translate in data){
ele.innerHTML = data[ele.dataset.translate];
}
});
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;
}
if (location.hostname !== "obs.ninja"){
document.title = location.hostname;
getById("qos").innerHTML = location.hostname;
getById("logoname").innerHTML = getById("qos").outerHTML;
getById("helpbutton").style.display = "none";
}
} else if (location.hostname !== "obs.ninja"){
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);
document.querySelectorAll('[data-translate]').forEach(function(ele){
//log(ele.dataset.translate);
//log(translations[ele.dataset.translate]);
if (ele.dataset.translate in data){
ele.innerHTML = data[ele.dataset.translate];
}
});
document.title = location.hostname;
getById("qos").innerHTML = location.hostname;
getById("logoname").innerHTML = getById("qos").outerHTML ;
getById("helpbutton").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;
});
document.title = location.hostname;
getById("qos").innerHTML = location.hostname;
getById("logoname").innerHTML = getById("qos").outerHTML;
getById("helpbutton").style.display = "none";
} catch (error){
errorlog(error);
}
} else { // check if automatic language translation is available
getById("mainmenu").style.opacity = 1;
}
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);
document.querySelectorAll('[data-translate]').forEach(function(ele){
//log(ele.dataset.translate);
//log(translations[ele.dataset.translate]);
ele.innerHTML = data[ele.dataset.translate];
});
});
}).catch(function(err){
errorlog(err);
});
}
if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')){
session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb');
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('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')
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');
}
if (urlParams.has('cleanoutput') || urlParams.has('clean')){
session.cleanOutput = true;
getById("translateButton").style.display="none";
getById("credits").style.display="none";
getById("header").style.display="none";
var style = document.createElement('style');
style.innerHTML = `
video {
background-image: none;
}
`;
document.head.appendChild(style);
}
if (urlParams.has('channeloffset')){
session.offsetChannel = parseInt(urlParams.get('channeloffset'));
log("max channels is 32; channels offset");
}
if (urlParams.has('channels')){
session.audioChannels = parseInt(urlParams.get('channels'));
log("max channels is 32; channels offset");
}
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('secure')){
session.security = true;
setTimeout(function() {alert("Enhanced Security Mode Enabled.");}, 100);
}
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('sync')){
session.sync = parseFloat(urlParams.get('sync'));
log("sync Changed");
log(session.sync);
}
if (urlParams.has('buffer')){
session.buffer = parseFloat(urlParams.get('buffer')) || 0;
log("buffer Changed: "+session.buffer);
}
if ((urlParams.has('mirror')) && (urlParams.has('flip'))){
try {
log("Mirror all videos");
var mirrorStyle = document.createElement('style');
mirrorStyle.innerHTML = "video {transform: scaleX(-1) scaleY(-1); }";
document.getElementsByTagName("head")[0].appendChild(mirrorStyle);
session.mirrored=true;
session.flipped=true;
} catch (e){errorlog(e);}
} else if (urlParams.has('mirror')){ // 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);
session.mirrored=true;
} catch (e){errorlog(e);}
} else if (urlParams.has('flip')){ // mirror the video vertically
try {
log("Mirror all videos");
session.flipped=true;
var mirrorStyle = document.createElement('style');
mirrorStyle.innerHTML = "video {transform: scaleY(-1);}";
document.getElementsByTagName("head")[0].appendChild(mirrorStyle);
} catch (e){errorlog(e);}
}
var turn = {};
if (urlParams.has('turn')){
try {
var turnstring = urlParams.get('turn').split(";");
if (turnstring !== "false"){ // false disables the TURN server. Useful for debuggin
turn = {};
turn.username = turnstring[0]; // myusername
turn.credential = turnstring[1]; //mypassword
turn.urls = [turnstring[2]]; // ["turn:turn.obs.ninja:443"];
session.configuration.iceServers.push(turn);
}
} catch (e){
alert("TURN server parameters were wrong.");
errorlog(e);
}
}
if (urlParams.has('privacy')){ // please only use if you are also using your own TURN service.
try {
session.configuration.iceTransportPolicy = "relay"; // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/address
} catch (e){
alert("Privacy mode failed to configure.");
errorlog(e);
}
}
function jumptoroom(){
var arr = window.location.href.split('?');
var roomname = getById("joinroomID").value;
roomname = roomname.replace(/[\W_]+/g,"_");
if (arr.length > 1 && arr[1] !== '') {
window.location+="&room="+roomname;
} else {
window.location+="?room="+roomname;
}
}
function sleep(ms = 0){
return new Promise(r => setTimeout(r, ms)); // LOLz!
}
session.connect();
// session.volume = 100; // needs to be set after?
var url = window.location.pathname;
var filename = url.substring(url.lastIndexOf('/')+1);
if (filename.split(".").length==1){
if (filename.length<2){
filename=false;
}
} else {
filename = false;
}
var permaid=false;
if (urlParams.has('permaid') || urlParams.has('push')){
permaid = urlParams.get('permaid') || urlParams.get('push');
session.changeStreamID(permaid);
getById("container-1").className = 'column columnfade advanced';
getById("container-4").className = 'column columnfade advanced';
getById("info").innerHTML = "";
getById("add_camera").innerHTML = "Share your Camera";
getById("add_screen").innerHTML = "Share your Screen";
getById("passwordRoom").value = "";
getById("videoname1").value = "";
getById("dirroomid").innerHTML = "";
getById("roomid").innerHTML = "";
getById("container-1").className = 'column columnfade advanced';
getById("container-4").className = 'column columnfade advanced';
getById("mainmenu").style.alignSelf= "center";
getById("mainmenu").style.display="inherit";
getById("header").style.alignSelf= "center";
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) || (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;
}
roomid = roomid.replace(/[\W_]+/g,"_");
session.roomid = roomid;
getById("info").innerHTML = "";
getById("info").style.color="#CCC";
getById("videoname1").value = roomid;
getById("dirroomid").innerHTML = roomid;
getById("roomid").innerHTML = roomid;
getById("container-1").className = 'column columnfade advanced';
getById("container-4").className = 'column columnfade advanced';
getById("mainmenu").style.alignSelf= "center";
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){
getById("add_camera").innerHTML = "Join Room with Camera";
getById("add_screen").innerHTML = "Screenshare with Room";
} else {
getById("add_camera").innerHTML = "Share your Camera";
getById("add_screen").innerHTML = "Share your Screen";
}
getById("head3").className = 'advanced';
if (urlParams.has('scene')){
session.scene = urlParams.get('scene');
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("head3").className = 'advanced';
getById("mainmenu").style.display = "none";
getById("translateButton").style.display = "none";
log("Update Mixer Event on REsize SET");
window.addEventListener("resize", updateMixer);
joinRoom(roomid); // this is a scene, so we want high resolutions
getById("main").style.overflow = "hidden";
}
} else if (urlParams.has('director')){ // if I do a short form of this, it will cause duplications in the code elsewhere.
createRoom(urlParams.get('director').replace(/[\W_]+/g,"_"));
} else if ((session.view) && (permaid===false)){
log("Update Mixer Event on REsize SET");
getById("translateButton").style.display = "none";
window.addEventListener("resize", updateMixer);
getById("main").style.overflow = "hidden";
}
function checkConnection(){
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 updateStats(){
log('resolution found');
try {
getById('previewWebcam').srcObject.getVideoTracks().forEach(
function(track) {
log(track.getSettings());
log(track.getSettings().frameRate);
//log(track.getSettings().frameRate);
getById("webcamstats").innerHTML = "Current Video Settings: "+(track.getSettings().width||0) +"x"+(track.getSettings().height||0)+"@"+(parseInt(track.getSettings().frameRate*10)/10)+"fps";
}
);
} catch (e){errorlog(e);}
}
window.onmessage = function(e){ // iFRAME support
log(e.data);
if ("mute" in e.data) {
if (e.data.mute == true){
for (var i in session.rpcs){
try {
session.rpcs[i].videoElement.muted = true;
} catch(e){
errorlog(e);
}
}
} else if (e.data.mute == false){
for (var i in session.rpcs){
try {
session.rpcs[i].videoElement.muted = false;
} catch(e){
errorlog(e);
}
}
} else if (e.data.mute == "toggle"){
for (var i in session.rpcs){
try {
session.rpcs[i].videoElement.muted = !(session.rpcs[i].videoElement.muted);
} catch(e){
errorlog(e);
}
}
}
}
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 ("reload" in e.data){
location.reload();
}
if ("close" in e.data){
for (var i in session.rpcs){
try {
session.rpcs[i].close();
} catch(e){
errorlog(e);
}
}
}
};
function toggleMute(){ // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (session.muted==false){
session.muted = true;
getById("mutetoggle").className="las la-microphone-slash my-float toggleSize";
getById("mutebutton").className="float2";
session.streamSrc.getAudioTracks().forEach((track) => {
track.enabled = false;
});
} else{
session.muted=false;
getById("mutetoggle").className="las la-microphone my-float toggleSize";
getById("mutebutton").className="float";
session.streamSrc.getAudioTracks().forEach((track) => {
track.enabled = true;
});
}
}
function toggleVideoMute(){ // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (session.videoMuted==false){
session.videoMuted = true;
getById("mutevideotoggle").className="las la-eye-slash my-float toggleSize";
getById("mutevideobutton").className="float2";
session.streamSrc.getVideoTracks().forEach((track) => {
track.enabled = false;
});
} else{
session.videoMuted=false;
getById("mutevideotoggle").className="las la-eye my-float toggleSize";
getById("mutevideobutton").className="float";
session.streamSrc.getVideoTracks().forEach((track) => {
track.enabled = true;
});
}
}
function hangup(){ // TODO: I need to have this be MUTE, toggle, with volume not touched.
session.hangup();
}
function directEnable(ele){ // A directing room only is controlled by the Director, with the exception of MUTE.
if (!(CtrlPressed)){ // reissues the command without toggling it
if (ele.parentNode.parentNode.dataset.enable==1){
ele.parentNode.parentNode.dataset.enable = 0;
ele.className = "";
ele.innerHTML = "Add to Group Scene";
ele.parentNode.parentNode.style.backgroundColor = "#E3E4FF";
} else {
ele.parentNode.parentNode.style.backgroundColor = "#AFA";
ele.parentNode.parentNode.dataset.enable = 1;
ele.className = "pressed";
ele.innerHTML = "Remove from Group Scene";
}
}
var msg = {};
msg.request = "sendroom";
//msg.roomid = session.roomid;
msg.scene = "1"; // scene
msg.action = "display";
msg.value = ele.parentNode.parentNode.dataset.enable;
msg.target = ele.parentNode.parentNode.dataset.UUID;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
function directMute(ele){ // A directing room only is controlled by the Director, with the exception of MUTE.
log("mute");
if (!(CtrlPressed)){
if (ele.parentNode.parentNode.dataset.mute==0){
ele.parentNode.parentNode.dataset.mute = 1;
ele.className = "";
ele.innerHTML = "Mute";
} else {
ele.parentNode.parentNode.dataset.mute = 0;
ele.className = "pressed";
ele.innerHTML = "Unmute";
}
}
var msg = {};
msg.request = "sendroom";
//msg.roomid = session.roomid;
msg.scene = "1";
msg.action = "mute";
msg.value = ele.parentNode.parentNode.dataset.mute;
msg.target = ele.parentNode.parentNode.dataset.UUID;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
function directVolume(ele){ // A directing room only is controlled by the Director, with the exception of MUTE.
log("volume");
var msg = {};
msg.request = "sendroom";
//msg.roomid = session.roomid;
msg.scene = "1";
msg.action = "volume";
msg.target = ele.parentNode.parentNode.dataset.UUID; // 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 chatRoom(chatmessage="hi"){ // A directing room only is controlled by the Director, with the exception of MUTE.
log("Chat message");
var msg = {};
msg.request = "sendroom";
//msg.roomid = session.roomid;
msg.action = "chat";
msg.value = chatmessage;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
function changeTitle(aTitle="Untitled"){
log("changing title; if connected at least");
session.changeTitle(aTitle);
}
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: session.echoCancellation, autoGainControl: session.autoGainControl, noiseSuppression: session.noiseSuppression },
video: {width: width, height: height, cursor: "never", mediaSource: "browser"}
};
if (!(urlParams.has("denoise"))){
constraints.audio.noiseSuppression = false; // the defaults for screen publishing should be off.
}
if (!(urlParams.has("autogain"))){
constraints.audio.autoGainControl = false; // the defaults for screen publishing should be off.
}
if (!(urlParams.has("aec"))){
constraints.audio.echoCancellation = false; // the defaults for screen publishing should be off.
}
if (session.framerate){
constraints.video.frameRate = session.framerate;
}
var audioSelect = document.querySelector('select#audioSourceScreenshare');
session.publishScreen(constraints, title, audioSelect).then((res)=>{
if (res==false){return;} // no screen selected
log("streamID is: "+session.streamID);
if (!(session.cleanOutput)){
getById("mutebutton").className="float";
getById("helpbutton").className="float";
getById("mutevideobutton").className="float";
getById("hangupbutton").className="float";
getById("controlButtons").style.display="flex";
} else {
getById("controlButtons").style.display="none";
}
getById("head1").className = 'advanced';
getById("head2").className = 'advanced';
}).catch(()=>{});
}
function publishWebcam(){
if( activatedStream == true){return;}
activatedStream = true;
log("PRESSED PUBLISH WEBCAM!!");
var title = "Webcam"; // getById("videoname3").value;
var ele = getById("previewWebcam");
var stream = ele.srcObject;
ele.parentNode.removeChild(ele);
formSubmitting = false;
window.scrollTo(0, 0); // iOS has a nasty habit of overriding the CSS when changing camaera selections, so this addresses that.
if (session.roomid!==false){
if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){
// no room, no viewing, viewing disabled
} else {
log("ROOM ID ENABLED");
log("Update Mixer Event on REsize SET");
window.addEventListener("resize", updateMixer);
getById("main").style.overflow = "hidden";
joinRoom(session.roomid);
}
getById("head3").className = 'advanced';
} else {
getById("head3").className = '';
getById("logoname").style.display = 'none';
}
log("streamID is: "+session.streamID);
getById("head1").className = 'advanced';
getById("head2").className = 'advanced';
if (!(session.cleanOutput)){
getById("mutebutton").className="float";
getById("helpbutton").className="float";
getById("mutevideobutton").className="float";
getById("hangupbutton").className="float";
getById("controlButtons").style.display="flex";
} else {
getById("controlButtons").style.display="none";
}
updateURL("push="+session.streamID);
session.publishStream(stream, title);
}
function joinRoom(roomname, maxbitrate=false){
roomname = roomname.replace(/[^0-9a-z]/gi, '');
if (roomname.length){
log("Join room",roomname);
log(roomname);
session.joinRoom(roomname, maxbitrate).then(function(response){ // callback from server; we've joined the room
if (session.director){
var msg = {};
msg.request = "claim";
session.sendMsg(msg);
}
log("Members in Room");
log(response);
for (var i in response){
if ("UUID" in response[i]){
if ("streamID" in response[i]){
if (response[i].UUID in session.pcs){
log("RTC already connected"); /// lets just say instead of Stream, we have
} else {
//var title = ""; // TODO: Assign labels
//if ("title" in response[i]){
// title = response[i]["title"];
//}
play(response[i].streamID); // play handles the group room mechanics here
}
}
}
}
},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 = roomname.replace(/[\W_]+/g,"_");
if (roomname.length!=0){
updateURL("director="+roomname); // make the link reloadable.
}
}
if (roomname.length==0){
alert("Please enter a room name before continuing");
return;
}
log(roomname);
var passwordRoom = getById("passwordRoom").value;
if (passwordRoom.length){
session.password=passwordRoom;
updateURL("password="+session.password);
}
var gridlayout = getById("gridlayout");
gridlayout.classList.add("directorsgrid");
session.roomid = roomname;
formSubmitting = false;
var m = getById("mainmenu");
m.remove();
getById("head1").className = 'advanced';
getById("head2").className = 'advanced';
getById("head3").className = 'advanced';
getById("head4").className = '';
getById("dirroomid").innerHTML = decodeURIComponent(roomname);
getById("roomid").innerHTML = roomname;
session.director = true;
getById("reshare").parentNode.removeChild(getById("reshare"));
var passAdd="";
var passAdd2="";
if (session.password){
passAdd="&password";
passAdd2="&password="+session.password;
}
gridlayout.innerHTML = "
- Invites users to join the group and broadcast their feed to it. These users will see every feed, so performance problems may arise for some guests if too many people join a room.
";
gridlayout.innerHTML += " - Link to Invite users to broadcast their feeds to the group. These users will not see or hear any feed from the group. ";
gridlayout.innerHTML += " - This is an OBS Browser Source link that contains the group chat in just a single scene. Videos must be added to Group Scene. ";
gridlayout.innerHTML += '';
gridlayout.innerHTML += "
\
Welcome. This is the control-room for the group-chat. There are different things you can use this room for:
\
You can host a small-group chat here. Share the blue link to invite guests who will join the chat automatically.
\
You can use it to invite and manage up to ~20 remote camera streams. Use the red-colored add camera link to bring in such streams.
\
You can add and remote control individual streams loaded into OBS. The required solo-links to add to OBS will appear under videos as they load.
\
You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.
\
You can use it to record video streams independently
\
\
As guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\
The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.
Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.
\
For advanced options and parameters, see the Wiki.
";
gridlayout.innerHTML += "
\
GUEST SLOT #1
(A video will appear here when a guest joins)
A Solo-Link for OBS will appear here.
\
GUEST SLOT #2
(A video will appear here when a guest joins)
A Solo Link for OBS will appear here
\
GUEST SLOT #3
(A video will appear here when a guest joins)
A Solo Link for OBS will appear here
\
GUEST SLOT #4
(A video will appear here when a guest joins)
A Solo Link for OBS will appear here
";
joinRoom(roomname); // setting this to limit bitrate may break things.
}
function toggle(ele, tog=false) {
var x = ele;
if (x.style.display === "none") {
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 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 requestAudioStream(){
try {
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");
//var temp = {};
//for (let i = 0; i !== deviceInfos.length; ++i) { // getting rid of duplicates. This is a bit useless; I need to revisit.
// if (deviceInfos[i].kind === 'audioinput') {
// if (deviceInfos[i].deviceId in temp){
// deviceInfos[i] = null;
// } else {
// temp[deviceInfos[i].deviceId]=true;
// }
// }
//}
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 (window.isSecureContext) {
alert("An error has occured when trying to access the webcam. The reason is not known.");
} else {
alert("Error acessing webcam.\n\nWebsite is 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');
const videoSelect = document.querySelector('select#videoSource');
const selectors = [ videoSelect];
// Handles being called several times to update labels. Preserve values.
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
//var temp = {};
//for (let i = 0; i !== deviceInfos.length; ++i) {
// if (deviceInfos[i].kind === 'audioinput') {
// if (deviceInfos[i].deviceId in temp){
// deviceInfos[i] = null;
// } else {
// temp[deviceInfos[i].deviceId]=true;
// }
// }
//}
var counter = 1;
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');
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;
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);
getById("multiselect1").onchange = function(event){ // make sure to clear 'no audio option' if anything else is selected
if (!(getById("multiselect1").checked)){
getById("multiselect1").checked= true;
}
};
option.onchange = function(event){ // make sure to clear 'no audio option' if anything else is selected
getById("multiselect1").checked= false;
if (!(CtrlPressed)){
document.querySelectorAll("#audioSource input[type='checkbox']").forEach(function(item) {
if (event.currentTarget.id !== item.id){
item.checked = false;
} else {
item.checked = true;
}
});
}
};
} else if (deviceInfo.kind === 'videoinput') {
const option = document.createElement('option');
option.value = deviceInfo.deviceId || "default";
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
} else {
log('Some other kind of source/device: ', deviceInfo);
}
}
//var option = document.createElement('option');
//option.text = "Disable Audio";
//option.value = "ZZZ";
//audioInputSelect.appendChild(option); // NO AUDIO 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];
}
});
//audioInputSelect.selectedIndex = 0;
} catch (e){
errorlog(e);
}
}
if (location.protocol !== 'https:') {
alert("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: { min: 360, ideal: 960, max: 960 }
};
}
case 5:
if (isSafariBrowser) {
return {
width: { min: 360, ideal: 640, max: 1440 },
height: { min: 360, ideal: 360, max: 720 }
};
}
else {
return {
width: { min: 360, ideal: 640, max: 3840 },
height: { min: 360, ideal: 360, max: 2160 }
};
}
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,max:1920},
height: {min:360, max:1920}}; // same as default, but I didn't want to mess with framerates until I gave it all a try first
}
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 },
frameRate: 10
};
case 8:
return {width: {min:360,max:1920}, height: {min:360, max:1920}}; // 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.
default:
return {};
}
}
function grabVideo(quality=0, audioEnable=false){
if( activatedPreview == true){log("activated preview return 2");return;}
activatedPreview = true;
log("trying with quality:"+quality);
var videoSelect = document.querySelector('select#videoSource');
var sq=0;
if (session.quality>2){ // 1080, 720, and 360p
sq = 2; // hacking my own code. TODO: ugly, so I need to revisit this.
} else {
sq = session.quality;
}
if (sq!==false){
if (quality>sq){
quality=sq; // override the user's setting
}
}
if (iOS){ // iOS will not work correctly at 1080p; likely a h264 codec issue.
if (quality==0){
quality=1;
}
} else if (iPad){
if (quality==0){
quality=1;
}
}
var audio = false;
var streams = [];
if ((videoSelect.value == "ZZZ") || (audioEnable==true)){ // if there is no video, or if manually set to audio ready, then do this step.
var audioSelect = document.querySelector('#audioSource').querySelectorAll("input");
var audioList = [];
for (var i=0; i{
try {
log("Trying Constraints");
var oldstream = getById('previewWebcam').srcObject;
if (oldstream){
oldstream.getTracks().forEach(function(track) {
track.stop();
});
}
} catch(e){
errorlog(e);
}
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
if (audioEnable == false){
stream.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();
});
log("GOT IT BUT WITH NO AUDIO");
activatedPreview = false;
grabVideo(quality,true);
} else {
log("adding tracks");
for (var i=0; i1){zoom =1.0;}
else if (zoom<-1){zoom=-1.0;}
//zoom=(zoom+1)/2;
input.value = zoom*(input.max - input.min) + input.min;
if (input.value != pos0){
track0.applyConstraints({advanced: [ {zoom: input.value} ]});
//getById("infof").innerHTML = input.value + " " + pos0;
}
//log(pos2 +" , "+ elmnt.offsetHeight +" , "+ parseFloat((3*pos2)/elmnt.offsetHeight) );
}
function closeDragElement(e) {
elmnt.controls=true;
/* stop moving when mouse button is released:*/
document.onmouseup = null;
document.onmousemove = null;
document.ontouchmove = null;
}
}
function setupWebcamSelection(){
log("setup webcam");
try {
return enumerateDevices().then(gotDevices).then(function(){
log("enumerated");
if (parseInt(getById("webcamquality").elements.namedItem("resolution").value)==3){
session.maxframerate = 30;
} else {
session.maxframerate = false;
}
if ((iOS) || (iPad)){
getById("multiselect1").parentNode.style.visibility="hidden";
getById("multiselect1").parentNode.style.height="0px";
}
var audioSelect = document.querySelector('#audioSource');
var videoSelect = document.querySelector('select#videoSource');
audioSelect.onchange = function(){
var gowebcam = getById("gowebcam");
gowebcam.disabled = true;
gowebcam.style.backgroundColor = "#DDDDDD";
gowebcam.style.fontWeight="normal";
gowebcam.innerHTML = "Waiting for Camera to load";
log("AUDIO source CHANGED");
activatedPreview=false;
grabVideo(parseInt(getById("webcamquality").elements.namedItem("resolution").value));
};
videoSelect.onchange = function(){
var gowebcam = getById("gowebcam");
gowebcam.disabled = true;
gowebcam.style.backgroundColor = "#DDDDDD";
gowebcam.style.fontWeight="normal";
gowebcam.innerHTML = "Waiting for Camera to load";
log("video source changed");
activatedPreview=false;
grabVideo(parseInt(getById("webcamquality").elements.namedItem("resolution").value));
};
getById("webcamquality").onchange = function(){
var gowebcam = getById("gowebcam");
gowebcam.disabled = true;
gowebcam.style.backgroundColor = "#DDDDDD";
gowebcam.style.fontWeight="normal";
gowebcam.innerHTML = "Waiting for Camera to load";
log("AUDIO source CHANGED");
activatedPreview=false;
if (parseInt(getById("webcamquality").elements.namedItem("resolution").value)==3){
session.maxframerate = 30;
} else {
session.maxframerate = false;
}
grabVideo(parseInt(getById("webcamquality").elements.namedItem("resolution").value));
};
activatedPreview = false;
grabVideo(parseInt(getById("webcamquality").elements.namedItem("resolution").value));
}).catch(e => {errorlog(e);})
} catch (e){errorlog(e);}
}
Promise.wait = function (ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
};
Promise.prototype.timeout = function(ms) {
return Promise.race([
this,
Promise.wait(ms).then(function () {
throw new Error("Time Out\n\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.");
})
])
};
function previewWebcam(){
if( activatedPreview == true){log("activeated preview return 1");return;}
activatedPreview = true;
if (session.mirrored && session.flipped){
getById('previewWebcam').style.transform = " scaleX(1) scaleY(-1)";
} else if (session.mirrored){
getById('previewWebcam').style.transform = "scaleX(1)";
} else if (session.flipped){
getById('previewWebcam').style.transform = "scaleY(-1) scaleX(-1)";
} else {
getById('previewWebcam').style.transform = "scaleX(-1)";
}
window.setTimeout(() => {
try{
var oldstream = getById('previewWebcam').srcObject;
if (oldstream){
log("old stream found");
oldstream.getTracks().forEach(function(track) {
track.stop();
log("stopping old track");
});
}
} catch (e){
errorlog(e);
}
try {
navigator.mediaDevices.getUserMedia({audio:true, video:true }).timeout(15000).then(function(stream){ // Apple needs thi to happen before I can access EnumerateDevices.
log("got first stream");
setupWebcamSelection().then(()=>{
log("Got second stream");
stream.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
});
});
}).catch(function(err){
errorlog(err); /* handle the error */
if (err.name == "NotFoundError" || err.name == "DevicesNotFoundError") {
//required track is missing
} else if (err.name == "NotReadableError" || err.name == "TrackStartError") {
//webcam or mic are already in use
} else if (err.name == "OverconstrainedError" || err.name == "ConstraintNotSatisfiedError") {
//constraints can not be satisfied by avb. devices
} else if (err.name == "NotAllowedError" || err.name == "PermissionDeniedError") {
//permission denied in browser
setTimeout(function(){alert("Permissions denied. Please ensure you have allowed the mic/camera permissions.");},1);
return;
} else if (err.name == "TypeError" || err.name == "TypeError") {
//empty constraints object
} else {
//permission denied in browser
setTimeout(function(){alert(err);},1);
}
errorlog("trying to list webcam again");
setupWebcamSelection();
});
} catch (e){
if (window.isSecureContext) {
alert("An error has occured when trying to access the webcam. The reason is not known.");
} else {
alert("Error acessing webcam.\n\nWebsite is loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia");
}
}
},10);
}
function copyFunction(copyText) {
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
}
function generateQRPage(){
try{
var title = encodeURI(getById("videoname4").value);
if (title.length){
title = "&label="+title;
}
var sid = session.generateStreamID();
var viewstr = "";
var sendstr = "";
if (getById("invite_bitrate").checked){
viewstr+="&bitrate=20000";
}
if (getById("invite_vp9").checked){
viewstr+="&codec=vp9";
}
if (getById("invite_stereo").checked){
viewstr+="&stereo";
sendstr+="&stereo";
}
if (getById("invite_secure").checked){
sendstr+="&secure";
}
if (getById("invite_hidescreen").checked){
sendstr+="&webcam";
}
if (getById("invite_remotecontrol").checked){ //
var remote_gen_id = session.generateStreamID();
sendstr+="&remote="+remote_gen_id; // security
viewstr+="&remote="+remote_gen_id;
}
if (getById("invite_joinroom").value.trim().length){
sendstr+="&room="+getById("invite_joinroom").value.trim();
viewstr+="&scene=1&room="+getById("invite_joinroom").value.trim();
}
if (getById("invite_password").value.trim().length){
sendstr+="&password";
viewstr+="&password="+getById("invite_password").value.trim();
}
if (getById("invite_group_chat_type").value){ // 0 is default
if (getById("invite_group_chat_type").value==1){ // no video
sendstr+="&novideo";
} else if (getById("invite_group_chat_type").value==2){ // no view or audio
sendstr+="&view";
}
}
if (getById("invite_quality").value){
if (getById("invite_quality").value==0){
sendstr+="&quality=0";
} else if (getById("invite_quality").value==1){
sendstr+="&quality=1";
} else if (getById("invite_quality").value==2){
sendstr+="&quality=2";
}
}
sendstr = 'https://' + location.host + location.pathname + '?push=' + sid + sendstr;
viewstr = 'https://' + location.host+ location.pathname + '?view=' + sid + viewstr + title;
getById("gencontent").innerHTML = '