just syncing up a bit of mid-dev code

THIS IS NOT FOR RELEASE YET
This commit is contained in:
Steve Seguin 2021-03-16 08:50:27 -04:00 committed by GitHub
parent f886e485db
commit 791aaf497e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1104 additions and 167 deletions

172
chat.html Normal file
View File

@ -0,0 +1,172 @@
<html>
<head>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OBSN Chat Overlay</title>
<style>
@font-face {
font-family: 'Cousine';
src: url('fonts/Cousine-Bold.ttf') format('truetype');
}
body {
margin:0;
padding:0 10px;
height:100%;
border: 0;
display: flex;
flex-direction: column-reverse;
position:absolute;
bottom:0;
overflow:hidden;
max-width:100%;
}
ul {
margin:0;
background-color: #0000;
color: white;
font-family: Cousine, monospace;
font-size: 3.2em;
line-height: 1.1em;
letter-spacing: 0.0em;
text-transform: uppercase;
padding: 0em;
text-shadow: 0.05em 0.05em 0px rgba(0,0,0,1);
max-width:100%;
}
ul li {
background-color: black;
padding: 8px 8px 0px 8px;
margin:0;
word-wrap: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
hyphens: auto;
max-width:100%;
}
a {
color:white;
font-size:1.2em;
text-transform: none;
word-wrap: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
hyphens: auto;
}
</style>
<script>
(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);
function loadIframe() {
var iframe = document.createElement("iframe");
var view= "";
if (urlParams.has("view")) {
view = "&view="+(urlParams.get("view") || "");
}
var room="";
if (urlParams.has("room")) {
room = "&room="+urlParams.get("room");
}
var password="";
if (urlParams.has("password")) {
password = "&password="+urlParams.get("password");
}
iframe.allow = "autoplay";
var srcString = "./?novideo&noaudio&label=chatOverlay&scene"+room+view+password;
iframe.src = srcString;
iframe.style.width="0";
iframe.style.height="0";
iframe.style.border="0";
document.body.appendChild(iframe);
//////////// LISTEN FOR EVENTS
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("gotChat" in e.data){
logData(e.data.gotChat.label,e.data.gotChat.msg);
}
});
}
function printValues(obj) {
var out = "";
for (var key in obj) {
if (typeof obj[key] === "object") {
out += "<br />";
out += printValues(obj[key]);
} else {
if (key.startsWith("_")) {
} else {
out += "<b>" + key + "</b>: " + obj[key] + "<br />";
}
}
}
return out;
}
function logData(type, data) {
var log = document.body.getElementsByTagName("ul")[0];
var entry = document.createElement('li');
if (type){
type = "<i>"+type+"</i>";
}
entry.innerHTML = type + data;
//setTimeout(function(entry){ // hide message after 60 seconds
// entry.innerHTML="";
// entry.remove();
// },60000,entry);
log.appendChild(entry);
}
</script>
</head>
<body onload="loadIframe();">
<ul></ul>
</body>
</html>

View File

@ -162,7 +162,7 @@
onclick="submitDebugLog();"
style="cursor: pointer; visibility: hidden; display:none;z-index:7;"
>
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 46px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-bug" aria-hidden="true"></i>
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 55px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-bug" aria-hidden="true"></i>
</span>
<span
id="helpbutton"
@ -171,10 +171,13 @@
style="cursor: pointer; display:none;"
alt="How to Use This with OBS"
>
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 24px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-question-circle" aria-hidden="true"></i>
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 33px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-question-circle" aria-hidden="true"></i>
</span>
<span title="Language Options" onclick="toggle(document.getElementById('languages'));" id="translateButton">
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 2px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-language" aria-hidden="true"></i>
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 10px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-language" aria-hidden="true"></i>
</span>
<span title="Add to Calendar" onclick="toggle(document.getElementById('calendar'));" id="calendarButton">
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 33px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-calendar" aria-hidden="true"></i>
</span>
<div id="mainmenu" class="row" style="opacity: 0; align: center;">
<div id="container-1" title="Add Group Chat to OBS" alt="Add Group Chat to OBS" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="column columnfade pointer card" style=" overflow-y: auto;">
@ -662,8 +665,12 @@
<li>
Chrome on Android 11 has an issue with the browser freezing at times. To unfreeze it, background the browser and then foreground it again.
</li>
<li>
A list of less common issues can <a href="https://github.com/steveseguin/obsninja/wiki/Known-Issues-(browser-bugs-and-more)">be found here</a>.
</li>
<br />
Site Updated: <a href="https://github.com/steveseguin/obsninja/wiki/v16.4-update-notes">March 3rd, 2021</a> (v16.5). The previous version can be found at <a href="https://obs.ninja/v16/">https://obs.ninja/v16/</a> if you are having issues with this minor update.
Site Updated: <a href="https://github.com/steveseguin/obsninja/wiki/v16.4-update-notes">March 9th, 2021</a> (v16.5). The previous version can be found at <a href="https://obs.ninja/v16/">https://obs.ninja/v16/</a> if you are having issues with this minor update.
<br />
<br />
@ -796,6 +803,12 @@
<span class="slider"></span>
</label>
Show display names
<Br />
<label class="switch" title="Display Names will be shown in the bottom-left corner of videos">
<input type="checkbox" data-param="&activespeaker" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Show active speakers
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch" title="Request 1080p60 from the Guest instead of 720p60, if possible">
@ -820,9 +833,21 @@
<input type="checkbox" data-param="&ns" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Hide Settings Button
Hide settings button
<Br />
<label class="switch" title="The guest won't have access to changing camera settings or screenshare">
<input type="checkbox" data-param="&mini" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Mini self-preview
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch" title="Increase video quality that guests in room see.">
<input type="checkbox" data-param="&trb=2000" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<font class="tooltip" style='cursor: help;position:relative;bottom:2px;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;'><span class="tooltiptext">Only use with powerful computers and small groups!!</span></font> Guests see HD video
<Br />
<label class="switch" title="The guest will not see their own self-preview after joining">
<input type="checkbox" data-param="&np" onchange="updateLink(1,this);">
<span class="slider"></span>
@ -940,7 +965,7 @@
<span data-translate="send-direct-chat"><i class="las la-envelope"></i> Message</span>
</button>
<button data-action-type="addToScene" style="grid-column: 1;" data-value="0" title="Add this Video to any remote '&scene=1'" onclick="directEnable(this, event, 1);">
<button data-action-type="addToScene" data-scene="1" style="grid-column: 1;" title="Add this Video to any remote '&scene=1'" onclick="directEnable(this, event, 1);">
<i class="las la-plus-square"></i>
<span data-translate="add-to-scene">add to scene</span>
</button>
@ -949,14 +974,23 @@
<span data-translate="mute-scene" >mute in scene</span>
</button>
<button class="" data-action-type="solo-chat" title="Toggle Solo Voice Chat" onclick="session.toggleSoloChat(this.dataset.UUID);">
<span data-translate="voice-chat"><i class="las la-microphone"></i> Solo Talk</span>
</button>
<button data-action-type="hangup" data-value="0" title="Force the user to Disconnect. They can always reconnect." onclick="directHangup(this, event);">
<i class="las la-sign-out-alt"></i>
<span data-translate="disconnect-guest" >Hangup</span>
</button>
<span id="sceneGroup1" style="display:none">
<button style="width: 35.2px" data-action-type="add-scene-2" title="Add to Scene 2" onclick="directEnable(this, event, 2);">
<button style="width: 35.2px" data-scene="2" data-action-type="add-scene-2" title="Add to Scene 2" onclick="directEnable(this, event, 2);">
<span >S2</span>
</button>
<button style="width:35.2px;" data-action-type="add-scene-3" title="Add to Scene 3" onclick="directEnable(this, event, 3);">
<button style="width:35.2px;" data-scene="3" data-action-type="add-scene-3" title="Add to Scene 3" onclick="directEnable(this, event, 3);">
<span >S3</span>
</button>
<button style="width: 35.2px" data-action-type="add-scene-4" title="Add to Scene 4" onclick="directEnable(this, event, 4);">
<button style="width: 35.2px" data-scene="4" data-action-type="add-scene-4" title="Add to Scene 4" onclick="directEnable(this, event, 4);">
<span >S4</span>
</button>
</span>
@ -966,20 +1000,17 @@
<input data-action-type="volume" type="range" min="0" max="200" value="100" title="Remotely change the volume of this guest" oninput="remoteVolumeUI(this)" onclick="remoteVolume(this);" style="grid-column: 2; margin:5px; width: 93%; position: relative;top: 0.6em; background-color:#fff0;"/><span class="tooltiptext" style='float: right; overflow: auto; left: 40px; width: 2.5em; top: -13px; margin: 0; position:relative;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus,Code2000, Code2001, Code2002, Musica, serif, LastResort;' >100</span>
</font>
<span id="sceneGroup2" style="display:none">
<button style="width: 35.2px" data-action-type="add-scene-5" title="Add to Scene 5" onclick="directEnable(this, event, 5);">
<button style="width: 35.2px" data-scene="5" data-action-type="add-scene-5" title="Add to Scene 5" onclick="directEnable(this, event, 5);">
<span >S5</span>
</button>
<button style="width: 35.2px" data-action-type="add-scene-6" title="Add to Scene 6" onclick="directEnable(this, event, 6);">
<button style="width: 35.2px" data-scene="6" data-action-type="add-scene-6" title="Add to Scene 6" onclick="directEnable(this, event, 6);">
<span >S6</span>
</button>
<button style="width: 35.2px" data-action-type="add-scene-7" title="Add to Scene 7" onclick="directEnable(this, event, 7);">
<button style="width: 35.2px" data-scene="7" data-action-type="add-scene-7" title="Add to Scene 7" onclick="directEnable(this, event, 7);">
<span >S7</span>
</button>
</span>
<button data-action-type="mute-guest" style="grid-column: 2;" title="Mute this guest everywhere" onclick="remoteMute(this, event);">
<i class="las la-microphone-slash"></i>
<span data-translate="mute-guest" >mute guest</span>
</button>
<span>
<button style="width: 35.2px" data-action-type="change-quality1" title="Disable Video Preview" onclick="toggleQualityDirector(0, this.dataset.UUID, this);">
@ -992,36 +1023,17 @@
<span data-translate="change-to-high-quality">&nbsp;&nbsp;<i class="las la-binoculars"></i></span>
</button>
</span>
<button data-action-type="mute-guest" style="grid-column: 1;" title="Mute this guest everywhere" onclick="remoteMute(this, event);">
<i class="las la-microphone-slash"></i>
<span data-translate="mute-guest" >mute guest</span>
</button>
<button data-action-type="hide-guest" style="grid-column: 2;" title="Hide this guest everywhere" onclick="remoteMuteVideo(this, event);">
<i class="las la-video-slash"></i>
<span data-translate="hide-guest" >hide guest</span>
</button>
<button data-action-type="hangup" data-value="0" title="Force the user to Disconnect. They can always reconnect." onclick="directHangup(this, event);">
<i class="las la-sign-out-alt"></i>
<span data-translate="disconnect-guest" >Hangup</span>
</button>
<button data-action-type="recorder-local" title="Start Recording this remote stream to this local drive. *experimental*'" onclick="recordVideo(this, event)">
<i class="las la-circle"></i>
<span data-translate="record-local"> Record Local</span>
</button>
<button data-action-type="recorder-remote" data-value="0" title="The Remote Guest will record their local stream to their local drive. *experimental*" onclick="requestVideoRecord(this)">
<i class="las la-circle"></i>
<span data-translate="record-remote"> Record Remote</span>
</button>
<button class="" data-action-type="solo-chat" title="Toggle Solo Voice Chat" onclick="session.toggleSoloChat(this.dataset.UUID);">
<span data-translate="voice-chat"><i class="las la-microphone"></i> Solo Talk</span>
</button>
<span>
<button style="width:34px;" data-action-type="order-down" title="Shift this Video Down in Order" onclick="changeOrder(-1,this.dataset.UUID);">
<span data-translate="order-down"><i class="las la-minus"></i></span>
</button>
<span class="orderspan">
<div style="text-align: center;font-size: 150%;" data-action-type="order-value" title="Current Index Order of this Video" >0</div>
Mix Order
</span>
<button style="width:34px;margin-left:0;" data-action-type="order-up" title="Shift this Video Up in Order" onclick="changeOrder(1,this.dataset.UUID);">
<span data-translate="order-up"><i class="las la-plus"></i></span>
</button>
</span>
<span id="channelGroup1" style="display:none">
<button style="width: 35.2px" data-action-type="add-channel" class="pressed" title="Set to Default Audio Channel" onclick="changeChannelOffset(this.dataset.UUID, false);">
@ -1035,10 +1047,6 @@
</button>
</span>
<button data-action-type="toggle-remote-speaker" title="Toggle the remote guest's speaker output" onclick="remoteSpeakerMute(this, event);">
<i class="las la-volume-off"></i> <span data-translate="toggle-remote-speaker">Deafen Guest</span>
</button>
<span id="channelGroup2" style="display:none" >
<button style="width: 35.2px" data-action-type="add-channel" title="Set to Audio Channel 3" onclick="changeChannelOffset(this.dataset.UUID, 2);">
<span >C3</span>
@ -1051,6 +1059,12 @@
</button>
</span>
<button data-action-type="toggle-remote-speaker" style="grid-column: 1;" title="Toggle the remote guest's speaker output" onclick="remoteSpeakerMute(this, event);">
<i class="las la-volume-off"></i> <span data-translate="toggle-remote-speaker">Deafen Guest</span>
</button>
<button data-action-type="toggle-remote-display" style="grid-column: 2;" title="Toggle the remote guest's display output" onclick="remoteDisplayMute(this, event);">
<i class="las la-eye-slash"></i> <span data-translate="toggle-remote-display">Blind Guest</span>
</button>
@ -1073,6 +1087,40 @@
<span data-translate="force-keyframe">Rainbow Puke</span>
</button>
<button data-action-type="solo-video" data-value="0" title="Solo this video everywhere" onclick="requestInfocus(this);">
<i class="las la-user"></i>
<span data-translate="solo-video">Highlight guest</span>
</button>
<button data-action-type="recorder-local" title="Start Recording this remote stream to this local drive. *experimental*'" onclick="recordVideo(this, event)">
<i class="las la-circle"></i>
<span data-translate="record-local"> Record Local</span>
</button>
<button data-action-type="recorder-remote" data-value="0" title="The Remote Guest will record their local stream to their local drive. *experimental*" onclick="requestVideoRecord(this)">
<i class="las la-circle"></i>
<span data-translate="record-remote"> Record Remote</span>
</button>
<span>
<button style="width:34px;" data-action-type="order-down" title="Shift this Video Down in Order" onclick="changeOrder(-1,this.dataset.UUID);">
<span data-translate="order-down"><i class="las la-minus"></i></span>
</button>
<span class="orderspan">
<div style="text-align: center;font-size: 150%;" data-action-type="order-value" title="Current Index Order of this Video" >0</div>
Mix Order
</span>
<button style="width:34px;margin-left:0;" data-action-type="order-up" title="Shift this Video Up in Order" onclick="changeOrder(1,this.dataset.UUID);">
<span data-translate="order-up"><i class="las la-plus"></i></span>
</button>
</span>
<button data-action-type="stats-remote" data-value="0" title="Request the statistics of this video in any active scene" onclick="session.sendRequest({'requestStats':'true', }, this.dataset.UUID);">
<i class="las la-info-circle"></i>
<span data-translate="stats-remote"> Scene Stats</span>
</button>
<button class="" data-action-type="advanced-audio-settings" data-active="false" style="grid-column: 1;" title="Remote Audio Settings" onclick="requestAudioSettings(this);">
<span data-translate="advanced-audio-settings"><i class="las la-sliders-h"></i> Audio Settings</span>
</button>
@ -1223,7 +1271,20 @@
<a href="https://github.com/steveseguin/obsninja/tree/master/translations" target="_blank" rel="noopener noreferrer" data-translate='add-more-here'>Add More Here!</a>
<br />
</div>
<div id="calendar" class="popup-message" style="display: none; right: 0; padding-left:5px; bottom: 25px; position: absolute;">
<b data-translate='add-to-calendar'>Add details to your Calendar:</b>
<br />
<u>
<br />
<a onclick="addToGoogleCalendar();" style="cursor: pointer;">Add to Google Calendar</a>
<br />
<a onclick="addToOutlookCalendar();" style="cursor: pointer;">Add to Outlook Calendar</a>
<br />
<a onclick="addToYahooCalendar();" style="cursor: pointer;">Add to Yahoo Calendar</a>
<br />
<br />
</u>
</div>
<script>
if (window.location.hostname.indexOf("www.obs.ninja") == 0) {
@ -1231,7 +1292,7 @@
}
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
session.version = "16.4";
session.version = "17.beta";
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
session.defaultPassword = "someEncryptionKey123"; // Disabling improves compatibility and is helpful for debugging.
@ -1243,13 +1304,11 @@
// ],
// sdpSemantics: 'unified-plan'
// };
// var turn = {};
// turn.username = "steve";
// turn.credential = "justtesting";
// turn.urls = ["turn:turn.obs.ninja:443"]; // US CENTRAL
// session.configuration.iceServers.push(turn);
// turn = {};
// turn.username = "steve";
// turn.credential = "justtesting";
@ -1258,8 +1317,15 @@
// session.configuration.iceTransportPolicy = "relay"; // uncomment to enable "&privacy" and force the TURN server
// session.wss = "wss://wss14.obs.ninja:443"; //false; // uses default handshake wss
///// Different endpoints are available; each isolated from each other.
// session.wss = "wss://wss13.obs.ninja:443"; // US-East (Default)
// session.wss = "wss://apibackup.obs.ninja:443"; // US-West
// session.wss = "wss://jp1wss.obs.ninja:443"; // Japan
// session.wss = "wss://au1wss.obs.ninja:443"; // Australia
// session.wss = "wss://de1wss.obs.ninja:443"; // Germany
// session.wss = "wss://insecure.cam:444"; // China
///// The following lets you set the defaults
// session.webcamonly // true,false
@ -1296,7 +1362,7 @@
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
<script type="text/javascript" crossorigin="anonymous" id="mixer-js" src="./mixer.js?ver=2"></script>
-->
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=174"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=175"></script>
<script type="text/javascript">
setTimeout(function(){ // lazy load
var script = document.createElement('script');

View File

@ -1063,7 +1063,7 @@ input[type=range]:focus::-ms-fill-upper {
width: 505px;
right: -400px;
overflow: auto;
z-index: 1;
z-index: 3;
}
h2 {
@ -1315,6 +1315,11 @@ img {
position: relative !important;
top: 50% !important;
}
#calendarButton {
cursor: pointer;
z-index: 6;
display:none;
}
#translateButton {
cursor: pointer;
z-index: 6;
@ -1567,8 +1572,8 @@ video.clean::-webkit-media-controls-timeline-container {
display: none;
align-text: center;
position: absolute;
z-index: 10 !important;
padding: 3px 0 !important;
z-index: 21 !important;
padding: 3px !important;
min-width: 180px !important;
background-color: #fff !important;
border: solid 1px #dfdfdf !important;
@ -2425,6 +2430,7 @@ input:checked + .slider:before {
z-index:2;
width:400px;
max-width:90%;
overflow-wrap: break-word;
}
.alertModalInner {

506
main.js
View File

@ -137,22 +137,40 @@ function warnUser(message){
// Allows for multiple alerts to stack better.
// Every modal and backdrop has an increasing z-index
// to block the previous modal
if (document.getElementById("alertModalBackdrop")){
getById("alertModal").innerHTML = ''; // Delete modal
getById("alertModal").remove();
getById("alertModalBackdrop").innerHTML = ''; // Delete modal
getById("alertModalBackdrop").remove();
}
zindex = document.querySelectorAll('.alertModal').length;
message = message.replace(/\n/g,"<br />");
modalTemplate =
`<div class="alertModal" onclick="closeModal(this)" style="z-index:${zindex + 2}">
`<div class="alertModal" id="alertModal" style="z-index:${zindex + 2}">
<div class="alertModalInner">
<span class='alertModalClose'>×</span>
<span class='alertModalClose' onclick="closeModal()">×</span>
<span class='alertModalMessage'>${message}</span>
</div>
</div>
<div class="alertModalBackdrop" style="z-index:${zindex + 1}"></div>`;
<div class="alertModalBackdrop" id="alertModalBackdrop" style="z-index:${zindex + 1}"></div>`;
document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end
document.getElementById("alertModalBackdrop").addEventListener("click", closeModal);
getById("alertModal").addEventListener("click", function(e) {
e.stopPropagation();
return false;
});
}
function closeModal(element){
element.nextElementSibling.outerHTML = ''; // Delete backdrop
element.outerHTML = ''; // Delete modal
function closeModal(){
getById("alertModalBackdrop").innerHTML = ''; // Delete modal
getById("alertModalBackdrop").remove();
getById("alertModal").innerHTML = ''; // Delete modal
getById("alertModal").remove();
}
var filename = false;
@ -664,6 +682,7 @@ if (urlParams.has('screenshareid') || urlParams.has('ssid')) {
}
}
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
//session.webcamonly = true;
getById("shareScreenGear").style.display = "none";
@ -934,7 +953,30 @@ if (session.webcamonly == true) {
}, 100);
}
getById("main").classList.remove('hidden');
if (urlParams.has('css')){
var cssURL = urlParams.get('css');
cssURL = decodeURI(cssURL);
log(cssURL);
var cssStylesheet = document.createElement('link');
cssStylesheet.rel = 'stylesheet';
cssStylesheet.type = 'text/css';
cssStylesheet.media = 'screen';
cssStylesheet.href = cssURL;
document.getElementsByTagName('head')[0].appendChild(cssStylesheet);
cssStylesheet.onload = function() {
getById("main").classList.remove('hidden');
log("loaded remote style sheet");
}
cssStylesheet.onerror = function() {
getById("main").classList.remove('hidden');
errorlog("REMOTE STYLE SHEET HAD ERROR");
}
} else {
getById("main").classList.remove('hidden');
}
if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) {
session.password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p');
@ -1235,6 +1277,10 @@ if (urlParams.has('nopreview') || urlParams.has('np')) {
} else if ((urlParams.has('preview')) || (urlParams.has('showpreview'))) {
log("preview ON");
session.nopreview = false;
} else if ((urlParams.has('minipreview')) || (urlParams.has('mini'))) {
log("preview ON");
session.nopreview = false;
session.minipreview = true;
}
if (urlParams.has('obsfix')) {
@ -1263,7 +1309,7 @@ if (urlParams.has('controlroombitrate') || urlParams.has('crb')) {
if (urlParams.has('remote') || urlParams.has('rem')) {
log("remote ENABLED");
session.remote = urlParams.get('remote') || urlParams.get('rem');
session.remote = urlParams.get('remote') || urlParams.get('rem') || "nosecurity";
session.remote = session.remote.trim();
}
@ -2113,6 +2159,13 @@ if (urlParams.has('effects') || urlParams.has('effect')) {
// green = 4
}
if (urlParams.has('activespeaker') || urlParams.has('speakerview')){
session.activeSpeaker = true;
setInterval(function(){activeSpeaker(false)},100);
} else {
setInterval(function(){activeSpeaker(true)},100);
}
if (urlParams.has('style') || urlParams.has('st')) {
session.style = urlParams.get('style') || urlParams.get('st') || 1;
@ -2148,6 +2201,13 @@ if (urlParams.has('noaudioprocessing') || urlParams.has('noap')) {
session.audioMeterGuest = false;
}
if (urlParams.has('tcp')){ // forces the TURN servers to use TCP mode; still need to add &private to force TURN also tho
session.forceTcpMode = true;
}
if (urlParams.has('speedtest')){ // forces essentially UDP mode, unless TCP is specified, and some other stuff
session.speedtest = true;
}
if (urlParams.has('turn')) {
var turnstring = urlParams.get('turn');
if (turnstring == "twilio") {
@ -2206,6 +2266,8 @@ if (urlParams.has('turn')) {
errorlog(e);
}
}
} else {
chooseBestTURN(); // obs.ninja turn servers, if needed.
}
@ -2221,7 +2283,7 @@ if (urlParams.has('privacy') || urlParams.has('private') || urlParams.has('relay
errorlog(e);
}
if (urlParams.has('speedtest')){
if (session.speedtest){
if (session.maxvideobitrate !== false) {
if (session.maxvideobitrate > 6000) {
session.maxvideobitrate = 6000; // Please feel free to get rid of this if using your own TURN servers...
@ -2432,7 +2494,7 @@ if (isIFrame) { // reduce CPU load if not needed.
if (stat.kind == "video") {
if ("qualityLimitationReason" in stat) {
session.pcs[UUID].stats.quality_Limitation_Reason = stat.qualityLimitationReason;
session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason;
}
if ("framesPerSecond" in stat) {
session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond;
@ -2478,6 +2540,10 @@ if (isIFrame) { // reduce CPU load if not needed.
}, "*");
}, 1000);
}
if ("getRemoteStats" in e.data) {
session.sendRequest({"requestStats":true, "remote":session.remote});
}
if ("getLoudness" in e.data) {
log("GOT LOUDNESS REQUEST");
@ -2607,13 +2673,13 @@ eventer(messageEvent, function(e) { // this listens for child IFRAMES.
function requestKeyframeScene(ele) {
var UUID = ele.dataset.UUID;
if (ele.dataset.active == "true") {
if (ele.dataset.value == 1) {
} else {
ele.dataset.active = "true";
ele.dataset.value = 1;
ele.classList.add("pressed");
session.requestKeyframe(UUID, true);
setTimeout(function(el){
el.dataset.active = "false";
el.dataset.value = 0;
el.classList.remove("pressed");
}, 1000, ele)
}
@ -2694,23 +2760,23 @@ function setupCanvas() {
if (session.canvas === null) {
warnlog("SETUP CANVAS");
session.canvas = document.createElement("canvas");
session.canvas.width = 1280;
session.canvas.height = 720;
session.canvas.width = 512;
session.canvas.height = 288;
session.canvasCtx = session.canvas.getContext('2d');
//session.canvasCtx.width=1280;
//session.canvasCtx.width=288;
//session.canvasCtx.height=720;
session.canvasCtx.fillStyle = "blue";
session.canvasCtx.fillRect(0, 0, 1280, 720);
session.canvasCtx.fillRect(0, 0, 512, 288);
session.canvasSource = document.createElement("video");
session.canvasSource.width=1280;
session.canvasSource.height=720;
session.canvasSource.width=512;
session.canvasSource.height=288;
session.canvasSource.autoplay = true;
session.canvasSource.srcObject = new MediaStream();
}
}
function applyEffects(track, stream) {
setupCanvas();
if (session.effects == 1) {
@ -2810,10 +2876,11 @@ function applyEffects(track, stream) {
session.streamSrc.addTrack(trk);
});
session.videoElement.srcObject = session.streamSrc;
session.canvasSource.requestVideoFrameCallback(draw2CanvasGreen);
session.canvasSource.requestVideoFrameCallback(segmentFilterGreen);
session.videoElement.srcObject = session.streamSrc;
warnlog("APPLY EFFECTS DONE");
} else if (session.effects == 6){
@ -2906,8 +2973,8 @@ async function segmentFilterBlur(now, metadata) { // runs at like 15fps
active2=true;
try {
if (net){
session.canvasSource.width = metadata.width;
session.canvasSource.height = metadata.height;
session.canvasCtx.width = session.canvasSource.srcObject.getSettings().width
session.canvasCtx.height = session.canvasSource.srcObject.getSettings().height;
mask = await net.segmentPerson(session.canvasSource);
}
} catch (e){
@ -2921,6 +2988,10 @@ function draw2CanvasGreen(now, metadata) { // runs fast; maybe like 30fps
active1=true;
try {
if (mask) {
session.canvasCtx.width = session.canvasSource.srcObject.getSettings().width
session.canvasCtx.height = session.canvasSource.srcObject.getSettings().height;
//session.canvasCtx.width = metadata.width;
//session.canvasCtx.height = metadata.height;
bodyPix.drawMask(session.canvas, session.canvasSource, mask, 1, 0, false);
}
} catch (e){
@ -2933,8 +3004,8 @@ async function segmentFilterGreen(now, metadata) { // runs at like 15fps
if (active2){return;}
active2=true;
try {
session.canvasSource.width = metadata.width;
session.canvasSource.height = metadata.height;
//session.canvasCtx.width = metadata.width;
//session.canvasCtx.height = metadata.height;
var segment = await net.segmentPerson(session.canvasSource);
mask = bodyPix.toMask(segment, {r: 0, g: 0, b: 0, a: 0}, {r: 0, g: 255, b: 0, a: 255});
} catch (e){
@ -3349,6 +3420,11 @@ function printValues(obj) { // see: printViewStats
value = sanitizeChat((value));
}
// <i class='las la-copy' data-sid='" + streamID + "' onmousedown='copyFunction(this.dataset.sid)' onclick='popupMessage(event);copyFunction(this.dataset.sid)' title='Copy this Stream ID to the clipboard' style='cursor:pointer'></i>
if (key == 'useragent') {
value = "<span style='cursor: pointer;' onmousedown='copyFunction(this.innerText)' onclick='popupMessage(event);copyFunction(this.innerText);' title='Copy this user-agent to the clipboard' style='cursor:pointer'>"+value+"</span>"
}
if (key == 'Bitrate_in_kbps') {
var unit = " kbps";
stat = "Bitrate";
@ -3547,6 +3623,7 @@ function updateLocalStats(){
stats.forEach(stat => {
if (stat.type == "outbound-rtp") {
if (stat.kind == "video") {
if ("qualityLimitationReason" in stat) {
session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason;
}
@ -3557,7 +3634,7 @@ function updateLocalStats(){
session.pcs[UUID].stats.video_encoder = stat.encoderImplementation;
}
if ("bytesSent" in stat) {
if (session.pcs[UUID].stats._bytesSent){
if ("_bytesSent" in session.pcs[UUID].stats){
if (session.pcs[UUID].stats._timestamp){
if (stat.timestamp){
session.pcs[UUID].stats.video_bitrate_kbps = parseInt(8*(stat.bytesSent - session.pcs[UUID].stats._bytesSent)/(stat.timestamp - session.pcs[UUID].stats._timestamp));
@ -3565,26 +3642,52 @@ function updateLocalStats(){
}
}
}
if ("timestamp" in stat) {
session.pcs[UUID].stats._timestamp = stat.timestamp;
if ("nackCount" in stat) {
if ("_nackCount" in session.pcs[UUID].stats){
if (session.pcs[UUID].stats._timestamp){
if (stat.timestamp){
session.pcs[UUID].stats.nacks_per_second = parseInt(10000*(stat.nackCount - session.pcs[UUID].stats._nackCount)/(stat.timestamp - session.pcs[UUID].stats._timestamp))/10;
}
}
}
}
if ("retransmittedBytesSent" in stat) {
if ("_retransmittedBytesSent" in session.pcs[UUID].stats){
if (session.pcs[UUID].stats._timestamp){
if (stat.timestamp){
session.pcs[UUID].stats.retransmitted_kbps = parseInt(8*(stat.retransmittedBytesSent - session.pcs[UUID].stats._retransmittedBytesSent)/(stat.timestamp - session.pcs[UUID].stats._timestamp));
}
}
}
}
if ("bytesSent" in stat) {
session.pcs[UUID].stats._bytesSent = stat.bytesSent;
}
if ("retransmittedBytesSent" in stat) {
session.pcs[UUID].stats.retransmitted_bytes_sent = stat.retransmittedBytesSent;
if ("nackCount" in stat) {
session.pcs[UUID].stats._nackCount = stat.nackCount;
}
if ("retransmittedBytesSent" in stat) {
session.pcs[UUID].stats._retransmittedBytesSent = stat.retransmittedBytesSent;
}
if ("timestamp" in stat) {
session.pcs[UUID].stats._timestamp = stat.timestamp;
}
if ("pliCount" in stat) {
session.pcs[UUID].stats.total_pli_count = stat.pliCount;
}
if ("keyFramesEncoded" in stat) {
session.pcs[UUID].stats.total_key_frames_encoded = stat.keyFramesEncoded;
}
if ("nackCount" in stat) {
session.pcs[UUID].stats.total_nack_ount = stat.nackCount;
}
} else if (stat.kind == "audio") {
if ("bytesSent" in stat) {
@ -4049,7 +4152,7 @@ function hangup() { // TODO: I need to have this be MUTE, toggle, with volume no
function hangup2() {
session.hangupDirector();
getById("miniPerformer").innerHTML = "";
getById("press2talk").dataset.enabled = "false";
getById("press2talk").dataset.value = 0;
getById("screensharebutton").classList.add("advanced");
getById("settingsbutton").classList.add("advanced");
getById("mutebutton").classList.add("advanced");
@ -4215,15 +4318,15 @@ function directHangup(ele, event) { // everyone in the room will hangup this gue
function directEnable(ele, event, scene=1) { // A directing room only is controlled by the Director, with the exception of MUTE.
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.enable == 1) {
ele.dataset.enable = 0;
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
if (ele.children[1]){
ele.children[1].innerHTML = "Add to Scene";
getById("container_" + ele.dataset.UUID).style.backgroundColor = null;
}
} else {
ele.dataset.enable = 1;
ele.dataset.value = 1;
ele.className = "pressed";
if (ele.children[1]){
ele.children[1].innerHTML = "Remove";
@ -4236,7 +4339,7 @@ function directEnable(ele, event, scene=1) { // A directing room only is control
//msg.roomid = session.roomid;
msg.scene = scene;
msg.action = "display";
msg.value = ele.dataset.enable;
msg.value = ele.dataset.value;
msg.target = ele.dataset.UUID;
//session.anysend(msg);
@ -4247,12 +4350,12 @@ function directEnable(ele, event, scene=1) { // A directing room only is control
function directMute(ele, event) { // A directing room only is controlled by the Director, with the exception of MUTE.
log("mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.mute == 0) {
ele.dataset.mute = 1;
if (ele.dataset.value == 0) {
ele.dataset.value = 1;
ele.className = "";
ele.children[1].innerHTML = "Mute in scene";
} else {
ele.dataset.mute = 0;
ele.dataset.value = 0;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-mute";
}
@ -4262,7 +4365,7 @@ function directMute(ele, event) { // A directing room only is controlled by the
//msg.roomid = session.roomid;
msg.scene = "1";
msg.action = "mute";
msg.value = ele.dataset.mute;
msg.value = ele.dataset.value;
msg.target = ele.dataset.UUID;
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
@ -4270,22 +4373,22 @@ function directMute(ele, event) { // A directing room only is controlled by the
function remoteSpeakerMute(ele, event) {
log("speaker mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.mute == 1) {
ele.dataset.mute = 0;
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "deafen guest";
} else {
ele.dataset.mute = 1;
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-deafen";
}
}
var msg = {};
if (ele.dataset.mute == 0) {
msg.speakerMute = false;
if (ele.dataset.value == 0) {
msg.speakerMute = ele.dataset.value;
} else {
msg.speakerMute = true;
msg.speakerMute = ele.dataset.value;
}
msg.UUID = ele.dataset.UUID;
session.sendRequest(msg, ele.dataset.UUID);
@ -4295,7 +4398,7 @@ function updateRemoteSpeakerMute(UUID) {
var ele = document.querySelectorAll('[data-action-type="toggle-remote-speaker"][data--u-u-i-d="' + UUID + '"]');
if (ele[0]) {
ele[0].classList.add("pressed");
ele[0].dataset.mute = 1;
ele[0].dataset.value = 1;
ele[0].className = "pressed";
ele[0].children[1].innerHTML = "Un-deafen";
}
@ -4305,7 +4408,7 @@ function updateRemoteDisplayMute(UUID) {
var ele = document.querySelectorAll('[data-action-type="toggle-remote-display"][data--u-u-i-d="' + UUID + '"]');
if (ele[0]) {
ele[0].classList.add("pressed");
ele[0].dataset.mute = 1;
ele[0].dataset.value = 1;
ele[0].className = "pressed";
ele[0].children[1].innerHTML = "Un-blind";
}
@ -4314,19 +4417,19 @@ function updateRemoteDisplayMute(UUID) {
function remoteDisplayMute(ele, event) {
log("display mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.mute == 1) {
ele.dataset.mute = 0;
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "blind guest";
} else {
ele.dataset.mute = 1;
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-blind";
}
}
var msg = {};
if (ele.dataset.mute == 0) {
if (ele.dataset.value == 0) {
msg.displayMute = false;
} else {
msg.displayMute = true;
@ -4346,12 +4449,12 @@ function remoteLowerhands(UUID) {
function remoteMute(ele, event) {
log("mute");
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.mute == 1) {
ele.dataset.mute = 0;
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "mute guest";
} else {
ele.dataset.mute = 1;
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Un-mute guest";
}
@ -4366,7 +4469,7 @@ function remoteMute(ele, event) {
}
var msg = {};
if (ele.dataset.mute == 0) {
if (ele.dataset.value == 0) {
msg.volume = volume;
} else {
msg.volume = 0;
@ -4375,6 +4478,57 @@ function remoteMute(ele, event) {
session.sendRequest(msg, ele.dataset.UUID);
}
function remoteMuteVideo(ele, event) {
log("video mute");
if ((event.ctrlKey) || (event.metaKey)) {
ele.children[1].innerHTML = "ARMED";
ele.style.backgroundColor = "#BF3F3F";
Callbacks.push([remoteMuteVideo, ele, false]);
log("video queued");
return;
} else {
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.className = "";
ele.children[1].innerHTML = "hide guest";
} else {
ele.dataset.value = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Unhide guest";
}
ele.style.backgroundColor = null;
}
var msg = {};
if (ele.dataset.value == 0) {
msg.directVideoMuted = false;
} else {
msg.directVideoMuted = true;
}
for (var i in session.pcs){
if (i === msg.target){
msg.target = true;
} else {
msg.target = ele.dataset.UUID;
}
try{
session.pcs[i].sendChannel.send(JSON.stringify(msg));
} catch(e){}
}
}
function updateDirectorVideoMute(UUID) {
var ele = document.querySelectorAll('[data-action-type="hide-guest"][data--u-u-i-d="' + UUID + '"]');
if (ele[0]) {
ele[0].dataset.value = 1;
ele[0].className = "pressed";
ele[0].children[1].innerHTML = "Unhide guest";
}
}
function directVolume(ele) { // A directing room only is controlled by the Director, with the exception of MUTE.
log("volume");
var msg = {};
@ -5062,39 +5216,59 @@ function audioLimiter(mediaStreamSource, audioContext) {
return compressor;
}
function activeSpeaker() {
function activeSpeaker(border=false) {
var lastActiveSpeaker = null;
var changed = false;
for (var UUID in session.rpcs) {
if (session.rpcs[UUID].stats.Audio_Loudness_average) {
session.rpcs[UUID].stats.Audio_Loudness_average = parseFloat(session.rpcs[UUID].stats.Audio_Loudness + session.rpcs[UUID].stats.Audio_Loudness_average) / 2;
if (session.rpcs[UUID].stats._Audio_Loudness_average) {
session.rpcs[UUID].stats._Audio_Loudness_average = parseFloat(session.rpcs[UUID].stats.Audio_Loudness*0.2 + session.rpcs[UUID].stats._Audio_Loudness_average*0.8);
} else {
session.rpcs[UUID].stats.Audio_Loudness_average = 1;
session.rpcs[UUID].stats._Audio_Loudness_average = 1;
}
log(session.rpcs[UUID].stats.Audio_Loudness_average);
if (session.rpcs[UUID].stats.Audio_Loudness_average > 10) {
if (session.rpcs[UUID].videoElement.style.display != "block") {
session.rpcs[UUID].videoElement.style.display = "block";
changed = true;
}
} else {
if (session.rpcs[UUID].videoElement) {
if (session.rpcs[UUID].videoElement.style.display != "none") {
changed = true;
session.rpcs[UUID].videoElement.style.display = "none";
//log(session.rpcs[UUID].stats._Audio_Loudness_average);
if (session.rpcs[UUID].stats._Audio_Loudness_average > 13) {
if (border) {
if (session.rpcs[UUID].videoElement) {
session.rpcs[UUID].videoElement.style.border = "green solid 1px";
session.rpcs[UUID].videoElement.style.padding = "0";
}
} else {
session.rpcs[UUID].videoElement.style.display = "none";
} else if (!session.rpcs[UUID].activelySpeaking){
session.rpcs[UUID].activelySpeaking = true;
changed = true;
lastActiveSpeaker = UUID;
session.rpcs[UUID].stats._Audio_Loudness_average+=2000000;
}
} else if (session.rpcs[UUID].stats._Audio_Loudness_average > 6) {
} else {
if (border){
if (session.rpcs[UUID].videoElement) {
session.rpcs[UUID].videoElement.style.border = "";
session.rpcs[UUID].videoElement.style.padding = "1px";
}
} else if (session.rpcs[UUID].activelySpeaking) {
changed = true;
session.rpcs[UUID].activelySpeaking=false;
lastActiveSpeaker = UUID;
}
}
}
if (changed) {
updateMixer();
var speaker = false;
for (var UUID in session.rpcs) {
if (session.rpcs[UUID].activelySpeaking){speaker=true;}
}
if (!speaker && lastActiveSpeaker && (session.nopreview || session.minipreview)){
session.rpcs[lastActiveSpeaker].activelySpeaking=true;
} else if (changed) {
setTimeout(function(){updateMixer();},1);
}
}
//setInterval(function(){activeSpeaker()},1000);
function randomizeArray(unshuffled) {
@ -5387,6 +5561,8 @@ function createRoomCallback(passAdd, passAdd2) {
getById("director_block_3").dataset.raw = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2;
getById("director_block_3").href = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2;
getById("director_block_3").innerText = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2;
getById("calendarButton").style.display = "inline-block";
} else {
@ -5465,15 +5641,84 @@ function handleRoomSelect(room) {
}
}
function getDirectorSettings(scene){
var settings = {};
var eles = document.querySelectorAll('[data-action-type="solo-video"]');
settings.soloVideo = false;
for (var i=0;i<eles.length;i++) {
if (parseInt(eles[i].dataset.value)==1){
warnlog(eles[i]);
if (eles[i].dataset.UUID){
settings.soloVideo = eles[i].dataset.UUID;
}
}
}
if (scene){
var eles = document.querySelectorAll('[data-action-type="addToScene"]');
settings.scene = {};
for (var i=0;i<eles.length;i++) {
if (parseInt(eles[i].dataset.value)==1){
if (parseInt(eles[i].dataset.scene) == scene){
if (eles[i].dataset.UUID){
var msg = {};
//msg.request = "sendroom";
//msg.roomid = session.roomid;
msg.scene = scene;
msg.action = "display";
msg.value = eles[i].dataset.value;
msg.target = eles[i].dataset.UUID;
settings.scene[eles[i].dataset.UUID]=msg;
}
}
}
}
}
return settings
}
function requestInfocus(ele) {
var UUID = ele.dataset.UUID;
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.classList.remove("pressed");
var actionMsg = {};
actionMsg.infocus = false;
session.sendMessage(actionMsg);
} else {
var actionMsg = {};
for (var i in session.pcs){
if (i === UUID){
actionMsg.infocus = true;
} else {
actionMsg.infocus = UUID;
}
try {
session.pcs[i].sendChannel.send(JSON.stringify(actionMsg));
} catch(e){}
}
var eles = document.querySelectorAll('[data-action-type="solo-video"]');
for (var i=0;i<eles.length;i++) {
log(eles);
eles[i].classList.remove("pressed");
eles[i].dataset.value = 0;
}
ele.dataset.value = 1;
ele.classList.add("pressed");
}
}
function requestAudioSettings(ele) {
var UUID = ele.dataset.UUID;
if (ele.dataset.active == "true") {
ele.dataset.active = "false";
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.classList.remove("pressed");
getById("advanced_audio_director_" + UUID).innerHTML = "";
getById("advanced_audio_director_" + UUID).className = "advanced";
} else {
ele.dataset.active = "true";
ele.dataset.value = 1;
ele.classList.add("pressed");
getById("advanced_audio_director_" + UUID).innerHTML = "";
var actionMsg = {};
@ -5484,13 +5729,13 @@ function requestAudioSettings(ele) {
function requestVideoSettings(ele) {
var UUID = ele.dataset.UUID;
if (ele.dataset.active == "true") {
ele.dataset.active = "false";
if (ele.dataset.value == 1) {
ele.dataset.value = 0;
ele.classList.remove("pressed");
getById("advanced_video_director_" + UUID).innerHTML = "";
getById("advanced_video_director_" + UUID).className = "advanced";
} else {
ele.dataset.active = "true";
ele.dataset.value = 1;
ele.classList.add("pressed");
getById("advanced_video_director_" + UUID).innerHTML = "";
var actionMsg = {};
@ -5579,7 +5824,7 @@ function createDirectorCam(vid, clean) {
getById("press2talk").outerHTML = "";
getById("miniPerformer").appendChild(vid);
getById("press2talk").dataset.enabled = "true";
getById("press2talk").dataset.value = 1;
session.muted = false;
toggleMute(true);
getById("screensharebutton").classList.remove("advanced");
@ -5608,6 +5853,45 @@ function press2talk(clean = false) {
}
function addToGoogleCalendar(){
var title = "Live Stream";
//var dates = "20180512T230000Z/20180513T030000Z";
var linkout = getById("director_block_1").innerText;
var details = "Join the live stream as a performer at the following link:<br/><br/>===> "+linkout+"<br/><br/>To test your connection and camera ahead of time, please visit https://obs.ninja/speedtest<br/><br/>Do not share the details of this invite with others, unless explicitly told to.";
details = details.split(' ').join('+');
details = details.split('&').join('%26');
var linkToOpen = "https://calendar.google.com/calendar/r/eventedit?text="+title+"&details="+details;
//https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134
window.open(linkToOpen);
}
function addToOutlookCalendar(){
var title = "Live Stream";
var linkout = getById("director_block_1").innerText;
var details = "Join the live stream as a performer at the following link:<br/><br/>===> "+linkout+"<br/><br/>To test your connection and camera ahead of time, please visit https://obs.ninja/speedtest<br/><br/>Do not share the details of this invite with others, unless explicitly told to.";
details = details.split(' ').join('%20');
details = details.split('&').join('%26');
var linkToOpen = "https://outlook.live.com/owa/?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&subject="+title+"&body="+details;
//https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134
window.open(linkToOpen);
}
function addToYahooCalendar(){
var title = "Live Stream";
var linkout = getById("director_block_1").innerText;
var details = "Join the live stream as a performer at the following link:<br/><br/>===> "+linkout+"<br/><br/>To test your connection and camera ahead of time, please visit https://obs.ninja/speedtest<br/><br/>Do not share the details of this invite with others, unless explicitly told to.";
details = details.split(' ').join('%20');
details = details.split('&').join('%26');
var linkToOpen = "https://calendar.yahoo.com?v60&title="+title+"&desc="+details;
//https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134
window.open(linkToOpen);
}
function toggle(ele, tog = false, inline = true) {
var x = ele;
@ -7880,8 +8164,9 @@ async function grabVideo(quality = 0, eleName = 'previewWebcam', selector = "sel
}
if (eleName == "previewWebcam") {
session.videoElement.controls = true;
} else {
updateConstraintSliders();
}
updateConstraintSliders();
dragElement(session.videoElement);
}, 1000); // focus
@ -9335,7 +9620,7 @@ function listAudioSettings() {
input.onchange = function(e) {
getById("label_" + e.target.dataset.keyname).innerHTML = e.target.dataset.keyname + ": " + e.target.value;
//updateAudioConstraints(e.target.dataset.keyname, e.target.value);
applyAudioHack(track0, e.target.dataset.keyname, e.target.value);
applyAudioHack(e.target.dataset.keyname, e.target.value);
};
@ -9390,7 +9675,7 @@ function listAudioSettings() {
input.onchange = function(e) {
//getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
//updateAudioConstraints(e.target.dataset.keyname, e.target.value);
applyAudioHack(track0, e.target.dataset.keyname, e.target.value);
applyAudioHack(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints_audio").appendChild(div);
@ -9425,7 +9710,7 @@ function listAudioSettings() {
input.onchange = function(e) {
//getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
//updateAudioConstraints(e.target.dataset.keyname, e.target.value);
applyAudioHack(track0, e.target.dataset.keyname, e.target.value);
applyAudioHack(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints_audio").appendChild(div);
@ -9440,7 +9725,7 @@ function listAudioSettings() {
}
function applyAudioHack(track, constraint, value = null) {
function applyAudioHack(constraint, value = null) {
if (value == parseFloat(value)) {
value = parseFloat(value);
value = {
@ -9452,7 +9737,34 @@ function applyAudioHack(track, constraint, value = null) {
value = false;
}
log(constraint);
var new_constraints = Object.assign(track.getSettings(), {
////////////////
try {
var track0 = session.streamSrc.getAudioTracks();
if (track0.length) {
track0 = track0[0];
if (track0.getCapabilities) {
session.audioConstraints = track0.getCapabilities();
}
log(session.audioConstraints);
} else {
warnlog("session.streamSrc contains no audio tracks");
return;
}
} catch (e) {
warnlog("session.streamSrc contains no audio tracks");
errorlog(e);
return;
}
try {
if (track0.getSettings) {
session.currentAudioConstraints = track0.getSettings();
}
} catch (e) {
errorlog(e);
}
////////
var new_constraints = Object.assign(track0.getSettings(), {
[constraint]: value
}, );
new_constraints = {
@ -9984,6 +10296,7 @@ function createIframePopup() {
iframe.style.height = "100%";
iframe.style.overflow = "hidden";
iframe.id = "screensharesource";
iframe.style.zIndex = "-1";
session.screenShareElement = iframe;
@ -10885,7 +11198,7 @@ function sendChatMessage(chatMsg = false) { // filtered + visual
function toggleQualityDirector(bitrate, UUID, ele = null) { // ele is specific to the button in the director's room
var eles = ele.parentNode.childNodes;
for (i in eles) {
for (var i=0;i<eles.length;i++) {
eles[i].className = "";
}
ele.className = "pressed";
@ -12008,7 +12321,7 @@ function fftWaveform( source, UUID, trackid){ // append the delay Node to the t
session.rpcs[uuid].canvasCtx.moveTo(0, dataArray[0]*m);
for (var i = 1; i < bufferLength; i++){
var y = dataArray[i] * m;
session.rpcs[uuid].canvasCtx.lineTo(x, y);
session.rpcs[uuid].canvasCtx.lineTo(x, y);
x += sliceWidth;
}
session.rpcs[uuid].canvasCtx.lineTo(session.rpcs[uuid].canvas.width, session.rpcs[uuid].canvas.height / 2);
@ -12087,7 +12400,6 @@ function audioMeterGuest(mediaStreamSource, UUID, trackid){
}
if ((session.effects==3) || (session.effects==4) || (session.effects==5)){
var mask = null;
var script = document.createElement('script');

366
monitor.html Normal file
View File

@ -0,0 +1,366 @@
<html>
<head>
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css" />
<link rel="stylesheet" href="./speedtest.css?ver=2" />
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OBSN Monitoring</title>
<script>
function getChromeVersion() {
var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
return raw ? parseInt(raw[2], 10) : false;
}
if (!getChromeVersion()){
alert("This speedtest is optimized for Chromium-based browsers; graphs will not work for Firefox or Safari browsers.");
}
(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 quality_reason = "";
var video_encoder="";
var resolution="";
function copyFunction(copyText) {
try {
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
} catch (e) {
var dummy = document.createElement("input");
document.body.appendChild(dummy);
dummy.value = copyText;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
return false;
}
}
function loadIframe() {
var iframe = document.createElement("iframe");
if (urlParams.has("remote")) {
var remote = urlParams.get("remote");
} else {
var remote="";
}
if (urlParams.has("sid")) {
var streamID = urlParams.get("sid");
} else if (urlParams.has("view")) {
var streamID = urlParams.get("view");
} else {
return;
}
var room="";
if (urlParams.has("room")) {
room = "&room="+urlParams.get("room");
}
var password="";
if (urlParams.has("password")) {
password = "&password="+urlParams.get("password");
}
iframe.allow = "autoplay";
var srcString = "./?view=" + streamID + room + password + "&vd=0&ad=0&autostart&novideo&noaudio&remote="+remote;
iframe.src = srcString;
iframe.style.width="0";
iframe.style.height="0";
iframe.style.border="0";
document.body.appendChild(iframe);
//var button = document.createElement("button");
//button.innerHTML = "Disconnect";
//button.className = "red";
//button.style.display = "none";
//button.onclick = function () {
// iframe.contentWindow.postMessage({ close: true }, "*");
//};
//buttonContainer.appendChild(button);
//document.getElementById("container").appendChild(buttonContainer);
setInterval(function () {
iframe.contentWindow.postMessage({ getRemoteStats: true }, "*");
}, 3000);
var eventMethod = window.addEventListener
? "addEventListener"
: "attachEvent";
var eventer = window[eventMethod];
var messageEvent =
eventMethod === "attachEvent" ? "onmessage" : "message";
var previousResolution;
eventer(messageEvent, function (e) {
if ("remoteStats" in e.data) {
var out = "";
console.log(e.data);
for (var UUID in e.data.remoteStats) {
if (e.data.remoteStats[UUID].quality_limitation_reason){
if (quality_reason != e.data.remoteStats[UUID].quality_limitation_reason) {
quality_reason = e.data.remoteStats[UUID].quality_limitation_reason;
logData("Quality Limitation Reason:", quality_reason, UUID);
}
}
if (e.data.remoteStats[UUID].video_encoder){
if (video_encoder != e.data.remoteStats[UUID].video_encoder) {
video_encoder = e.data.remoteStats[UUID].video_encoder;
logData("Video encoder used:", video_encoder, UUID);
}
}
if (e.data.remoteStats[UUID].resolution){
if (resolution != e.data.remoteStats[UUID].resolution) {
resolution = e.data.remoteStats[UUID].resolution;
logData("Resolution:", resolution, UUID);
}
}
if (e.data.remoteStats[UUID].video_bitrate_kbps){
var video_bitrate_kbps = e.data.remoteStats[UUID].video_bitrate_kbps;
updateData("bitrate", video_bitrate_kbps, UUID);
} else if (document.getElementById(UUID)){
updateData("bitrate", 0, UUID);
}
if (e.data.remoteStats[UUID].nacks_per_second){
var nacks_per_second = e.data.remoteStats[UUID].nacks_per_second;
updateData("nackrate", nacks_per_second, UUID);
} else if (document.getElementById(UUID)){
updateData("nackrate", 0, UUID);
}
if (e.data.remoteStats[UUID].retransmitted_kbps){
var retransmitted_kbps = e.data.remoteStats[UUID].retransmitted_kbps;
updateData("retransmit", retransmitted_kbps, UUID);
} else if (document.getElementById(UUID)){
updateData("retransmit", 0, UUID);
}
//updateData("buffer", buffer);
//if (packetloss != undefined) {
// packetloss = packetloss.toFixed(2);
// updateData("packetloss", packetloss);
//}
}
}
});
}
function printValues(obj) {
var out = "";
for (var key in obj) {
if (typeof obj[key] === "object") {
out += "<br />";
out += printValues(obj[key]);
} else {
if (key.startsWith("_")) {
} else {
out += "<b>" + key + "</b>: " + obj[key] + "<br />";
}
}
}
return out;
}
function logData(type, data, UUID) {
try{
var log = document.getElementById(UUID).querySelectorAll('[data-log]')[0].getElementsByTagName("ul")[0];
var entry = document.createElement('li');
entry.style.color ="white";
entry.textContent = "[" + new Date().toLocaleTimeString() + "] " + type + " : " + data;
log.prepend(entry);
} catch(e){}
}
</script>
</head>
<body onload="loadIframe();">
<div id="container">
<h1>
OBS.Ninja Remote Monitor
</h1>
</div>
<div id="graphs" style="display:none">
<div data-log="true" onclick="copyFunction(this.innerText)" style="max-height:400px;display:inline-block;">
<ul></ul>
</div>
<div class="graph">
<h2>Bitrate (kbps)</h2>
<span>0</span>
<canvas data-bitrate-graph="true"></canvas>
</div>
<div class="graph">
<h2>Nacks (per second)</h2>
<span>0</span>
<canvas data-nackrate-graph="true"></canvas>
</div>
<div class="graph">
<h2>Retransmitted (kbps)</h2>
<span>0</span>
<canvas data-retransmit-graph="true"></canvas>
</div>
</div>
<script>
var bitrate = {
element: "bitrate-graph",
data: 0,
max: 6000,
target: 3000,
};
var frames;
var nackrate = {
element: "nackrate-graph",
data: 0,
max: 15,
target: 15,
};
var retransmit = {
element: "retransmit-graph",
data: 0,
max: 100,
target: 100,
};
function updateData(type, data, UUID) {
if (type == "bitrate") {
bitrate.data = data;
plotData("bitrate", bitrate, UUID);
}
if (type == "nackrate") {
nackrate.data = data;
plotData("nackrate", nackrate, UUID);
}
if (type == "retransmit") {
retransmit.data = data;
plotData("retransmit", retransmit, UUID);
}
}
function plotData(type, stat, UUID) {
var canvas;
var context;
var yScale;
console.log(type);
console.log(stat);
var container = document.getElementById(UUID);
if (container){
canvas = container.querySelectorAll('[data-'+stat.element+']')[0];
} else {
container = document.getElementById("graphs").cloneNode(true);
container.id = UUID;
container.className = "graphContainer";
container.style.display = "block";
document.getElementById("graphs").parentNode.insertBefore(container, document.getElementById("graphs").nextSibling);
canvas = container.querySelectorAll('[data-'+stat.element+']')[0];
}
context = canvas.getContext("2d");
if (isNaN(stat.data)) {
stat.data = 0;
}
var text = (canvas.previousElementSibling.innerHTML = stat.data);
var height = context.canvas.height;
var width = context.canvas.width;
var borderWidth = 5;
var offset = borderWidth * 2;
// Create gradient
var grd = context.createLinearGradient(0, 0, 0, height);
if (type == "bitrate") {
// Higher values are green
grd.addColorStop(0, "#33C433");
grd.addColorStop(0.7, "#F3F304");
grd.addColorStop(0.9, "#F30404");
} else {
// Higher values are red
grd.addColorStop(0, "#F30404");
grd.addColorStop(0.85, "#F3F304");
grd.addColorStop(0.95, "#33C433");
}
context.strokeStyle = "white";
context.fillStyle = grd;
//context.fillStyle = "#009933";
//context.imageSmoothingEnabled = true;
yScale = height / stat.target;
if (stat.data > stat.target) {
stat.data = stat.target;
}
if (type == "retransmit" && stat.data == 0.0) {
stat.data = 0.5;
}
if (type == "nackrate" && stat.data == 0.0) {
stat.data = 0.1;
}
var x = width - 1;
var y = height - stat.data * yScale;
var w = 1;
context.fillStyle = grd;
context.fillRect(x, y, w, height);
// shift everything to the left:
var imageData = context.getImageData(1, 0, width - 1, height);
context.putImageData(imageData, 0, 0);
// now clear the right-most pixels:
context.clearRect(width - 1, 0, 1, height);
}
</script>
</body>
</html>

View File

@ -116,7 +116,15 @@ canvas {
padding: 20px 0px;
border: 1px solid #383838;
text-align: center;
}
.graphContainer {
display: block;
margin-top: 20px;
background: #313131;
padding: 20px 0px;
border: 1px solid #383838;
text-align: center;
}
.graph {

View File

@ -59,13 +59,18 @@
// this is pretty important if you want to avoid camera permission popup problems. YOu need to load the iFRAME after you load the parent body. A quick solution is like: <body onload=>loadIframe();"> !!!
var streamID = "";
var possible =
"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789";
for (var i = 0; i < 7; i++) {
streamID += possible.charAt(
Math.floor(Math.random() * possible.length)
);
}
if (urlParams.has("sid")) {
streamID = urlParams.get("sid");
}
document.getElementById("remote").innerHTML = "<a style='color:#CCC' href='./monitor?sid="+streamID+"'>Remote Monitoring Link</a><br /><br /><br /><br />";
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("span");
@ -75,7 +80,7 @@
iframe.allowfullscreen ="true";
//iframe.allow = "autoplay";
var srcString = "./?push=" + streamID + "&cleanoutput&privacy&speedtest&webcam&audiodevice=0&fullscreen&transparent";
var srcString = "./?push=" + streamID + "&cleanoutput&privacy&speedtest&webcam&audiodevice=0&fullscreen&transparent&remote";
if (urlParams.has("turn")) {
srcString = srcString + "&turn=" + urlParams.get("turn");
@ -115,7 +120,7 @@
var iframeContainer = document.createElement("span");
iframe.allow = "autoplay";
var srcString = "./?view=" + streamID + "&cleanoutput&privacy&noaudio&scale=0";
var srcString = "./?view=" + streamID + "&cleanoutput&privacy&noaudio&speedtest&scale=0";
if (urlParams.has("turn")) {
srcString = srcString + "&turn=" + urlParams.get("turn");
@ -187,13 +192,12 @@
? "addEventListener"
: "attachEvent";
var eventer = window[eventMethod];
var messageEvent =
eventMethod === "attachEvent" ? "onmessage" : "message";
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
var previousResolution;
eventer(messageEvent, function (e) {
if ("action" in e.data) {
logData(e.data.action, e.data.value);
logData(e.data.action, e.data.value || "");
if (e.data.action == "new-view-connection") {
buttonContainer.querySelectorAll(
@ -230,9 +234,9 @@
}
for (var streamID in e.data.stats.outbound_stats) {
if (e.data.stats.outbound_stats[streamID].quality_Limitation_Reason){
if (quality_reason != e.data.stats.outbound_stats[streamID].quality_Limitation_Reason) {
quality_reason = e.data.stats.outbound_stats[streamID].quality_Limitation_Reason;
if (e.data.stats.outbound_stats[streamID].quality_limitation_reason){
if (quality_reason != e.data.stats.outbound_stats[streamID].quality_limitation_reason) {
quality_reason = e.data.stats.outbound_stats[streamID].quality_limitation_reason;
logData("Quality Limitation Reason:", quality_reason);
}
}
@ -357,7 +361,9 @@
Change the video bitrate by pressing the buttons below the video. It
should approach 6000-kbps if the network allows.
</li>
</ol>
<div id="remote"></div>
</div>
<script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long