/////////////
// Some browsers partially implement mediaDevices. We can't just assign an object
// with getUserMedia as it would overwrite existing properties.
// Here, we will just add the getUserMedia property if it's missing.
var VIS = vis;
var formSubmitting = true;
var activatedPreview = false;
var setFormSubmitting = function() { formSubmitting = true; };
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({}, '');
}
});
window.onpopstate = function() {
if (interacted){
window.location.reload(true);
}
};
var session = Ooblex.Media;
session.streamID = session.generateStreamID();
(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);
var isMobile = false;
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
isMobile=true;
}
if ((urlParams.has('permaid')) || (urlParams.has('push'))){
var permaid = urlParams.get('permaid') || urlParams.get('push');
session.changeStreamID(permaid);
document.getElementById("container-1").className = 'column columnfade advanced';
document.getElementById("container-4").className = 'column columnfade advanced';
}
if (urlParams.has('stereo')){
log("STEREO ENABLED");
session.stereo = 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('bitrate')){
session.bitrate = parseInt(urlParams.get('bitrate'));
log("BITRATE ENABLED");
log(session.bitrate);
}
if (urlParams.has('height')){
session.height = parseInt(urlParams.get('height'));
}
if (urlParams.has('width')){
session.width = parseInt(urlParams.get('width'));
}
if (urlParams.has('secure')){
session.security = true;
setTimeout(function() {alert("Enhanced Security Mode Enabled.");}, 100);
}
if (urlParams.has('framerate')){
session.framerate = parseInt(urlParams.get('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'));
log("buffer Changed");
log(session.buffer);
}
//var sync = session.sync | 0;
//var buffer = session.buffer | 0;
if (urlParams.has('turn')){
try {
var turnstring = urlParams.get('turn').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.push(turn);
}
} catch (e){
alert("TURN server parameters were wrong.");
errorlog(e);
}
} else { // THIS IS ME being extra Generous.
var turn = {};
turn.username = "steve";
turn.credential = "justtesting";
turn.urls = ["turn:turn.obs.ninja:443"]; // main TURN server. Do not abuse. I pay out of pocket.
session.configuration.iceServers.push(turn);
turn.urls = ["turn:turn2.obs.ninja:443"]; // backup TURN server. Do not abuse. I pay out of pocket.
session.configuration.iceServers.push(turn);
}
function updateURL(param) {
var para = param.split('=')[0];
if (!(urlParams.has(para))){
if (history.pushState){
var arr = window.location.href.split('?');
if (arr.length > 1 && arr[1] !== '') {
var newurl = window.location.href + '&' +param;
} else {
var newurl = window.location.href + '?' +param;
}
window.history.pushState({path:newurl},'',newurl);
}
}
}
function jumptoroom(){
document.getElementById("joinroomID").value;
var arr = window.location.href.split('?');
if (arr.length > 1 && arr[1] !== '') {
window.location+="&room="+document.getElementById("joinroomID").value;
} else {
window.location+="?room="+document.getElementById("joinroomID").value;
}
}
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;
}
if ( (urlParams.has('roomid')) || (filename) || (urlParams.has('room')) ){
if (filename){
var roomid = filename;
} else if (urlParams.has('room')){
var roomid = urlParams.get('room');
} else {
var roomid = urlParams.get('roomid');
}
roomid = encodeURIComponent(roomid);
session.roomid = roomid;
document.getElementById("info").innerHTML = "";
document.getElementById("info").style.color="#CCC";
document.getElementById("videoname1").value = roomid;
document.getElementById("dirroomid").innerHTML = roomid;
document.getElementById("roomid").innerHTML = roomid;
document.getElementById("container-1").className = 'column columnfade advanced';
document.getElementById("container-4").className = 'column columnfade advanced';
document.getElementById("mainmenu").style.alignSelf= "center";
document.getElementById("header").style.alignSelf= "center";
if (isMobile){
document.getElementById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
document.getElementById("head1").innerHTML = '';
} else {
document.getElementById("head1").innerHTML = ' Please select an option to join.';
}
document.getElementById("add_camera").innerHTML = "Join Room with Camera";
document.getElementById("add_screen").innerHTML = "Screenshare with Room";
document.getElementById("head3").className = 'advanced';
if (urlParams.has('scene')){
session.scene = urlParams.get('scene');
document.getElementById("container-4").className = 'column columnfade';
document.getElementById("container-3").className = 'column columnfade';
document.getElementById("container-2").className = 'column columnfade';
document.getElementById("container-1").className = 'column columnfade';
document.getElementById("header").className = 'advanced';
document.getElementById("info").className = 'advanced';
document.getElementById("header").className = 'advanced';
document.getElementById("head1").className = 'advanced';
document.getElementById("head2").className = 'advanced';
document.getElementById("head3").className = 'advanced';
document.getElementById("mainmenu").style.display = "none";
window.addEventListener("resize", updateMixer);
joinRoom(roomid); // this is a scene, so we want high resolutions
}
}
function checkConnection(){
if (session.ws.readyState === WebSocket.OPEN) {
document.getElementById("qos").style.color = "white";
} else {
document.getElementById("qos").style.color = "red";
}
}
setInterval(function(){checkConnection();},5000);
function updateStats(){
log('resolution found');
//log(document.getElementById('previewWebcam').videoWidth|0);
//log(document.getElementById('previewWebcam').videoHeight|0);
document.getElementById('previewWebcam').srcObject.getVideoTracks().forEach(
function(track) {
log(track.getSettings());
log(track.getSettings().frameRate);
//log(track.getSettings().frameRate);
document.getElementById("webcamstats").innerHTML = "Current Video Settings: "+(track.getSettings().width|0) +"x"+(track.getSettings().height|0)+"@"+(parseInt(track.getSettings().frameRate*10)/10)+"fps";
}
);
}
function toggleMute(){ // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (session.muted==false){
session.muted = true;
document.getElementById("mutetoggle").className="fa fa-microphone-slash my-float";
document.getElementById("mutebutton").className="float";
session.streamSrc.getAudioTracks().forEach((track) => {
track.enabled = false;
});
} else{
session.muted=false;
document.getElementById("mutetoggle").className="fa fa-microphone my-float";
document.getElementById("mutebutton").className="float3";
session.streamSrc.getAudioTracks().forEach((track) => {
track.enabled = true;
});
}
}
////////////////////////////
function directEnable(ele){ // A directing room only is controlled by the Director, with the exception of MUTE.
log("enable");
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.director = "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 (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.director = "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.director = "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";//document.getElementById("videoname2").value;
formSubmitting = false;
var width = {ideal: 1280};
var height = {ideal: 720};
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 }, // I hope this doesn't break things..
video: {width: width, height: height, cursor: "never", mediaSource: "browser"}
};
if (session.framerate){
constraints.video.frameRate = {exact: session.framerate};
}
var audioSelect = document.querySelector('select#audioSourceScreenshare');
session.publishScreen(constraints, title, audioSelect);
log("streamID is: "+session.streamID);
document.getElementById("mutebutton").className="float3";
document.getElementById("helpbutton").className="float2";
document.getElementById("head1").className = 'advanced';
document.getElementById("head2").className = 'advanced';
}
function publishWebcam(){
if( activatedStream == true){return;}
activatedStream = true;
log("PRESSED PUBLISH WEBCAM!!");
var title = "Webcam"; // document.getElementById("videoname3").value;
var ele = document.getElementById("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){
console.log("ROOM ID ENABLED");
window.addEventListener("resize", updateMixer);
joinRoom(session.roomid);
document.getElementById("head3").className = 'advanced';
} else {
document.getElementById("head3").className = '';
}
updateURL("push="+session.streamID);
session.publishStream(stream, title);
log("streamID is: "+session.streamID);
document.getElementById("head1").className = 'advanced';
document.getElementById("head2").className = 'advanced';
document.getElementById("mutebutton").className="float3";
document.getElementById("helpbutton").className="float2";
}
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
log("Members in Room");
log(response);
for (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"];
//}
if ((urlParams.has('streamid')) || (urlParams.has('view'))){
play(response[i]['streamID']);
} else {
session.watchStream(response[i]['streamID']); // How do I make sure they aren't requesting the same movie twice as a race condition?
}
}
}
}
}
},function(error){return {}});
} else {
errorlog("Room name not long enough or contained all bad characaters");
}
}
function createRoom(){
var roomname = document.getElementById("videoname1").value;
log(roomname);
if (roomname.length==0){
alert("Please enter a room name before continuing");
return;
}
var gridlayout = document.getElementById("gridlayout");
gridlayout.classList.add("directorsgrid");
// var sheet = document.createElement('style');
// sheet.innerHTML = ".tile{object-fit:contain }";
// document.body.appendChild(sheet);
var roomname = document.getElementById("videoname1").value;
log(roomname);
session.roomid = roomname;
formSubmitting = false;
var m = document.getElementById("mainmenu");
m.remove();
document.getElementById("head1").className = 'advanced';
document.getElementById("head2").className = 'advanced';
document.getElementById("head3").className = 'advanced';
document.getElementById("head4").className = '';
document.getElementById("dirroomid").innerHTML = roomname;
document.getElementById("roomid").innerHTML = roomname;
//document.getElementById("mutebutton").className="float3";
//document.getElementById("helpbutton").className="float2";
session.director = true;
document.getElementById("reshare").parentNode.removeChild(document.getElementById("reshare"));
gridlayout.innerHTML = "
- Invite link to add guests to the group. These guests will see every camera feed, so more than 4 is not recommended.
";
gridlayout.innerHTML += " - Link to add a camera to the group. This source will not be able to see or hear any other guest in the group. ";
gridlayout.innerHTML += " - This is a 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 let OBS.Ninja 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. The Group Scene includes all audio sources by default, but they can be muted manually.
Apple mobile devices, such as iPhones and iPads, do not support Group Chat. This is a hardware constraint.
";
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){
tog.style.display = "none";
}
}
function enumerateDevices() {
log("enumerated start");
if (typeof navigator.enumerateDevices === "function") {
errorlog("enumerated failed 1");
return navigator.enumerateDevices();
}
else if (typeof navigator.mediaDevices === "object" &&
typeof navigator.mediaDevices.enumerateDevices === "function") {
errorlog("enumerated failed 2");
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
});
console.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) {
if (deviceInfos[i].kind === 'audioinput') {
if (deviceInfos[i].groupId in temp){
deviceInfos[i] = null;
} else {
temp[deviceInfos[i].groupId]=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);
}
}
});
});
} 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].groupId in temp){
deviceInfos[i] = null;
} else {
temp[deviceInfos[i].groupId]=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";
document.getElementById("multiselect1").checked = false;
document.getElementById("multiselect1").parentNode.style.display="none";
} else {
listele.style.display="none";
}
option.value = deviceInfo.deviceId;
option.name = "multiselect"+counter;
option.id = "multiselect"+counter;
const label = document.createElement('label');
label.for = option.name;
label.innerHTML = " " + (deviceInfo.label || `microphone ${audioInputSelect.length + 1}`);
listele.appendChild(option);
listele.appendChild(label);
audioInputSelect.appendChild(listele);
document.getElementById("multiselect1").onchange = function(event){ // make sure to clear 'no audio option' if anything else is selected
if (!(document.getElementById("multiselect1").checked)){
document.getElementById("multiselect1").checked= true;
}
};
option.onchange = function(event){ // make sure to clear 'no audio option' if anything else is selected
document.getElementById("multiselect1").checked= false;
};
} else if (deviceInfo.kind === 'videoinput') {
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
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);
}
}
function handleError(error) {
errorlog(error);
}
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: 360, ideal: 1280, max: 1440 },
};
}
else {
return {
width: { min: 360, ideal: 1280, max: 1440 },
};
}
case 3:
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 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("activeated preview return 2");return;}
activatedPreview = true;
log(quality);
log("trying with quality:");
var videoSelect = document.querySelector('select#videoSource');
var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
var iPad = (navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform));
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= document.getElementById('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} ]});
//document.getElementById("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;
}
}
//Make the DIV element draggagle:
//dragElement(document.getElementById("previewWebcam"));
function setupWebcamSelection(){
log("setup webcam");
try {
return enumerateDevices().then(gotDevices).then(function(){
log("enumerated");
if (parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value)==3){
session.maxframerate = 30;
} else {
session.maxframerate = false;
}
var audioSelect = document.querySelector('#audioSource');
var videoSelect = document.querySelector('select#videoSource');
audioSelect.onchange = function(){
document.getElementById('gowebcam').innerHTML="Waiting for Camera to load";
log("AUDIO source CHANGED");
activatedPreview=false;
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
};
videoSelect.onchange = function(){
document.getElementById('gowebcam').innerHTML="Waiting for Camera to load";
log("video source changed");
activatedPreview=false;
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
};
document.getElementById("webcamquality").onchange = function(){
document.getElementById('gowebcam').innerHTML="Waiting for Camera to load";
log("AUDIO source CHANGED");
activatedPreview=false;
if (parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value)==3){
session.maxframerate = 30;
} else {
session.maxframerate = false;
}
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
};
activatedPreview = false;
grabVideo(parseInt(document.getElementById("webcamquality").elements.namedItem("resolution").value));
}).catch(handleError);
} catch (e){errorlog(e);}
}
function previewWebcam(){
if( activatedPreview == true){log("activeated preview return 1");return;}
activatedPreview = true;
window.setTimeout(() => {
try{
var oldstream= document.getElementById('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 }).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(e){
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 checkOBS(){
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
log("enumerateDevices() not supported.");
return;
}
navigator.mediaDevices.enumerateDevices().then(function(devices) {
var matchFound = false;
devices.forEach(function(device) {
if (device.label.startsWith("OBS-Camera")){
alert("An OBS Virtual Camera was detected; Success!");
log(device.kind + ": " + device.label +
" id = " + device.deviceId);
matchFound = true;
}
log(device.kind + ": " + device.label + " id = " + device.deviceId);
});
if (matchFound == false){
alert("No OBS Virtual Camera was found");
}
}).catch(function(err) {
log(err.name + ": " + err.message);
});
}
function play(streamid=null){ // play whatever is in the URL params; or filter by a streamID option
log("play stream");
if ((urlParams.has('streamid')) || (urlParams.has('view'))){
var streamlist = urlParams.get('streamid') || urlParams.get('view');
log(streamlist);
streamlist = streamlist.split(",");
for (j in streamlist){
if (streamid===null){
session.watchStream(streamlist[j]);
} else if (streamid === streamlist[j]){
session.watchStream(streamlist[j]);
}
}
}
}
var retry=null;
function recordVideo(event, video, UUID, videoKbps=2500){
var target = event.currentTarget;
if ("recording" in video){
log("ALREADY RECORDING!");
target.style.backgroundColor = null;
target.innerHTML = "Record";
video.recorder.stop();
session.requestRateLimit(100,UUID);
delete(video.recorder);
delete(video.recording);
return;
} else {
target.style.backgroundColor = "#FCC";
target.innerHTML = "Download";
video.recording = true;
}
videoKbps = prompt("Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",videoKbps);
videoKbps = parseInt(videoKbps);
if (videoKbps<35){
videoKbps=35;
}
session.requestRateLimit(videoKbps, UUID);
var filename = Date.now().toString();
//var canvas = document.createElement('canvas');
//canvas.width = video.videoWidth;
//canvas.height = video.videoHeight;
//var ctx = canvas.getContext('2d');
var recordedBlobs = [];
///var stream = canvas.captureStream();
stream = video.srcObject;//.getVideoTracks().forEach(
//stream.getAudioTracks
var cancell = false;
if (typeof stream == undefined || !stream) {return;}
this.stop = stopRecording;
let options = {
mimeType: "video/webm",
videoBitsPerSecond: parseInt(videoKbps*1000) // 2.5Mbps
};
var mediaRecorder = new MediaRecorder(stream,options);
var lasttime = 0;
//function drawVideoFrame() {
// if (Date.now() - lasttime <= 16){return;}
// lasttime=Date.now();
// ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// };
//var drawtimer = setInterval(function(){
// requestAnimationFrame(drawVideoFrame);
// },25);
function download() {
const blob = new Blob(recordedBlobs, { type: "video/webm" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename+".webm";
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
function handleDataAvailable(event) {
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
}
function stopRecording() {
mediaRecorder.stop();
//clearInterval(drawtimer);
cancell = true;
console.log('Recorded Blobs: ', recordedBlobs);
download();
}
mediaRecorder.ondataavailable = handleDataAvailable;
//drawVideoFrame();
mediaRecorder.onerror = function(event) {
errorlog(event);
stopRecording();
session.requestRateLimit(100,UUID);
alert("an error occured with the media recorder; stopping recording");
};
stream.ended = function(event) {
stopRecording();
session.requestRateLimit(100,UUID);
alert("stream ended! stopping recording");
};
mediaRecorder.start(100); // 100ms chunks
console.log('MediaRecorder started', mediaRecorder);
return this
}
function copyFunction(copyText) {
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
}
function generateQRPage(){
try{
var title = encodeURI(document.getElementById("videoname4").value);
if (title.length){
title = "&label="+title;
}
var sid = session.generateStreamID();
var viewstr = "";
var sendstr = "";
if (document.getElementById("invite_bitrate").checked){
viewstr+="&bitrate=20000";
}
if (document.getElementById("invite_vp9").checked){
viewstr+="&codec=vp9";
}
if (document.getElementById("invite_stereo").checked){
viewstr+="&stereo";
sendstr+="&stereo";
}
if (document.getElementById("invite_secure").checked){
sendstr+="&secure";
}
sendstr = 'https://' + location.host + location.pathname + '?push=' + sid + sendstr;
viewstr = 'https://' + location.host+ location.pathname + '?view=' + sid + viewstr + title;
document.getElementById("gencontent").innerHTML = '