+
- +
+
@@ -530,13 +534,16 @@ video {
Code is open-sourced: https://github.com/steveseguin/obsninja
You can also check out StageTEN.tv for a more feature-rich paid-solution
- Known issues:
+ Known issues:
-** MacOS users need to use OBS v23. v24/v25 have a bug in it
+ ** MacOS users need to use OBS v23 and a local microphone for the time being.
+ ** The rear camera on some smartphones have issues. Please report these issues, including your phone's model.
+ ** For some users the video fails to load in OBS. Try a few times and if it still fails, contact me.
+
+March 30th, 2020: Site updated. Previous version can be found at https://obs.ninja/old/
@@ -571,12 +578,12 @@ document.addEventListener('touchend', function (event) {
}, false);
/////////////
- function updateURL(param) {
- if (history.pushState) {
- var newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' +param;
- window.history.pushState({path:newurl},'',newurl);
- }
+function updateURL(param) {
+ if (history.pushState) {
+ var newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' +param;
+ window.history.pushState({path:newurl},'',newurl);
}
+}
var session = Ooblex.Media;
session.streamID = session.generateStreamID();
var urlParams = new URLSearchParams(window.location.search);
@@ -668,6 +675,7 @@ function publishWebcam(){
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.
session.publishStream(stream, title);
log("streamID is: "+session.streamID);
document.getElementById("head1").className = 'advanced';
@@ -676,7 +684,7 @@ function publishWebcam(){
document.getElementById("mutebutton").className="float3";
document.getElementById("helpbutton").className="float2";
-
+
}
@@ -758,7 +766,7 @@ function enumerateDevices() {
});
}
catch (e) {
- console.error(e);
+ errorlog(e);
}
});
}
@@ -771,16 +779,13 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
// TODO: Add in the option to select the OUTPUT and Disable Mic/Cam
// Handles being called several times to update labels. Preserve values.
- const values = selectors.map(select => select.value);
+ const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
- console.log("JSDFK");
- console.log(deviceInfos);
- //deviceInfos = extractCamerasFromDevices(deviceInfos);
- console.log(deviceInfos);
+ log(deviceInfos);
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
const option = document.createElement('option');
@@ -794,17 +799,16 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
} else {
log('Some other kind of source/device: ', deviceInfo);
}
-}
-selectors.forEach((select, selectorIndex) => {
- if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
- select.value = values[selectorIndex];
}
-});
+ selectors.forEach((select, selectorIndex) => {
+ if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
+ select.value = values[selectorIndex];
+ }
+ });
}
function handleError(error) {
- console.log(error);
- log('Error: ', error);
+ errorlog(error);
}
function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
@@ -812,14 +816,14 @@ function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
case 0:
if (isSafariBrowser) {
return {
- width: { min: 1400, ideal: 1920, max: 1920 },
- height: { min: 900, ideal: 1080, max: 1920 }
+ width: { min: 640 },
+ height: { min: 360 }
};
}
else {
return {
- width: { min: 1400, ideal: 1920, max: 1920 },
- height: { min: 900, ideal: 1080, max: 1920 }
+ width: { min: 720, ideal: 1920, max: 1920 },
+ height: { min: 720, ideal: 1080, max: 1920 }
};
}
case 1:
@@ -844,7 +848,7 @@ function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
}
else {
return {
- width: { min: 540, ideal: 1280, max: 1920 },
+ width: { min: 640, ideal: 1280, max: 1920 },
height: { min: 640, ideal: 1280, max: 1080 }
};
}
@@ -862,36 +866,53 @@ function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
case 4:
if (isSafariBrowser) {
return {
- width: { min: 720, ideal: 1024, max: 1440 },
+ width: { min: 360, ideal: 1280, max: 1440 },
};
}
else {
return {
- width: { min: 720, ideal: 1280, max: 1440 },
+ width: { min: 360, ideal: 1280, max: 1440 },
};
}
case 5:
if (isSafariBrowser) {
return {
- width: { min: 320, ideal: 640, max: 1440 },
- height: { min: 320, ideal: 360, max: 720 }
+ width: { min: 360, ideal: 640, max: 1440 },
+ height: { min: 360, ideal: 360, max: 720 }
};
}
else {
return {
- width: { min: 320, ideal: 640, max: 1440 },
- height: { min: 320, ideal: 360, max: 720 }
+ width: { min: 360, ideal: 640, max: 1440 },
+ height: { min: 360, ideal: 360, max: 720 }
};
}
+ case 6:
+ if (isSafariBrowser) {
+ return {}; // iphone users probably don't need to wait any longer, so let them just get to it
+ }
+ else {
+ 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 7:
+ return {}; // same as default, but I didn't want to mess with framerates until I gave it all a try first
+ case 8:
+ return {framerate: 0 }; // Some Samsung Devices report they can only support a framerate of 0.
default:
- return {};
+ return {};
}
}
function grabVideo(quality=0, audio=false){
if( activatedPreview == true){log("activeated preview return");return;}
activatedPreview = true;
- console.log("trying with quality:",quality);
+ log(quality);
+ log("trying with quality:");
+
var audioSelect = document.querySelector('select#audioSource');
var videoSelect = document.querySelector('select#videoSource');
var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
@@ -914,35 +935,48 @@ function grabVideo(quality=0, audio=false){
constraints.video.height = {exact: height};
}
- console.log(constraints);
+ log(constraints);
- var oldstream= document.getElementById('previewWebcam').srcObject;
- if (oldstream){
- oldstream.getTracks().forEach(function(track) {
- track.stop();
- });
- }
setTimeout(()=>{
+ 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 (audio ==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 {
- console.log("DONE");
- document.getElementById('previewWebcam').srcObject=stream;
+ document.getElementById('previewWebcam').srcObject = stream; // set the preview window and run with it
+ log("DONE - found stream");
}
}).catch(function(e){
activatedPreview = false;
if (e.name === "OverconstrainedError"){
log("Resolution didn't work");
} else if (e.name === "NotReadableError"){
- alert("Error: Could not read from selected Camera");
+ alert("Error Listing Media Devices.\n\nThe default Camera may already be in use with another app. Typically webcams can only be accessed by one program at a time.\n\nThe selected device may also not be supported.");
activatedPreview=true;
return;
+ } else if (e.name === "NavigatorUserMediaError"){
+ alert("Unknown error: 'NavigatorUserMediaError'");
+ return;
} else {
errorlog(e);
}
- if (quality<=6){
+ if (quality<=8){
grabVideo(quality+1);
} else {
errorlog(e);
@@ -953,101 +987,6 @@ function grabVideo(quality=0, audio=false){
},0);
}
-
-
-function isBackCameraLabel(label) {
- const lowercaseLabel = label.toLowerCase();
- return backCameraKeywords.some(keyword => {
- return lowercaseLabel.includes(keyword);
- });
-}
-const backCameraKeywords = [
- "rear",
- "back",
- "rück",
- "arrière",
- "trasera",
- "trás",
- "traseira",
- "posteriore",
- "åŽé¢",
- "後é¢",
- "背é¢",
- "åŽç½®",
- "後置",
- "背置",
- "задней",
- "الخلÙية",
- "후",
- "arka",
- "achterzijde",
- "หลัง",
- "baksidan",
- "bagside",
- "sau",
- "bak",
- "tylny",
- "takakamera",
- "belakang",
- "×חורית",
- "πίσω",
- "spate",
- "hátsó",
- "zadnÃ",
- "darrere",
- "zadná",
- "заднÑ",
- "stražnja",
- "belakang",
- "बैक"
-];
-function extractCamerasFromDevices(devices) {
- const cameraObjects = new Map();
- var Camera = {"Type":{"FRONT":"front","BACK":"back"}};
- const cameras = devices
- .filter(device => {
- return device.kind === "videoinput";
- })
- .map(videoDevice => {
- if (cameraObjects.has(videoDevice.deviceId)) {
- return cameraObjects.get(videoDevice.deviceId);
- }
- const label = videoDevice.label != null ? videoDevice.label : "";
- const camera = {
- deviceId: videoDevice.deviceId,
- label,
- cameraType: isBackCameraLabel(label) ? Camera.Type.BACK : Camera.Type.FRONT
- };
- if (label !== "") {
- cameraObjects.set(videoDevice.deviceId, camera);
- }
- return camera;
- });
- if (cameras.length > 1 &&
- !cameras.some(camera => {
- return camera.cameraType === Camera.Type.BACK;
- })) {
- // Check if cameras are labeled with resolution information, take the higher-resolution one in that case
- // Otherwise pick the last camera
- let backCameraIndex = cameras.length - 1;
- const cameraResolutions = cameras.map(camera => {
- const match = camera.label.match(/\b([0-9]+)MP?\b/i);
- if (match != null) {
- return parseInt(match[1], 10);
- }
- return NaN;
- });
- if (!cameraResolutions.some(cameraResolution => {
- return isNaN(cameraResolution);
- })) {
- backCameraIndex = cameraResolutions.lastIndexOf(Math.max(...cameraResolutions));
- }
- // tslint:disable-next-line:no-any
- cameras[backCameraIndex].cameraType = Camera.Type.BACK;
- }
- return cameras;
-}
-
var activatedPreview = false;
function previewWebcam(){
if( activatedPreview == true){log("activeated preview return");return;}
@@ -1060,22 +999,51 @@ function previewWebcam(){
window.setTimeout(() => {
- navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
- document.getElementById('previewWebcam').srcObject=stream;
- window.setTimeout(() => {
+
+ var oldstream= document.getElementById('previewWebcam').srcObject;
+ if (oldstream){
+ oldstream.getTracks().forEach(function(track) {
+ track.stop();
+ });
+ }
+
+ navigator.mediaDevices.getUserMedia(constraints).then(function(stream){ // Apple needs thi to happen before I can access EnumerateDevices.
+ //document.getElementById('previewWebcam').srcObject=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();
+ });
enumerateDevices().then(gotDevices).then(function(){
var audioSelect = document.querySelector('select#audioSource');
var videoSelect = document.querySelector('select#videoSource');
- audioSelect.onchange = function(){console.log("CHANGED");activatedPreview=false;grabVideo();};
- videoSelect.onchange = function(){activatedPreview=false;grabVideo();};
+ audioSelect.onchange = function(){log("AUDIO source CHANGED");activatedPreview=false;grabVideo();};
+ videoSelect.onchange = function(){log("video source changed");activatedPreview=false;grabVideo();};
activatedPreview = false;
grabVideo();
}).catch(handleError);
- },0);
- }).catch(handleError);
+ }).catch(function(e){
+ if (e.name === "NotReadableError"){
+ window.setTimeout(() => {
+ enumerateDevices().then(gotDevices).then(function(){
+
+ var audioSelect = document.querySelector('select#audioSource');
+ var videoSelect = document.querySelector('select#videoSource');
+
+ audioSelect.onchange = function(){log("AUDIO source CHANGED");activatedPreview=false;grabVideo();};
+ videoSelect.onchange = function(){log("video source changed");activatedPreview=false;grabVideo();};
+ activatedPreview = false;
+ grabVideo();
+
+ }).catch(handleError);
+ },0);
+ } else if (e.name === "NotAllowedError"){
+ alert("Error: Cannot access media devices \n\nPlease ensure both CAMERA and MICROPHONE permissions are allowed for this website to continue");
+ } else {
+ errorlog(e);
+ }
+ });
},0);
}
@@ -1110,6 +1078,7 @@ function play(streamName){
log("play stream");
session.watchStream(streamName);
}
+var retry=null;
function browse(){
log("browse streams");
session.listStreams().then(function(response){
@@ -1120,10 +1089,32 @@ function browse(){
},function(error){return {}});
}
+function generateQRPage(ele){
+ try{
+ var sid = session.generateStreamID();
+ ele.parentNode.innerHTML = '
\ +
and don\'t forget the
In OBS v25 you can drag this link directly into OBS, or you can create a Browse element in OBS and insert it the URL source. \ +
\ + Please also note, the invite link and OBS ingestion link created is reusable, but only one person may use a specific invite at a time.'; + var qrcode = new QRCode(document.getElementById("qrcode"), { + width : 300, + height : 300, + colorDark : "#000000", + colorLight : "#FFFFFF", + useSVG: false + }); + qrcode.makeCode('https://' + location.hostname + location.pathname + '?permaid=' + sid); + + } catch(e){ + errorlog(e); + } + +} + var urlParams = new URLSearchParams(window.location.search); if (urlParams.has('streamid')){ - + 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'; @@ -1135,27 +1126,38 @@ if (urlParams.has('streamid')){ document.getElementById("head3").className = 'advanced'; document.getElementById("roomid").innerHTML = urlParams.get('streamid'); document.getElementById("mainmenu").style.backgroundImage = "url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHdpZHRoPSI0MHB4IiBoZWlnaHQ9IjQwcHgiIHZpZXdCb3g9IjAgMCA0MCA0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEuNDE0MjE7IiB4PSIwcHgiIHk9IjBweCI+CiAgICA8ZGVmcz4KICAgICAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwhW0NEQVRBWwogICAgICAgICAgICBALXdlYmtpdC1rZXlmcmFtZXMgc3BpbiB7CiAgICAgICAgICAgICAgZnJvbSB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoLTM1OWRlZykKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgQGtleWZyYW1lcyBzcGluIHsKICAgICAgICAgICAgICBmcm9tIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKC0zNTlkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIHN2ZyB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybS1vcmlnaW46IDUwJSA1MCU7CiAgICAgICAgICAgICAgICAtd2Via2l0LWFuaW1hdGlvbjogc3BpbiAxLjVzIGxpbmVhciBpbmZpbml0ZTsKICAgICAgICAgICAgICAgIC13ZWJraXQtYmFja2ZhY2UtdmlzaWJpbGl0eTogaGlkZGVuOwogICAgICAgICAgICAgICAgYW5pbWF0aW9uOiBzcGluIDEuNXMgbGluZWFyIGluZmluaXRlOwogICAgICAgICAgICB9CiAgICAgICAgXV0+PC9zdHlsZT4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJvdXRlciI+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwwQzIyLjIwNTgsMCAyMy45OTM5LDEuNzg4MTMgMjMuOTkzOSwzLjk5MzlDMjMuOTkzOSw2LjE5OTY4IDIyLjIwNTgsNy45ODc4MSAyMCw3Ljk4NzgxQzE3Ljc5NDIsNy45ODc4MSAxNi4wMDYxLDYuMTk5NjggMTYuMDA2MSwzLjk5MzlDMTYuMDA2MSwxLjc4ODEzIDE3Ljc5NDIsMCAyMCwwWiIgc3R5bGU9ImZpbGw6YmxhY2s7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNNS44NTc4Niw1Ljg1Nzg2QzcuNDE3NTgsNC4yOTgxNSA5Ljk0NjM4LDQuMjk4MTUgMTEuNTA2MSw1Ljg1Nzg2QzEzLjA2NTgsNy40MTc1OCAxMy4wNjU4LDkuOTQ2MzggMTEuNTA2MSwxMS41MDYxQzkuOTQ2MzgsMTMuMDY1OCA3LjQxNzU4LDEzLjA2NTggNS44NTc4NiwxMS41MDYxQzQuMjk4MTUsOS45NDYzOCA0LjI5ODE1LDcuNDE3NTggNS44NTc4Niw1Ljg1Nzg2WiIgc3R5bGU9ImZpbGw6cmdiKDIxMCwyMTAsMjEwKTsiLz4KICAgICAgICA8L2c+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwzMi4wMTIyQzIyLjIwNTgsMzIuMDEyMiAyMy45OTM5LDMzLjgwMDMgMjMuOTkzOSwzNi4wMDYxQzIzLjk5MzksMzguMjExOSAyMi4yMDU4LDQwIDIwLDQwQzE3Ljc5NDIsNDAgMTYuMDA2MSwzOC4yMTE5IDE2LjAwNjEsMzYuMDA2MUMxNi4wMDYxLDMzLjgwMDMgMTcuNzk0MiwzMi4wMTIyIDIwLDMyLjAxMjJaIiBzdHlsZT0iZmlsbDpyZ2IoMTMwLDEzMCwxMzApOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksMjguNDkzOUMzMC4wNTM2LDI2LjkzNDIgMzIuNTgyNCwyNi45MzQyIDM0LjE0MjEsMjguNDkzOUMzNS43MDE5LDMwLjA1MzYgMzUuNzAxOSwzMi41ODI0IDM0LjE0MjEsMzQuMTQyMUMzMi41ODI0LDM1LjcwMTkgMzAuMDUzNiwzNS43MDE5IDI4LjQ5MzksMzQuMTQyMUMyNi45MzQyLDMyLjU4MjQgMjYuOTM0MiwzMC4wNTM2IDI4LjQ5MzksMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxMDEsMTAxLDEwMSk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMy45OTM5LDE2LjAwNjFDNi4xOTk2OCwxNi4wMDYxIDcuOTg3ODEsMTcuNzk0MiA3Ljk4NzgxLDIwQzcuOTg3ODEsMjIuMjA1OCA2LjE5OTY4LDIzLjk5MzkgMy45OTM5LDIzLjk5MzlDMS43ODgxMywyMy45OTM5IDAsMjIuMjA1OCAwLDIwQzAsMTcuNzk0MiAxLjc4ODEzLDE2LjAwNjEgMy45OTM5LDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoMTg3LDE4NywxODcpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTUuODU3ODYsMjguNDkzOUM3LjQxNzU4LDI2LjkzNDIgOS45NDYzOCwyNi45MzQyIDExLjUwNjEsMjguNDkzOUMxMy4wNjU4LDMwLjA1MzYgMTMuMDY1OCwzMi41ODI0IDExLjUwNjEsMzQuMTQyMUM5Ljk0NjM4LDM1LjcwMTkgNy40MTc1OCwzNS43MDE5IDUuODU3ODYsMzQuMTQyMUM0LjI5ODE1LDMyLjU4MjQgNC4yOTgxNSwzMC4wNTM2IDUuODU3ODYsMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxNjQsMTY0LDE2NCk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMzYuMDA2MSwxNi4wMDYxQzM4LjIxMTksMTYuMDA2MSA0MCwxNy43OTQyIDQwLDIwQzQwLDIyLjIwNTggMzguMjExOSwyMy45OTM5IDM2LjAwNjEsMjMuOTkzOUMzMy44MDAzLDIzLjk5MzkgMzIuMDEyMiwyMi4yMDU4IDMyLjAxMjIsMjBDMzIuMDEyMiwxNy43OTQyIDMzLjgwMDMsMTYuMDA2MSAzNi4wMDYxLDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoNzQsNzQsNzQpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksNS44NTc4NkMzMC4wNTM2LDQuMjk4MTUgMzIuNTgyNCw0LjI5ODE1IDM0LjE0MjEsNS44NTc4NkMzNS43MDE5LDcuNDE3NTggMzUuNzAxOSw5Ljk0NjM4IDM0LjE0MjEsMTEuNTA2MUMzMi41ODI0LDEzLjA2NTggMzAuMDUzNiwxMy4wNjU4IDI4LjQ5MzksMTEuNTA2MUMyNi45MzQyLDkuOTQ2MzggMjYuOTM0Miw3LjQxNzU4IDI4LjQ5MzksNS44NTc4NloiIHN0eWxlPSJmaWxsOnJnYig1MCw1MCw1MCk7Ii8+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K')"; - document.getElementById("mainmenu").style.backgroundRepeat = "no-repeat"; + +document.getElementById("mainmenu").style.backgroundRepeat = "no-repeat"; document.getElementById("mainmenu").style.backgroundPosition = "bottom center"; document.getElementById("mainmenu").style.minHeight = "300px"; document.getElementById("mainmenu").style.backgroundSize = "100px 100px"; document.getElementById("mainmenu").innerHTML = '
'; - document.getElementById("mainmenu").innerHTML += 'If the stream does not load within a few seconds, the stream may not be available or some other error has occured. If the issue persists, please check out the https://reddit.com/r/obsninja for possible solutions or contact steve@seguin.email.
'; - - if (urlParams.get("streamid")){ - document.getElementById("mainmenu").innerHTML += '
';
- var qrcode = new QRCode(document.getElementById("qrcode"), {
- width : 300,
- height : 300,
- colorDark : "#000000",
- colorLight : "#FFFFFF",
- useSVG: false
- });
- qrcode.makeCode('https://' + location.hostname + location.pathname + '?permaid=' + urlParams.get("streamid"));
-
+ document.getElementById("mainmenu").innerHTML += '
';
+ var qrcode = new QRCode(document.getElementById("qrcode"), {
+ width : 300,
+ height : 300,
+ colorDark : "#000000",
+ colorLight : "#FFFFFF",
+ useSVG: false
+ });
+ qrcode.makeCode('https://' + location.hostname + location.pathname + '?permaid=' + urlParams.get("streamid"));
+ retry = setInterval(function(){
+ if (document.getElementById("mainmenu")){
+ play(urlParams.get('streamid'));
+ } else {
+ clearInterval(retry);
+ }
+ },10000)
+ }}
+ } catch(e){
+ errorlog("Error handling QR Code failure");
}
},2000);
@@ -1328,7 +1330,7 @@ function poker(){
}
});
-
+
Add Group Video Chat to OBS
+ (COMING VERY SOON)Select the audio/video source below and when you're ready just click START SHARING WEBCAM
- +
Video source:
Audio source:
@@ -516,6 +506,20 @@ video {
+
Generate Invite Link
+
+
+
+ +
+
+
+
+
+
+ - Known issues:
+ Known issues:
-
+
Send feature requests and support to steve@seguin.email, or check out the sub-reddit
-\ +
and don\'t forget the
OBS Link:
https://' + location.hostname + location.pathname + '?streamid=' + sid + '
In OBS v25 you can drag this link directly into OBS, or you can create a Browse element in OBS and insert it the URL source. \ +
\ + Please also note, the invite link and OBS ingestion link created is reusable, but only one person may use a specific invite at a time.'; + var qrcode = new QRCode(document.getElementById("qrcode"), { + width : 300, + height : 300, + colorDark : "#000000", + colorLight : "#FFFFFF", + useSVG: false + }); + qrcode.makeCode('https://' + location.hostname + location.pathname + '?permaid=' + sid); + + } catch(e){ + errorlog(e); + } + +} + var urlParams = new URLSearchParams(window.location.search); if (urlParams.has('streamid')){ - + 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'; @@ -1135,27 +1126,38 @@ if (urlParams.has('streamid')){ document.getElementById("head3").className = 'advanced'; document.getElementById("roomid").innerHTML = urlParams.get('streamid'); document.getElementById("mainmenu").style.backgroundImage = "url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHdpZHRoPSI0MHB4IiBoZWlnaHQ9IjQwcHgiIHZpZXdCb3g9IjAgMCA0MCA0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEuNDE0MjE7IiB4PSIwcHgiIHk9IjBweCI+CiAgICA8ZGVmcz4KICAgICAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwhW0NEQVRBWwogICAgICAgICAgICBALXdlYmtpdC1rZXlmcmFtZXMgc3BpbiB7CiAgICAgICAgICAgICAgZnJvbSB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoLTM1OWRlZykKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgQGtleWZyYW1lcyBzcGluIHsKICAgICAgICAgICAgICBmcm9tIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKC0zNTlkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIHN2ZyB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybS1vcmlnaW46IDUwJSA1MCU7CiAgICAgICAgICAgICAgICAtd2Via2l0LWFuaW1hdGlvbjogc3BpbiAxLjVzIGxpbmVhciBpbmZpbml0ZTsKICAgICAgICAgICAgICAgIC13ZWJraXQtYmFja2ZhY2UtdmlzaWJpbGl0eTogaGlkZGVuOwogICAgICAgICAgICAgICAgYW5pbWF0aW9uOiBzcGluIDEuNXMgbGluZWFyIGluZmluaXRlOwogICAgICAgICAgICB9CiAgICAgICAgXV0+PC9zdHlsZT4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJvdXRlciI+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwwQzIyLjIwNTgsMCAyMy45OTM5LDEuNzg4MTMgMjMuOTkzOSwzLjk5MzlDMjMuOTkzOSw2LjE5OTY4IDIyLjIwNTgsNy45ODc4MSAyMCw3Ljk4NzgxQzE3Ljc5NDIsNy45ODc4MSAxNi4wMDYxLDYuMTk5NjggMTYuMDA2MSwzLjk5MzlDMTYuMDA2MSwxLjc4ODEzIDE3Ljc5NDIsMCAyMCwwWiIgc3R5bGU9ImZpbGw6YmxhY2s7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNNS44NTc4Niw1Ljg1Nzg2QzcuNDE3NTgsNC4yOTgxNSA5Ljk0NjM4LDQuMjk4MTUgMTEuNTA2MSw1Ljg1Nzg2QzEzLjA2NTgsNy40MTc1OCAxMy4wNjU4LDkuOTQ2MzggMTEuNTA2MSwxMS41MDYxQzkuOTQ2MzgsMTMuMDY1OCA3LjQxNzU4LDEzLjA2NTggNS44NTc4NiwxMS41MDYxQzQuMjk4MTUsOS45NDYzOCA0LjI5ODE1LDcuNDE3NTggNS44NTc4Niw1Ljg1Nzg2WiIgc3R5bGU9ImZpbGw6cmdiKDIxMCwyMTAsMjEwKTsiLz4KICAgICAgICA8L2c+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwzMi4wMTIyQzIyLjIwNTgsMzIuMDEyMiAyMy45OTM5LDMzLjgwMDMgMjMuOTkzOSwzNi4wMDYxQzIzLjk5MzksMzguMjExOSAyMi4yMDU4LDQwIDIwLDQwQzE3Ljc5NDIsNDAgMTYuMDA2MSwzOC4yMTE5IDE2LjAwNjEsMzYuMDA2MUMxNi4wMDYxLDMzLjgwMDMgMTcuNzk0MiwzMi4wMTIyIDIwLDMyLjAxMjJaIiBzdHlsZT0iZmlsbDpyZ2IoMTMwLDEzMCwxMzApOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksMjguNDkzOUMzMC4wNTM2LDI2LjkzNDIgMzIuNTgyNCwyNi45MzQyIDM0LjE0MjEsMjguNDkzOUMzNS43MDE5LDMwLjA1MzYgMzUuNzAxOSwzMi41ODI0IDM0LjE0MjEsMzQuMTQyMUMzMi41ODI0LDM1LjcwMTkgMzAuMDUzNiwzNS43MDE5IDI4LjQ5MzksMzQuMTQyMUMyNi45MzQyLDMyLjU4MjQgMjYuOTM0MiwzMC4wNTM2IDI4LjQ5MzksMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxMDEsMTAxLDEwMSk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMy45OTM5LDE2LjAwNjFDNi4xOTk2OCwxNi4wMDYxIDcuOTg3ODEsMTcuNzk0MiA3Ljk4NzgxLDIwQzcuOTg3ODEsMjIuMjA1OCA2LjE5OTY4LDIzLjk5MzkgMy45OTM5LDIzLjk5MzlDMS43ODgxMywyMy45OTM5IDAsMjIuMjA1OCAwLDIwQzAsMTcuNzk0MiAxLjc4ODEzLDE2LjAwNjEgMy45OTM5LDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoMTg3LDE4NywxODcpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTUuODU3ODYsMjguNDkzOUM3LjQxNzU4LDI2LjkzNDIgOS45NDYzOCwyNi45MzQyIDExLjUwNjEsMjguNDkzOUMxMy4wNjU4LDMwLjA1MzYgMTMuMDY1OCwzMi41ODI0IDExLjUwNjEsMzQuMTQyMUM5Ljk0NjM4LDM1LjcwMTkgNy40MTc1OCwzNS43MDE5IDUuODU3ODYsMzQuMTQyMUM0LjI5ODE1LDMyLjU4MjQgNC4yOTgxNSwzMC4wNTM2IDUuODU3ODYsMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxNjQsMTY0LDE2NCk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMzYuMDA2MSwxNi4wMDYxQzM4LjIxMTksMTYuMDA2MSA0MCwxNy43OTQyIDQwLDIwQzQwLDIyLjIwNTggMzguMjExOSwyMy45OTM5IDM2LjAwNjEsMjMuOTkzOUMzMy44MDAzLDIzLjk5MzkgMzIuMDEyMiwyMi4yMDU4IDMyLjAxMjIsMjBDMzIuMDEyMiwxNy43OTQyIDMzLjgwMDMsMTYuMDA2MSAzNi4wMDYxLDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoNzQsNzQsNzQpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksNS44NTc4NkMzMC4wNTM2LDQuMjk4MTUgMzIuNTgyNCw0LjI5ODE1IDM0LjE0MjEsNS44NTc4NkMzNS43MDE5LDcuNDE3NTggMzUuNzAxOSw5Ljk0NjM4IDM0LjE0MjEsMTEuNTA2MUMzMi41ODI0LDEzLjA2NTggMzAuMDUzNiwxMy4wNjU4IDI4LjQ5MzksMTEuNTA2MUMyNi45MzQyLDkuOTQ2MzggMjYuOTM0Miw3LjQxNzU4IDI4LjQ5MzksNS44NTc4NloiIHN0eWxlPSJmaWxsOnJnYig1MCw1MCw1MCk7Ii8+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K')"; - document.getElementById("mainmenu").style.backgroundRepeat = "no-repeat"; + +document.getElementById("mainmenu").style.backgroundRepeat = "no-repeat"; document.getElementById("mainmenu").style.backgroundPosition = "bottom center"; document.getElementById("mainmenu").style.minHeight = "300px"; document.getElementById("mainmenu").style.backgroundSize = "100px 100px"; document.getElementById("mainmenu").innerHTML = '
Attempting to load video stream.
'; setTimeout(function(){ + try{ + if (urlParams.get("streamid")){ + if (document.getElementById("mainmenu")){ + document.getElementById("mainmenu").innerHTML += 'If the stream does not load within a few seconds, the stream may not be available or some other error has occured. If the issue persists, please check out the https://reddit.com/r/obsninja for possible solutions or contact steve@seguin.email.'; - document.getElementById("mainmenu").innerHTML += 'If the stream does not load within a few seconds, the stream may not be available or some other error has occured. If the issue persists, please check out the https://reddit.com/r/obsninja for possible solutions or contact steve@seguin.email.
'; - - if (urlParams.get("streamid")){ - document.getElementById("mainmenu").innerHTML += '
Stream Invite URL:
https://' + location.hostname + location.pathname + '?permaid=' + urlParams.get("streamid") + '
Stream Invite URL:
https://' + location.hostname + location.pathname + '?permaid=' + urlParams.get("streamid") + '
Icons made by Lucy G from www.flaticon.com is licensed by CC 3.0 BY and by Gregor Cresnar from www.flaticon.com