Merge pull request #579 from jcalado/14.0-alpha

speedtest graph color, button status, log copy
This commit is contained in:
Steve Seguin 2020-12-07 08:00:12 -05:00 committed by GitHub
commit a0e545da4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 215 additions and 74 deletions

View File

@ -32,9 +32,27 @@ h1 small {
padding-bottom: 5px; padding-bottom: 5px;
} }
#feeds {
display: flex;
}
#feeds span {
flex: 1;
height: 30vh;
display: flex;
flex-direction: column;
}
#feeds h3 {
color: whitesmoke;
margin: 10px;
text-align: center;
}
iframe { iframe {
min-height: 30vh; height: auto;
width: 39vw; width: 100%;
flex: 1;
} }
#controls { #controls {
@ -45,6 +63,10 @@ iframe {
margin: 5px; margin: 5px;
} }
#controls button.active {
background-color: green;
}
canvas { canvas {
background-color: black; background-color: black;
margin: 20px; margin: 20px;
@ -55,6 +77,7 @@ canvas {
background: #2a2a2a; background: #2a2a2a;
padding: 20px 0px; padding: 20px 0px;
border: 1px solid #383838; border: 1px solid #383838;
cursor: pointer;
} }
#log ul { #log ul {
@ -111,9 +134,19 @@ ol {
} }
iframe { iframe {
width: 90vw; width: 100%;
min-height: 0; }
#feeds {
flex-direction: column;
}
#feeds h3 {
display: none;
} }
} }
#statsdiv {display: none;} #statsdiv {display: none;}

View File

@ -27,6 +27,24 @@
})(window); })(window);
var urlParams = new URLSearchParams(window.location.search); 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 loadIframe() { function loadIframe() {
// 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();"> !!! // 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();"> !!!
@ -66,7 +84,16 @@
iframe.src = srcString; iframe.src = srcString;
iframeContainer.appendChild(iframe); iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
var title = document.createElement("h3");
title.innerText = "Local video feed";
iframeContainer.appendChild(title);
var feeds = document.createElement("div");
feeds.id = "feeds";
document.getElementById("container").appendChild(feeds);
document.getElementById("feeds").appendChild(iframeContainer);
var iframe = document.createElement("iframe"); var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("span"); var iframeContainer = document.createElement("span");
@ -85,7 +112,12 @@
iframe.src = srcString; iframe.src = srcString;
iframeContainer.appendChild(iframe); iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
var title = document.createElement("h3");
title.innerText = "Server video feed";
iframeContainer.appendChild(title);
document.getElementById("feeds").appendChild(iframeContainer);
var button = document.createElement("br"); var button = document.createElement("br");
document.getElementById("container").appendChild(button); document.getElementById("container").appendChild(button);
@ -93,19 +125,12 @@
var buttonContainer = document.createElement("div"); var buttonContainer = document.createElement("div");
buttonContainer.id = "controls"; buttonContainer.id = "controls";
var button = document.createElement("button");
button.innerHTML = "Disconnect";
button.className = "red";
button.onclick = function () {
iframe.contentWindow.postMessage({ close: true }, "*");
};
buttonContainer.appendChild(button);
var button = document.createElement("button"); var button = document.createElement("button");
button.innerHTML = "Low Bitrate"; button.innerHTML = "Low Bitrate";
button.className = "grey"; button.className = "grey";
button.onclick = function () { button.onclick = function () {
iframe.contentWindow.postMessage({ bitrate: 30 }, "*"); iframe.contentWindow.postMessage({ bitrate: 30 }, "*");
bitrate.target = 30;
}; };
buttonContainer.appendChild(button); buttonContainer.appendChild(button);
@ -114,14 +139,25 @@
button.className = "grey"; button.className = "grey";
button.onclick = function () { button.onclick = function () {
iframe.contentWindow.postMessage({ bitrate: 6000 }, "*"); iframe.contentWindow.postMessage({ bitrate: 6000 }, "*");
bitrate.target = 6000;
}; };
buttonContainer.appendChild(button); buttonContainer.appendChild(button);
var button = document.createElement("button"); var button = document.createElement("button");
button.innerHTML = "Default Bitrate"; button.innerHTML = "Default Bitrate";
button.className = "grey"; button.className = "grey active";
button.onclick = function () { button.onclick = function () {
iframe.contentWindow.postMessage({ bitrate: -1 }, "*"); iframe.contentWindow.postMessage({ bitrate: -1 }, "*");
bitrate.target = 3000;
};
buttonContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Disconnect";
button.className = "red";
button.style.display = "none";
button.onclick = function () {
iframe.contentWindow.postMessage({ close: true }, "*");
}; };
buttonContainer.appendChild(button); buttonContainer.appendChild(button);
@ -141,7 +177,34 @@
eventer(messageEvent, function (e) { eventer(messageEvent, function (e) {
if ("action" in e.data) { if ("action" in e.data) {
logData("Action",e.data.action); logData(e.data.action, e.data.value);
if (e.data.action == "new-view-connection") {
buttonContainer.querySelectorAll(
"#controls button:last-child"
)[0].style.display = "inline";
}
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) { if ("stats" in e.data) {
var out = ""; var out = "";
@ -149,41 +212,37 @@
out += printValues(e.data.stats.inbound_stats[streamID]); out += printValues(e.data.stats.inbound_stats[streamID]);
} }
if (out.split("Bitrate_in_kbps").length > 1) { if (out.split("Bitrate_in_kbps").length > 1) {
for (var key in e.data.stats.inbound_stats[streamID]) { for (var key in e.data.stats.inbound_stats[streamID]) {
if (key.startsWith("RTCMediaStreamTrack_receiver")) { if (key.startsWith("RTCMediaStreamTrack_receiver")) {
var bitrate = var bitrate =
e.data.stats.inbound_stats[streamID][key][ e.data.stats.inbound_stats[streamID][key][
"Bitrate_in_kbps" "Bitrate_in_kbps"
]; ];
plotData("bitrate-graph", bitrate, 6000); updateData("bitrate", bitrate);
var buffer = var buffer =
e.data.stats.inbound_stats[streamID][key][ e.data.stats.inbound_stats[streamID][key][
"Buffer_Delay_in_ms" "Buffer_Delay_in_ms"
]; ];
plotData("buffer-graph", buffer, 200); updateData("buffer", buffer);
var packetloss = var packetloss =
e.data.stats.inbound_stats[streamID][key][ e.data.stats.inbound_stats[streamID][key][
"packetLoss_in_percentage" "packetLoss_in_percentage"
].toFixed(2); ];
plotData("packetloss-graph", packetloss, 3); if (packetloss != undefined) {
packetloss = packetloss.toFixed(2);
updateData("packetloss", packetloss);
}
var resolution = var resolution =
e.data.stats.inbound_stats[streamID][key][ e.data.stats.inbound_stats[streamID][key]["Resolution"];
"Resolution"
]
if(previousResolution != resolution) { if (previousResolution != resolution) {
previousResolution = resolution; previousResolution = resolution;
logData("Resolution", resolution); logData("Resolution", resolution);
} }
} }
} }
@ -210,10 +269,11 @@
return out; return out;
} }
function logData(type, data){ function logData(type, data) {
var log = document.getElementById("log").getElementsByTagName("ul")[0]; var log = document.getElementById("log").getElementsByTagName("ul")[0];
var entry = document.createElement('li'); var entry = document.createElement('li');
entry.innerText = "[" + new Date().toLocaleTimeString() + "] " + type + " : " + data; entry.textContent =
"[" + new Date().toLocaleTimeString() + "] " + type + " : " + data;
log.prepend(entry); log.prepend(entry);
} }
</script> </script>
@ -252,17 +312,14 @@
<div class="graph"> <div class="graph">
<h2>Packet Loss (%)</h2> <h2>Packet Loss (%)</h2>
<span>0</span> <span>0</span>
<canvas <canvas id="packetloss-graph"></canvas>
id="packetloss-graph"
></canvas>
</div> </div>
</div> </div>
<div id="log"> <div id="log" onclick="copyFunction(this.innerText)">
<h2>Log</h2> <h2>Log <i class="las la-clipboard"></i></h2>
<ul></ul> <ul></ul>
</div> </div>
<div id="explanation"> <div id="explanation">
<h2>How to use</h2> <h2>How to use</h2>
<ol> <ol>
@ -277,66 +334,117 @@
CTRL + LeftClick on the new video to open stats that way) CTRL + LeftClick on the new video to open stats that way)
</li> </li>
<li> <li>
Bitrate, Buffer delay, and packet loss are Bitrate, Buffer delay, and packet loss are important connection
important connection quality metrics quality metrics
</li> </li>
<li> <li>
Change the video bitrate by pressing the buttons below the video. Change the video bitrate by pressing the buttons below the video. It
It should approach 6000-kbps if the network allows. should approach 6000-kbps if the network allows.
</li> </li>
</ol> </ol>
</div> </div>
<div id="statsdiv"></div> <div id="statsdiv"></div>
<script> <script>
function plotData(element, data, max) { 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);
}
if (type == "buffer") {
buffer.data = data;
plotData("buffer", buffer);
}
if (type == "packetloss") {
packetloss.data = data;
plotData("packetloss", packetloss);
}
}
function plotData(type, stat) {
var canvas; var canvas;
var context; var context;
var yScale; var yScale;
canvas = document.getElementById(element); canvas = document.getElementById(stat.element);
context = canvas.getContext("2d"); context = canvas.getContext("2d");
if (isNaN(data)) { if (isNaN(stat.data)) {
data = 0; stat.data = 0;
} }
var text = (canvas.previousElementSibling.innerHTML = data); var text = (canvas.previousElementSibling.innerHTML = stat.data);
var height = context.canvas.height; var height = context.canvas.height;
var width = context.canvas.width; var width = context.canvas.width;
context.fillStyle = "#009933"; var borderWidth = 5;
context.imageSmoothingEnabled = true; var offset = borderWidth * 2;
yScale = height / max; // Create gradient
var grd = context.createLinearGradient(0, 0, 0, height);
if (data > max) { if (type == "bitrate") {
data = max; // 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.fillRect( context.strokeStyle = "white";
width - 1, context.fillStyle = grd;
height - data * yScale, //context.fillStyle = "#009933";
1, //context.imageSmoothingEnabled = true;
height
); 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: // shift everything to the left:
var imageData = context.getImageData( var imageData = context.getImageData(1, 0, width - 1, height);
1,
0,
width - 1,
height
);
context.putImageData(imageData, 0, 0); context.putImageData(imageData, 0, 0);
// now clear the right-most pixels: // now clear the right-most pixels:
context.clearRect( context.clearRect(width - 1, 0, 1, height);
width - 1,
0,
1,
height
);
} }
</script> </script>
</body> </body>