bug fixes; features; but echo-problems still

This commit is contained in:
Steve Seguin 2022-09-06 06:02:29 -04:00
parent 17ff60e17e
commit 99c98b7443
13 changed files with 2322 additions and 361 deletions

701
check.html Normal file
View File

@ -0,0 +1,701 @@
<html>
<head>
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css" />
<link rel="stylesheet" href="./speedtest.css?ver=1" />
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>VDON Speed Test</title>
<style>
.fullscreen {
width:100%;
height: calc(100% - 35px);
position:absolute;
left:0;
display:block;
background-color: #444;
color:white;
margin: auto;
padding-top: 35px;
transition: all ease-in 1s;
animation-name: fadein;
animation-duration: .3s;
}
@keyframes fadein {
0% {opacity: 0.5;}
100% {opacity: 1;}
}
a {
color: white;
}
#controls button {
cursor: pointer;
display: inline;
padding: 20px;
}
.hidden {
display:none!important;
}
body {
text-align: center;
height:unset;
background: #444;
font-family: 'Noto Sans', sans-serif;
color:white;
}
h2 {
width: 760px;
margin: auto;
max-width: 90%;
}
li {
text-align: left;
}
button{
margin: 50px auto;
font-size: 120%;
padding: 20px 30px;
cursor:pointer;
}
</style>
</head>
<body>
<div class="fullscreen" id="page1">
<h1>
Welcome
</h1>
<h2>
This application will access your camera and complete a video test stream.
<br />
<br />
The test will take a few minutes to complete.<br />
<button onclick="next1();">Continue</button>⭐⭐⭐
</h2>
</div>
<div class="fullscreen hidden" id="page2">
<h2>
Please note, for best results:<br /><br />
<li>Connect your computer to a wired connection, instead of Wi-Fi</li><br />
<li>Have no other applications open while running this test</li><br />
<li>If using a laptop, connect your laptop to a power outlet</li><br />
🌠<button onclick="next2();">Continue</button>⭐⭐
</h2>
</div>
<div class="fullscreen hidden" id="page3">
<h2>
The next step will access your camera and microphone.<br /><br />
<br />
Accept the camera and microphone permissions if prompted.
<br /><br />
<img src='./media/accept.png'/><br />
🌠🌠<button onclick="next3();">Continue</button>
</h2>
</div>
<div id="mainapp" class="hidden">
<h1>
Video and stream quality check
</h1>
<div id="container">
</div>
<div class="hidden" id="graphs">
<div class="graph">
<h3>Bitrate (kbps)</h3>
<span>0</span>
<canvas id="bitrate-graph"></canvas>
</div>
<div class="graph">
<h3>Buffer delay (ms)</h3>
<span>0</span>
<canvas id="buffer-graph"></canvas>
</div>
<div class="graph">
<h3>Packet Loss (%)</h3>
<span>0</span>
<canvas id="packetloss-graph"></canvas>
</div>
</div>
<div style="display:none;" id="explanation">
<div id="remote"></div>
<br />
Testing location: <select name="turnlist" id="turnlist" onchange="reloadTurn();" title="Select an exact location to test against">
<option selected value="">Automatic</option>
<option value="de1">Saarbruecken, Germany</option>
<option value="de2">Frankfurt, Germany</option>
<option value="fr1">Strasbourg, France</option>
<option value="bra1">São Paulo, Brazil</option>
<option value="pol1">Warsaw, Poland</option>
<option value="cae1">Montreal, Canada</option>
<option value="use1">Virgina, USA</option>
<option disabled value="usc1">Chicago, USA</option>
<option disabled 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>
<option value="ind1">Mumbai, India</option>
<option value="pol1">Warsaw, Poland</option>
</select>
<br /><br /><br />
</div>
</div>
<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 next1(){
document.getElementById("page1").classList.add("hidden");
document.getElementById("page2").classList.remove("hidden");
}
function next2(){
document.getElementById("page2").classList.add("hidden");
document.getElementById("page3").classList.remove("hidden");
loadIframe();
}
function next3(){
document.getElementById("page3").classList.add("hidden");
document.getElementById("mainapp").classList.remove("hidden");
loadIframe();
}
(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 encoder = "";
var Round_Trip_Time_ms = "";
var recordResults = false;
function copyFunction(copyText) {
alert("Log copied to the clipboard.");
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 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;
}
var logged = [];
function logData(data) {
logged.push(data);
}
function reloadTurn(){
console.log("Reloading to change TURN servers");
loadIframe(document.getElementById("turnlist").value);
}
function updateTurnlist(value){
var select = document.getElementById("turnlist");
var selected = select.value;
select.innerHTML = "";
var opt = document.createElement("option");
opt.value = ""
opt.title = "Choose the closest location automatically";
opt.innerHTML = "Automatic";
select.appendChild(opt);
if (selected == ""){
opt.selected = true;
}
for (var i =0;i<value.length;i++){
var opt = document.createElement("option");
opt.value = value[i].locale;
opt.title = value[i].name;
opt.innerHTML = value[i].name;
select.appendChild(opt);
if (selected == opt.value){
opt.selected = true;
}
}
}
var eventMethod = window.addEventListener
? "addEventListener"
: "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
var previousResolution;
var timer= null;
var statsSent = false;
eventer(messageEvent, function (e) {
console.log(e.data);
if ("action" in e.data) {
if (e.data.action == "available-speedtest-servers"){
console.warn("Speedtest server list loaded");
updateTurnlist(e.data.value);
}
if (e.data.action == "started-camera"){
loadIframe2();
document.getElementById("localVideoText").innerText = "Local video before transmission";
}
if (e.data.action == "new-view-connection") {
if (timer===null){
timer = 0;
} else {
return;
}
buttonContainer.querySelectorAll(
"#controls button:last-child"
)[0].style.display = "inline";
var showdetails = document.createElement("button");
showdetails.onclick = function(){
document.getElementById("graphs").classList.toggle('hidden');
}
showdetails.innerText = "Show testing details";
buttonContainer.appendChild(showdetails);
setTimeout(function(button){
button.click();
}, 90000,button);
}
if (e.data.action == "setVideoBitrate") {
buttonContainer.querySelectorAll("button").forEach((button) => {
button.classList.remove("active");
});
if (e.data.value == 30) {
document
.querySelectorAll("#controls button")[0]
.classList.add("active");
}
if (e.data.value == 6000) {
document
.querySelectorAll("#controls button")[1]
.classList.add("active");
}
if (e.data.value == -1) {
document
.querySelectorAll("#controls button")[2]
.classList.add("active");
}
}
}
if ("stats" in e.data) {
var out = "";
for (var streamID in e.data.stats.inbound_stats) {
out += printValues(e.data.stats.inbound_stats[streamID]);
if (e.data.stats.inbound_stats[streamID]){
if (!statsSent){
statsSent = e.data.stats.inbound_stats[streamID];
}
}
}
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;
logData({"QLR": quality_reason});
}
}
if (e.data.stats.outbound_stats[streamID].encoder){
if (encoder != e.data.stats.outbound_stats[streamID].encoder) {
encoder = e.data.stats.outbound_stats[streamID].encoder;
logData({"encoder":encoder});
}
}
}
if (out.split("Bitrate_in_kbps").length > 1) {
for (var key in e.data.stats.inbound_stats[streamID]) {
if (key.startsWith("RTCMediaStreamTrack_receiver")) {
var bitrate = e.data.stats.inbound_stats[streamID][key][
"Bitrate_in_kbps"
];
updateData("bitrate", bitrate);
var buffer = e.data.stats.inbound_stats[streamID][key][
"Buffer_Delay_in_ms"
];
updateData("buffer", buffer);
var packetloss = e.data.stats.inbound_stats[streamID][key]["packetLoss_in_percentage"];
if (packetloss != undefined) {
packetloss = packetloss.toFixed(2);
updateData("packetloss", packetloss);
}
var resolution = e.data.stats.inbound_stats[streamID][key]["Resolution"];
if (previousResolution != resolution) {
previousResolution = resolution;
logData({"resolution": resolution});
}
}
}
}
}
});
var streamID = "";
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");
}
var iframe1 = document.createElement("iframe");
function loadIframe(zone="") {
// 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();"> !!!
document.getElementById("container").innerHTML = "";
var iframeContainer = document.createElement("span");
iframe1.allow="autoplay;camera;microphone;display-capture;";
iframe1.allowtransparency="true";
iframe1.allowfullscreen ="true";
//iframe.allow = "autoplay";
var srcString = "./?push=" + streamID + "&cleanoutput&privacy&"+testType+"&audiodevice=1&fullscreen&transparent&remote&maxbandwidth&speedtest="+zone;
if (urlParams.has("turn")) {
srcString = srcString + "&turn=" + urlParams.get("turn");
}
// we are changing some text on page load, just to demonstrate what's possible.
iframe1.onload = function (e) {
e.target.contentWindow.postMessage(
{
function: "changeHTML",
target: "add_camera",
value: "Select your Camera",
},
"*"
);
};
iframe1.src = srcString;
iframeContainer.appendChild(iframe1);
var title = document.createElement("h3");
title.innerText = "Select the camera you intend to use";
title.id = "localVideoText";
iframeContainer.appendChild(title);
var feeds = document.createElement("div");
feeds.id = "feeds";
document.getElementById("container").appendChild(feeds);
document.getElementById("feeds").appendChild(iframeContainer);
setInterval(function (iframe1) {
try {
iframe1.contentWindow.postMessage({ getStats: true }, "*");
} catch(e){
clearInterval(this);
}
}, 1000, iframe1);
}
var buttonContainer = document.createElement("div");
buttonContainer.id = "controls";
var button = document.createElement("button");
function loadIframe2(zone="") {
//document.getElementById("graphs").classList.remove('hidden');
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("span");
iframe.allow = "autoplay";
var srcString = "./?view=" + streamID + "&cleanoutput&privacy&noaudio&transparent&bitrate=6000&scale=100&speedtest="+zone; // No TURN servers set on the reciever. Don't want to query for TURN servers needlessly.
if (urlParams.has("turn")) {
srcString = srcString + "&turn=" + urlParams.get("turn");
}
if (urlParams.has("buffer")) {
srcString = srcString + "&buffer=" + urlParams.get("buffer");
}
if (urlParams.has("id")) {
recordResults = urlParams.get("id") || false;
}
iframe.src = srcString;
iframeContainer.appendChild(iframe);
var title = document.createElement("h3");
title.innerText = "Video after traversing the Internet";
iframeContainer.appendChild(title);
document.getElementById("feeds").appendChild(iframeContainer);
var br = document.createElement("br");
document.getElementById("container").appendChild(br);
var div = document.createElement("h1");
buttonContainer.appendChild(div);
button.className = "red";
//button.disabled = true;
button.innerHTML = "Abort the test";
button.style.display = "none";
button.onclick = function (){
logData(statsSent);
if (!recordResults){
recordResults = "results_"+streamID;
document.getElementById("container").innerHTML = "<br />Link to results: <a target='_blank' href='https://vdo.ninja/alpha/results?id="+recordResults+"'>https://vdo.ninja/alpha/results?id="+recordResults+"</a><br /><br /><small><i>Results are anonymous and deleted after 7-days</i></small><br /><br /><br />Final Test Results:<br />";
document.getElementById("graphs").classList.remove('hidden');
}
var request = new XMLHttpRequest();
request.open('POST', "https://record.vdo.workers.dev/?name="+recordResults);
try {
logged = JSON.stringify(logged);
} catch(e){
console.error(e);
}
request.send(logged);
timer = 91;
div.innerHTML = "Test ended";
clearInterval(interval);
try{
if (iframe){
iframe.contentWindow.postMessage({ close: true }, "*");
}
button.remove();
iframe1.remove();
iframe.remove();
feeds.style.display = "none";
} catch(e){};
};
buttonContainer.appendChild(button);
document.getElementById("container").appendChild(buttonContainer);
var interval = setInterval(function (iframe1) {
if (timer==90){
document.body.innerHTML = "<h1>Test complete. Thank you</h1>";
return;
}
if (timer>90){
clearInterval(interval);
return;
}
if (timer!==null){
timer+=1
div.innerHTML = "Test completes in "+(90-timer)+" seconds";
}
try {
if (iframe1){
iframe1.contentWindow.postMessage({ getStats: true }, "*");
}
} catch(e){
clearInterval(interval);
}
}, 1000, iframe);
}
var testType= "webcam&quality=-1&css=speedtest.css";
if (urlParams.has("screen") || urlParams.has("ss") || urlParams.has("screenshare") || urlParams.has("screentest")) {
document.getElementById("screen").innerHTML = '<a href="./speedtest" style="color: #CCC;">Test webcam-streaming performance here</a>';
testType = "quality=0&screenshare&css=speedtest.css"
}
var bitrate = {
element: "bitrate-graph",
data: 0,
max: 6000,
target: 3000,
};
var frames;
var buffer = {
element: "buffer-graph",
data: 0,
max: 200,
target: 100,
};
var packetloss = {
element: "packetloss-graph",
data: 0,
max: 3,
target: 2,
};
function updateData(type, data) {
if (type == "bitrate") {
bitrate.data = data;
plotData("bitrate", bitrate);
plotData("bitrate", bitrate);
plotData("bitrate", bitrate);
}
if (type == "buffer") {
buffer.data = data;
plotData("buffer", buffer);
plotData("buffer", buffer);
plotData("buffer", buffer);
}
if (type == "packetloss") {
packetloss.data = data;
plotData("packetloss", packetloss);
plotData("packetloss", packetloss);
plotData("packetloss", packetloss);
}
logData({[type]: data});
}
function plotData(type, stat) {
var canvas;
var context;
var yScale;
canvas = document.getElementById(stat.element);
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.3, "#F3F304");
grd.addColorStop(0.7, "#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 == "packetloss" && 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>

185
examples/socal.html Normal file
View File

@ -0,0 +1,185 @@
<html>
<head><title>SocialStream + Video</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=1.0, user-scalable=yes" />
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<style>
body{
padding:0;
margin:0;
background-color:#003;
width:100%;
height:100%;
}
iframe {
width:100%;
height:100%;
border:0;
margin:0;
padding:0;
position:absolute;
display:block;
}
input{
padding:10px;
width:80%;
font-size:1.2em;
z-index: 1000;
}
h1{
color: white;
font-family: verdana;
margin: 10px;
}
#container2{
width:100vw;
height:100vh;
position: fixed;
top: 0;
left:0;
display:none;
z-index:2;
}
#container1{
width:100vw;
height:100vh;
position: fixed;
top: 0;
left:0;
display:none;
}
iframe{
width:100vw;
height:100vh;
}
@media screen and (orientation:portrait) {
#container2{
}
#container1{
}
iframe{
}
}
@media screen and (orientation:landscape) {
#container2{
}
#container1{
}
iframe{
}
}
</style>
</head>
<body>
<div id="container2"></div>
<div id="container1" ></div>
<div id="selectChatSource">
<h1>Which social integration are you adding?</h1>
</div>
<div id="clean">
<h1>Use VDO.Ninja and SocialStream chat at the same time</h1>
<input placeholder="Enter a VDON stream ID or VDON URL" id="viewlink" type="text" />
<input placeholder="Enter the SocialStream URL" id="social" type="text" />
<button onclick="loadIframes()" style="display:block;padding:10px;margin:10px;">START</button>
</div>
<script>
window.addEventListener("orientationchange", function() {
// Announce the new orientation number
// alert(window.orientation);
}, false);
function removeStorage(cname){
localStorage.removeItem(cname);
}
function setStorage(cname, cvalue, hours=9999){ // not actually a cookie
var now = new Date();
var item = {
value: cvalue,
expiry: now.getTime() + (hours * 60 * 60 * 1000),
};
try{
localStorage.setItem(cname, JSON.stringify(item));
}catch(e){errorlog(e);}
}
function getStorage(cname) {
try {
var itemStr = localStorage.getItem(cname);
} catch(e){
errorlog(e);
return;
}
if (!itemStr) {
return "";
}
var item = JSON.parse(itemStr);
var now = new Date();
if (now.getTime() > item.expiry) {
localStorage.removeItem(cname);
return "";
}
return item.value;
}
if (getStorage("SocialStreamChatLink")){
document.getElementById("social").value = getStorage("SocialStreamChatLink");
}
if (getStorage("vdoNinjaSocialStreamURL")){
document.getElementById("viewlink").value = getStorage("vdoNinjaSocialStreamURL");
}
function loadIframes(url=false){
var roomname = document.getElementById("viewlink").value;
var room2 = document.getElementById("social").value;
document.getElementById("clean").parentNode.removeChild(document.getElementById("clean"));
document.getElementById("container1").style.display="inline-block";
document.getElementById("container2").style.display="inline-block";
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
path = path.replace("/examples","");
if (roomname.startsWith("https://")){
var room1 = roomname;
} else {
var room1 = "https://"+path+"/?push="+roomname+"&webcam&autostart&vd=front&ad=1&transparent&noheader";
}
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room1;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container1").appendChild(iframeContainer);
setStorage("SocialStreamChatLink", room2);
setStorage("vdoNinjaSocialStreamURL", room1);
setTimeout(function(){
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room2;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container2").appendChild(iframeContainer);
},3000);
}
</script>
</body>
</html>

View File

@ -72,6 +72,11 @@
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if (typeof e.data !== "object"){
console.log(e.data);
return;
}
if (e.data.frame){ // add `&sendframes` to the view link to trigger this event; it lets you capture video/audio from the parent window
if (!media.tracks[e.data.trackID]){
media.tracks[e.data.trackID] = {};
@ -109,7 +114,8 @@
return;
} // end of video/audio capture
if ("stats" in e.data) {
var consolelog = true;
if (e.data.stats){
console.log(e.data.stats);
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
@ -120,10 +126,11 @@
out += printValues(e.data.stats.inbound_stats[streamID]);
}
logOutput(out)
}
consolelog = false;
}
if ("gotChat" in e.data){
logOutput(e.data.gotChat.msg)
if (e.data.gotChat){
logOutput(e.data.gotChat)
}
if ("action" in e.data && e.data.action !== "loudness") {
@ -161,6 +168,7 @@
out += key + " Loudness: " + e.data.loudness[key] + "\n";
}
logOutput(out, { window: loudnessOutput, replace: true, style: { paddingBottom: '20px', borderBottom: 'dotted 1px #4d66a8' } });
consolelog = false;
}
if ("detailedState" in e.data){
@ -201,6 +209,15 @@
function roundSensorData(data) {
return Math.round(data * 100000) / 100000
}
consolelog = false;
}
if (("action" in e.data) && (e.data.action === "view-stats-updated")){
consolelog = false;
}
if (consolelog){
console.log(e.data);
}
});

View File

@ -57,7 +57,7 @@
<meta property="twitter:image" content="./media/vdoNinja_logo_full.png" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<link rel="stylesheet" href="./main.css?ver=177" />
<link rel="stylesheet" href="./main.css?ver=190" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.min.js"></script>
<style id="lightbox-animations" type="text/css"></style>
<!-- <link rel="manifest" href="manifest.json" /> -->
@ -82,7 +82,7 @@
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=37"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=508"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=515"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:2%;-webkit-app-region: drag;min-height:20px;"></span>
<div id="header">
@ -118,6 +118,7 @@
<div id="dirroomid" style="font-size: 140%; color: #99c; display: inline-block;"></div>
<span id="saveRoom" onclick="saveRoom(this)" style='cursor:pointer;margin-left:10px;' title="Will remember the room, prompting you the next time you visit if you wish to load this director's room again">💾</span>
<span id="togglePreviewMode" onclick="switchModes()" style='cursor:pointer;margin-left:2px;' title="Toggle between the director control-room view and a scene preview-mode.">🪟</span>
</font>
</div>
<div id="head2" class="hidden" style="display: inline-block; text-decoration: none; font-size: 60%; color: white;">
@ -877,7 +878,7 @@
<a href='https://github.com/steveseguin/vdoninja'>VDO.Ninja, by Steve Seguin</a>
</div>
</div>
<div id="gridlayout" >
<div id="directorlayout" class="hidden directorsgrid">
<div id="roomHeader" style="display:none">
<div class="hideLinksClass">
<span style='color:white' id="directorLinksButton" onclick="hideDirectorinvites(this);">
@ -917,7 +918,6 @@
</div>
<div class='directorContainer half' id='directorLinks1' style='display:none;margin-top:0;'>
<div class='directorBlock'>
<h2 title="Invite a guest or camera source to publish into the group room" style="margin-top: 5px;"><i class="las la-video director-link-icons" ></i><span data-translate="invite-a-guest">INVITE A GUEST</span></h2>
<span style="margin:5px; line-height: 1.6;" data-translate='invite-users-to-join'>Guests can use the link to join the group room</span>
@ -935,8 +935,8 @@
<span>
</div>
</div>
<div class='directorContainer half' id='directorLinks2' style='margin-left: 5px;display:none;margin-top:0;'>
<div class='directorContainer half' id='directorLinks2' style='margin-left: 5px;display:none;margin-top:0;'>
<div class='directorBlock' style="background-color: var(--green-accent);" >
<h2 title="Use this link in the OBS Browser Source to capture the video or audio" style="margin-left: 1px;margin-top: 5px;"><i class="las la-th-large director-link-icons" style="margin-right: 6px;" ></i> <span data-translate="capture-a-group-scene">CAPTURE A GROUP SCENE</span></h2>
<span style="margin:5px; line-height: 1.6;" data-translate='this-is-obs-browser-source-link'>Use in OBS or other studio software to capture the group video mix</span>
@ -953,9 +953,8 @@
<button class='pull-right grey' style='font-size:1.15em' id="showCustomizerButton3" onclick='showCustomizer(3,this)'><i class='las la-tools'></i><span data-translate="customize">Customize</span></button>
<span>
</div>
</div>
<div class='directorContainer' id="customizeLinks" style='display:none;margin-top:0;padding-top:15px'>
<div class='directorBlock' id="customizeLinks1" style='display:none;margin-top:0;padding-bottom:0;'>
<div style="display:inline-block;margin-top: 12px; position: relative; margin-right:10px;">
@ -1286,13 +1285,16 @@
</a>
</div>
<div></div>
<div id='guestFeeds' style="display:none"><div id='deleteme'>
<div class='vidcon directorMargins' id='fakeguest1' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-1">Guest 1</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon directorMargins' id='fakeguest2' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-2">Guest 2</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon directorMargins' id='fakeguest3' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-3">Guest 3</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon directorMargins' id='fakeguest4' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-4">Guest 4</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
</div></div>
<div id='guestFeeds' style="display:none">
<div id='deleteme'>
<div class='vidcon directorMargins' id='fakeguest1' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-1">Guest 1</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon directorMargins' id='fakeguest2' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-2">Guest 2</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon directorMargins' id='fakeguest3' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-3">Guest 3</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon directorMargins' id='fakeguest4' style='min-height: 300px;text-align: center;'><h2><span data-translate="guest-4">Guest 4</span></h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
</div>
</div>
</div>
<div id="hiddenElements"></div>
<div id="overlayClockContainer" data-initial="600" class="hidden"><span id="overlayClock"></span></div>
<div id="overlayMsgs" onclick="this.innerHTML = '';" style="display:none"></div>
@ -2051,7 +2053,6 @@
<div id="userList">
</div>
</div>
<div id="signalMeterTemplate" class="signal-meter hidden" data-cpu="0" data-level="0">
<i class="las la-signal"></i>
<i class="las la-fire-alt"></i>
@ -2093,6 +2094,7 @@
<input type="file" accept="image/*" onchange="changeTFLiteImage(event, this)" style="position: fixed; top: -100em; margin-left:10px; border:1px solid #555;">
</label>
</div>
<div id="gridlayout"></div>
<audio id="testtone" style="display:none;" preload="none">
<source src="./media/tone.mp3" type="audio/mpeg">
<source src="./media/tone.ogg" type="audio/ogg">
@ -2109,7 +2111,6 @@
<a style="text-decoration: none; color: blue;" target="_blank" href="https://docs.vdo.ninja/advanced-settings">documentation</a> for more options and info.
</div>
</div>
<div id="messagePopup" class="popup-message"></div>
<div id="languages" class="popup-message" style="display: none; right: 0; bottom: 25px; position: absolute;">
<b data-translate='available-languages'>Available Languages:</b>
@ -2151,7 +2152,7 @@
</u>
</div>
<div id="meshcastMenu" class="hidden">
Publishing Region: <select name="edgelist" id="edgelist" onchange="reloadEdge();" title="Select a location that is closest to both you and your audience."></select>
Publishing Region: <select name="edgelist" id="edgelist" onchange="selectMeshcast(this);" title="Select a location that is closest to both you and your audience."></select>
</div>
<script>
@ -2237,11 +2238,11 @@
// session.defaultBackgroundImages = ["./media/bg_sample1.webp", "./media/bg_sample2.webp"]; // for &effects=5 (virtual backgrounds)
</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=408"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=440"></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=418"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=432"></script>
</body>
</html>

1161
lib.js

File diff suppressed because it is too large Load Diff

View File

@ -271,9 +271,13 @@ button.white:active {
color: white;
border: 1px solid black;
}
#meshcastMenu{
display: inline-block;
color: #e0dfdf;
}
#header {
width: 100%;
padding: 1px;
margin: 1px;
background-color: #0F131D;
color: #FFF;
-webkit-app-region: drag;
@ -456,6 +460,10 @@ body.darktheme .credits>a:visited {
background-color: black;
}
.darktheme .advDirectGuestSettings label {
color: #FFF;
}
.pressed {
background: #1e0000;
-webkit-box-shadow: inset 0px 0px 1px #b90000;
@ -526,7 +534,7 @@ hr {
border-radius: var(--video-rounded);
}
#gridlayout {
#gridlayout,#directorlayout {
padding: 0;
width: 100%;
height: 100%;
@ -728,7 +736,18 @@ hr {
cursor: pointer;
font-size:12px;
}
.rem-con-count{
position: absolute;
left: 49px;
top: 0px;
color: white;
background-color: #0006;
font-size: 1em;
z-index: 2;
cursor: help;
border-radius: 4px;
padding: 1px 4px 1px 0px;
}
.signal-meter{
width: 22px;
height: 22px;
@ -860,6 +879,7 @@ button.glyphicon-button.active.focus {
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center;
overflow-x: hidden;
}
#controlButtons {
@ -887,6 +907,8 @@ button.glyphicon-button.active.focus {
height:100%;
}
.labelSmall {
display:none;
}
@ -1334,10 +1356,6 @@ body {
-webkit-app-region: no-drag;
}
#popupSelector{
-webkit-app-region: no-drag;
}
select{
-webkit-app-region: no-drag;
}
@ -1359,6 +1377,8 @@ select{
background-color:#474!important;
}
#effectSelector{
display: inline-block;
vertical-align: middle;
@ -1693,7 +1713,8 @@ input[type=range]:focus::-ms-fill-upper {
width: 505px;
right: -400px;
overflow: auto;
z-index: 3;
z-index: 4;
-webkit-app-region: no-drag;
}
#audioSourceScreenshare {
@ -1859,6 +1880,12 @@ label {
animation: fadeout 1s;
opacity: 0!important;
}
.zeroHeight {
max-height:0!important;
height:0!important;
bottom:30px!important;
}
.partialFadeout{
opacity: .1 !important;
}
@ -2931,10 +2958,13 @@ div#chatBody a {
}
.debugStats li span:first-child {
flex: 1;
white-space: nowrap;
}
.debugStats li span:last-child {
flex: 1;
text-align: right;
max-height: 49px;
overflow: auto;
}
.debugStats .close {
font-weight: bold;
@ -4108,7 +4138,7 @@ body.darktheme .multiselect .multiselect-contents {
background-color:unset;
}
body.darktheme .directorsgrid .vidcon > .las {
background-color: #646464;
background-color: #424242;
}
body.darktheme .form-group .multiselect {
background-color: #414141;
@ -4260,4 +4290,10 @@ body.darktheme .invite_setting_item>input{
}
body.darktheme .invite_setting_item>select{
background-color:white;
}
.containerGreen{
background-color: #649166!important;
}
body.darktheme .containerGreen{
background-color: #243824!important;
}

30
main.js
View File

@ -100,6 +100,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.cleanViewer = true;
}
if (urlParams.has('previewmode')){
session.switchMode = true;
}
if (urlParams.has('director') || urlParams.has('dir')) {
session.director = urlParams.get('director') || urlParams.get('dir') || true;
@ -378,6 +381,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('meshcast')) {
session.meshcast = urlParams.get('meshcast') || "any";
meshcast(true);
}
@ -1155,7 +1159,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('salt') && urlParams.get('salt')){
session.salt = urlParams.get('salt');
}
if (urlParams.has('showconnections')){
session.showConnections = true; // shows remote guest connections as a stat
}
if (urlParams.has('hash') || urlParams.has('crc') || urlParams.has('check')) { // could be brute forced in theory, so not as safe as just not using a hash check.
session.taintedSession = null; // waiting to see if valid or not.
@ -2407,6 +2414,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.welcomeMessage = decodeURIComponent(session.welcomeMessage);
}
if (urlParams.has('mixminus')){
session.mixMinus = true;
}
if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')) {
session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb');
if (session.bitrate) {
@ -2900,7 +2911,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.audioMeterGuest = true;
setInterval(function(){activeSpeaker(false);},100);
}
}
if (urlParams.has('noisegatesettings')){
session.noisegateSettings = urlParams.get('noisegatesettings');
session.noisegateSettings = session.noisegateSettings.split(",");
}
if (urlParams.has('fadein')) {
@ -3199,8 +3214,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
}
if (urlParams.has('wss')) {
session.customWSS = true;
@ -3219,8 +3232,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('osc') || urlParams.has('api')) {
if (urlParams.get('osc') || urlParams.get('api')) {
session.api = urlParams.get('osc') || urlParams.get('api');
setTimeout(function(){oscClient();},1000);
session.api = urlParams.get('osc') || urlParams.get('api') || false;
if (session.api){
setTimeout(function(){oscClient();},1000);
}
}
}
@ -3809,7 +3824,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.autohide=true;
}
if (session.autohide && !session.mobile && (session.scene===false)){// && (session.roomid!==false)){
getById("main").onmouseover = session.showControls;
getById("main").onmouseover = showControl; // this is correct. (it's not session.showControls)
getById("controlButtons").classList.add("zeroHeight");
}
if (urlParams.has('flagship')) {

BIN
media/accept.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -51,8 +51,8 @@
}
iframe.aspectRatio{
max-height: min(calc(100vh - 80px), calc((100vw - 160px) / var(--aspect-ratio)));
max-width: min(calc((100vh - 80px) * var(--aspect-ratio)), calc(100vw - 160px));
max-height: min(calc(100vh - 80px), calc(100vw - 160px - var(--chat-width)) / var(--aspect-ratio))) !important;
max-width: min(calc((100vh - 80px) * var(--aspect-ratio)), calc(100vw - 160px - var(--chat-width))) !important;
height: 720px;
width: 1280px;
}
@ -1413,7 +1413,7 @@
delete origThing.dataset.slot;
}
origThing.style.backgroundColor = "#000";
origThing.style.backgroundColor = "#000";
if (ev.target.classList.contains("thing")){
ev.target.parentNode.insertBefore(origThing, ev.target.nextSibling);
} else {
@ -2620,10 +2620,10 @@
container.cover = true;
}
container.zIndex = parseInt(object[i].zIndex) || parseInt(object[i].z) || 0;
//container.backgroundColor = object[i].backgroundColor || "#000";
//container.backgroundColor = object[i].backgroundColor || "#0000";
container.borderThickness = object[i].borderThickness || 0;
container.animated = object[i].animated || 0;
container.borderColor = object[i].borderColor || "#000";
container.borderColor = object[i].borderColor || "#0000";
container.backgroundMedia = object[i].backgroundMedia || "";
container.defaultStreamID = object[i].defaultStreamID || "";
container.margin = object[i].margin || 0;
@ -2949,7 +2949,7 @@
input.type = "text";
input.style.width = "80px";
setEle.appendChild(input);
input.value = parent.borderColor || "#000";
input.value = parent.borderColor || "#0000";
input.onchange = function(){
parent.borderColor = this.value;
}
@ -3051,7 +3051,7 @@
//ele.backgroundColor = eles[i].backgroundColor || "#000";
ele.borderThickness = parseInt(eles[i].borderThickness) || 0;
ele.animated = eles[i].animated || 0;
ele.borderColor = eles[i].borderColor || "#000";
ele.borderColor = eles[i].borderColor || "#0000";
ele.backgroundMedia = eles[i].backgroundMedia || "";
ele.defaultStreamID = eles[i].defaultStreamID || "";
ele.margin = parseInt(eles[i].margin) || 0;
@ -3096,7 +3096,7 @@
//ele.backgroundColor = eles[i].backgroundColor || "#000";
ele.borderThickness = parseInt(eles[i].borderThickness) || 0;
ele.animated = eles[i].animated || 0;
ele.borderColor = eles[i].borderColor || "#000";
ele.borderColor = eles[i].borderColor || "#0000";
ele.backgroundMedia = eles[i].backgroundMedia || "";
ele.defaultStreamID = eles[i].defaultStreamID || "";
ele.margin = parseInt(eles[i].margin) || 0;

431
results.html Normal file
View File

@ -0,0 +1,431 @@
<html>
<head>
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css" />
<link rel="stylesheet" href="./speedtest.css?ver=1" />
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>VDON Speed Test</title>
<style>
.fullscreen {
width:100%;
height: calc(100% - 35px);
position:absolute;
left:0;
display:block;
background-color: #444;
color:white;
margin: auto;
padding-top: 35px;
transition: all ease-in 1s;
animation-name: fadein;
animation-duration: .3s;
}
@keyframes fadein {
0% {opacity: 0.5;}
100% {opacity: 1;}
}
a {
color: white;
}
#controls button {
cursor: pointer;
display: inline;
padding: 20px;
}
.hidden {
display:none!important;
}
body {
text-align: center;
height:unset;
background: #444;
font-family: 'Noto Sans', sans-serif;
color:white;
}
h2 {
width: 760px;
max-width: 90%;
margin: auto;
}
li {
text-align: left;
}
button{
margin: 50px auto;
font-size: 120%;
padding: 20px 30px;
cursor:pointer;
}
</style>
</head>
<body>
<div id="mainapp" >
<h1>
Video and stream quality check results
</h1>
<div id="container">
</div>
<div id="graphs">
<div class="graph">
<h3>Bitrate (kbps)</h3>
<span>0</span>
<canvas id="bitrate-graph"></canvas>
</div>
<div class="graph">
<h3>Buffer delay (ms)</h3>
<span>0</span>
<canvas id="buffer-graph"></canvas>
</div>
<div class="graph">
<h3>Packet Loss (%)</h3>
<span>0</span>
<canvas id="packetloss-graph"></canvas>
</div>
</div>
</div>
<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 next1(){
document.getElementById("page1").classList.add("hidden");
document.getElementById("page2").classList.remove("hidden");
}
function next2(){
document.getElementById("page2").classList.add("hidden");
document.getElementById("page3").classList.remove("hidden");
loadIframe();
}
function next3(){
document.getElementById("page3").classList.add("hidden");
document.getElementById("mainapp").classList.remove("hidden");
loadIframe();
}
(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 copyFunction(copyText) {
alert("Log copied to the clipboard.");
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 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;
}
var streamID = "";
if (urlParams.has("id")) {
streamID = urlParams.get("id");
}
var bitrate = {
element: "bitrate-graph",
data: 0,
max: 6000,
target: 3000,
};
var frames;
var buffer = {
element: "buffer-graph",
data: 0,
max: 200,
target: 100,
};
var packetloss = {
element: "packetloss-graph",
data: 0,
max: 3,
target: 2,
};
// https://record.vdo.workers.dev/?name="+recordResults
var QLR_1 = 0;
var QLR_2 = 0;
var QLR_3 = 0;
var BBB = 0;
var counter = 0;
var BUFF = 0;
var BUFFCCC = 0;
var PAK = 0;
var PAKCCC = 0;
function process(arr) {
console.log(arr);
arr.forEach(data=>{
if ("bitrate" in data){
updateData("bitrate", data.bitrate);
if (data.bitrate!==null){
BBB += data.bitrate;
counter += 1;
}
}
if ("buffer" in data){
updateData("buffer", data.buffer);
if (data.buffer!==null){
BUFF += data.buffer;
BUFFCCC += 1;
}
}
if ("packetloss" in data){
updateData("packetloss", data.packetloss);
if (data.packetloss!==null){
PAK += parseFloat(data.packetloss) || 0;
PAKCCC += 1;
}
}
if ("resolution" in data){
updateData("resolution", data.resolution);
}
if ("QLR" in data){
if (data.QLR == "none"){
QLR_1 +=1;
} else if (data.QLR.toLowerCase() == "cpu"){
QLR_2 +=1;
} else if (data.QLR.toLowerCase() == "network"){
QLR_3 +=1;
}
}
if ("info" in data){
if (data.info.Browser){
document.getElementById("container").innerHTML += "<br />Browser used: "+data.info.Browser+"<br />";
if (!data.info.Browser.startsWith("Chromium")){
document.getElementById("container").innerHTML += "<h3>A Chromium-based browser is recommended.</h3>";
} else {
document.getElementById("container").innerHTML += "<br />";
}
}
}
});
// container
var total = QLR_1 + QLR_2 + QLR_3;
if (QLR_2/total>0.5){
document.getElementById("container").innerHTML += "Serious CPU overload issues. Consider reducing the capture resolution.<br />";
} else if (QLR_2/total>0.1){
document.getElementById("container").innerHTML += "Occassional CPU overload issues. Consider reducing the capture resolution.<br />";
}
if (QLR_3/total>0.5){
document.getElementById("container").innerHTML += "The network quality or bandwidth limited the performance.<br />";
} else if (QLR_3/total>0.1){
document.getElementById("container").innerHTML += "The network quality or bandwidth may have limited the performance.<br />";
}
document.getElementById("container").innerHTML += "The average bitrate was: "+parseInt(BBB/counter)+"-kbps<br />";
if (BBB/counter<500){
document.getElementById("container").innerHTML += "<h3>Bitrate is really bad</h3>";
}
else if (BBB/counter<1000){
document.getElementById("container").innerHTML += "<h3>Bitrate is poor</h3>";
}
else if (BBB/counter<2000){
document.getElementById("container").innerHTML += "<h3>Bitrate a bit low</h3>";
}
else {
document.getElementById("container").innerHTML += "<h3>Bitrate is good</h3>";
}
document.getElementById("container").innerHTML += "<br />The average buffer length was: "+parseInt(BUFF/BUFFCCC)+"-ms<br />";
if (BUFF/BUFFCCC>500){
document.getElementById("container").innerHTML += "<h3>Video delay is really bad</h3><br />";
}
else if (BUFF/BUFFCCC>200){
document.getElementById("container").innerHTML += "<h3>Video delay is poor</h3><br />";
}
else if (BUFF/BUFFCCC>100){
document.getElementById("container").innerHTML += "<h3>Video delay is sub-optimal</h3><br />";
}
else {
document.getElementById("container").innerHTML += "<h3>Video delay is good</h3><br />";
}
document.getElementById("container").innerHTML += "The average packet loss was: "+(parseInt(PAK*1000/PAKCCC)/1000.0)+"%<br />";
if (PAK/PAKCCC>3){
document.getElementById("container").innerHTML += "<h3>Packet loss is extremely bad; Must Fix This</h3>";
}
else if (PAK/PAKCCC>0.8){
document.getElementById("container").innerHTML += "<h3>Packet loss is quite bad; expect problems with audio and video</h3>";
}
else if (PAK/PAKCCC>.15){
document.getElementById("container").innerHTML += "<h3>Packet loss is a bit high; might be a testing-server issue though</h3>";
}
else {
document.getElementById("container").innerHTML += "<h3>Packet loss is good</h3>";
}
console.log(QLR_1, QLR_2, QLR_3);
}
var xmlhttp = new XMLHttpRequest();
var url = "https://record.vdo.workers.dev/?name="+streamID;
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myArr = JSON.parse(this.responseText);
process(myArr);
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
function updateData(type, data) {
if (type == "bitrate") {
bitrate.data = data;
plotData("bitrate", bitrate);
plotData("bitrate", bitrate);
plotData("bitrate", bitrate);
}
if (type == "buffer") {
buffer.data = data;
plotData("buffer", buffer);
plotData("buffer", buffer);
plotData("buffer", buffer);
}
if (type == "packetloss") {
packetloss.data = data;
plotData("packetloss", packetloss);
plotData("packetloss", packetloss);
plotData("packetloss", packetloss);
}
}
function plotData(type, stat) {
var canvas;
var context;
var yScale;
canvas = document.getElementById(stat.element);
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.3, "#F3F304");
grd.addColorStop(0.7, "#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 == "packetloss" && 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

@ -29,7 +29,18 @@ body {
transition: opacity .1s linear;
background-color: #141926;
}
.container-inner{
background-color: #0000!important;
}
.card{
background-color: #0000!important;
}
#container-3{
background-image:unset;
}
.column>h2{
color:white!important;
}
#add_screen{
display:none;
}
@ -201,17 +212,20 @@ ol {
}
#feeds h3 {
font-size:50%;
font-size:100%;
}
h1{
color: white;
margin: 2px;
font-size:70%
font-size:1.4em;
}
h2{
color: white;
font-size:1.2em;
}
#feeds span{
height: 50%;
height: :calc(100% - 100px);
width:100%;
display: inline-block;
}

View File

@ -31,7 +31,10 @@
<canvas id="packetloss-graph"></canvas>
</div>
</div>
<div id="log" onclick="copyFunction(this.innerText)">
<h2>Log <i class="las la-clipboard"></i></h2>
<ul></ul>
</div>
<div id="explanation">
<h2>How to use</h2>
<ol>
@ -151,10 +154,12 @@
return out;
}
var logged = [];
function logData(type, data) {
data.timestamp = new Date().now();
logged.push(data);
var log = document.getElementById("log").getElementsByTagName("ul")[0];
var entry = document.createElement('li');
entry.textContent =
"[" + new Date().toLocaleTimeString() + "] " + type + " : " + data;
log.prepend(entry);
}
function reloadTurn(){
@ -444,7 +449,7 @@
var testType= "webcam";
var testType= "webcam&css=speedtest.css";
if (urlParams.has("screen") || urlParams.has("ss") || urlParams.has("screenshare") || urlParams.has("screentest")) {
document.getElementById("screen").innerHTML = '<a href="./speedtest" style="color: #CCC;">Test webcam-streaming performance here</a>';
testType = "quality=0&screenshare&css=speedtest.css"

File diff suppressed because one or more lines are too long