mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-11 13:48:38 +00:00
bug fixes; features; but echo-problems still
This commit is contained in:
parent
17ff60e17e
commit
99c98b7443
701
check.html
Normal file
701
check.html
Normal 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
185
examples/socal.html
Normal 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>
|
||||
25
iframe.html
25
iframe.html
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
39
index.html
39
index.html
@ -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>
|
||||
|
||||
54
main.css
54
main.css
@ -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
30
main.js
@ -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
BIN
media/accept.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.9 KiB |
16
mixer.html
16
mixer.html
@ -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
431
results.html
Normal 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>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user