Skip to content

Commit

Permalink
audio updates, webaudio etc
Browse files Browse the repository at this point in the history
  • Loading branch information
steveseguin committed Dec 11, 2020
1 parent 7baa26c commit 4ec34fd
Show file tree
Hide file tree
Showing 29 changed files with 1,719 additions and 610 deletions.
12 changes: 7 additions & 5 deletions animations.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ $('#audioSource').on('mousedown touchend focusin focusout', function(e) {
});

$('#audioSource3').on('mousedown touchend focusin focusout', function(e) {
var state = $('#multiselect-trigger3').data('state') || 0;
var state = $('#multiselect-trigger3').attr('data-state') || 0;
if( state == 0 ) {
////open the dropdown
$('#multiselect-trigger3').data('state', '1').addClass('open').removeClass('closed');
$('#multiselect-trigger3').attr('data-state', '1').addClass('open').removeClass('closed');
$('#multiselect-trigger3').find('.chevron').removeClass('bottom');
$('#multiselect-trigger3').parent().find('.multiselect-contents').show();
$('#multiselect-trigger3').parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
Expand Down Expand Up @@ -154,17 +154,19 @@ $('#multiselect-trigger').on('mousedown touchend focusin focusout', function(e)
});
// multiselect dropdowns
$('#multiselect-trigger3').on('mousedown touchend focusin focusout', function(e) {
var state = $(this).data('state') || 0;
var state = $(this).attr('data-state') || 0;

if( state == 0 ) {
// open the dropdown
$(this).data('state', '1').addClass('open').removeClass('closed');
errorlog("STATE: "+state);
$(this).attr('data-state', '1').addClass('open').removeClass('closed');
$(this).find('.chevron').removeClass('bottom');
$(this).parent().find('.multiselect-contents').show();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').show();;
} else {
// close the dropdown
$(this).data('state', '0').addClass('closed').removeClass('open');
$(this).attr('data-state', '0').addClass('closed').removeClass('open');
$(this).find('.chevron').addClass('bottom');
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').not(":checked").parent().hide();;
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();;
Expand Down
264 changes: 264 additions & 0 deletions examples/stats.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
<html>

<head>
<title>OBS.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="./images/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="./images/favicon-16x16.png" />
<link rel="icon" href="./images/favicon.ico" />
<link itemprop="thumbnailUrl" href="./images/obsNinja_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 OBS.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/obsninja/blob/master/IFRAME.md">iFrame API</a>.</p>
<p>Click start to generate a stream using the OBS.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 ($("#obsn_viewer_" + viewer).length == 0) {
var h = now.getHours();
var m = now.getMinutes();
var s = now.getSeconds();
$('#viewers tbody').append('<tr id="obsn_viewer_' + viewer + '"><th class="obsn_viewer_label" scope="row"></th><td class="obsn_viewer_added">' + ("0" + h).slice(-2) + ':' + ("0" + m).slice(-2) + ':' + ("0" + s).slice(-2) + '</td><td class="obsn_viewer_qlr"></td><td class="obsn_viewer_resolution"></td><td class="obsn_viewer_platform"></td><td class="obsn_viewer_encoder"></td><td class="obsn_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) {
$("#obsn_viewer_" + viewer).find('.obsn_viewer_label').text(e.data.stats.outbound_stats[viewer].info.label);
}
if (e.data.stats.outbound_stats[viewer].quality_Limitation_Reason != undefined) {
$("#obsn_viewer_" + viewer).find('.obsn_viewer_qlr').text(e.data.stats.outbound_stats[viewer].quality_Limitation_Reason);
}
if (e.data.stats.outbound_stats[viewer].resolution != undefined) {
$("#obsn_viewer_" + viewer).find('.obsn_viewer_resolution').text(e.data.stats.outbound_stats[viewer].resolution);
}
if (e.data.stats.outbound_stats[viewer].info.platform != undefined) {
$("#obsn_viewer_" + viewer).find('.obsn_viewer_platform').text(e.data.stats.outbound_stats[viewer].info.platform);
}
if (e.data.stats.outbound_stats[viewer].encoder != undefined) {
$("#obsn_viewer_" + viewer).find('.obsn_viewer_encoder').text(e.data.stats.outbound_stats[viewer].encoder);
}
if (e.data.stats.outbound_stats[viewer].info.useragent != undefined) {
$("#obsn_viewer_" + viewer).find('.obsn_viewer_useragent').text(e.data.stats.outbound_stats[viewer].info.useragent);
}
$("#obsn_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://obs.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>
10 changes: 10 additions & 0 deletions iframe.html
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":false}, '*');};
iframeContainer.appendChild(button);

var button = document.createElement("button");
button.innerHTML = "ENABLE TALLY LIGHT";
button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":true}, '*');};
iframeContainer.appendChild(button);

var button = document.createElement("button");
button.innerHTML = "STOP TALLY LIGHT";
button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":false}, '*');};
iframeContainer.appendChild(button);

var button = document.createElement("button");
button.innerHTML = "Add Target Video";
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "add":true, "settings":{"style":{"width":"640px", "height":"360px", "float":"left", "border":"10px solid red", "display":"block"}}}, '*');}; // target can be a stream ID or * for all.
Expand Down
2 changes: 2 additions & 0 deletions images/hd.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/mic-animate.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/mic-slash.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/mic.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/obsNinja_logo_full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/old_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions images/sd.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4ec34fd

Please sign in to comment.