@jonz94/getstats
Version:
Fork from https://github.com/manuquentin/getStats
723 lines (606 loc) • 31.2 kB
HTML
<script>window.demoVersion = '2019.02.20';</script>
<!--
> Muaz Khan - www.MuazKhan.com
> MIT License - www.WebRTC-Experiment.com/licence
> Documentation - github.com/muaz-khan/getStats
-->
<html lang="en">
<head>
<title>getStats.js Demo | WebRTC getStats API</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="author" type="text/html" href="https://muazkhan.com">
<meta name="author" content="Muaz Khan">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="https://www.webrtc-experiment.com/style.css">
<style>
audio, video {
-moz-transition: all 1s ease;
-ms-transition: all 1s ease;
-o-transition: all 1s ease;
-webkit-transition: all 1s ease;
transition: all 1s ease;
vertical-align: top;
}
input {
border: 1px solid #d9d9d9;
border-radius: 1px;
font-size: 2em;
margin: .2em;
width: 30%;
}
.setup {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
font-size: 102%;
height: 47px;
margin-left: -9px;
margin-top: 8px;
position: absolute;
}
p { padding: 1em; }
li {
border-bottom: 1px solid rgb(189, 189, 189);
border-left: 1px solid rgb(189, 189, 189);
padding: .5em;
}
.videos-container {
border: 2px solid black;
padding: .1em;
border-radius: 0.2em;
background: white;
overflow: hidden;
}
.videos-container h2 {
border: 0;
border-top: 1px solid black;
margin: 0;
text-align: center;
display:block;
}
video {
width:100%;
background: black;
}
.videos-container p {
margin: 0;
padding: 0;
text-align: left;
}
table {
width: 100%;
}
td, th, tr {
padding: 1px 3px;
}
td {
width: 40%;
}
td:first-child, th {
font-weight: bold;
color: #EC008C;
}
td:first-child, th:first-child {
width: 150px;
}
table {
border-bottom: 1px dotted #BBA9A9;
border-left: 1px dotted #BBA9A9;
}
label {
width: 100px;
display: inline-block;
text-align: right;
padding-right: 15px;
}
tr {
display: none;
}
</style>
<script src="https://www.webrtc-experiment.com/getStats.js"></script>
<script src="https://www.webrtc-experiment.com/IceServersHandler.js"></script>
<!-- for Edge/FF/Chrome/Opera/etc. getUserMedia support -->
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
</head>
<body>
<article>
<header style="text-align: center;">
<h1>
getStats.js Demo | WebRTC getStats API
</h1>
<p>
<a href="https://www.webrtc-experiment.com/">HOME</a>
<span> © </span>
<a href="http://www.MuazKhan.com/" target="_blank">Muaz Khan</a> .
<a href="http://twitter.com/WebRTCWeb" target="_blank" title="Twitter profile for WebRTC Experiments">@WebRTCWeb</a> .
<a href="https://github.com/muaz-khan?tab=repositories" target="_blank" title="Github Profile">Github</a> .
<a href="https://github.com/muaz-khan/getStats/issues?state=open" target="_blank">Latest issues</a> .
<a href="https://github.com/muaz-khan/getStats/commits/master" target="_blank">What's New?</a>
</p>
</header>
<div class="github-stargazers"></div>
<section style="margin: 20px; text-align: center; color: red;">
<small id="demoVersion"></small>
<script>var date=new Date(window.demoVersion),month=date.toLocaleString("en-us",{month:"long"}),day=date.getUTCDate();day.toString().length<=9&&(day=day.toString().length == 1 ? "0"+day : day);var year=date.getUTCFullYear(); document.getElementById("demoVersion").innerHTML="Last Updated On: <time>"+month+" "+day+" "+year+"</time>";</script>
</section>
<div style="text-align: center; margin-bottom: 20px;">
<button id="btn-stop" disabled>Stop Camera & getStats</button>
<select id="select-iceTransportPolicy">
<option value="all">STUN+TURN</option>
<option value="relay">TURN Only</option>
</select>
<select id="select-iceTransportLimitation">
<option value="all">UDP+TCP</option>
<option value="udp">UDP Only</option>
<option value="tcp">TCP Only</option>
</select>
<select id="select-codec">
<option value="vp8">VP8</option>
<option value="vp9">VP9</option>
<option value="h264">H264</option>
</select>
</div>
<table>
<thead>
<th></th>
<th>First Peer</th>
<th>Second Peer</th>
</thead>
<tbody>
<tr class="firefox-allowed chrome-allowed">
<td>
Videos
</td>
<td>
<video id="peer2-to-peer1" autoplay playsinilne controls muted volume=0></video>
</td>
<td>
<video id="peer1-to-peer2" autoplay playsinilne controls muted volume=0></video>
</td>
</tr>
<tr class="firefox-allowed chrome-allowed">
<td>Bandwidth Speed</td>
<td><span id="peer1-bandwidthSpeed"></span> per second</td>
<td><span id="peer2-bandwidthSpeed"></span> per second</td>
</tr>
<tr class="firefox-allowed chrome-allowed">
<td>STUN/TURN?</td>
<td>
<label>Local:</label> <span id="peer1-localIceType"></span> (<span id="peer1-localTransport"></span>)<br>
<label>Remote:</label> <span id="peer1-remoteIceType"></span> (<span id="peer1-remoteTransport"></span>)<br>
</td>
<td>
<label>Local:</label> <span id="peer2-localIceType"></span> (<span id="peer2-localTransport"></span>)<br>
<label>Remote:</label> <span id="peer2-remoteIceType"></span> (<span id="peer2-remoteTransport"></span>)<br>
</td>
</tr>
<tr class="firefox-not-allowed chrome-allowed">
<td>Codecs</td>
<td>
<label>Send:</label> <span id="peer1-codecsSend"></span><br>
<label>Recv:</label> <span id="peer1-codecsRecv"></span><br>
</td>
<td>
<label>Send:</label> <span id="peer2-codecsSend"></span><br>
<label>Recv:</label> <span id="peer2-codecsRecv"></span><br>
</td>
</tr>
<tr class="firefox-not-allowed chrome-allowed">
<td>Encryption</td>
<td><span id="peer1-encryptedAs"></span></td>
<td><span id="peer2-encryptedAs"></span></td>
</tr>
<tr class="firefox-allowed chrome-allowed">
<td>IP Address</td>
<td>
<label>Send:</label> <span id="peer1-externalIPAddressLocal"></span><br>
<label>Recv:</label> <span id="peer1-externalIPAddressRemote"></span>
</td>
<td>
<label>Send:</label> <span id="peer2-externalIPAddressLocal"></span><br>
<label>Recv:</label> <span id="peer2-externalIPAddressRemote"></span>
</td>
</tr>
<tr class="firefox-not-allowed chrome-allowed">
<td>Resolutions</td>
<td>
<label>Send:</label> <span id="peer1-videoResolutionsForSenders"></span><br>
<label>Recv:</label> <span id="peer1-videoResolutionsForReceivers"></span>
</td>
<td>
<label>Send:</label> <span id="peer2-videoResolutionsForSenders"></span><br>
<label>Recv:</label> <span id="peer2-videoResolutionsForReceivers"></span>
</td>
</tr>
<tr class="firefox-allowed chrome-allowed">
<td>Data</td>
<td>
<label>Send:</label> <span id="peer1-totalDataForSenders"></span><br>
<label>Recv:</label> <span id="peer1-totalDataForReceivers"></span>
</td>
<td>
<label>Send:</label> <span id="peer2-totalDataForSenders"></span><br>
<label>Recv:</label> <span id="peer2-totalDataForReceivers"></span>
</td>
</tr>
<tr class="firefox-allowed chrome-not-allowed">
<td>framerateMean</td>
<td>
<label>Send:</label> <span id="peer1-framerateMean"></span>
</td>
<td>
<label>Send:</label> <span id="peer2-framerateMean"></span>
</td>
</tr>
<tr class="firefox-allowed chrome-not-allowed">
<td>bitrateMean</td>
<td>
<label>Send:</label> <span id="peer1-bitrateMean"></span>
</td>
<td>
<label>Send:</label> <span id="peer2-bitrateMean"></span>
</td>
</tr>
<tr class="firefox-not-allowed chrome-allowed">
<td>latency</td>
<td>
<label>Audio:</label> <span id="peer1-audio-latency"></span><br>
<label>Video:</label> <span id="peer1-video-latency"></span>
</td>
<td>
<label>Audio:</label> <span id="peer2-audio-latency"></span><br>
<label>Video:</label> <span id="peer2-video-latency"></span>
</td>
</tr>
<tr class="firefox-not-allowed chrome-allowed">
<td>packetsLost</td>
<td>
<label>Audio:</label> <span id="peer1-audio-packetsLost"></span><br>
<label>Video:</label> <span id="peer1-video-packetsLost"></span>
</td>
<td>
<label>Audio:</label> <span id="peer2-audio-packetsLost"></span><br>
<label>Video:</label> <span id="peer2-video-packetsLost"></span>
</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 20px;">
Multi-user peer-to-peer demo: <a href="https://rtcmulticonnection.herokuapp.com/demos/getStats.html">getStats using RTCMultiConnection</a>
</blockquote>
<script>
var iceTransportPolicy = document.getElementById('select-iceTransportPolicy');
if(localStorage.getItem('iceTransportPolicy')) {
iceTransportPolicy.value = localStorage.getItem('iceTransportPolicy');
}
iceTransportPolicy.onchange = function() {
localStorage.setItem('iceTransportPolicy', this.value);
location.reload();
};
var iceTransportLimitation = document.getElementById('select-iceTransportLimitation');
if(localStorage.getItem('iceTransportLimitation')) {
iceTransportLimitation.value = localStorage.getItem('iceTransportLimitation');
}
iceTransportLimitation.onchange = function() {
localStorage.setItem('iceTransportLimitation', this.value);
location.reload();
};
var codec = document.getElementById('select-codec');
if(localStorage.getItem('codec')) {
codec.value = localStorage.getItem('codec');
}
codec.onchange = function() {
localStorage.setItem('codec', this.value);
location.reload();
};
function addIceCandidate(peer, candidate) {
if(iceTransportLimitation.value === 'tcp') {
if(candidate.candidate.toLowerCase().indexOf('tcp') === -1) {
return; // ignore UDP
}
}
if(iceTransportLimitation.value === 'udp') {
if(candidate.candidate.toLowerCase().indexOf('udp') === -1) {
return; // ignore UDP
}
}
peer.addIceCandidate(candidate);
}
</script>
<script>
var offerer, answerer;
var offererToAnswerer = document.getElementById('peer1-to-peer2');
var answererToOfferer = document.getElementById('peer2-to-peer1');
var iceServers = {
iceServers: IceServersHandler.getIceServers(),
iceTransportPolicy: iceTransportPolicy.value,
rtcpMuxPolicy: 'require',
bundlePolicy: 'max-bundle'
};
var mediaConstraints = {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
};
</script>
<script>
/* offerer */
function offererPeer(video_stream) {
offerer = new RTCPeerConnection(iceServers);
offerer.idx = 1;
video_stream.getTracks().forEach(function(track) {
offerer.addTrack(track, video_stream);
});
var firedOnce = false;
offerer.ontrack = function (event) {
if(firedOnce) return;
firedOnce = true;
offererToAnswerer.srcObject = event.streams[0];
if(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
getStats(offerer, event.streams[0].getTracks()[0], function(result) {
previewGetStatsResult(offerer, result);
}, 1000);
return;
}
getStats(offerer, function(result) {
previewGetStatsResult(offerer, result);
}, 1000);
};
offerer.onicecandidate = function (event) {
if (!event || !event.candidate) return;
addIceCandidate(answerer, event.candidate);
};
offerer.createOffer(mediaConstraints).then(function (offer) {
offer.sdp = preferSelectedCodec(offer.sdp);
offerer.setLocalDescription(offer).then(function() {
answererPeer(offer, video_stream);
});
});
}
</script>
<script>
/* answerer */
function answererPeer(offer, video_stream) {
answerer = new RTCPeerConnection(iceServers);
answerer.idx = 2;
video_stream.getTracks().forEach(function(track) {
answerer.addTrack(track, video_stream);
});
var firedOnce = false;
answerer.ontrack = function (event) {
if(firedOnce) return;
firedOnce = true;
answererToOfferer.srcObject = event.streams[0];
if(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
getStats(offerer, event.streams[0].getTracks()[0], function(result) {
previewGetStatsResult(offerer, result);
}, 1000);
return;
}
getStats(answerer, function(result) {
previewGetStatsResult(answerer, result);
}, 1000);
};
answerer.onicecandidate = function (event) {
if (!event || !event.candidate) return;
addIceCandidate(offerer, event.candidate);
};
answerer.setRemoteDescription(offer).then(function() {
answerer.createAnswer(mediaConstraints).then(function (answer) {
answer.sdp = preferSelectedCodec(answer.sdp);
answerer.setLocalDescription(answer).then(function() {
offerer.setRemoteDescription(answer);
});
});
});
}
</script>
<script>
var video_constraints = {
mandatory: {},
optional: []
};
function getUserMedia(successCallback) {
function errorCallback(e) {
alert(JSON.stringify(e, null, '\t'));
}
var mediaConstraints = { video: true, audio: true };
navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);
}
</script>
<script>
var CAMERA_STREAM;
getUserMedia(function (video_stream) {
CAMERA_STREAM = video_stream;
offererPeer(video_stream);
document.getElementById('btn-stop').disabled = false;
});
</script>
<script>
var STOP_GETSTATS = false;
document.getElementById('btn-stop').onclick = function() {
this.disabled = true;
STOP_GETSTATS = true;
if(CAMERA_STREAM && CAMERA_STREAM.active === true) {
CAMERA_STREAM.getTracks().forEach(function(track) {
track.stop();
});
}
};
function previewGetStatsResult(peer, result) {
if(STOP_GETSTATS) {
result.nomore();
return;
}
if(result.connectionType.remote.candidateType.indexOf('relayed') !== -1) {
result.connectionType.remote.candidateType = 'TURN';
}
else {
result.connectionType.remote.candidateType = 'STUN';
}
document.getElementById('peer' + peer.idx + '-remoteIceType').innerHTML = result.connectionType.remote.candidateType;
document.getElementById('peer' + peer.idx + '-externalIPAddressRemote').innerHTML = result.connectionType.remote.ipAddress.join(', ');
document.getElementById('peer' + peer.idx + '-remoteTransport').innerHTML = result.connectionType.remote.transport.join(', ');
if(result.connectionType.local.candidateType.indexOf('relayed') !== -1) {
result.connectionType.local.candidateType = 'TURN';
}
else {
result.connectionType.local.candidateType = 'STUN';
}
document.getElementById('peer' + peer.idx + '-localIceType').innerHTML = result.connectionType.local.candidateType;
document.getElementById('peer' + peer.idx + '-externalIPAddressLocal').innerHTML = result.connectionType.local.ipAddress.join(', ');
document.getElementById('peer' + peer.idx + '-localTransport').innerHTML = result.connectionType.local.transport.join(', ');
document.getElementById('peer' + peer.idx + '-encryptedAs').innerHTML = result.encryption;
document.getElementById('peer' + peer.idx + '-videoResolutionsForSenders').innerHTML = result.resolutions.send.width + 'x' + result.resolutions.send.height;
document.getElementById('peer' + peer.idx + '-videoResolutionsForReceivers').innerHTML = result.resolutions.recv.width + 'x' + result.resolutions.recv.height;
document.getElementById('peer' + peer.idx + '-totalDataForSenders').innerHTML = bytesToSize(result.audio.bytesSent + result.video.bytesSent);
document.getElementById('peer' + peer.idx + '-totalDataForReceivers').innerHTML = bytesToSize(result.audio.bytesReceived + result.video.bytesReceived);
document.getElementById('peer' + peer.idx + '-codecsSend').innerHTML = result.audio.send.codecs.concat(result.video.send.codecs).join(', ');
document.getElementById('peer' + peer.idx + '-codecsRecv').innerHTML = result.audio.recv.codecs.concat(result.video.recv.codecs).join(', ');
document.getElementById('peer' + peer.idx + '-bandwidthSpeed').innerHTML = bytesToSize(result.bandwidth.speed);
document.getElementById('peer' + peer.idx + '-framerateMean').innerHTML = bytesToSize(result.video.send.framerateMean);
document.getElementById('peer' + peer.idx + '-bitrateMean').innerHTML = bytesToSize(result.video.send.bitrateMean);
document.getElementById('peer' + peer.idx + '-audio-latency').innerHTML = result.audio.latency + 'ms';
document.getElementById('peer' + peer.idx + '-video-latency').innerHTML = result.video.latency + 'ms';
document.getElementById('peer' + peer.idx + '-audio-packetsLost').innerHTML = result.audio.packetsLost;
document.getElementById('peer' + peer.idx + '-video-packetsLost').innerHTML = result.video.packetsLost;
if (result.ended === true) {
result.nomore();
}
window.getStatsResult = result;
}
if (typeof window.InstallTrigger !== 'undefined') {
var all = document.querySelectorAll('.firefox-allowed');
for(var i = 0; i < all.length; i++) {
all[i].style.display = 'table-row';
}
var all = document.querySelectorAll('.firefox-not-allowed');
for(var i = 0; i < all.length; i++) {
all[i].style.display = 'none';
}
}
else {
var all = document.querySelectorAll('.chrome-allowed');
for(var i = 0; i < all.length; i++) {
all[i].style.display = 'table-row';
}
var all = document.querySelectorAll('.chrome-not-allowed');
for(var i = 0; i < all.length; i++) {
all[i].style.display = 'none';
}
}
function bytesToSize(bytes) {
var k = 1000;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes <= 0) {
return '0 Bytes';
}
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10);
if(!sizes[i]) {
return '0 Bytes';
}
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}
function preferSelectedCodec(sdp) {
var info = splitLines(sdp);
if(codec.value === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
if(codec.value === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
if(codec.value === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
sdp = preferCodec(sdp, codec.value, info);
return sdp;
}
function preferCodec(sdp, codec, info) {
var preferCodecNumber = '';
if(codec === 'vp8') {
if(!info.vp8LineNumber) {
return sdp;
}
preferCodecNumber = info.vp8LineNumber;
}
if(codec === 'vp9') {
if(!info.vp9LineNumber) {
return sdp;
}
preferCodecNumber = info.vp9LineNumber;
}
if(codec === 'h264') {
if(!info.h264LineNumber) {
return sdp;
}
preferCodecNumber = info.h264LineNumber;
}
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
var newOrder = [preferCodecNumber];
info.videoCodecNumbers.forEach(function(codecNumber) {
if(codecNumber === preferCodecNumber) return;
newOrder.push(codecNumber);
});
newLine += newOrder.join(' ');
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
return sdp;
}
function splitLines(sdp) {
var info = {};
sdp.split('\n').forEach(function(line) {
if (line.indexOf('m=video') === 0) {
info.videoCodecNumbers = [];
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
codecNumber = codecNumber.trim();
if(!codecNumber || !codecNumber.length) return;
info.videoCodecNumbers.push(codecNumber);
info.videoCodecNumbersOriginal = line;
});
}
if (line.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
});
return info;
}
</script>
<section>
<h2 class="header" id="updates" style="color: red; padding-bottom: .1em;"><a href="https://github.com/muaz-khan/getStats/issues" target="_blank">getStats Issues</a>
</h2>
<div id="github-issues"></div>
</section>
<section>
<h2 class="header" id="feedback">Feedback</h2>
<div>
<textarea id="message" style="border: 1px solid rgb(189, 189, 189); height: 8em; margin: .2em; outline: none; resize: vertical; width: 98%;" placeholder="Have any message? Suggestions or something went wrong?"></textarea>
</div>
<button id="send-message" style="font-size: 1em;">Send Message</button>
<small style="margin-left: 1em;">Enter your email too; if you want "direct" reply!</small>
</section>
<section>
<h2 class="header" id="updates" style="color: red; padding-bottom: .1em;"><a href="https://github.com/muaz-khan/getStats/commits/master" target="_blank">Latest Updates</a>
</h2>
<div id="github-commits"></div>
</section>
</article>
<a href="https://github.com/muaz-khan/getStats" class="fork-left"></a>
<footer>
<p>
<a href="https://www.webrtc-experiment.com/">WebRTC Experiments</a> © <a href="https://plus.google.com/+MuazKhan" rel="author" target="_blank">Muaz Khan</a>
<a href="mailto:muazkh@gmail.com" target="_blank">muazkh@gmail.com</a>
</p>
</footer>
<!-- commits.js is useless for you! -->
<script>
window.useThisGithubPath = 'muaz-khan/getStats';
</script>
<script src="https://www.webrtc-experiment.com/commits.js" async></script>
</body>
</html>