mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-11 13:48:38 +00:00
265 lines
15 KiB
HTML
265 lines
15 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<title>VDO.Ninja IFRAME Outgoing Stats Example</title>
|
|
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
|
|
<link rel="icon" type="image/png" sizes="32x32" href="../media//favicon-32x32.png" />
|
|
<link rel="icon" type="image/png" sizes="16x16" href="../media/favicon-16x16.png" />
|
|
<link rel="icon" href=".../media/favicon.ico" />
|
|
<link itemprop="thumbnailUrl" href="../media/vdoNinja_logo_full.png" />
|
|
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
|
<style>
|
|
body {
|
|
padding: 0;
|
|
margin: 0;
|
|
background-color: rgb(20, 25, 38);
|
|
}
|
|
|
|
iframe {
|
|
border: 0;
|
|
margin: 2px auto;
|
|
padding: 0;
|
|
display: block;
|
|
margin: 10px;
|
|
width: 640px;
|
|
height: 320px;
|
|
background-color: black;
|
|
}
|
|
|
|
input {
|
|
padding: 5px;
|
|
margin: 5px;
|
|
}
|
|
|
|
button {
|
|
padding: 5px;
|
|
margin: 5px;
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container-fluid">
|
|
<div class="row controls" style="margin-bottom:15px;border-bottom:1px solid black;">
|
|
<div class="col-8">
|
|
<input type="text" class="form-control" style="width:95%;margin:10px auto;" placeholder="Enter an VDO.Ninja View URL here" value="" id="viewlink" />
|
|
</div>
|
|
<div class="col-4">
|
|
<div class="row">
|
|
<div class="col-2"></div>
|
|
<div class="col-10">
|
|
<button type="button" class="btn btn-primary" style="margin:10px 0;width:calc(90% + 15px);margin-left:5px;" id="btnStart">Start</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row output">
|
|
<div class="col-7" id="source">
|
|
<iframe style="margin:0 auto;" allow="autoplay;camera;microphone" src=""></iframe>
|
|
</div>
|
|
<div class="col-5" id="sourcecontrols">
|
|
<div class="row text-light" style="margin-top:15px;">
|
|
<div class="col">
|
|
<p>This example will show all connections to the stream generated from this page using statistics gathered using the <a href="https://github.com/steveseguin/vdoninja/blob/master/IFRAME.md">iFrame API</a>.</p>
|
|
<p>Click start to generate a stream using the VDO.Ninja URL shown. If you use the example URL shown, you can <a id="aView" href="" target="_blank">click here</a> to connect to this stream as a viewer in a new window/tab, this will then show in the table below. Expired connections will be removed after a short delay.</p>
|
|
</div>
|
|
</div>
|
|
<div class="row" style="margin-top:5px;">
|
|
<div style="padding-top:10px;" class="col-4 text-right font-weight-bold text-light">Audio:</div>
|
|
<div class="col-8">
|
|
<button type="button" class="btn btn-sm btn-secondary" style="width:45%;" id="btnMuteAudio">Disable</button>
|
|
<button type="button" class="btn btn-sm btn-success" style="width:45%;" id="btnUnMuteAudio">Enabled</button>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div style="padding-top:10px;" class="col-4 text-right font-weight-bold text-light">Video:</div>
|
|
<div class="col-8">
|
|
<button type="button" class="btn btn-sm btn-secondary" style="width:45%;" id="btnMuteVidio">Disable</button>
|
|
<button type="button" class="btn btn-sm btn-success" style="width:45%;" id="btnUnMuteVidio">Enabled</button>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div style="padding-top:10px;" class="col-4 text-right font-weight-bold text-light">Stats:</div>
|
|
<div class="col-8">
|
|
<button type="button" class="btn btn-sm btn-secondary" style="width:45%;" id="btnStatsAuto">Auto Refresh Off</button>
|
|
<button type="button" class="btn btn-sm btn-secondary" style="width:45%;" id="btnStatsRefresh">Refresh</button>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div style="padding-top:5px;" class="col-6 text-right font-weight-bold text-light">Outbound Connections:</div>
|
|
<div style="padding-top:5px;" class="col-6 font-weight-bold text-light" id="divTotalConnections">0</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<table id="viewers" style="margin-top:15px;" class="table table-hover text-center table-striped table-dark">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col" class="align-middle">Label</th>
|
|
<th scope="col" class="align-middle">Added</th>
|
|
<th scope="col" class="align-middle">Quality Limit Reason</th>
|
|
<th scope="col" class="align-middle">Resolution</th>
|
|
<th scope="col" class="align-middle">Platform</th>
|
|
<th scope="col" class="align-middle">Encoder</th>
|
|
<th scope="col" class="align-middle">User Agent</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
|
<script>
|
|
var autorefresh = false;
|
|
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
|
var eventer = window[eventMethod];
|
|
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
|
|
|
eventer(messageEvent, function(e) {
|
|
//Check message is coming from our iframe, otherwise we don't care
|
|
if (e.source != $('#source iframe')[0].contentWindow) return;
|
|
|
|
if ("stats" in e.data) {
|
|
var now = new Date(); //Used for "Added" column and to remove stale viewers
|
|
for (var viewer in e.data.stats.outbound_stats) {
|
|
//Check to see if a row exists for this viewier, if not then its a new viewer and we should create a row
|
|
if ($("#vdon_viewer_" + viewer).length == 0) {
|
|
var h = now.getHours();
|
|
var m = now.getMinutes();
|
|
var s = now.getSeconds();
|
|
$('#viewers tbody').append('<tr id="vdon_viewer_' + viewer + '"><th class="vdon_viewer_label" scope="row"></th><td class="vdon_viewer_added">' + ("0" + h).slice(-2) + ':' + ("0" + m).slice(-2) + ':' + ("0" + s).slice(-2) + '</td><td class="vdon_viewer_qlr"></td><td class="vdon_viewer_resolution"></td><td class="vdon_viewer_platform"></td><td class="vdon_viewer_encoder"></td><td class="vdon_viewer_useragent"></td></tr>');
|
|
}
|
|
//Insert/update stats
|
|
//Initially objects can be available but without any attributes, check they exist and ignore till the basics are available
|
|
if (e.data.stats.outbound_stats[viewer] == undefined) continue;
|
|
if (e.data.stats.outbound_stats[viewer].info == undefined) continue;
|
|
//Checking these exist as not all attributes are available straight away when stats are created
|
|
if (e.data.stats.outbound_stats[viewer].info.label != undefined) {
|
|
$("#vdon_viewer_" + viewer).find('.vdon_viewer_label').text(e.data.stats.outbound_stats[viewer].info.label);
|
|
}
|
|
if (e.data.stats.outbound_stats[viewer].quality_Limitation_Reason != undefined) {
|
|
$("#vdon_viewer_" + viewer).find('.vdon_viewer_qlr').text(e.data.stats.outbound_stats[viewer].quality_Limitation_Reason);
|
|
}
|
|
if (e.data.stats.outbound_stats[viewer].resolution != undefined) {
|
|
$("#vdon_viewer_" + viewer).find('.vdon_viewer_resolution').text(e.data.stats.outbound_stats[viewer].resolution);
|
|
}
|
|
if (e.data.stats.outbound_stats[viewer].info.platform != undefined) {
|
|
$("#vdon_viewer_" + viewer).find('.vdon_viewer_platform').text(e.data.stats.outbound_stats[viewer].info.platform);
|
|
}
|
|
if (e.data.stats.outbound_stats[viewer].encoder != undefined) {
|
|
$("#vdon_viewer_" + viewer).find('.vdon_viewer_encoder').text(e.data.stats.outbound_stats[viewer].encoder);
|
|
}
|
|
if (e.data.stats.outbound_stats[viewer].info.useragent != undefined) {
|
|
$("#vdon_viewer_" + viewer).find('.vdon_viewer_useragent').text(e.data.stats.outbound_stats[viewer].info.useragent);
|
|
}
|
|
$("#vdon_viewer_" + viewer).data('last', now.getTime()); //Used below to remove old viewers
|
|
}
|
|
//Mark and then remove viewers who have not been seen for a while
|
|
$('#viewers tbody tr').each(function(el) {
|
|
if (parseInt($(this).data('last')) < (now.getTime() - 10000)) { //10 seconds
|
|
$(this).remove();
|
|
} else if (parseInt($(this).data('last')) < (now.getTime())) { //Mark viewer in red to show they have disappeared, note that it takes a few seconds for this to happen
|
|
$(this).addClass('bg-danger');
|
|
} else { //Viewer is there, make sure they're not marked as missing
|
|
$(this).removeClass('bg-danger');
|
|
}
|
|
});
|
|
$('#divTotalConnections').text(e.data.stats.total_outbound_connections);
|
|
}
|
|
});
|
|
|
|
$(document).ready(function() {
|
|
$('#btnMuteAudio').on('click', function() {
|
|
$(this).addClass('btn-success').removeClass('btn-secondary').text('Disabled');
|
|
$('#btnUnMuteAudio').removeClass('btn-success').addClass('btn-secondary').text('Enable');
|
|
$('#source iframe')[0].contentWindow.postMessage({
|
|
"mic": false
|
|
}, '*');
|
|
});
|
|
$('#btnUnMuteAudio').on('click', function() {
|
|
$(this).addClass('btn-success').removeClass('btn-secondary').text('Enabled');
|
|
$('#btnMuteAudio').removeClass('btn-success').addClass('btn-secondary').text('Disable');
|
|
$('#source iframe')[0].contentWindow.postMessage({
|
|
"mic": true
|
|
}, '*');
|
|
});
|
|
$('#btnMuteVidio').on('click', function() {
|
|
$(this).addClass('btn-success').removeClass('btn-secondary').text('Disabled');
|
|
$('#btnUnMuteVidio').removeClass('btn-success').addClass('btn-secondary').text('Enable');
|
|
$('#source iframe')[0].contentWindow.postMessage({
|
|
"camera": false
|
|
}, '*');
|
|
});
|
|
$('#btnUnMuteVidio').on('click', function() {
|
|
$(this).addClass('btn-success').removeClass('btn-secondary').text('Enabled');
|
|
$('#btnMuteVidio').removeClass('btn-success').addClass('btn-secondary').text('Disable');
|
|
$('#source iframe')[0].contentWindow.postMessage({
|
|
"camera": true
|
|
}, '*');
|
|
});
|
|
|
|
$('#btnStatsAuto').on('click', function() {
|
|
if (autorefresh) {
|
|
autorefresh = false;
|
|
$('#btnStatsAuto').removeClass('btn-success').addClass('btn-secondary').text('Auto Refresh Off');
|
|
} else {
|
|
autorefresh = true;
|
|
$('#btnStatsAuto').addClass('btn-success').removeClass('btn-secondary').text('Auto Refresh On');
|
|
}
|
|
});
|
|
$('#btnStatsRefresh').on('click', function() {
|
|
$(this).addClass('btn-success').removeClass('btn-secondary').attr('disabled', true);
|
|
$('#source iframe')[0].contentWindow.postMessage({
|
|
"getStats": true
|
|
}, '*');
|
|
setTimeout(function() {
|
|
$('#btnStatsRefresh').addClass('btn-secondary').removeClass('btn-success').attr('disabled', false);
|
|
}, 700);
|
|
});
|
|
|
|
$('#btnStart').on('click', function() {
|
|
//Reset buttons as currently we can't check the state of these properties
|
|
$('#btnMuteAudio,#btnMuteVidio').removeClass('btn-success').addClass('btn-secondary').text('Disable');
|
|
$('#btnUnMuteAudio,#btnUnMuteVidio').addClass('btn-success').removeClass('btn-secondary').text('Enabled');
|
|
//Update the iframe source from the input field, yup, that simple
|
|
$('#source iframe').attr('src', $('#viewlink').val());
|
|
//Start autorefresh of stats
|
|
autorefresh = true;
|
|
$('#btnStatsAuto').addClass('btn-success').removeClass('btn-secondary').text('Auto Refresh On');
|
|
});
|
|
//Start checking for stats
|
|
setInterval(function() {
|
|
if (autorefresh == false) return;
|
|
$('#source iframe')[0].contentWindow.postMessage({
|
|
"getStats": true
|
|
}, '*');
|
|
}, 1000);
|
|
//Add in random ID and password strings to URL's, the below is purely for the purposes of this example
|
|
var pushid = makeid();
|
|
var password = makeid();
|
|
var baseUrl = "https://vdo.ninja/";
|
|
$('#aView').attr('href', baseUrl + '?view=' + pushid + '&password=' + password + '&label=Test_Link');
|
|
$('#viewlink').val(baseUrl + '?push=' + pushid + '&password=' + password + '&autostart&turn=false&fps=25&maxbitrate=1000&cleanoutput&audiobitrate=32&aec=0&denoise=0&webcam');
|
|
});
|
|
//This function is purely used to generate random push id and password strings for the purposes of this example
|
|
function makeid() {
|
|
var result = '';
|
|
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
var charactersLength = characters.length;
|
|
for (var i = 0; i < 8; i++) {
|
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|