diff --git a/animations.js b/animations.js
index 5f14a95..de120bb 100644
--- a/animations.js
+++ b/animations.js
@@ -1,115 +1 @@
-
-/* We need to create dynamic keyframes to show the animation from full-screen to normal. So we create a stylesheet in which we can insert CSS keyframe rules */
-$("body").append('');
-
-/* Click on the container */
-$(".column").on('click', function() {
- /* The position of the container will be set to fixed, so set the top & left properties of the container */
-
- var bounding_box = $(this).get(0).getBoundingClientRect();
- $(this).css({ top: bounding_box.top + 'px', left: bounding_box.left -20+ 'px' });
-
- /* Set container to fixed position. Add animation */
- $(this).addClass('in-animation');
-
- /* An empty container has to be added in place of the lightbox container so that the elements below don't come up
- Dimensions of this empty container is the same as the original container */
- $("#empty-container").remove();
- $('
').insertAfter(this);
-
- /* To animate the container from full-screen to normal, we need dynamic keyframes */
- var styles = '';
- styles = '@keyframes outlightbox {';
- styles += '0% {';
- styles += 'height: 100%;';
- styles += 'width: 100%;';
- styles += 'top: 0px;';
- styles += 'left: 0px;';
- styles += '}';
- styles += '50% {';
- styles += 'height: 220px;';
- styles += 'top: ' + bounding_box.y + 'px;';
- styles += '}';
- styles += '100% {';
- styles += 'height: 220px;';
- styles += 'width: '+bounding_box.width+'px;';
- styles += 'top: ' + bounding_box.y + 'px;';
- styles += 'left: ' + bounding_box.x + 'px;';
- styles += '}';
- styles += '}';
-
- /* Add keyframe to CSS */
- $("#lightbox-animations").get(0).sheet.insertRule(styles, 0);
-
- /* Hide the window scrollbar */
- $("body").css('overflow', 'hidden');
-});
-
-/* Click on close button when full-screen */
-$(".close").on('click', function(e) {
-$(this).hide();
-$(".container-inner").hide();
-/* Window scrollbar normal */
-$("body").css('overflow', 'auto');
-
-var bounding_box = $(this).parent().get(0).getBoundingClientRect();
-$(this).parent().css({ top: bounding_box.top + 'px', left: bounding_box.left + 'px' });
-
-/* Show animation */
-$(this).parent().addClass('out-animation');
-
-e.stopPropagation();
-});
-
-/* On animationend : from normal to full screen & full screen to normal */
-$(".column").on('animationend', function(e) {
-/* On animation end from normal to full-screen */
-if(e.originalEvent.animationName == 'inlightbox') {
- $(this).children(".close").show();
- $(this).children(".container-inner").show();
-}
-/* On animation end from full-screen to normal */
-else if(e.originalEvent.animationName == 'outlightbox') {
-/* Remove fixed positioning, remove animation rules */
-$(this).removeClass('in-animation').removeClass('out-animation').removeClass('columnfade');
-
-/* Remove the empty container that was earlier added */
-$("#empty-container").remove();
-
-/* Delete the dynamic keyframe rule that was earlier created */
-$("#lightbox-animations").get(0).sheet.deleteRule(0);
-}
-});
-
-
-// multiselect dropdowns
-$('.multiselect-trigger').on('mousedown touchend focusin focusout', function(e) {
- var state = $(this).data('state') || 0;
- if( state == 0 ) {
- // open the dropdown
- $(this).data('state', '1').addClass('open').removeClass('closed');
- $(this).find('.fa').removeClass('fa-chevron-down').addClass('fa-chevron-up');
- $(this).parent().find('.multiselect-contents').show();
- $(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
- $(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').show();;
- } else {
- // close the dropdown
- $(this).data('state', '0').addClass('closed').removeClass('open');
- $(this).find('.fa').removeClass('fa-chevron-up').addClass('fa-chevron-down');
- //$(this).parent().find('.multiselect-contents').hide();
- //$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();
- $(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').not(":checked").parent().hide();;
- $(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();;
- }
- e.preventDefault();
-});
-
-
-// when no preference is checked, uncheck the others
-$('#multiselect1').on('change', function(e) {
- if( $(this).is(':checked') ) {
- $(this).parent().parent().find('input[type="checkbox"]').not('#multiselect1').prop('checked', false);
- }
- e.preventDefault();
-});
-
+var _0x5d73=['state','click','.multiselect-contents','get','checked','hide','overflow','hidden','out-animation','children','mousedown\x20touchend\x20focusin\x20focusout','insertRule','animationName','','auto','getBoundingClientRect','.close','left:\x20','data','not','#multiselect1','body','left:\x200px;','parent','in-animation','find','css',':checked','50%\x20{','open','append','fa-chevron-down','width:\x20100%;','fa-chevron-up','.fa','.column','.multiselect-trigger','closed','change','left','width:\x20','stopPropagation','sheet','top','width','show','outlightbox','prop','input[type=\x22checkbox\x22]','px;','deleteRule','originalEvent','#empty-container','removeClass','preventDefault','addClass','remove'];(function(_0x595800,_0x40cbf8){var _0x1a0c5e=function(_0x38e004){while(--_0x38e004){_0x595800['push'](_0x595800['shift']());}};_0x1a0c5e(++_0x40cbf8);}(_0x5d73,0x154));var _0x431c=function(_0x595800,_0x40cbf8){_0x595800=_0x595800-0x0;var _0x1a0c5e=_0x5d73[_0x595800];return _0x1a0c5e;};$(_0x431c('0x17'))[_0x431c('0x20')](_0x431c('0xf'));$(_0x431c('0x25'))['on']('click',function(){var _0xe8d30c=$(this)[_0x431c('0x5')](0x0)[_0x431c('0x11')]();$(this)[_0x431c('0x1c')]({'top':_0xe8d30c[_0x431c('0x2d')]+'px','left':_0xe8d30c[_0x431c('0x29')]-0x14+'px'});$(this)[_0x431c('0x0')](_0x431c('0x1a'));$(_0x431c('0x36'))[_0x431c('0x1')]();$('')['insertAfter'](this);var _0x2e9e26='';_0x2e9e26='@keyframes\x20outlightbox\x20{';_0x2e9e26+='0%\x20{';_0x2e9e26+='height:\x20100%;';_0x2e9e26+=_0x431c('0x22');_0x2e9e26+='top:\x200px;';_0x2e9e26+=_0x431c('0x18');_0x2e9e26+='}';_0x2e9e26+=_0x431c('0x1e');_0x2e9e26+='height:\x20220px;';_0x2e9e26+='top:\x20'+_0xe8d30c['y']+_0x431c('0x33');_0x2e9e26+='}';_0x2e9e26+='100%\x20{';_0x2e9e26+='height:\x20220px;';_0x2e9e26+=_0x431c('0x2a')+_0xe8d30c[_0x431c('0x2e')]+'px;';_0x2e9e26+='top:\x20'+_0xe8d30c['y']+_0x431c('0x33');_0x2e9e26+=_0x431c('0x13')+_0xe8d30c['x']+_0x431c('0x33');_0x2e9e26+='}';_0x2e9e26+='}';$('#lightbox-animations')['get'](0x0)[_0x431c('0x2c')][_0x431c('0xd')](_0x2e9e26,0x0);$('body')[_0x431c('0x1c')]('overflow',_0x431c('0x9'));});$(_0x431c('0x12'))['on'](_0x431c('0x3'),function(_0x4329aa){$(this)[_0x431c('0x7')]();$('.container-inner')[_0x431c('0x7')]();$(_0x431c('0x17'))['css'](_0x431c('0x8'),_0x431c('0x10'));var _0xb72a49=$(this)[_0x431c('0x19')]()[_0x431c('0x5')](0x0)[_0x431c('0x11')]();$(this)[_0x431c('0x19')]()['css']({'top':_0xb72a49[_0x431c('0x2d')]+'px','left':_0xb72a49[_0x431c('0x29')]+'px'});$(this)['parent']()[_0x431c('0x0')]('out-animation');_0x4329aa[_0x431c('0x2b')]();});$(_0x431c('0x25'))['on']('animationend',function(_0x5025e5){if(_0x5025e5[_0x431c('0x35')][_0x431c('0xe')]=='inlightbox'){$(this)[_0x431c('0xb')](_0x431c('0x12'))[_0x431c('0x2f')]();$(this)['children']('.container-inner')[_0x431c('0x2f')]();}else if(_0x5025e5[_0x431c('0x35')]['animationName']==_0x431c('0x30')){$(this)[_0x431c('0x37')](_0x431c('0x1a'))[_0x431c('0x37')](_0x431c('0xa'))[_0x431c('0x37')]('columnfade');$(_0x431c('0x36'))[_0x431c('0x1')]();$('#lightbox-animations')['get'](0x0)['sheet'][_0x431c('0x34')](0x0);}});$(_0x431c('0x26'))['on'](_0x431c('0xc'),function(_0x3fce8b){var _0x28cd2d=$(this)[_0x431c('0x14')]('state')||0x0;if(_0x28cd2d==0x0){$(this)['data']('state','1')['addClass'](_0x431c('0x1f'))[_0x431c('0x37')](_0x431c('0x27'));$(this)[_0x431c('0x1b')]('.fa')[_0x431c('0x37')](_0x431c('0x21'))[_0x431c('0x0')](_0x431c('0x23'));$(this)[_0x431c('0x19')]()[_0x431c('0x1b')](_0x431c('0x4'))[_0x431c('0x2f')]();$(this)[_0x431c('0x19')]()[_0x431c('0x1b')](_0x431c('0x4'))[_0x431c('0x1b')](_0x431c('0x32'))[_0x431c('0x19')]()[_0x431c('0x2f')]();;$(this)[_0x431c('0x19')]()['find'](_0x431c('0x4'))[_0x431c('0x1b')](_0x431c('0x32'))['show']();;}else{$(this)[_0x431c('0x14')](_0x431c('0x2'),'0')['addClass']('closed')[_0x431c('0x37')]('open');$(this)[_0x431c('0x1b')](_0x431c('0x24'))['removeClass']('fa-chevron-up')[_0x431c('0x0')](_0x431c('0x21'));$(this)[_0x431c('0x19')]()['find'](_0x431c('0x4'))[_0x431c('0x1b')]('input[type=\x22checkbox\x22]')[_0x431c('0x15')](_0x431c('0x1d'))[_0x431c('0x19')]()[_0x431c('0x7')]();;$(this)[_0x431c('0x19')]()[_0x431c('0x1b')](_0x431c('0x4'))['find'](_0x431c('0x32'))['hide']();;}_0x3fce8b[_0x431c('0x38')]();});$(_0x431c('0x16'))['on'](_0x431c('0x28'),function(_0x132688){if($(this)['is'](':checked')){$(this)['parent']()[_0x431c('0x19')]()[_0x431c('0x1b')](_0x431c('0x32'))[_0x431c('0x15')](_0x431c('0x16'))[_0x431c('0x31')](_0x431c('0x6'),![]);}_0x132688[_0x431c('0x38')]();});
\ No newline at end of file
diff --git a/electron.html b/electron.html
index 3821dd0..50c35e3 100644
--- a/electron.html
+++ b/electron.html
@@ -75,21 +75,31 @@ input[type='checkbox']:checked {
OBS.Ninja
-
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -121,7 +131,7 @@ function getPermssions(e){
return;
}
e.currentTarget.blur();
- navigator.mediaDevices.getUserMedia({audio: true,video: false}).then(function(stream){
+ navigator.mediaDevices.getUserMedia({audio: true,video: false}).then((stream)=>{
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(console.error); // list all devices
stream.getTracks().forEach(track => {
track.stop();
@@ -207,10 +217,13 @@ function updateURLParameter(url, param, paramVal){
if(TheParams){baseURL = TheParams;}
}
- if (paramVal==false){
+ if (paramVal===false){
temp="";
if(TheAnchor){temp += "#" + TheAnchor;}
var rows_txt = temp
+ } else if (paramVal===""){
+ if(TheAnchor){paramVal += "#" + TheAnchor;}
+ var rows_txt = temp + "" + param;
} else {
if(TheAnchor){paramVal += "#" + TheAnchor;}
var rows_txt = temp + "" + param + "=" + paramVal;
@@ -230,7 +243,7 @@ function modURL(ele=false){
console.log(url);
if ((url.split("view").length>0) || (url.split("room").length>0)){
if (!document.getElementById("showcursor").checked){
- url=updateURLParameter(url, "nocursor", "1");
+ url=updateURLParameter(url, "nocursor", "");
} else {
url=updateURLParameter(url, "nocursor", false);
}
@@ -254,12 +267,20 @@ function modURL(ele=false){
if (ele.id =="stereo"){
if (document.getElementById("stereo").checked){
- url=updateURLParameter(url, "stereo", "1");
+ url=updateURLParameter(url, "stereo", "");
alert('Audio bitrate increased to 256kbps.\n\nPlease note: the Video Publisher must also have the stereo flag enabled for stereo to work.');
} else {
url=updateURLParameter(url, "stereo", false);
}
}
+
+ if (ele.id =="buffer"){
+ if (document.getElementById("buffer").checked){
+ url=updateURLParameter(url, "buffer", "");
+ } else {
+ url=updateURLParameter(url, "buffer", false);
+ }
+ }
}
}
diff --git a/index.html b/index.html
index 6d6f07f..278770c 100644
--- a/index.html
+++ b/index.html
@@ -2,24 +2,49 @@
-
+
+
+
+
+
+OBS.Ninja
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
@@ -27,7 +52,8 @@
OBS.Ninja
-
+
+
Copy this URL into an OBS "Browser Source" =>
@@ -46,17 +72,22 @@
+
+
+
+
+
-
+
Add Group Chat to OBS
@@ -72,11 +103,9 @@
Added Notes:
Anyone can enter a room if they know the name, so keep it unique
-
Having more than four (4) people in a room is not advisable due to performance reasons, but it depends on your hardware.
-
iOS devices are limited to group sizes of no more than two (2) people. This is a hardware limitation.
-
The "Recording" option is new and is considered experimental.
-
You must "Add" a video feed to the "Group Scene" for it to appear there.
-
There is a new "enhanced fullscreen" button added to the Guest's view.
+
Invite only guests to the room you trust.
+
iOS devices will share just their audio with other guests; this is mainly a hardware limitation
+
The "Recording" option is considered experimental.
+
diff --git a/main.css b/main.css
index e434369..4c4b874 100644
--- a/main.css
+++ b/main.css
@@ -50,7 +50,10 @@ a:active {
}
.gowebcam {
font-size:110%;
- padding:10px;border:3px solid #CCC; cursor:pointer; background-color:#FFF
+ padding:10px;
+ border:3px solid #DDDDDD;
+ cursor:pointer;
+ background-color:#DDDDDD;
}
.pressed {
@@ -122,6 +125,7 @@ hr {
grid-auto-columns: minmax(100px,500px);
grid-auto-rows: minmax(100px, 300px);
display:block ! important;
+ overflow-y: auto !important;
}
.directorsgrid video {
@@ -453,19 +457,7 @@ button {
box-shadow: 2px 2px 3px #999;
z-index:10;
}
-.float2{
- position:fixed;
- width:60px;
- height:60px;
- bottom:80px;
- right:132px;
- background-color:#15B;
- color:#FFF;
- border-radius:50px;
- text-align:center;
- box-shadow: 2px 2px 3px #999;
- z-index:10;
-}
+
.float3{
position:fixed;
width:60px;
@@ -479,7 +471,45 @@ button {
box-shadow: 2px 2px 3px #999;
z-index:10;
}
-
+.float4{
+ position:fixed;
+ width:60px;
+ height:60px;
+ bottom:80px;
+ right:132px;
+ background-color:#399;
+ color:#FFF;
+ border-radius:50px;
+ text-align:center;
+ box-shadow: 2px 2px 3px #999;
+ z-index:10;
+}
+.float5{
+ position:fixed;
+ width:60px;
+ height:60px;
+ bottom:80px;
+ right:132px;
+ background-color:#C23;
+ color:#FFF;
+ border-radius:50px;
+ text-align:center;
+ box-shadow: 2px 2px 3px #999;
+ z-index:10;
+}
+.float2{
+ position:fixed;
+ width:60px;
+ height:60px;
+ bottom:80px;
+ right:212px;
+ background-color:#15B;
+ color:#FFF;
+ border-radius:50px;
+ text-align:center;
+ box-shadow: 2px 2px 3px #999;
+ z-index:10;
+}
.my-float{
margin-top:7px;
@@ -682,7 +712,7 @@ video {
div.multiselect {
background-color:#FFF;
- max-width:320px;
+ max-width:550px;
white-space: nowrap;
overflow:hidden;
min-width:100px;
diff --git a/main.js b/main.js
index b0bf590..cf0f01d 100644
--- a/main.js
+++ b/main.js
@@ -1,2100 +1,9 @@
/*
* 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.
-*
+* This file is part of OBS.Ninja, yet is not intended to be modified.
+* This file cannot be modified without the express permission of its author.
+* No warranty, explicit or implicit, provided.
+*
*/
-
-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 = WebRTC.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')){ // 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 = true;
-}
-
-if ((urlParams.has('streamid')) || (urlParams.has('view'))){ // 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'); // this value can be comma seperated for multiple streams to pull
-}
-
-if (urlParams.has('remote')){
- log("remote ENABLED");
- session.remote = parseInt(urlParams.get('remote'));
-}
-
-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('ln')){ // checking if manual lanuage override enabled
- try {
- fetch("./translations/"+urlParams.get('ln')+'.json').then(function(response){
- if (response.status !== 200) {
- console.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]);
- ele.innerHTML = data[ele.dataset.translate];
- });
- });
- }).catch(function(err){
- errorlog(err);
- });
-
- } catch (error){
- errorlog(error);
- }
-} else { // check if automatic language translation is available
-
- if (window.navigator.language.slice(0, 2) !== 'en'){
- fetch("./translations/"+window.navigator.language.slice(0, 2)+'.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('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('sink')){
- session.sink = urlParams.get('sink');
-}
-
-
-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);
-
- var turn = {};
- turn.username = "steve";
- turn.credential = "justtesting";
- turn.urls = ["turn:turn2.obs.ninja:443"]; // main 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(){
- 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 (document.getElementById("qos")){
- 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 = 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 (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"];
- //}
-
- if (session.view){ // if they want to watch specific streams only; perhaps from a list even
- 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");
-
- 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 = "
- Invites users to join the group and broadcast their feed to it. These users will see every feed, so a limit of 4 is recommended.
";
-
- 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 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.
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].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);
- }
- }
- });
- });
- } 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";
- 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 (session.view){
- var streamlist = session.view.split(",");
- for (var 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 = '
Anyone can enter a room if they know the name, so keep it unique
\n\t\t\t\t
Having more than four (4) people in a room is not advisable due to performance reasons, but it depends on your hardware.
\n\t\t\t\t
iOS devices will have their video only be visible to the director. This is a hardware limitation.
\n\t\t\t\t
The \"Recording\" option is new and is considered experimental.
\n\t\t\t\t
You must \"Add\" a video feed to the \"Group Scene\" for it to appear there.
\n\t\t\t\t
There is a new \"enhanced fullscreen\" button added to the Guest's view.
\n\t\t\t\t",
+ "advanced-paramaters": "Advanced Parameters",
+ "audio-sources": "Audio Sources",
+ "back": "Back",
+ "balanced": "Balanced",
+ "copy-this-url": "Sharable Link to this video",
+ "copy-to-clipboard": "Copy to Clipboard",
+ "create-reusable-invite": "Create Reusable Invite",
+ "enable-stereo-and-pro": "Enable Stereo and Pro HD Audio",
+ "enter-the-rooms-control": "Enter the Room's Control Center",
+ "force-vp9-video-codec": "Force VP9 Video Codec (less artifacting)",
+ "generate-invite-link": "GENERATE THE INVITE LINK",
+ "here-you-can-pre-generate": "Here you can pre-generate a reusable view link and a related guest invite link.",
+ "high-security-mode": "High Security Mode",
+ "info-blob": "",
+ "joining-room": "You are joining room",
+ "logo-header": "OBS Ninja",
+ "max-resolution": "Max Resolution",
+ "mute": "Mute",
+ "no-audio": "No Audio",
+ "note-share-audio": "\n\t\t\t\t\tnote: Do not forget to click \"Share audio\" in Chrome. (Firefox does not support audio sharing.)",
+ "open-in-new-tab": "Open in new Tab",
+ "record": "Record",
+ "remote-control-for-obs": "Remote Control",
+ "remote-screenshare-obs": "Remote Screenshare",
+ "room-name": "Room Name",
+ "rooms-allow-for": "Rooms allow for simplified group-chat and the advanced management of multiple streams at once.",
+ "select-audio-source": "Select Audio Sources",
+ "select-audio-video": "Select the audio/video source below",
+ "select-screen-to-share": "SELECT SCREEN TO SHARE",
+ "show-tips": "Show me some tips..",
+ "smooth-cool": "Smooth and Cool",
+ "unlock-video-bitrate": "Unlock Video Bitrate (20mbps)",
+ "video-source": "Video source",
+ "volume": "Volume",
+ "you-are-in-the-control-center": "You are in the room's control center"
+}
diff --git a/translations/en.json b/translations/en.json
index 390b6f8..dd0a607 100644
--- a/translations/en.json
+++ b/translations/en.json
@@ -17,7 +17,7 @@
"generate-invite-link": "GENERATE THE INVITE LINK",
"here-you-can-pre-generate": "Here you can pre-generate a reusable Browser Source link and a related guest invite link.",
"high-security-mode": "High Security Mode",
- "info-blob": "\n\t\t\t\t\t\t
What is OBS.Ninja
\n\t\t\t\t\t\t
100% free; no downloads; no personal data collection; no sign-in
\n\t\t\t\t\t\t
Bring video from your smartphone, laptop, computer, or from your friends directly into your OBS video stream
\n\t\t\t\t\t\t
We use cutting edge Peer-to-Peer forwarding technology that offers privacy and ultra-low latency
MacOS users need to use OBS v23 or resort to Window Capturing a Chrome Browser with OBS v25
\n\t\t\t\t\t\t
Some users will have \"pixelation\" problems with videos. Please add the URL parameter &codec=vp9 to the OBS Links to correct it.
\n\t\t\t\t\t\t \n\t\t\t\t\t\t\n\t\t\t\t\t\tSite last updated: May 7th, 2020. The previous version can be found at https://obs.ninja/v3/ if you are having new issues.\n\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
Check out the sub-reddit for help and advanced info. I'm also on Discord and you can email me at steve@seguin.email
\n\t\t\t\t\t",
+ "info-blob": "\n\t\t\t\t\t\t
What is OBS.Ninja
\n\t\t\t\t\t\t
100% free; no downloads; no personal data collection; no sign-in
\n\t\t\t\t\t\t
Bring video from your smartphone, laptop, computer, or from your friends directly into your OBS video stream
\n\t\t\t\t\t\t
We use cutting edge Peer-to-Peer forwarding technology that offers privacy and ultra-low latency