mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-10 21:28:34 +00:00
846 lines
25 KiB
HTML
846 lines
25 KiB
HTML
<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 Stream 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>
|
|
<br />
|
|
<h2>
|
|
This application will access your camera and complete a video test stream.
|
|
<br />
|
|
|
|
<br />
|
|
The full test will take a few minutes to complete.<br />
|
|
<button onclick="next1();">Continue</button>⭐⭐⭐⭐
|
|
|
|
</h2>
|
|
</div>
|
|
<div class="fullscreen hidden" id="page2">
|
|
<h1>
|
|
Please note, for best results:<br />
|
|
</h1>
|
|
<br />
|
|
<h2>
|
|
|
|
<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="page2a">
|
|
|
|
<h1>
|
|
This first step will measure your network bandwidth<br />
|
|
</h1>
|
|
<br />
|
|
<h2>
|
|
We will test against Cloudflare's speed test servers.<br /><br />
|
|
It will take a minute to complete once started.<br />
|
|
|
|
<br />
|
|
<div id="cloudflareresults">
|
|
🌠🌠<button id="cloudflare" disabled>Loading Files</button>⭐⭐
|
|
</div>
|
|
|
|
</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 />
|
|
<small><i>No one will be able to see your video or audio, other than you.</i></small>
|
|
<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;
|
|
}
|
|
|
|
function next1(){
|
|
document.getElementById("page1").classList.add("hidden");
|
|
document.getElementById("page2").classList.remove("hidden");
|
|
}
|
|
|
|
function next2(){
|
|
document.getElementById("page2").classList.add("hidden");
|
|
document.getElementById("page2a").classList.remove("hidden");
|
|
setTimeout(function(){
|
|
if (document.getElementById("playButton") && !document.getElementById("playButton").skip){
|
|
next2a();
|
|
}
|
|
},10000);
|
|
}
|
|
|
|
function next2a(){
|
|
document.getElementById("page2a").classList.add("hidden");
|
|
document.getElementById("page3").classList.remove("hidden");
|
|
}
|
|
|
|
function next3(){
|
|
document.getElementById("page3").classList.add("hidden");
|
|
document.getElementById("mainapp").classList.remove("hidden");
|
|
loadIframe(region);
|
|
}
|
|
|
|
(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) {
|
|
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";
|
|
|
|
|
|
logData({"timestart": Date.now()});
|
|
|
|
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 someValue in e.data.stats.inbound_stats) {
|
|
out += printValues(e.data.stats.inbound_stats[someValue]);
|
|
if (e.data.stats.inbound_stats[someValue]){
|
|
if (!statsSent){
|
|
statsSent = e.data.stats.inbound_stats[someValue];
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var someValue in e.data.stats.outbound_stats) {
|
|
if (e.data.stats.outbound_stats[someValue].quality_limitation_reason){
|
|
if (quality_reason != e.data.stats.outbound_stats[someValue].quality_limitation_reason) {
|
|
quality_reason = e.data.stats.outbound_stats[someValue].quality_limitation_reason;
|
|
logData({"QLR": quality_reason});
|
|
}
|
|
}
|
|
|
|
if (e.data.stats.outbound_stats[someValue].encoder){
|
|
if (encoder != e.data.stats.outbound_stats[someValue].encoder) {
|
|
encoder = e.data.stats.outbound_stats[someValue].encoder;
|
|
logData({"encoder":encoder});
|
|
}
|
|
} else if (e.data.stats.outbound_stats[someValue].video_codec){
|
|
if (encoder != e.data.stats.outbound_stats[someValue].video_codec) {
|
|
encoder = e.data.stats.outbound_stats[someValue].video_codec;
|
|
logData({"encoder":encoder});
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var key in e.data.stats.inbound_stats[streamID]){
|
|
if (typeof e.data.stats.inbound_stats[streamID][key] == "object"){
|
|
if ("Bitrate_in_kbps" in e.data.stats.inbound_stats[streamID][key]){
|
|
var bitrate = e.data.stats.inbound_stats[streamID][key]["Bitrate_in_kbps"];
|
|
updateData("bitrate", bitrate);
|
|
}
|
|
|
|
|
|
|
|
|
|
if ("Jitter_Buffer_ms" in e.data.stats.inbound_stats[streamID][key]){
|
|
var buffer = e.data.stats.inbound_stats[streamID][key]["Jitter_Buffer_ms"];
|
|
updateData("buffer", buffer);
|
|
} else if ("Buffer_Delay_in_ms" in e.data.stats.inbound_stats[streamID][key]){
|
|
var buffer = e.data.stats.inbound_stats[streamID][key]["Buffer_Delay_in_ms"];
|
|
updateData("buffer", buffer);
|
|
} else if ("Added_Buffer_Delay_ms" in e.data.stats.inbound_stats[streamID][key]){
|
|
console.log("Added_Buffer_Delay_ms");
|
|
var buffer = e.data.stats.inbound_stats[streamID][key]["Added_Buffer_Delay_ms"];
|
|
updateData("buffer", buffer);
|
|
}
|
|
|
|
if ("packetLoss_in_percentage" in e.data.stats.inbound_stats[streamID][key]){
|
|
var packetloss = e.data.stats.inbound_stats[streamID][key]["packetLoss_in_percentage"];
|
|
if (packetloss != undefined) {
|
|
packetloss = packetloss.toFixed(2);
|
|
updateData("packetloss", packetloss);
|
|
}
|
|
}
|
|
|
|
if ("Resolution" in e.data.stats.inbound_stats[streamID][key]){
|
|
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");
|
|
} else if (urlParams.has("push")) {
|
|
streamID = urlParams.get("push");
|
|
}
|
|
|
|
var region = "";
|
|
if (urlParams.has("location")) {
|
|
region = urlParams.get("location");
|
|
} else if (urlParams.has("region")) {
|
|
region = urlParams.get("region");
|
|
}
|
|
|
|
<!-- <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> -->
|
|
|
|
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;
|
|
} else if (urlParams.has("session")) {
|
|
recordResults = urlParams.get("session") || 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 == 0){
|
|
iframe.contentWindow.postMessage({ bitrate: 2500 }, "*");
|
|
bitrate.target = 2500;
|
|
updateData("target", bitrate.target);
|
|
}
|
|
if (timer == 30){
|
|
iframe.contentWindow.postMessage({ bitrate: 4000 }, "*");
|
|
bitrate.target = 4000;
|
|
updateData("target", bitrate.target);
|
|
}
|
|
if (timer == 60){
|
|
iframe.contentWindow.postMessage({ bitrate: 6000 }, "*");
|
|
bitrate.target = 6000;
|
|
updateData("target", bitrate.target);
|
|
}
|
|
|
|
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=0&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: 6500,
|
|
target: 2500,
|
|
};
|
|
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") {
|
|
|
|
if (stat.target == 2500){
|
|
grd.addColorStop(0, "#33C433");
|
|
grd.addColorStop(0.7, "#33C433");
|
|
grd.addColorStop(0.8, "#F3F304");
|
|
grd.addColorStop(0.92, "#F30404");
|
|
} else if (stat.target == 4000){
|
|
grd.addColorStop(0, "#33C433");
|
|
grd.addColorStop(0.5, "#33C433");
|
|
grd.addColorStop(0.8, "#F3F304");
|
|
grd.addColorStop(0.92, "#F30404");
|
|
} else if (stat.target == 6000){
|
|
grd.addColorStop(0, "#33C433");
|
|
grd.addColorStop(0.3, "#33C433");
|
|
grd.addColorStop(0.8, "#F3F304");
|
|
grd.addColorStop(0.92, "#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.max;
|
|
|
|
if (stat.data > stat.max) {
|
|
stat.data = stat.max;
|
|
}
|
|
|
|
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>
|
|
|
|
<script type="module">
|
|
import SpeedTest from 'https://cdn.skypack.dev/@cloudflare/speedtest';
|
|
|
|
const controlEl = document.getElementById('controls');
|
|
const playButton = document.getElementById("cloudflare");
|
|
|
|
const resultsCl = document.getElementById("cloudflareresults");
|
|
|
|
var Runstate = false;
|
|
const engine = new SpeedTest({
|
|
autoStart: false
|
|
});
|
|
engine.onRunningChange = (running) => {playButton.textContent = running ? 'Running...' : 'Finished!';Runstate=running;}
|
|
engine.onResultsChange = ({ type }) => {
|
|
console.log(type);
|
|
if (Runstate){
|
|
console.log(engine.results.raw);
|
|
}
|
|
resultsCl.style.color = "green";
|
|
resultsCl.innerHTML = Runstate ? '<b>Please wait</b><br .><br .>Testing in progress: '+type : 'Finished!';
|
|
};
|
|
|
|
engine.onFinish = results => {
|
|
console.log(results.getSummary());
|
|
console.log(results.getScores());
|
|
logData({['summary']: results.getSummary()});
|
|
logData({['scores']: results.getScores()});
|
|
next2a();
|
|
};
|
|
playButton.skip=true;
|
|
playButton.textContent = "Start test";
|
|
playButton.disabled = null;
|
|
|
|
playButton.onclick = function(){playButton.disabled=true;playButton.onclick=null;engine.play();}
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|