nearing v20

lots of small features/improvements
This commit is contained in:
Steve Seguin 2021-11-12 22:37:47 -05:00 committed by GitHub
parent 3793bd6e4f
commit 8168911488
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1039 additions and 229 deletions

View File

@ -54,7 +54,7 @@
transition: opacity .1s linear;
}
</style>
<link rel="stylesheet" href="./main.css?ver=142" />
<link rel="stylesheet" href="./main.css?ver=145" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.min.js"></script>
<style id="lightbox-animations" type="text/css"></style>
</head>
@ -67,7 +67,7 @@
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=34"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=328"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=334"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<div id="header">
@ -375,7 +375,7 @@
<li>
<input type="checkbox" id="multiselect1" name="multiselect1" style="display: none;" checked value="ZZZ" />
<label for="multiselect1">
<span data-translate="no-audio">No Audio</span>
<span data-translate="no-audio"> No Audio</span>
</label>
</li>
</ul>
@ -751,7 +751,7 @@
<font style="color:#daad09;">Welcome to VDO Ninja! We've rebranded! Nothing else is changing and we're staying 100% free.</font>
</h4>
<br />
🎁 Site updated October 19th (v19.4). The <a href="https://docs.vdo.ninja/release-notes/v19">v19 release notes are here</a>. If new issues occur, the older v18 can be <a href="https://vdo.ninja/v183/">found here</a>.
🎁 Site updated November 4th (v19.5). The <a href="https://docs.vdo.ninja/release-notes/v19">v19 release notes are here</a>. If new issues occur, the older v18 can be <a href="https://vdo.ninja/v183/">found here</a>.
<br />
<br />
@ -896,7 +896,12 @@
<span class="slider"></span>
</label>
<span data-translate="show-active-speaker">Show active speakers</span>
<Br />
<label class="switch" title="Show a custom welcome message to the joining guest of this invite link">
<input type="checkbox" data-param="&welcome" onchange="updateLinkWelcome(1,this);">
<span class="slider"></span>
</label>
<span data-translate="show-welcome-message">Show welcome message</span>
</div>
<div style="display:inline-block;margin-top: 12px; position: relative; margin-right:10px;">
@ -1606,7 +1611,7 @@
<div id="chatModule" style="display:none;text-align:right">
<a target="popup" id="popOutChat" style="cursor:pointer;text-align:right;color:#B3C7F9;" onclick="createPopoutChat();"><i class="las la-external-link-alt"></i></a>
<div id="chatBody">
<div class="inMessage" data-translate='welcome-to-obs-ninja-chat'>
<div class="inMessage" id="welcomeMsg" data-translate='welcome-to-obs-ninja-chat'>
Welcome to VDO.Ninja! You can send text messages directly to connected peers from here.
</div>
</div>
@ -1713,7 +1718,10 @@
<div id="userList">
</div>
</div>
<div id="signalMeterTemplate" class="signal-meter" data-cpu="0" data-level="0">
<i class="las la-signal"></i>
<i class="las la-fire-alt"></i>
</div>
<div id="voiceMeterTemplate" class="video-meter">
</div>
<div id="voiceMeterTemplate2" class="video-meter2">
@ -1867,11 +1875,11 @@
// session.introOnClean = true; // this will load the page with the webcam selection screen if &push or &room is in the URL; no need to use &webcam.
</script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=206"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=228"></script>
<!--
// If you wish to change branding, blank offers a good clean start.
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
-->
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=278"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=281"></script>
</body>
</html>

444
lib.js
View File

@ -72,7 +72,8 @@ var miscTranslations = {
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?",
"enter-new-codirector-password": "Enter a co-director password to use",
"control-room-co-director": "Control Room: Co-Director"
"control-room-co-director": "Control Room: Co-Director",
"signal-meter": "Video packet loss indicator of video preview; green is good, red is bad. Flame implies CPU is overloaded. May not reflect the packet loss seen by scenes or other guests."
};
// function log(msg){ // uncomment to enable logging.
@ -2224,7 +2225,7 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
}
}
var sscount = false
var mpl = mediaPool.length;
if (mpl>1){
var BB = 0;
@ -2248,13 +2249,57 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
rw = NW;
rh = NH;
}
if (("screenshare" in mediaPool[NW-1]) && (mediaPool[NW-1].screenshare)){
sscount = mediaPool[NW-1].dataset.sid;
}
}
} else { var rw=1; var rh=1;}
var playarea = getById("gridlayout");
} catch(e){
errorlog(e);
sscount = false
}
var playarea = getById("gridlayout");
var customLayout=false;
if (sscount && !session.layout){
customLayout = {};
console.log(sscount);
if (mediaPool.length>=5){
customLayout[sscount] = {"x":0,"y":33.333,"w":66.667,"h":66.667, "c": session.cover};
} else {
customLayout[sscount] = {"x":0,"y":0,"w":66.667,"h":100, "c": session.cover};
}
var posCount = 0;
for (var i = 0; i<mediaPool.length; i++){
if (mediaPool[i].dataset.sid === sscount){continue;}
if (mediaPool.length==2){
customLayout[mediaPool[i].dataset.sid] = {"x":66.667,"y":33.333,"w":33.333,"h":33.333, "c":true};
} else if (mediaPool.length==3){
customLayout[mediaPool[i].dataset.sid] = {"x":66.667,"y":posCount*33.333+16.667,"w":33.333,"h":33.333, "c":true};
} else if (mediaPool.length==4){
customLayout[mediaPool[i].dataset.sid] = {"x":66.667,"y":posCount*33.333,"w":33.333,"h":33.333, "c":true};
} else if (mediaPool.length==5){
if (posCount==0){
customLayout[mediaPool[i].dataset.sid] = {"x":33.333,"y":0,"w":33.333,"h":33.333, "c":true};
} else {
customLayout[mediaPool[i].dataset.sid] = {"x":66.667,"y":(posCount-1)*33.333,"w":33.333,"h":33.333, "c":true};
}
} else if (mediaPool.length>=6){
if (posCount==0){
customLayout[mediaPool[i].dataset.sid] = {"x":0,"y":0,"w":33.333,"h":33.333, "c":true};
} else if (posCount==1){
customLayout[mediaPool[i].dataset.sid] = {"x":33.333,"y":0,"w":33.333,"h":33.333, "c":true};
} else {
customLayout[mediaPool[i].dataset.sid] = {"x":66.667,"y":(posCount-2)*33.333,"w":33.333,"h":33.333, "c":true};
}
} else {
customLayout[mediaPool[i].dataset.sid] = {"x":66.667,"y":posCount*33.333,"w":33.333,"h":33.333, "c":true};
}
posCount+=1;
}
}
@ -2495,9 +2540,45 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
} else {
vid.style.objectFit = "contain";
}
container.style.width="0";
container.style.height="0";
//container.style.width="0";
//container.style.height="0";
session.requestRateLimit(session.hiddenSceneViewBitrate, i, false); // it's added already, so we know it needs sound. But lets d
vid.style.width = "0px";
vid.style.height = "0px";
vid.style.top = "0px";
vid.style.left = "0px";
vid.isInvisible = true;
vid.alreadyAdded=false;
return;
}
} else if (typeof customLayout === "object"){
if (vid.dataset.sid in customLayout){
var left = (window.innerWidth/100*customLayout[vid.dataset.sid].x) || 0;
var top = (window.innerHeight/100*customLayout[vid.dataset.sid].y) || 0;
var width = (window.innerWidth/100*customLayout[vid.dataset.sid].w) || 0;
var height = (window.innerHeight/100*customLayout[vid.dataset.sid].h) || 0;
container.style.zIndex = customLayout[vid.dataset.sid].z || 0;
if (customLayout[vid.dataset.sid].c){
vid.style.objectFit = "cover";
} else {
vid.style.objectFit = "contain";
}
} else {
container.style.zIndex = 0;
if (session.cover){
vid.style.objectFit = "cover";
} else {
vid.style.objectFit = "contain";
}
//container.style.width="0";
//container.style.height="0";
session.requestRateLimit(session.hiddenSceneViewBitrate, i, false); // it's added already, so we know it needs sound. But lets d
vid.style.width = "0px";
vid.style.height = "0px";
vid.style.top = "0px";
vid.style.left = "0px";
vid.isInvisible = true;
vid.alreadyAdded=false;
return;
}
} else {
@ -2507,14 +2588,14 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
var width = Math.ceil(w/rw);
var height = Math.ceil(h/rh);
if (session.layout===null){ // if using layouts, layouts should never be false, but NULL to indicate auto mixing.
//if (session.layout===null){ // if using layouts, layouts should never be false, but NULL to indicate auto mixing.
container.style.zIndex = 0;
if (session.cover){
vid.style.objectFit = "cover";
} else {
vid.style.objectFit = "contain";
}
}
//}
}
@ -2621,8 +2702,6 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
container.style.display = "flex";
container.style.alignItems = "center";
var left = (window.innerWidth/100*session.layout[vid.dataset.sid].x) || 0;
var top = (window.innerHeight/100*session.layout[vid.dataset.sid].y) || 0;
var width = (window.innerWidth/100*session.layout[vid.dataset.sid].w) || 0;
@ -2648,6 +2727,39 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
session.requestRateLimit(session.hiddenSceneViewBitrate, i, false); // it's added already, so we know it needs sound. But lets d
return;
}
} else if (typeof customLayout === "object"){
if (vid.dataset.sid in customLayout){
var container = document.createElement("div");
container.style.position = "absolute";
container.style.display = "flex";
container.style.alignItems = "center";
var left = (window.innerWidth/100*customLayout[vid.dataset.sid].x) || 0;
var top = (window.innerHeight/100*customLayout[vid.dataset.sid].y) || 0;
var width = (window.innerWidth/100*customLayout[vid.dataset.sid].w) || 0;
var height = (window.innerHeight/100*customLayout[vid.dataset.sid].h) || 0;
container.style.left = left+"px";
container.style.top = top+"px";
container.style.width = width+"px";
container.style.height = height+"px";
container.style.zIndex = customLayout[vid.dataset.sid].z || 0;
if (customLayout[vid.dataset.sid].c){
vid.style.objectFit = "cover";
} else {
vid.style.objectFit = "contain";
}
} else {
if (session.cover){
vid.style.objectFit = "cover";
} else {
vid.style.objectFit = "contain";
}
session.requestRateLimit(session.hiddenSceneViewBitrate, i, false); // it's added already, so we know it needs sound. But lets d
return;
}
} else {
var container = document.createElement("div");
container.style.position = "absolute";
@ -2659,14 +2771,14 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
container.style.width = Math.ceil(w/rw)+"px";
container.style.height = Math.ceil(h/rh)+"px";
if (session.layout===null){
//if (session.layout===null){
container.style.zIndex = 0;
if (session.cover){
vid.style.objectFit = "cover";
} else {
vid.style.objectFit = "contain";
}
}
//}
}
try {
@ -2853,9 +2965,6 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
holder.style.top = 0;
}
if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID] && ("label" in session.rpcs[vid.dataset.UUID]) && (session.rpcs[vid.dataset.UUID].label !== false) && (session.showlabels===true)){ // remote source
var CCC = vid.parentNode;
@ -2938,6 +3047,19 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
}
}
if (session.signalMeter){
if (vid.dataset.UUID && !session.rpcs[vid.dataset.UUID].signalMeter){
session.rpcs[vid.dataset.UUID].signalMeter = getById("signalMeterTemplate").cloneNode(true);
session.rpcs[vid.dataset.UUID].signalMeter.style.display = "block";
session.rpcs[vid.dataset.UUID].signalMeter.id = "signalMeter_" + vid.dataset.UUID;
session.rpcs[vid.dataset.UUID].signalMeter.dataset.level = 0;
session.rpcs[vid.dataset.UUID].signalMeter.title = miscTranslations["signal-meter"];
holder.appendChild(session.rpcs[vid.dataset.UUID].signalMeter);
} else if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID].signalMeter){
holder.appendChild(session.rpcs[vid.dataset.UUID].signalMeter);
}
}
if (session.ruleOfThirds){
if (vid.id == "videosource"){
var svg = document.createElement("div");
@ -3273,6 +3395,7 @@ eventer(messageEvent, function(e) { // this listens for child IFRAMES.
} else if (e.data.action == "video-loaded") {
// TODO: if (e.source == session...iframeEle.contentWindow) {
warnlog(e);
toggleSpeakerMute(true);
updateMixer(); // harmless to let run.
}
}
@ -3336,7 +3459,7 @@ async function jumptoroom(event = null) {
var passStr = "";
window.focus();
var pass = await promptAlt("Enter a password if provided, otherwise just click cancel", false); //sanitizePassword(session.password);
var pass = await promptAlt("Enter a password if provided, otherwise just click cancel", false, true); //sanitizePassword(session.password);
if (pass && pass.length) {
session.password = sanitizePassword(pass);
passStr = "&password=" + session.password;
@ -5696,7 +5819,7 @@ async function directPageReload(ele, event) {
reloadURL = previousURL;
} else {
window.focus();
var reloadURL = await promptAlt(miscTranslations["transfer-guest-to-url"], false, value=previousURL);
var reloadURL = await promptAlt(miscTranslations["transfer-guest-to-url"], false, false, previousURL);
stillNeedURL = true;
if (reloadURL === null) { // user cancelled
ele.innerHTML = '<i class="las la-sync"></i> <span data-translate="change-url">change URL</span>';
@ -5733,7 +5856,7 @@ async function directTimer(ele, event=false) { // A directing room only is cont
var msg = {};
if (!event || (!((event.ctrlKey) || (event.metaKey)))) {
if (ele.dataset.value == 0 || ele.dataset.value == 2) {
var getTime = await promptAlt("Time in seconds to count down", false, value=parseInt(getById("overlayClockContainer").dataset.initial));
var getTime = await promptAlt("Time in seconds to count down", false, false, parseInt(getById("overlayClockContainer").dataset.initial));
if (!getTime){return;}
getById("overlayClockContainer").dataset.initial = parseInt(getTime) || 600;
ele.dataset.value = 1;
@ -6693,7 +6816,6 @@ session.publishIFrame = function(iframeURL){
container.style.backgroundColor = "#666";
}
getById("mainmenu").parentElement.removeChild(getById("mainmenu"));
getById("gridlayout").innerHTML = "";
container.appendChild(iframe);
@ -6701,10 +6823,10 @@ session.publishIFrame = function(iframeURL){
session.seeding=true;
getById("reshare").href = "https://"+location.host+location.pathname+"?view="+session.streamID;
getById("reshare").text = "https://"+location.host+location.pathname+"?view="+session.streamID;
getById("reshare").style.width = ((getById("reshare").text.length + 1)*1.15 * 8) + 'px';
updateReshareLink();
pokeIframeAPI('started-iframe-share');
session.seedStream();
@ -6721,7 +6843,7 @@ function outboundAudioPipeline(stream) {
});
return newStream;
}
try {
log("Web Audio");
var tracks = stream.getAudioTracks();
@ -7217,7 +7339,7 @@ function activeSpeaker(border=false) {
lastActiveSpeaker = UUID;
}
}
if ((session.rpcs[UUID].stats.Audio_Loudness > 15) || ((session.rpcs[UUID].stats.Audio_Loudness > 5) && (session.rpcs[UUID].stats._Audio_Loudness_average>5)) || (session.rpcs[UUID].stats._Audio_Loudness_average>30)){
if ((session.rpcs[UUID].stats.Audio_Loudness > 13) || ((session.rpcs[UUID].stats.Audio_Loudness > 5) && (session.rpcs[UUID].stats._Audio_Loudness_average>3)) || (session.rpcs[UUID].stats._Audio_Loudness_average>6)){
someoneElseIfSpeaking = true;
}
@ -7697,6 +7819,13 @@ function createRoomCallback(passAdd, passAdd2) {
session.director = true;
screensharesupport = false;
if (session.meterStyle ===false){
session.meterStyle = 1;
}
if (session.signalMeter===null){
session.signalMeter = true;
}
if (session.directorPassword){
getById("coDirectorEnable").checked = true;
getById("coDirectorEnableSpan").style.display = "none";
@ -8069,7 +8198,7 @@ async function createDirectorOnlyBox() {
oldlabel = "";
}
window.focus();
var newlabel = await promptAlt(miscTranslations["enter-new-display-name"], false, value=oldlabel);
var newlabel = await promptAlt(miscTranslations["enter-new-display-name"], false, false, oldlabel);
if (newlabel!==null){
if (newlabel == ""){
newlabel = false;
@ -8190,6 +8319,39 @@ function createControlBox(UUID, soloLink, streamID) {
container.innerHTML = buttons;
container.appendChild(videoContainer);
if (session.signalMeter){
if (!session.rpcs[UUID].signalMeter){
session.rpcs[UUID].signalMeter = getById("signalMeterTemplate").cloneNode(true);
session.rpcs[UUID].signalMeter.id = "signalMeter_" + UUID;
session.rpcs[UUID].signalMeter.dataset.level = 0;
session.rpcs[UUID].signalMeter.style.display = "block";
session.rpcs[UUID].signalMeter.dataset.UUID = UUID;
session.rpcs[UUID].signalMeter.title = miscTranslations["signal-meter"];
session.rpcs[UUID].signalMeter.addEventListener('click', function(e) { // show stats of video if double clicked
log("clicked signal meter");
try {
e.preventDefault();
var uid = e.currentTarget.dataset.UUID;
if ("stats" in session.rpcs[uid]){
var [menu, innerMenu] = statsMenuCreator();
printViewStats(innerMenu, uid );
menu.interval = setInterval(printViewStats,3000, innerMenu, uid);
}
e.stopPropagation();
return false;
} catch(e){errorlog(e);}
});
}
videoContainer.appendChild(session.rpcs[UUID].signalMeter);
}
videoContainer.appendChild(session.rpcs[UUID].voiceMeter);
videoContainer.appendChild(session.rpcs[UUID].remoteMuteElement);
videoContainer.appendChild(session.rpcs[UUID].remoteVideoMuteElement);
@ -8557,7 +8719,7 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
var label = document.createElement('label');
label.for = option.name;
label.innerHTML = '<span data-translate="no-audio">No Audio</span>';
label.innerHTML = '<span data-translate="no-audio"> No Audio</span>';
var listele = document.createElement('li');
listele.appendChild(option);
@ -8723,6 +8885,8 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
option.value = deviceInfo.deviceId || "default";
option.name = "multiselect" + counter;
option.id = "multiselect" + counter;
option.label = deviceInfo.label;
label = document.createElement('label');
label.for = option.name;
@ -8749,6 +8913,9 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) > -1) {} else {
SelectedAudioInputDevices.push(event.currentTarget.value);
}
if (session.mobile && (!(iOS || iPad)) && (event.currentTarget.label === "USB audio") && !session.cleanOutput){
warnUser("Notice: USB audio devices may not work on all mobile devices.\n\nConsider using FireFox mobile instead, as it tends to work with USB audio devices more often.");
}
}
});
}
@ -9046,7 +9213,7 @@ function addScreenDevices(device) {
}
activatedPreview = false;
grabAudio("videosource", "#audioSource3"); // exclude item.id
grabAudio("#audioSource3"); // exclude item.id
} else {
if (SelectedAudioInputDevices.indexOf(item.value) > -1) {} else {
@ -9055,7 +9222,7 @@ function addScreenDevices(device) {
item.checked = true;
activatedPreview = false;
grabAudio("videosource", "#audioSource3", item.value); // exclude item.id. we will reconnect, even if already connected, as a way to 'reset' a device if it isn't working.
grabAudio("#audioSource3", item.value); // exclude item.id. we will reconnect, even if already connected, as a way to 'reset' a device if it isn't working.
}
});
}
@ -9302,7 +9469,7 @@ function gotDevices2(deviceInfos) {
}
}
activatedPreview = false;
grabAudio("videosource", "#audioSource3", trackid); // exclude item.id.
grabAudio("#audioSource3", trackid); // exclude item.id.
event.stopPropagation();
return false;
};
@ -9455,7 +9622,7 @@ function gotDevices2(deviceInfos) {
log("Audio OPTION HAS CHANGED? 2");
activatedPreview = false;
setTimeout(function(){
grabAudio("videosource", "#audioSource3");
grabAudio("#audioSource3");
},10)
};
@ -10008,7 +10175,7 @@ function reconnectDevices(event) { /// TODO: Perhaps change this to only if the
// }
activatedPreview = false;
grabAudio("videosource", "#audioSource3");
grabAudio("#audioSource3");
setTimeout(function() {
enumerateDevices().then(gotDevices2).then(function() {});
}, 1000);
@ -10446,24 +10613,42 @@ if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { // this ena
const sources = await desktopCapturer.getSources({ types: ['screen', 'window'] });
const selectionElem = document.createElement('div');
selectionElem.classList = 'desktop-capturer-selection';
selectionElem.innerHTML = `
<div class="desktop-capturer-selection__scroller">
<ul class="desktop-capturer-selection__list">
${sources.map(({id, name, thumbnail, display_id, appIcon}) => `
<li class="desktop-capturer-selection__item">
<button class="desktop-capturer-click desktop-capturer-selection__btn" data-id="${id}" title="${name}">
<img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" />
<span class="desktop-capturer-selection__name">${name}</span>
</button>
</li>
`).join('')}
<div style="text-align: center;margin: auto 5px;font-size: 120%;"><i class="las la-music" style="font-size:40px;"></i><br />Include Desktop Audio<br /><input id="alsoCaptureAudio" style="width:20px;height:20px;margin-top: 10px;" type="checkbox" checked></div>
<button id="captureDesktopAudio" class="desktop-capturer-click" style="margin: 10px;"><i class="las la-music" style="font-size:40px;"></i><br />Capture ONLY<br />Desktop Audio</button>
<button id="cancelscreenshare" style="margin: 10px; background-color: #F88; width: 100px;"><i class="las la-window-close" style="font-size:40px;"></i><br />Cancel</button>
</ul>
</div>
`;
if (session.screenshareVideoOnly){
selectionElem.innerHTML = `
<div class="desktop-capturer-selection__scroller">
<ul class="desktop-capturer-selection__list">
${sources.map(({id, name, thumbnail, display_id, appIcon}) => `
<li class="desktop-capturer-selection__item">
<button class="desktop-capturer-click desktop-capturer-selection__btn" data-id="${id}" title="${name}">
<img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" />
<span class="desktop-capturer-selection__name">${name}</span>
</button>
</li>
`).join('')}
<button id="cancelscreenshare" style="margin: 10px; background-color: #F88; width: 100px;"><i class="las la-window-close" style="font-size:40px;"></i><br />Cancel</button>
</ul>
</div>
`;
} else {
selectionElem.innerHTML = `
<div class="desktop-capturer-selection__scroller">
<ul class="desktop-capturer-selection__list">
${sources.map(({id, name, thumbnail, display_id, appIcon}) => `
<li class="desktop-capturer-selection__item">
<button class="desktop-capturer-click desktop-capturer-selection__btn" data-id="${id}" title="${name}">
<img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" />
<span class="desktop-capturer-selection__name">${name}</span>
</button>
</li>
`).join('')}
<div style="text-align: center;margin: auto 5px;font-size: 120%;"><i class="las la-music" style="font-size:40px;"></i><br />Include Desktop Audio<br /><input id="alsoCaptureAudio" style="width:20px;height:20px;margin-top: 10px;" type="checkbox" checked></div>
<button id="captureDesktopAudio" class="desktop-capturer-click" style="margin: 10px;"><i class="las la-music" style="font-size:40px;"></i><br />Capture ONLY<br />Desktop Audio</button>
<button id="cancelscreenshare" style="margin: 10px; background-color: #F88; width: 100px;"><i class="las la-window-close" style="font-size:40px;"></i><br />Cancel</button>
</ul>
</div>
`;
}
document.body.appendChild(selectionElem);
document.getElementById('cancelscreenshare').addEventListener('click', async () => {
@ -10686,6 +10871,10 @@ async function grabScreen(quality = 0, audio = true, videoOnEnd = false) {
max: session.maxframerate
};
}
if (session.screenshareVideoOnly){
constraints.audio = false;
}
return navigator.mediaDevices.getDisplayMedia(constraints).then(function(stream) {
log("adding video tracks 2245");
@ -10863,7 +11052,7 @@ function changeAudioDevice(index){
audioSelect[i].checked = false;
}
audioSelect[index-1].checked = true;
grabAudio("videosource", "#audioSource3");
grabAudio("#audioSource3");
});
}
@ -10918,7 +11107,7 @@ function changeAudioDeviceById(deviceID, UUID=false){
}
}
activatedPreview=false;
grabAudio("videosource", "#audioSource3", callback=UUID);
grabAudio("#audioSource3", callback=UUID);
});
} else {
try {
@ -10940,7 +11129,7 @@ function changeAudioDeviceById(deviceID, UUID=false){
}
}
activatedPreview=false;
grabAudio("videosource", "#audioSource3", callback=UUID);
grabAudio("#audioSource3", callback=UUID);
});
}
}
@ -11489,7 +11678,7 @@ function updateRenderOutpipe(){ // video only.
}
async function grabAudio(eleName = "previewWebcam", selector = "#audioSource", trackid = null, override = false, callback = false) { // trackid is the excluded track
async function grabAudio(selector = "#audioSource", trackid = null, override = false, callback = false) { // trackid is the excluded track
if (activatedPreview == true) {
log("activated preview return 2");
return;
@ -11501,6 +11690,7 @@ async function grabAudio(eleName = "previewWebcam", selector = "#audioSource", t
try {
if (session.videoElement.srcObject) {
var audioSelect = document.querySelector(selector).querySelectorAll("input");
var audioExcludeList = [];
for (var i = 0; i < audioSelect.length; i++) {
try {
@ -12309,28 +12499,11 @@ session.publishStream = function(v){ // stream is used to generated an SDP
});
try{
var m = getById("mainmenu");
m.remove();
} catch (e){}
var added = "";
if (session.defaultPassword===false){
if (session.password){
added="&pw="+session.password;
}
if (session.welcomeMessage){
getChatMessage(session.welcomeMessage, false, true, true);
}
var pie = "";
if (session.customWSS){
if (session.customWSS!==true){
pie = "&pie="+session.customWSS;
}
}
getById("reshare").href = "https://"+location.host+location.pathname+"?view="+session.streamID+added+pie;
getById("reshare").text = "https://"+location.host+location.pathname+"?view="+session.streamID+added+pie;
getById("reshare").style.width = ((getById("reshare").text.length + 1)*1.15 * 8) + 'px';
updateReshareLink();
pokeIframeAPI('started-camera');
if (session.videoMutedFlag){
@ -12407,6 +12580,10 @@ async function publishScreen2(constraints, audioList=[], audio=true){ // webcam
constraints.audio = false;
}
if (session.screenshareVideoOnly){
constraints.audio = false;
}
log(constraints);
getUserMediaRequestID+=1;
var gumID = getUserMediaRequestID;
@ -12659,22 +12836,9 @@ async function publishScreen2(constraints, audioList=[], audio=true){ // webcam
} catch(e){errorlog(e);}
});
try{
var m = getById("mainmenu");
m.remove();
} catch (e){}
var pie = "";
if (session.customWSS){
if (session.customWSS!==true){
pie = "&pie="+session.customWSS;
}
}
getById("reshare").href = "https://"+location.host+location.pathname+"?view="+session.streamID+pie;
getById("reshare").text = "https://"+location.host+location.pathname+"?view="+session.streamID+pie;
getById("reshare").style.width = ((getById("reshare").text.length + 1)*1.15 * 8) + 'px';
updateReshareLink();
if (session.videoMutedFlag){
session.videoMuted = true;
@ -12956,11 +13120,35 @@ session.hostFile = function(ele, event){ // webcam stream is used to generated a
}
updateReshareLink();
pokeIframeAPI('started-fileshare');
clearInterval(session.updateLocalStatsInterval);
session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},3000);
session.seeding=true;
session.seedStream();
}
function updateReshareLink(){
try{
var m = getById("mainmenu");
m.remove();
} catch (e){}
var added = "";
if (session.defaultPassword===false){
if (session.password!==false){
added="&pw="+session.password;
} else {
added="&pw=false";
}
}
var pie = "";
if (session.customWSS){
if (session.customWSS!==true){
@ -12968,16 +13156,9 @@ session.hostFile = function(ele, event){ // webcam stream is used to generated a
}
}
getById("reshare").href = "https://"+location.host+location.pathname+"?view="+session.streamID+pie;
getById("reshare").text = "https://"+location.host+location.pathname+"?view="+session.streamID+pie;
getById("reshare").href = "https://"+location.host+location.pathname+"?view="+session.streamID+added+pie;
getById("reshare").text = "https://"+location.host+location.pathname+"?view="+session.streamID+added+pie;
getById("reshare").style.width = ((getById("reshare").text.length + 1)*1.15 * 8) + 'px';
pokeIframeAPI('started-fileshare');
clearInterval(session.updateLocalStatsInterval);
session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},3000);
session.seeding=true;
session.seedStream();
}
session.publishFile = function(ele, event){ // webcam stream is used to generated an SDP
@ -13287,21 +13468,9 @@ session.publishFile = function(ele, event){ // webcam stream is used to generate
});
try{
var m = getById("mainmenu");
m.remove();
} catch (e){}
var pie = "";
if (session.customWSS){
if (session.customWSS!==true){
pie = "&pie="+session.customWSS;
}
}
getById("reshare").href = "https://"+location.host+location.pathname+"?view="+session.streamID+pie;
getById("reshare").text = "https://"+location.host+location.pathname+"?view="+session.streamID+pie;
getById("reshare").style.width = ((getById("reshare").text.length + 1)*1.15 * 8) + 'px';
updateReshareLink();
pokeIframeAPI('started-fileshare');
clearInterval(session.updateLocalStatsInterval);
@ -13945,7 +14114,7 @@ async function requestVideoRecord(ele) {
msg.requestVideoRecord = true;
msg.UUID = UUID;
window.focus();
var bitrate = await promptAlt(miscTranslations["what-bitrate"], false, value=6000);
var bitrate = await promptAlt(miscTranslations["what-bitrate"], false, false, 6000);
if (bitrate) {
msg.value = bitrate;
session.sendRequest(msg, msg.UUID);
@ -15169,7 +15338,7 @@ function applyAudioHack(constraint, value = null, deviceid="default") {
log(new_constraints);
activatedPreview = false;
enumerateDevices().then(gotDevices2).then(function() {
grabAudio("videosource", "#audioSource3", null, new_constraints);
grabAudio("#audioSource3", null, new_constraints);
});
}
@ -15340,7 +15509,6 @@ function listCameraSettings() {
label.style = "display:inline-block; padding:0;margin: 15px 0px 29px;";
label.dataset.keyname = i;
var input = document.createElement("select");
var c = document.createElement("option");
if (session.cameraConstraints[i].length > 1) {
var included = false;
@ -15363,10 +15531,16 @@ function listCameraSettings() {
}
}
} else if (i.toLowerCase == "torch") {
warnlog("TORCH");
var opt = new Option("Off", false);
input.options.add(opt);
opt = new Option("On", true);
input.options.add(opt);
try{
if (session.currentCameraConstraints[i]){
opt.selected = "selected";
}
} catch(e){}
} else if (session.cameraConstraints[i].length && ("continuous" == session.cameraConstraints[i][0])){
var opt = new Option("continuous", "continuous");
input.options.add(opt);
@ -15456,13 +15630,16 @@ function listCameraSettings() {
label.style = "display:inline-block; padding:0;margin: 15px 0px 29px;";
label.dataset.keyname = i;
var input = document.createElement("select");
var c = document.createElement("option");
var opt = new Option("Off", false);
var opt = new Option("Off", "false");
input.options.add(opt);
opt = new Option("On", true);
opt = new Option("On", "true");
input.options.add(opt);
if (session.currentCameraConstraints[i]){
opt.selected = "true";
}
if (getById("popupSelector_constraints_video").style.display == "none") {
getById("advancedOptionsCamera").style.display = "inline-block";
}
@ -15475,7 +15652,7 @@ function listCameraSettings() {
input.onchange = function(e) {
//getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
updateCameraConstraints(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname + " " + e.target.value);
};
getById("popupSelector_constraints_video").appendChild(div);
div.appendChild(label);
@ -15633,16 +15810,18 @@ function updateCameraConstraints(constraint, value = null) {
[constraint]: value
}]
}).then(() => {
if (track0.getSettings){
if (session.currentCameraConstraints.deviceId){
cameraSettings['current'] = track0.getSettings(); // this won't let failed settings be stored.
//cameraSettings['current'][constraint] = value; // setting value is a problem, as it will allow for failed settings to be stored.
setStorage("camera_"+session.currentCameraConstraints.deviceId, cameraSettings);
if (toggleSettingsState == true) {
listCameraSettings();
//setTimeout(function(){
if (track0.getSettings){
if (session.currentCameraConstraints.deviceId){
cameraSettings['current'] = track0.getSettings(); // this won't let failed settings be stored.
//cameraSettings['current'][constraint] = value; // setting value is a problem, as it will allow for failed settings to be stored.
setStorage("camera_"+session.currentCameraConstraints.deviceId, cameraSettings);
if (toggleSettingsState == true) {
listCameraSettings();
}
}
}
}
//},500, track0);
}).catch(e => {
errorlog("coulnd't save defaults"); // this doesn't get triggered when a setting fails for some reason.
});
@ -15911,7 +16090,7 @@ async function shareWebsite(autostart=false, evt=false){
getById("websitesharebutton").title = "Hold CTRL (or CMD) and click to spotlight this video";
if (autostart===false){
window.focus();
var iframeURL = await promptAlt(miscTranslations["enter-website"], false, value=session.defaultIframeSrc);
var iframeURL = await promptAlt(miscTranslations["enter-website"], false, false, session.defaultIframeSrc);
} else {
var iframeURL = autostart;
}
@ -16341,6 +16520,17 @@ function generateQRPage() {
}
}
async function updateLinkWelcome(arg, input) {
if (input.checked){
var response = await promptAlt("Enter the message you'd like the guests to see");
response = encodeURIComponent(response);
var param = input.dataset.param.split("=")[0];
input.dataset.param = param + "=" + response;
}
updateLink(arg, input);
}
function updateLinkWebP(arg, input) {
if (input.checked){
if (!((getById("director_block_" + arg).dataset.raw.includes("&broadcast")) || (getById("director_block_" + arg).dataset.raw.includes("?broadcast")))){
@ -17474,7 +17664,7 @@ async function recordVideo(target, event, videoKbps = false) { // event.currentT
if (defaultRecordingBitrate == false) {
videoKbps = 4000; // 4mbps recording bitrate
window.focus();
videoKbps = await promptAlt(miscTranslations["press-ok-to-record"], false, value=videoKbps);
videoKbps = await promptAlt(miscTranslations["press-ok-to-record"], false, false, videoKbps);
if (videoKbps === null) {
//target.style.backgroundColor = null;
//target.innerHTML = '<i class="las la-circle"></i><span data-translate="record"> record local</span>';

View File

@ -530,6 +530,49 @@ hr {
transform: scale(1);
animation: pulse 2s infinite;
}
.signal-meter{
width: 22px;
height: 22px;
position: absolute;
left: 5px;
top: 1px;
background-color: #FFF2;
font-size: 1.5em;
display:none;
z-index: 2;
cursor: help;
}
.signal-meter[data-cpu="0"]>.la-signal {
display:block;
}
.signal-meter[data-cpu="0"]>.la-fire-alt {
display:none;
}
.signal-meter[data-cpu="1"]>.la-signal {
display:none;
}
.signal-meter[data-cpu="1"]>.la-fire {
display:block;
}
.signal-meter[data-level="0"] {
color:#000F;
}
.signal-meter[data-level="1"] {
color:#FF1B01;
}
.signal-meter[data-level="2"] {
color:#FF8D01;
}
.signal-meter[data-level="3"] {
color:#FFD201;
}
.signal-meter[data-level="4"] {
color:#C6FF01;
}
.signal-meter[data-level="5"] {
color:#00FF00;
}
.mirror {
transform: scaleX(-1);
@ -2987,8 +3030,8 @@ input:checked + .slider:before {
border-radius: 10px;
font-weight: bold;
z-index:31;
min-width:500px;
max-width:90%;
width:550px;
max-width:100%;
overflow: hidden;
overflow-wrap: break-word;
}
@ -3004,7 +3047,7 @@ input:checked + .slider:before {
.promptModalInner {
position: relative;
padding: 1em;
max-width: 500px;
width: 100%;
}
.promptModalMessage {
@ -3335,6 +3378,8 @@ input:checked + .slider:before {
content: "\f017"; }
.la-tachometer-alt:before {
content: "\f3fd"; }
.la-fire-alt:before {
content: "\f7e4"; }
.la-book-open:before {
content: "\f518"; }
.la-caret-down:before {
@ -3419,7 +3464,9 @@ input:checked + .slider:before {
content: "\f001"; }
.la-hdd:before {
content: "\f0a0"; }
.la-signal:before {
content: "\f012"; }
@media (prefers-color-scheme: dark) {
:root {
--color-mode: dark;

36
main.js
View File

@ -320,10 +320,27 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
directorLanding = false;
}
session.meterStyle = 1;
session.signalMeter = true;
} else if (filename === "director") {
directorLanding = true;
filename = false;
session.meterStyle = 1;
session.signalMeter = true;
}
if (urlParams.has('signalmeter')) {
session.signalMeter = urlParams.get('signalmeter');
if (session.signalMeter === "false") {
session.signalMeter = false;
} else if (session.signalMeter=== "0") {
session.signalMeter = false;
} else if (session.signalMeter === "no") {
session.signalMeter = false;
} else if (session.signalMeter === "off") {
session.signalMeter = false;
} else {
session.signalMeter = true;
}
}
if (urlParams.has('rooms')) {
@ -466,7 +483,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
// session.devicePixelRatio = window.devicePixelRatio; // this annoys me to no end.
//}
if (urlParams.has('speakermute') || urlParams.has('mutespeaker') || urlParams.has('sm') || urlParams.has('ms')) {
if (urlParams.has('speakermute') || urlParams.has('speakermuted') || urlParams.has('mutespeaker') || urlParams.has('sm') || urlParams.has('ms')) {
session.speakerMuted = true;
getById("mutespeakertoggle").className = "las la-volume-mute my-float toggleSize";
//getById("mutespeakerbutton").className="advanced float2 red";
@ -979,6 +996,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('ruler') || urlParams.has('grid') || urlParams.has('thirds')) {
session.ruleOfThirds=true;
}
if (urlParams.has('proxy')) { // routes the wss traffic via an alternative network path. Not
session.proxy=true; // only works if session.wss is set to false
}
if (urlParams.has('nopreview') || urlParams.has('np')) {
log("preview OFF");
@ -1544,6 +1565,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('easyexit') || urlParams.has('ee')) {
session.noExitPrompt = true;
}
if (urlParams.has('entrymsg') || urlParams.has('welcome')) {
session.welcomeMessage = urlParams.get('entrymsg') || urlParams.get('welcome');
session.welcomeMessage = decodeURIComponent(session.welcomeMessage);
}
if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')) {
session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb');
@ -2335,6 +2361,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.screenshareid = session.streamID + "_screenshare";
}
}
if (urlParams.has('screensharevideoonly') || urlParams.has('ssvideoonly') || urlParams.has('ssvo')) {
session.screenshareVideoOnly = true;
}
if (urlParams.has('screensharefps') || urlParams.has('ssfps')) {
if (urlParams.get('screensharefps') || urlParams.get('ssfps')) {
@ -3221,7 +3251,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
var command = e.controller.number;
var value = e.value;
midiHotkeysCommand(command, value)
midiHotkeysCommand(command, value);
}
});
}
@ -3275,7 +3305,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
visAudioTimeout = setTimeout(function() {
resetupAudioOut();
activatedPreview=false;
grabAudio("videosource", "#audioSource3");
grabAudio("#audioSource3");
}, 500);
}
}

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>IFRAME Example</title>
<title>Scene Mixer - prototype</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<style>
@ -13,9 +13,10 @@
border:0;
padding:0;
display:block;
width:1280px;
width: calc(100vw - 330px);
height:720px;
background-color: #111;
background-color: #0002;
border-radius: 3px;
}
#viewlink {
width:400px;
@ -31,10 +32,12 @@
button{
padding:5px;
margin:5px;
border-radius: 3px;
}
canvas{
padding:10px;
cursor:pointer;
border-radius: 3px;
}
.thing {
width: 100px;
@ -46,9 +49,10 @@
font-family: sans-serif;
cursor: grab;
text-align: center;
border-radius: 3px;
}
.empty {
width: 100px;
width: 108px;
height: 2em;
padding: 10px 0.5em 0 0.5em;
margin: 0.5em;
@ -57,18 +61,22 @@
font-family: sans-serif;
user-select: none;
text-align: center;
font-size: 70%;
border-radius: 3px;
}
.col {
width: 130px;
height: 450px;
padding: 1em;
height: calc(100vh - 20px);
padding: 5px;
border: 1px solid;
border-radius: 5px;
position: relative;
float: left;
user-select: none;
}
.pressed{
border: solid 2px black;
background-color: #FFFA;
}
a{
display:block;
@ -78,8 +86,455 @@
background-color: rgb(191 191 191);
margin: 3px 0 10px 0;
text-align: center;
padding: 10px 15px 0 15px;
padding: 10px 10px 0 15px;
border: 1px solid black;
}
@keyframes move {
100% {
transform: translate3d(0, 0, 1px) rotate(360deg);
}
}
.background {
position: fixed;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
background: #bfbfbf;
overflow: hidden;
z-index:-1;
}
.background span {
width: 50vmin;
height: 50vmin;
border-radius: 50vmin;
backface-visibility: hidden;
position: absolute;
animation: move;
animation-duration: 7;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.background span:nth-child(0) {
color: #a9a0bb;
top: 26%;
left: 54%;
animation-duration: 133s;
animation-delay: -143s;
transform-origin: 17vw 23vh;
box-shadow: 100vmin 0 13.032236536031524vmin currentColor;
}
.background span:nth-child(1) {
color: #888aa0;
top: 32%;
left: 60%;
animation-duration: 98s;
animation-delay: -19s;
transform-origin: -9vw -17vh;
box-shadow: -100vmin 0 13.361880379427102vmin currentColor;
}
.background span:nth-child(2) {
color: #a9a0bb;
top: 76%;
left: 30%;
animation-duration: 27s;
animation-delay: -382s;
transform-origin: -21vw -12vh;
box-shadow: -100vmin 0 13.29654068929897vmin currentColor;
}
.background span:nth-child(3) {
color: #a9a0bb;
top: 13%;
left: 3%;
animation-duration: 89s;
animation-delay: -71s;
transform-origin: -5vw -23vh;
box-shadow: -100vmin 0 13.269376230706396vmin currentColor;
}
.background span:nth-child(4) {
color: #888aa0;
top: 98%;
left: 83%;
animation-duration: 61s;
animation-delay: -57s;
transform-origin: 19vw -16vh;
box-shadow: 100vmin 0 13.465063933009704vmin currentColor;
}
.background span:nth-child(5) {
color: #4b6477;
top: 73%;
left: 100%;
animation-duration: 312s;
animation-delay: -390s;
transform-origin: 23vw -6vh;
box-shadow: -100vmin 0 12.719463296930117vmin currentColor;
}
.background span:nth-child(6) {
color: #888aa0;
top: 6%;
left: 84%;
animation-duration: 176s;
animation-delay: -255s;
transform-origin: 16vw 3vh;
box-shadow: -100vmin 0 13.358884039462355vmin currentColor;
}
.background span:nth-child(7) {
color: #888aa0;
top: 99%;
left: 9%;
animation-duration: 221s;
animation-delay: -385s;
transform-origin: -16vw -9vh;
box-shadow: 100vmin 0 13.435082385169103vmin currentColor;
}
.background span:nth-child(8) {
color: #a9a0bb;
top: 8%;
left: 92%;
animation-duration: 142s;
animation-delay: -99s;
transform-origin: -1vw -14vh;
box-shadow: -100vmin 0 13.456440775816723vmin currentColor;
}
.background span:nth-child(9) {
color: #a9a0bb;
top: 80%;
left: 70%;
animation-duration: 99s;
animation-delay: -124s;
transform-origin: 21vw -10vh;
box-shadow: 100vmin 0 13.007405893839119vmin currentColor;
}
.background span:nth-child(10) {
color: #888aa0;
top: 42%;
left: 2%;
animation-duration: 70s;
animation-delay: -341s;
transform-origin: -15vw -23vh;
box-shadow: 100vmin 0 13.005431373357645vmin currentColor;
}
.background span:nth-child(11) {
color: #a9a0bb;
top: 28%;
left: 2%;
animation-duration: 244s;
animation-delay: -369s;
transform-origin: -17vw -16vh;
box-shadow: 100vmin 0 13.351086475613746vmin currentColor;
}
.background span:nth-child(12) {
color: #4b6477;
top: 80%;
left: 50%;
animation-duration: 117s;
animation-delay: -399s;
transform-origin: 24vw 8vh;
box-shadow: 100vmin 0 12.979558964166843vmin currentColor;
}
.background span:nth-child(13) {
color: #888aa0;
top: 74%;
left: 65%;
animation-duration: 283s;
animation-delay: -50s;
transform-origin: 13vw 14vh;
box-shadow: 100vmin 0 13.282742227479792vmin currentColor;
}
.background span:nth-child(14) {
color: #888aa0;
top: 64%;
left: 61%;
animation-duration: 194s;
animation-delay: -278s;
transform-origin: -6vw 4vh;
box-shadow: -100vmin 0 13.333280580396286vmin currentColor;
}
.background span:nth-child(15) {
color: #a9a0bb;
top: 11%;
left: 86%;
animation-duration: 403s;
animation-delay: -329s;
transform-origin: 3vw 0vh;
box-shadow: -100vmin 0 13.1162690433691vmin currentColor;
}
.background span:nth-child(16) {
color: #4b6477;
top: 14%;
left: 59%;
animation-duration: 100s;
animation-delay: -15s;
transform-origin: -23vw 21vh;
box-shadow: -100vmin 0 13.30508866110655vmin currentColor;
}
.background span:nth-child(17) {
color: #888aa0;
top: 68%;
left: 45%;
animation-duration: 394s;
animation-delay: -137s;
transform-origin: 18vw -20vh;
box-shadow: -100vmin 0 12.809008306332636vmin currentColor;
}
.background span:nth-child(18) {
color: #a9a0bb;
top: 7%;
left: 33%;
animation-duration: 132s;
animation-delay: -346s;
transform-origin: -22vw -17vh;
box-shadow: -100vmin 0 12.940258664941906vmin currentColor;
}
.background span:nth-child(19) {
color: #a9a0bb;
top: 24%;
left: 65%;
animation-duration: 49s;
animation-delay: -44s;
transform-origin: 0vw -24vh;
box-shadow: -100vmin 0 12.733081490401828vmin currentColor;
}
.background span:nth-child(20) {
color: #888aa0;
top: 98%;
left: 49%;
animation-duration: 204s;
animation-delay: -160s;
transform-origin: -1vw 3vh;
box-shadow: -100vmin 0 13.05844646387434vmin currentColor;
}
.background span:nth-child(21) {
color: #a9a0bb;
top: 2%;
left: 29%;
animation-duration: 294s;
animation-delay: -272s;
transform-origin: 13vw -18vh;
box-shadow: -100vmin 0 12.626917095797205vmin currentColor;
}
.background span:nth-child(22) {
color: #a9a0bb;
top: 73%;
left: 32%;
animation-duration: 424s;
animation-delay: -59s;
transform-origin: 19vw 3vh;
box-shadow: 100vmin 0 13.092155905631065vmin currentColor;
}
.background span:nth-child(23) {
color: #888aa0;
top: 45%;
left: 93%;
animation-duration: 277s;
animation-delay: -215s;
transform-origin: 0vw -14vh;
box-shadow: 100vmin 0 12.768802405913084vmin currentColor;
}
.background span:nth-child(24) {
color: #4b6477;
top: 35%;
left: 40%;
animation-duration: 375s;
animation-delay: -205s;
transform-origin: -16vw -21vh;
box-shadow: -100vmin 0 12.845025825087218vmin currentColor;
}
.background span:nth-child(25) {
color: #a9a0bb;
top: 82%;
left: 77%;
animation-duration: 211s;
animation-delay: -150s;
transform-origin: 15vw -14vh;
box-shadow: 100vmin 0 12.825514275205975vmin currentColor;
}
.background span:nth-child(26) {
color: #4b6477;
top: 99%;
left: 95%;
animation-duration: 342s;
animation-delay: -118s;
transform-origin: -16vw -10vh;
box-shadow: 100vmin 0 13.09952938429165vmin currentColor;
}
.background span:nth-child(27) {
color: #a9a0bb;
top: 24%;
left: 43%;
animation-duration: 191s;
animation-delay: -249s;
transform-origin: 10vw 25vh;
box-shadow: -100vmin 0 12.948079018854608vmin currentColor;
}
.background span:nth-child(28) {
color: #888aa0;
top: 35%;
left: 83%;
animation-duration: 38s;
animation-delay: -5s;
transform-origin: -15vw 21vh;
box-shadow: -100vmin 0 12.884993041770123vmin currentColor;
}
.background span:nth-child(29) {
color: #4b6477;
top: 50%;
left: 19%;
animation-duration: 83s;
animation-delay: -367s;
transform-origin: -17vw 24vh;
box-shadow: 100vmin 0 12.943907699048086vmin currentColor;
}
.background span:nth-child(30) {
color: #4b6477;
top: 94%;
left: 43%;
animation-duration: 139s;
animation-delay: -151s;
transform-origin: 15vw 13vh;
box-shadow: -100vmin 0 13.188888046545854vmin currentColor;
}
.background span:nth-child(31) {
color: #4b6477;
top: 1%;
left: 30%;
animation-duration: 276s;
animation-delay: -118s;
transform-origin: 20vw -13vh;
box-shadow: 100vmin 0 12.67400347628477vmin currentColor;
}
.background span:nth-child(32) {
color: #a9a0bb;
top: 62%;
left: 91%;
animation-duration: 179s;
animation-delay: -95s;
transform-origin: 21vw 0vh;
box-shadow: 100vmin 0 13.078495151792405vmin currentColor;
}
.background span:nth-child(33) {
color: #a9a0bb;
top: 62%;
left: 24%;
animation-duration: 176s;
animation-delay: -417s;
transform-origin: 11vw 1vh;
box-shadow: -100vmin 0 13.180095388225237vmin currentColor;
}
.background span:nth-child(34) {
color: #4b6477;
top: 44%;
left: 7%;
animation-duration: 15s;
animation-delay: -23s;
transform-origin: -5vw 24vh;
box-shadow: -100vmin 0 12.99638150831451vmin currentColor;
}
.background span:nth-child(35) {
color: #888aa0;
top: 41%;
left: 18%;
animation-duration: 52s;
animation-delay: -66s;
transform-origin: 10vw 18vh;
box-shadow: 100vmin 0 12.881618623995024vmin currentColor;
}
.background span:nth-child(36) {
color: #888aa0;
top: 32%;
left: 75%;
animation-duration: 377s;
animation-delay: -252s;
transform-origin: 19vw -21vh;
box-shadow: -100vmin 0 13.235523992130878vmin currentColor;
}
.background span:nth-child(37) {
color: #4b6477;
top: 26%;
left: 96%;
animation-duration: 352s;
animation-delay: -359s;
transform-origin: -22vw -3vh;
box-shadow: 100vmin 0 13.203141789834469vmin currentColor;
}
.background span:nth-child(38) {
color: #4b6477;
top: 63%;
left: 64%;
animation-duration: 9s;
animation-delay: -70s;
transform-origin: 16vw 10vh;
box-shadow: 100vmin 0 12.63072769188053vmin currentColor;
}
.background span:nth-child(39) {
color: #a9a0bb;
top: 69%;
left: 81%;
animation-duration: 56s;
animation-delay: -361s;
transform-origin: -17vw 11vh;
box-shadow: 100vmin 0 13.123746520776026vmin currentColor;
}
.background span:nth-child(40) {
color: #888aa0;
top: 54%;
left: 33%;
animation-duration: 142s;
animation-delay: -2s;
transform-origin: 4vw 24vh;
box-shadow: 100vmin 0 13.216474278399133vmin currentColor;
}
.background span:nth-child(41) {
color: #4b6477;
top: 56%;
left: 10%;
animation-duration: 65s;
animation-delay: -105s;
transform-origin: 3vw -24vh;
box-shadow: 100vmin 0 13.322200697362886vmin currentColor;
}
.background span:nth-child(42) {
color: #4b6477;
top: 2%;
left: 39%;
animation-duration: 213s;
animation-delay: -62s;
transform-origin: 4vw 10vh;
box-shadow: -100vmin 0 12.580279613916804vmin currentColor;
}
.background span:nth-child(43) {
color: #888aa0;
top: 100%;
left: 49%;
animation-duration: 12s;
animation-delay: -71s;
transform-origin: 9vw -21vh;
box-shadow: 100vmin 0 13.356960223158397vmin currentColor;
}
.background span:nth-child(44) {
color: #a9a0bb;
top: 75%;
left: 59%;
animation-duration: 286s;
animation-delay: -81s;
transform-origin: -6vw 18vh;
box-shadow: 100vmin 0 12.718648874626872vmin currentColor;
}
.background span:nth-child(45) {
color: #888aa0;
top: 22%;
left: 12%;
animation-duration: 87s;
animation-delay: -298s;
transform-origin: -17vw 5vh;
box-shadow: 100vmin 0 13.066314361220197vmin currentColor;
}
</style>
<script>
@ -114,6 +569,15 @@
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
var eles = document.querySelectorAll(".thing");
for (var i=0;i<eles.length;i++){
if (eles[i].id == ev.target.id){continue;}
eles[i].style.boxShadow = "0px 0px 8px 2px #Fff";
}
var eles = document.querySelectorAll(".empty");
for (var i=0;i<eles.length;i++){
eles[i].style.boxShadow = "0px 0px 8px 2px #Fff";
}
}
function drop(ev) {
@ -126,6 +590,16 @@
ev.target.style.border = "";
origThing.style.border = "";
//var newThing = origThing.cloneNode(true);
var eles = document.querySelectorAll(".thing");
for (var i=0;i<eles.length;i++){
eles[i].style.boxShadow = "unset";
}
var eles = document.querySelectorAll(".empty");
for (var i=0;i<eles.length;i++){
eles[i].style.boxShadow = "unset";
}
if (ev.target.classList.contains("thing")){
//ev.target.parentNode.insertBefore(origThing, ev.target.nextSibling);
//elem.parentNode.insertBefore(elem, elem.parentNode.firstChild);
@ -133,13 +607,19 @@
var slot = origThing.dataset.slot;
origThing.dataset.slot = ev.target.dataset.slot;
ev.target.dataset.slot = slot;
var oldColor = origThing.style.backgroundColor;
origThing.style.backgroundColor = ev.target.style.backgroundColor;
ev.target.style.backgroundColor = oldColor;
} else if (ev.target.classList.contains("empty")){
if (!ev.target.dataset.slot){return;}
if (ev.target.dataset.slot=="undefined"){return;}
if (ev.target.dataset.slot=="undefined"){return;}
ev.target.parentNode.insertBefore(origThing, ev.target.nextSibling);
console.warn(origThing);
if (origThing.dataset.slot){
if (origThing.dataset.slot && (origThing.dataset.slot!=="undefined")){
document.querySelector(".empty[data-slot='"+origThing.dataset.slot+"']").style.display = "block";
}
origThing.dataset.slot = ev.target.dataset.slot;
@ -237,8 +717,12 @@
for (var i=0;i<layout.length;i++){
//layout[i].z = i;
if (!layout[i]){continue;}
if (!("i" in layout[i])){continue;}
var stream = document.querySelector(".thing[data-slot='"+(i+1)+"'");
var stream = document.querySelector(".thing[data-slot='"+(layout[i].i+1)+"'");
console.log(stream);
console.log(i);
console.log(layout);
if (!stream){continue;}
combined[stream.dataset.sid] = layout[i];
}
@ -246,7 +730,7 @@
var layout = null;
combined = false;
}
console.log(combined);
iframe.contentWindow.postMessage({"scene":"0", "layout":combined}, '*');
var layoutButtons = document.querySelectorAll("canvas[data-layout]");
@ -256,41 +740,6 @@
this.classList.add("pressed");
}
function activate(){
console.log(this.dataset.layout);
var layout = JSON.parse(this.dataset.layout);
iframe.contentWindow.postMessage({"target":"*", "remove":true}, '*');
for (var i=0;i<layout.length;i++){
var stream = document.querySelector(".thing[data-slot='"+(i+1)+"'");
if (!stream){continue;}
var x = layout[i].x|| 0;
var y = layout[i].y || 0;
var w = layout[i].w || 0;
var h = layout[i].h || 0;
var cover = layout[i].c || false;
if (!(w && h)){continue;}
x = x + "%";
y = y + "%";
w = w + "%";
h = h + "%";
if (cover){
cover = "object-fit:cover;";
} else {
cover = "";
}
iframe.contentWindow.postMessage({"target":stream.dataset.sid, "add":true, "settings":{"style": "width:"+w+";height:"+h+";position:absolute;left:"+x+";top:"+y+";display:block;"+cover}}, '*');
}
}
var button = document.createElement("button");
button.innerHTML = "Refresh list";
@ -329,10 +778,17 @@
var slots = document.getElementById("col1").children;
for (var i=0;i<slots.length;i++){
slots[i].style.backgroundColor = colors[i];
slots[i].style.opacity = "0.4";
slots[i].style.opacity = "0.9";
}
function drawLayout(layoutOriginal){
function addLayout(){
var layout = prompt("Enter your new layout as a JSON string", '[{"x":0,"y":0,"w":100,"h":100}]');
layout = JSON.parse(layout);
console.log(layout);
drawLayout(layout, true);
}
function drawLayout(layoutOriginal, customlayout=false){
var layout = [];
for (var i=0;i<layoutOriginal.length;i++){
@ -361,7 +817,11 @@
canvas.width="80";
canvas.height="45";
var ctx = canvas.getContext('2d');
document.getElementById("container").appendChild(canvas);
if (customlayout){
document.getElementById("addlayout").parentNode.insertBefore(canvas, document.getElementById("addlayout"));
} else {
document.getElementById("container").appendChild(canvas);
}
ctx.beginPath();
ctx.rect(0, 0, 80, 45);
ctx.fillStyle = "#000";
@ -382,7 +842,6 @@
}
canvas.dataset.layout = JSON.stringify(layout);
//canvas.onclick = activate;
canvas.onclick = remoteActivate;
if (slotsNeeded<layout.length){
@ -414,13 +873,36 @@
ctx.fillStyle = "#FFF";
ctx.font = "15px Arial";
ctx.fillText(" clear ", 4, 25);
ctx.fillText(" Clear ", 16, 18);
ctx.fillText(" Layout ", 12, 35);
canvas.dataset.layout = false;
//canvas.onclick = activate;
canvas.onclick = remoteActivate;
}
function drawAddNewLayout(){
var canvas = document.createElement('canvas');
canvas.width="80";
canvas.height="45";
var ctx = canvas.getContext('2d');
document.getElementById("container").appendChild(canvas);
ctx.beginPath();
ctx.rect(0, 0, 80, 45);
ctx.fillStyle = "#000";
ctx.fill();
ctx.fillStyle = "#FFF";
ctx.font = "15px Arial";
ctx.fillText(" Add ", 20, 18);
ctx.fillText(" Layout ", 12, 35);
canvas.id = "addlayout";
canvas.dataset.layout = false;
canvas.onclick = addLayout;
}
drawAutoLayout();
@ -530,6 +1012,8 @@
drawLayout(data);
drawAddNewLayout();
iframe.src = iframesrc;
iframeContainer.appendChild(iframe);
@ -589,5 +1073,53 @@
</div>
<div id="container">
</div>
<div class="background">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</body>
</html>

View File

@ -36,7 +36,8 @@ body {
h1 {
color: white;
margin: 20px 0px;
margin: 20px auto;
width: 80%;
}
h1 small {

View File

@ -8,7 +8,7 @@
</head>
<body onload="loadIframe();">
<h1>
VDO.Ninja Speed Test
VDO.Ninja's video streaming quality test
</h1>
<div id="container">
</div>
@ -42,22 +42,23 @@
<li>Select your camera.</li>
<li>Hit start</li>
<li>
Wait for the video to load side-by-side. *If it does not auto-load
within 20s, refresh and try again.*
Wait for the video to load side-by-side. *If it does not auto-load within 20s, refresh and try again.*
</li>
<li>
Stats will load on the right-hand side of the page here. (or press
CTRL + LeftClick on the new video to open stats that way)
Stats will be printed to screen, along with graphs visualizing the most important metrics. You can also hold CTRL (cmd) + Click on the video to access even more stats.
</li>
<li>
Bitrate, Buffer delay, and packet loss are important connection
quality metrics
Bitrate, Buffer delay, and packet loss are important connection quality metrics. Monitor them for at least a minute.
</li>
<li>
Change the video bitrate by pressing the buttons below the video. It
should approach 6000-kbps if the network allows.
Change the video bitrate by pressing the buttons below the video. It should approach 6000-kbps if the network allows.
</li>
<li>
Good packet loss will not exceed 0.1% and bad packet loss will exceed 1%. You can try testing against different global regions with the drop-down menu below.
</li>
<li>
Check out <a href="https://youtu.be/je2ljlvLzlY" target="_blank" style='color:#DEF;'>this Youtube video</a> for more details and solutions to reduce packet loss if suffering from any.
</li>
</ol>
<div id="screen" style="color: #CCC; margin:10px 0;"><a href="./speedtest?screen" style="color: #CCC;">Test screen-sharing performance here</a></div>
<div id="remote"></div>
@ -69,9 +70,10 @@
<option value="fr1">Strasbourg, France</option>
<option value="bra1">São Paulo, Brazil</option>
<option value="cae1">Montreal, Canada</option>
<option value="use1">Virgina, USA</option>
<option value="usc1">Chicago, USA</option>
<option value="usw1">Los Angeles 1, USA</option>
<option value="usw2">Los Angeles 2, USA</option>
<option value="usw1">Los Angeles, USA</option>
<option value="usw2">Oregon, USA</option>
<option value="aus1">Sydney, Australia</option>
<option value="jap1">Tokyo, Japan</option>
<option value="sing1">Singapore</option>

View File

@ -4,7 +4,7 @@
"join-room": "Unirse sala",
"load-the-next-guest-in-queue": "Cargar el siguiente invitado en la cola",
"toggle-the-chat": "Conmutar Chat",
"mute-the-speaker": "Mute Orador",
"mute-the-speaker": "Mutear Orador",
"mute-the-mic": "Mute Micro",
"disable-the-camera": "Desactivar Cámara",
"share-a-screen-with-others": "Compartir Pantalla con otros",
@ -78,27 +78,27 @@
"add-this-video-to-any-remote-scene-1-": "Añadir este Video a '&scene=1' remoto",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"add-this-video-to-any-remote-scene-2-": "Añadir este Video a '&scene=2' remoto",
"remotely-mute-this-audio-in-all-remote-scene-views": "Remotely Mute this Audio in all remote '&scene' views",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"add-to-scene-8": "Add to Scene 8",
"remotely-mute-this-audio-in-all-remote-scene-views": "Silenciar este Audio de forma remota, en todas las vista (y escena)",
"add-to-scene-3": "Añadir Escena 3",
"add-to-scene-4": "Añadir Escena 4",
"add-to-scene-5": "Añadir Escena 5",
"add-to-scene-6": "Añadir Escena 6",
"add-to-scene-7": "Añadir Escena 7",
"add-to-scene-8": "Añadir Escena 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"solo-this-video-everywhere": "Solo this video everywhere",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"request-the-statistics-of-this-video-in-any-active-scene": "Solicitar las estadisticas de este video en cualquier escena activa",
"solo-this-video-everywhere": "Solamente este video en todas vistas",
"hide-this-guest-everywhere": "Ocultar este video de todas las vistas",
"toggle-the-remote-guest-s-speaker-output": "Alternar la salida de audio del invitado",
"toggle-the-remote-guest-s-display-output": "Alternar la salida de pantalla del invitado",
"shift-this-video-down-in-order": "Desplaza este Video en el orden",
"current-index-order-of-this-video": "Orden del indice actual del Video",
"shift-this-video-up-in-order": "Prioriza este Video en el orden",
"remotely-reload-the-guest-s-page-with-a-new-url": "Vuelva a cargar la pagina del invitado con la nueva URL",
"change-user-parameters": "Cambia los parametros del usuario",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Comenzar Grabación de este stream remoto en local. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "El invitado grabara su transmisión en su unidad local. *experimental*",
"remotely-change-the-volume-of-this-guest": "Cambiar remotamente el volumente de este invitado",
"disable-video-preview": "Deshabilitar Previsualización",
"low-quality-preview": "Previo Baja Calidad",
"high-quality-preview": "Previo Alta Calidad",
@ -403,4 +403,4 @@
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}
}

File diff suppressed because one or more lines are too long