mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-11 21:58:35 +00:00
fixed an issue with advanced audio settings; added meter
This commit is contained in:
parent
dd950b1c4e
commit
2a31e5508e
@ -81,6 +81,20 @@ $("#lightbox-animations").get(0).sheet.deleteRule(0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// multiselect dropdowns
|
||||
$('#audioSource').on('mousedown touchend focusin focusout', function(e) {
|
||||
var state = $('.multiselect-trigger').data('state') || 0;
|
||||
if( state == 0 ) {
|
||||
// open the dropdown
|
||||
$('.multiselect-trigger').data('state', '1').addClass('open').removeClass('closed');
|
||||
$('.multiselect-trigger').find('.chevron').removeClass('bottom');
|
||||
$('.multiselect-trigger').parent().find('.multiselect-contents').show();
|
||||
$('.multiselect-trigger').parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
|
||||
$('.multiselect-trigger').parent().find('.multiselect-contents').find('input[type="checkbox"]').show();;
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// multiselect dropdowns
|
||||
$('.multiselect-trigger').on('mousedown touchend focusin focusout', function(e) {
|
||||
@ -88,14 +102,14 @@ $('.multiselect-trigger').on('mousedown touchend focusin focusout', function(e)
|
||||
if( state == 0 ) {
|
||||
// open the dropdown
|
||||
$(this).data('state', '1').addClass('open').removeClass('closed');
|
||||
$(this).find('.fa').removeClass('fa-chevron-down').addClass('fa-chevron-up');
|
||||
$(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).find('.fa').removeClass('fa-chevron-up').addClass('fa-chevron-down');
|
||||
$(this).find('.chevron').addClass('bottom');
|
||||
//$(this).parent().find('.multiselect-contents').hide();
|
||||
//$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();
|
||||
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').not(":checked").parent().hide();;
|
||||
|
||||
71
dual.html
Normal file
71
dual.html
Normal file
@ -0,0 +1,71 @@
|
||||
<html>
|
||||
<head><title>Dual Input</title>
|
||||
<style>
|
||||
body{
|
||||
padding:0;
|
||||
margin:0;
|
||||
}
|
||||
iframe {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
display:block;
|
||||
margin:10px;
|
||||
width:40%;;
|
||||
height:40%;
|
||||
}
|
||||
#viewlink {
|
||||
width:400px;
|
||||
}
|
||||
#container {
|
||||
display:block;
|
||||
padding:0px;
|
||||
}
|
||||
input{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
button{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
function loadIframe(){
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
var iframeContainer = document.createElement("div");
|
||||
|
||||
iframe.allow="autoplay";
|
||||
|
||||
iframe.src = document.getElementById("viewlink").value;
|
||||
|
||||
|
||||
iframeContainer.appendChild(iframe);
|
||||
document.getElementById("container").appendChild(iframeContainer);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Close";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');iframe.parentNode.parentNode.removeChild(iframeContainer);}
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Reload";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');}
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<input placeholder="Enter an OBS.Ninja Room Link" id="viewlink" />
|
||||
<button onclick="loadIframe();">ADD</button>
|
||||
<div id="container">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
52
index.html
52
index.html
@ -48,8 +48,8 @@
|
||||
<span itemprop="thumbnail" itemscope itemtype="http://schema.org/ImageObject">
|
||||
<link itemprop="url" href="./images/obsNinja_logo_full.png" />
|
||||
</span>
|
||||
<script language="javascript" type="text/javascript" src="./thirdparty/CodecsHandler.js?ver=7"></script>
|
||||
<script language="javascript" type="text/javascript" src="./webrtc.js?ver=9"></script>
|
||||
<script language="javascript" type="text/javascript" src="./thirdparty/CodecsHandler.js?ver=8"></script>
|
||||
<script language="javascript" type="text/javascript" src="./webrtc.js?ver=11"></script>
|
||||
<input id="zoomSlider" type="range" style="display: none;" />
|
||||
<div id="header">
|
||||
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px;">
|
||||
@ -183,10 +183,10 @@
|
||||
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
|
||||
</button>
|
||||
<br />
|
||||
<span style="background-color: #f3f3f3; display: inline-block; padding: 5px 10px; border: 1px solid #ccc; vertical-align: middle;">
|
||||
<span data-translate="video-source">Video source</span>:
|
||||
<span class="videoMenu">
|
||||
<i class="las la-video"></i><span data-translate="video-source"> Video Source </span>
|
||||
|
||||
<select id="videoSource" style="background-color: #FFF; padding:5px; display: display:inline-block;vertical-align: middle;"></select>
|
||||
<select id="videoSource" ></select>
|
||||
<span id="gear_webcam" style="display: inline-block;" onclick="toggle(document.getElementById('videoSettings'));">
|
||||
|
||||
<i class="las la-cog" style="font-size: 170%; vertical-align: middle;" aria-hidden="true"></i>
|
||||
@ -194,7 +194,7 @@
|
||||
</span>
|
||||
<br />
|
||||
<center>
|
||||
<span id="videoSettings" style="margin: auto auto; display: none; background-color: #f3f3f3; max-width: 500px; padding: 10px 0; margin: 0 0 5px 0;">
|
||||
<span id="videoSettings" style="display: none;">
|
||||
<form id="webcamquality">
|
||||
<input type="radio" id="fullhd" name="resolution" value="0" />
|
||||
<label for="fullhd">
|
||||
@ -217,9 +217,10 @@
|
||||
</center>
|
||||
<div class="form-group multiselect">
|
||||
<a class="form-control multiselect-trigger" tabindex="3">
|
||||
<div id="audioTitle" style="padding: 5px;" class="title">
|
||||
<span data-translate="select-audio-source">Select Audio Source</span>:
|
||||
<i class="las la-chevron-down" aria-hidden="true"></i>
|
||||
<div id="audioTitle" class="title">
|
||||
<i class="las la-microphone-alt"></i><span data-translate="select-audio-source"> Audio Source(s) </span>
|
||||
<i class="chevron bottom" aria-hidden="true"></i>
|
||||
<div class="meter" id="meter1"></div>
|
||||
</div>
|
||||
</a>
|
||||
<ul id="audioSource" class="multiselect-contents">
|
||||
@ -231,6 +232,14 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br />
|
||||
<span id="headphonesDiv" style="text-align:left; margin:17px 0; max-width: 550px; min-width: 420px; background-color: #f3f3f3; display: none; padding: 10px 10px; border: 1px solid #ccc; vertical-align: middle;">
|
||||
<div id="audioTitle2" class="title">
|
||||
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination:</span></div>
|
||||
<select id="outputSource" style="background-color: #FFF; padding:5px; display: display:inline-block;vertical-align: middle;"></select>
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div class="outer close">
|
||||
<div class="inner">
|
||||
@ -240,6 +249,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="container-2" class="column columnfade" style="background-color: #ddd; overflow-y: auto;">
|
||||
<h2 id="add_screen">
|
||||
<span data-translate="remote-screenshare-obs">Remote Screenshare into OBS</span>
|
||||
@ -259,7 +269,7 @@
|
||||
<i class="las la-cog" style="font-size: 170%; vertical-align: middle;" aria-hidden="true"></i>
|
||||
</span>
|
||||
<center>
|
||||
<span id="videoSettings2" style="margin: auto auto; display: none; background-color: white; vertical-aligh: middle; border: 3px solid #ccc; max-width: 500px; padding: 10px 0 5px 0; margin: 10px 0 5px 0;">
|
||||
<span id="videoSettings2" style="margin: auto auto; display: none; background-color: white; vertical-aligh: middle; border: 3px solid #ccc; max-width: 500px; padding: 10px 10px 5px 10px; margin: 10px 0 5px 0;">
|
||||
<form id="webcamquality2">
|
||||
<input type="radio" id="fullhd2" name="resolution2" value="0" />
|
||||
<label for="fullhd">
|
||||
@ -281,7 +291,7 @@
|
||||
<br />
|
||||
</center>
|
||||
<p>
|
||||
<span data-translate="audio-sources">Audio Sources</span>:
|
||||
<span data-translate="audio-sources">Audio Sources</span>
|
||||
<br />
|
||||
<select id="audioSourceScreenshare" multiple style="height: 60px; width: 200px; resize: both; overflow: auto; padding: 5px;" onchange="requestAudioStream();">
|
||||
<option value="screenshare" selected>
|
||||
@ -292,6 +302,15 @@
|
||||
</option>
|
||||
</select>
|
||||
</p>
|
||||
<br />
|
||||
<span id="headphonesDiv2" style="background-color: #f3f3f3; display: none; padding: 5px 10px; border: 1px solid #ccc; vertical-align: middle;">
|
||||
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination:</span>:<br />
|
||||
<select id="outputSourceScreenshare" style="background-color: #FFF; padding:5px; display: display:inline-block;vertical-align: middle;" onclick="requestOutputAudioStream();">
|
||||
<option value="default">
|
||||
<span data-translate="default">Default Device</span>
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
<div class="outer close">
|
||||
<div class="inner">
|
||||
@ -436,8 +455,8 @@
|
||||
</li>
|
||||
<br />
|
||||
|
||||
Site last updated: <a href="https://www.reddit.com/r/OBSNinja/comments/hhba50/version_8_just_released_see_the_change_log_here/">July 10th, 2020</a>. The previous version can be found at
|
||||
<a href="https://obs.ninja/v7/">https://obs.ninja/v7/</a> if you are having new issues.
|
||||
Site last updated: July 20th, 2020. The previous version can be found at
|
||||
<a href="https://obs.ninja/v8/">https://obs.ninja/v8/</a> if you are having new issues.
|
||||
|
||||
|
||||
<br />
|
||||
@ -470,6 +489,9 @@
|
||||
<a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="gridlayout"></div>
|
||||
<div id="controls_blank" style="display: none;">
|
||||
<center>
|
||||
@ -596,7 +618,7 @@
|
||||
// If you wish to change branding, blank offers a good clean start.
|
||||
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
|
||||
-->
|
||||
<script type="text/javascript" id="main-js" src="./main.js?ver=9"></script>
|
||||
<script type="text/javascript" src="./animations.js"></script>
|
||||
<script type="text/javascript" id="main-js" src="./main.js?ver=11"></script>
|
||||
<script type="text/javascript" src="./animations.js?ver=1"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
123
main.css
123
main.css
@ -5,10 +5,21 @@
|
||||
border:0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.meter {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 10px;
|
||||
background: green;
|
||||
transition: all 52ms linear;
|
||||
}
|
||||
|
||||
#mynetwork {
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
border: 1px solid lightgray;
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.email {
|
||||
@ -71,6 +82,29 @@ button {
|
||||
color:black;
|
||||
}
|
||||
|
||||
.chevron{
|
||||
padding:0px 6px;
|
||||
}
|
||||
|
||||
.chevron::before {
|
||||
border-style: solid;
|
||||
border-width: 0.14em 0.14em 0 0;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 0.32em;
|
||||
left: 0.15em;
|
||||
position: relative;
|
||||
top: .43em;
|
||||
transform: rotate(-45deg);
|
||||
vertical-align: top;
|
||||
width: 0.32em;
|
||||
}
|
||||
|
||||
.chevron.bottom:before {
|
||||
top: .28em;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
|
||||
|
||||
.pressed {
|
||||
background: #e3e3e3;
|
||||
@ -196,10 +230,11 @@ body {
|
||||
|
||||
.gowebcam {
|
||||
font-size:110%;
|
||||
padding:10px;
|
||||
border:3px solid #DDDDDD;
|
||||
cursor:pointer;
|
||||
background-color:#DDDDDD;
|
||||
margin: 20px;
|
||||
padding:10px 50px;
|
||||
}
|
||||
|
||||
.gobutton {
|
||||
@ -243,6 +278,7 @@ body {
|
||||
}
|
||||
.gowebcam {
|
||||
padding:5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.infoblob {
|
||||
@ -302,11 +338,44 @@ body {
|
||||
float:none !important;
|
||||
padding: 15px 10px 1px 10px !important;
|
||||
}
|
||||
input[type=radio]{
|
||||
b
|
||||
|
||||
|
||||
#videoSettings {
|
||||
max-width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
}
|
||||
|
||||
.videoMenu {
|
||||
max-width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
}
|
||||
|
||||
div.multiselect {
|
||||
max-width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
}
|
||||
|
||||
#headphonesDiv {
|
||||
max-width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
#outputSource {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#audioSourceScreenshare {
|
||||
max-width: 90% !important;
|
||||
min-width: 90% !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
#videoSettings2 {
|
||||
max-width: 90% !important;
|
||||
min-width: 90% !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -519,7 +588,6 @@ img {
|
||||
display: block !important;
|
||||
margin: auto auto !important;
|
||||
position: relative !important;
|
||||
transform: translate(0, -50%) !important;
|
||||
top: 50% !important;
|
||||
}
|
||||
|
||||
@ -758,6 +826,15 @@ video {
|
||||
background-color: #0066aa !important;
|
||||
}
|
||||
|
||||
#audioTitle{
|
||||
text-align:left;
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
#audioTitle2{
|
||||
text-align:left;
|
||||
padding: 0px 10px 10px 1px;
|
||||
}
|
||||
.multiselect .multiselect-trigger:hover {
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
@ -774,15 +851,40 @@ video {
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
#videoSettings {
|
||||
margin: auto auto;
|
||||
background-color: #f3f3f3;
|
||||
width: 420px;
|
||||
padding: 10px 0;
|
||||
margin: 0 0 5px 0;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#videoSource {
|
||||
background-color: #FFF;
|
||||
display: display:inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.videoMenu{
|
||||
background-color: #f3f3f3;
|
||||
width: 420px;
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid #ccc;
|
||||
vertical-align: middle;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
div.multiselect {
|
||||
background-color:#FFF;
|
||||
max-width:550px;
|
||||
width: 420px;
|
||||
white-space: nowrap;
|
||||
overflow:hidden;
|
||||
min-width:100px;
|
||||
margin:auto auto;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom:0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.multiselect .multiselect-contents {
|
||||
@ -859,6 +961,7 @@ input[type=checkbox]
|
||||
-o-transform: scale(1.3); /* Opera */
|
||||
transform: scale(1.3);
|
||||
padding: 5px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
#screenshare {
|
||||
|
||||
711
main.js
711
main.js
@ -268,7 +268,6 @@ if (urlParams.has('password')){
|
||||
if (urlParams.has('stereo') || urlParams.has('s')){ // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono
|
||||
log("STEREO ENABLED");
|
||||
session.stereo = urlParams.get('stereo') || urlParams.get('s');
|
||||
session.stereo = session.stereo;
|
||||
|
||||
if (session.stereo){
|
||||
session.stereo = session.stereo.toLowerCase();
|
||||
@ -321,7 +320,6 @@ if (urlParams.has("aec") || urlParams.has("ec")){
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (urlParams.has("autogain") || urlParams.has("ag")){
|
||||
|
||||
session.autoGainControl = urlParams.get('autogain') || urlParams.get('ag');
|
||||
@ -375,6 +373,13 @@ if (urlParams.has('audiobitrate') || urlParams.has('ab')){ // both peers need th
|
||||
|
||||
if (urlParams.has('streamid') || urlParams.has('view') || urlParams.has('v') || urlParams.has('pull')){ // the streams we want to view; if set, but let blank, we will request no streams to watch.
|
||||
session.view = urlParams.get('streamid') || urlParams.get('view') || urlParams.get('v') || urlParams.get('pull'); // this value can be comma seperated for multiple streams to pull
|
||||
getById("headphonesDiv").style.display="inline-block";
|
||||
getById("headphonesDiv2").style.display="inline-block";
|
||||
|
||||
if (session.view.split(",").length>1){
|
||||
session.view_set = session.view.split(",");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (urlParams.has('icefilter')){
|
||||
@ -396,14 +401,27 @@ if (urlParams.has('obsoff') || urlParams.has('oo')){
|
||||
session.disableOBS = true;
|
||||
}
|
||||
|
||||
if (urlParams.has('noaudio') || urlParams.has('na')){
|
||||
log("disable audio playback");
|
||||
session.audio = false;
|
||||
|
||||
|
||||
if (urlParams.has('novideo') || urlParams.has('nv') || urlParams.has('hidevideo')){
|
||||
if (session.novideo===""){
|
||||
session.novideo=[];
|
||||
} else {
|
||||
session.novideo = urlParams.get('novideo') || urlParams.get('nv') || urlParams.has('hidevideo');
|
||||
session.novideo = session.novideo.split(",");
|
||||
}
|
||||
log("disable video playback");
|
||||
log(session.novideo);
|
||||
}
|
||||
|
||||
if (urlParams.has('novideo') || urlParams.has('nv')){
|
||||
log("disable video playback");
|
||||
session.video = false;
|
||||
if (urlParams.has('noaudio') || urlParams.has('na') || urlParams.has('hideaudio')){
|
||||
if (session.noaudio==""){
|
||||
session.noaudio=[];
|
||||
} else {
|
||||
session.noaudio = urlParams.get('noaudio') || urlParams.get('na') || urlParams.has('hideaudio');
|
||||
session.noaudio = session.noaudio.split(",");
|
||||
}
|
||||
log("disable audio playback");
|
||||
}
|
||||
|
||||
|
||||
@ -429,6 +447,7 @@ if (urlParams.has('nocursor')){
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (urlParams.has('codec')){
|
||||
log("CODEC CHANGED");
|
||||
session.codec = urlParams.get('codec');
|
||||
@ -557,7 +576,14 @@ function changeLg(lang){
|
||||
|
||||
if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')){
|
||||
session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb');
|
||||
session.bitrate = parseInt(session.bitrate);
|
||||
|
||||
|
||||
if ((session.view_set) && (session.bitrate.split(",").length>1)){
|
||||
session.bitrate_set = session.bitrate.split(",");
|
||||
session.bitrate = parseInt(session.bitrate_set[0]);
|
||||
} else {
|
||||
session.bitrate = parseInt(session.bitrate);
|
||||
}
|
||||
if (session.bitrate<1){session.bitrate=false;}
|
||||
log("BITRATE ENABLED");
|
||||
log(session.bitrate);
|
||||
@ -734,6 +760,7 @@ session.connect();
|
||||
|
||||
var url = window.location.pathname;
|
||||
var filename = url.substring(url.lastIndexOf('/')+1);
|
||||
|
||||
if (filename.split(".").length==1){
|
||||
if (filename.length<2){
|
||||
filename=false;
|
||||
@ -788,6 +815,8 @@ if ( (session.roomid) || (urlParams.has('roomid')) || (urlParams.has('r')) || (u
|
||||
roomid = roomid.replace(/[\W_]+/g,"_");
|
||||
session.roomid = roomid;
|
||||
|
||||
getById("headphonesDiv2").style.display="inline-block";
|
||||
getById("headphonesDiv").style.display="inline-block";
|
||||
getById("info").innerHTML = "";
|
||||
getById("info").style.color="#CCC";
|
||||
getById("videoname1").value = roomid;
|
||||
@ -1100,7 +1129,7 @@ function publishScreen(){
|
||||
|
||||
var constraints = window.constraints = {
|
||||
audio: {echoCancellation: session.echoCancellation, autoGainControl: session.autoGainControl, noiseSuppression: session.noiseSuppression },
|
||||
video: {width: width, height: height, cursor: "never", mediaSource: "browser"}
|
||||
video: {width: width, height: height, mediaSource: "screen"}
|
||||
};
|
||||
|
||||
if (!(urlParams.has("denoise"))){
|
||||
@ -1113,12 +1142,20 @@ function publishScreen(){
|
||||
constraints.audio.echoCancellation = false; // the defaults for screen publishing should be off.
|
||||
}
|
||||
|
||||
|
||||
if (session.framerate){
|
||||
constraints.video.frameRate = session.framerate;
|
||||
}
|
||||
|
||||
var audioSelect = document.querySelector('select#audioSourceScreenshare');
|
||||
var outputSelect = document.querySelector('select#outputSourceScreenshare');
|
||||
|
||||
|
||||
session.sink = outputSelect.options[outputSelect.selectedIndex].value;
|
||||
log("Session SInk: "+session.sink);
|
||||
if (session.sink=="default"){session.sink=false;}
|
||||
|
||||
log("*");
|
||||
session.publishScreen(constraints, title, audioSelect).then((res)=>{
|
||||
if (res==false){return;} // no screen selected
|
||||
log("streamID is: "+session.streamID);
|
||||
@ -1186,6 +1223,99 @@ function publishWebcam(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
var audioContext = null;
|
||||
var meter = null;
|
||||
var mediaStreamSource = null;
|
||||
var drawLoopLimiter = null;
|
||||
|
||||
function volumeStream(stream) {
|
||||
log("gostream");
|
||||
if (meter){
|
||||
meter.shutdown;
|
||||
}
|
||||
if (stream.getAudioTracks().length){
|
||||
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
audioContext = new AudioContext();
|
||||
mediaStreamSource = audioContext.createMediaStreamSource(stream);
|
||||
meter = createAudioMeter(audioContext);
|
||||
mediaStreamSource.connect(meter);
|
||||
clearInterval(drawLoopLimiter);
|
||||
drawLoopLimiter = setTimeout(function(){drawLoop();},1)
|
||||
}
|
||||
}
|
||||
|
||||
function drawLoop( time ) {
|
||||
log("draw volume");
|
||||
if (!document.getElementById("meter1")){
|
||||
return
|
||||
}
|
||||
if (meter.clipping){
|
||||
getById("meter1").style.width = "100px";
|
||||
getById("meter1").style.background = "red";
|
||||
} else {
|
||||
if ((100-meter.volume*100*4)<=1){
|
||||
getById("meter1").style.width = "100px";
|
||||
getById("meter1").style.background = "green";
|
||||
} else if ((100-meter.volume*100*4)<100){
|
||||
getById("meter1").style.width = (meter.volume*100*4)+"px";
|
||||
getById("meter1").style.background = "green";
|
||||
} else {
|
||||
getById("meter1").style.width = "0px";
|
||||
}
|
||||
}
|
||||
clearInterval(drawLoopLimiter);
|
||||
drawLoopLimiter = setTimeout(function(){drawLoop();},50)
|
||||
}
|
||||
function createAudioMeter(audioContext,clipLevel,averaging,clipLag) {
|
||||
var processor = audioContext.createScriptProcessor(512);
|
||||
processor.onaudioprocess = volumeAudioProcess;
|
||||
processor.clipping = false;
|
||||
processor.lastClip = 0;
|
||||
processor.volume = 0;
|
||||
processor.clipLevel = clipLevel || 0.95;
|
||||
processor.averaging = averaging || 0.90;
|
||||
processor.clipLag = clipLag || 750;
|
||||
|
||||
processor.connect(audioContext.destination);
|
||||
|
||||
processor.checkClipping = function(){
|
||||
if (!this.clipping)
|
||||
return false;
|
||||
if ((this.lastClip + this.clipLag) < window.performance.now())
|
||||
this.clipping = false;
|
||||
return this.clipping;
|
||||
};
|
||||
|
||||
processor.shutdown = function(){
|
||||
this.disconnect();
|
||||
this.onaudioprocess = null;
|
||||
};
|
||||
|
||||
return processor;
|
||||
}
|
||||
|
||||
function volumeAudioProcess( event ) {
|
||||
var buf = event.inputBuffer.getChannelData(0);
|
||||
var bufLength = buf.length;
|
||||
var sum = 0;
|
||||
var x;
|
||||
|
||||
for (var i=0; i<bufLength; i++) {
|
||||
x = buf[i];
|
||||
if (Math.abs(x)>=this.clipLevel) {
|
||||
this.clipping = true;
|
||||
this.lastClip = window.performance.now();
|
||||
} else {
|
||||
this.clipping = false;
|
||||
}
|
||||
sum += x * x ;
|
||||
}
|
||||
|
||||
var rms = Math.pow(sum / bufLength,0.3);
|
||||
this.volume = Math.max(rms, this.volume*this.averaging);
|
||||
}
|
||||
|
||||
function joinRoom(roomname, maxbitrate=false){
|
||||
roomname = roomname.replace(/[^0-9a-z]/gi, '');
|
||||
if (roomname.length){
|
||||
@ -1309,7 +1439,7 @@ function createRoom(roomname=false){
|
||||
function toggle(ele, tog=false) {
|
||||
var x = ele;
|
||||
if (x.style.display === "none") {
|
||||
x.style.display = "block";
|
||||
x.style.display = "inline-block";
|
||||
} else {
|
||||
x.style.display = "none";
|
||||
}
|
||||
@ -1364,9 +1494,52 @@ function enumerateDevices() {
|
||||
}
|
||||
}
|
||||
|
||||
function requestOutputAudioStream(){
|
||||
try {
|
||||
warnlog("GET USER MEDIA");
|
||||
return navigator.mediaDevices.getUserMedia({audio:true, video:false }).then(function(stream1){ // Apple needs thi to happen before I can access EnumerateDevices.
|
||||
log("get media sources; request audio stream");
|
||||
return enumerateDevices().then(function(deviceInfos){
|
||||
stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
|
||||
track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels
|
||||
});
|
||||
const audioOutputSelect = document.querySelector('#outputSourceScreenshare');
|
||||
audioOutputSelect.remove(0);
|
||||
audioOutputSelect.removeAttribute("onclick");
|
||||
|
||||
for (let i = 0; i !== deviceInfos.length; ++i) {
|
||||
const deviceInfo = deviceInfos[i];
|
||||
if (deviceInfo==null){continue;}
|
||||
const option = document.createElement('option');
|
||||
option.value = deviceInfo.deviceId;
|
||||
if (deviceInfo.kind === 'audiooutput') {
|
||||
const option = document.createElement('option');
|
||||
option.value = deviceInfo.deviceId || "default";
|
||||
if (option.value == session.sink){
|
||||
option.selected = true;
|
||||
}
|
||||
option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`;
|
||||
audioOutputSelect.appendChild(option);
|
||||
} else {
|
||||
log('Some other kind of source/device: ', deviceInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (e){
|
||||
if (window.isSecureContext) {
|
||||
alert("An error has occured when trying to access the webcam. The reason is not known.");
|
||||
} else {
|
||||
alert("Error acessing webcam.\n\nWebsite is loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function requestAudioStream(){
|
||||
try {
|
||||
return navigator.mediaDevices.getUserMedia({audio:true, video:false }).then(function(stream1){ // Apple needs thi to happen before I can access EnumerateDevices.
|
||||
warnlog("GET USER MEDIA");
|
||||
return navigator.mediaDevices.getUserMedia({audio:true, video:false }).then(function(stream1){ // Apple needs thi to happen before I can access EnumerateDevices.
|
||||
log("get media sources; request audio stream");
|
||||
return enumerateDevices().then(function(deviceInfos){
|
||||
stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
|
||||
@ -1376,16 +1549,7 @@ function requestAudioStream(){
|
||||
const audioInputSelect = document.querySelector('select#audioSourceScreenshare');
|
||||
audioInputSelect.remove(1);
|
||||
audioInputSelect.removeAttribute("onchange");
|
||||
//var temp = {};
|
||||
//for (let i = 0; i !== deviceInfos.length; ++i) { // getting rid of duplicates. This is a bit useless; I need to revisit.
|
||||
// if (deviceInfos[i].kind === 'audioinput') {
|
||||
// if (deviceInfos[i].deviceId in temp){
|
||||
// deviceInfos[i] = null;
|
||||
// } else {
|
||||
// temp[deviceInfos[i].deviceId]=true;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
for (let i = 0; i !== deviceInfos.length; ++i) {
|
||||
const deviceInfo = deviceInfos[i];
|
||||
@ -1393,7 +1557,7 @@ function requestAudioStream(){
|
||||
const option = document.createElement('option');
|
||||
option.value = deviceInfo.deviceId;
|
||||
if (deviceInfo.kind === 'audioinput') {
|
||||
option.text = deviceInfo.label || `microphone ${audioInputSelect.length + 1}`;
|
||||
option.text = deviceInfo.label || `Microphone ${audioInputSelect.length + 1}`;
|
||||
audioInputSelect.appendChild(option);
|
||||
} else {
|
||||
log('Some other kind of source/device: ', deviceInfo);
|
||||
@ -1420,25 +1584,16 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
|
||||
try{
|
||||
const audioInputSelect = document.querySelector('#audioSource');
|
||||
const videoSelect = document.querySelector('select#videoSource');
|
||||
const audioOutputSelect = document.querySelector('#outputSource');
|
||||
const selectors = [ videoSelect];
|
||||
|
||||
// Handles being called several times to update labels. Preserve values.
|
||||
const values = selectors.map(select => select.value);
|
||||
selectors.forEach(select => {
|
||||
while (select.firstChild) {
|
||||
select.removeChild(select.firstChild);
|
||||
}
|
||||
});
|
||||
//var temp = {};
|
||||
//for (let i = 0; i !== deviceInfos.length; ++i) {
|
||||
// if (deviceInfos[i].kind === 'audioinput') {
|
||||
// if (deviceInfos[i].deviceId in temp){
|
||||
// deviceInfos[i] = null;
|
||||
// } else {
|
||||
// temp[deviceInfos[i].deviceId]=true;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
var counter = 1;
|
||||
for (let i = 0; i !== deviceInfos.length; ++i) {
|
||||
const deviceInfo = deviceInfos[i];
|
||||
@ -1497,16 +1652,26 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
|
||||
option.value = deviceInfo.deviceId || "default";
|
||||
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
|
||||
videoSelect.appendChild(option);
|
||||
} else if (deviceInfo.kind === 'audiooutput'){
|
||||
const option = document.createElement('option');
|
||||
option.value = deviceInfo.deviceId || "default";
|
||||
if (option.value == session.sink){
|
||||
option.selected = true;
|
||||
}
|
||||
option.text = deviceInfo.label || `Speaker ${outputSelect.length + 1}`;
|
||||
audioOutputSelect.appendChild(option);
|
||||
} else {
|
||||
log('Some other kind of source/device: ', deviceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (audioOutputSelect.childNodes.length==0){
|
||||
const option = document.createElement('option');
|
||||
option.value = "default";
|
||||
option.text = "System Default";
|
||||
audioOutputSelect.appendChild(option);
|
||||
}
|
||||
|
||||
//var option = document.createElement('option');
|
||||
//option.text = "Disable Audio";
|
||||
//option.value = "ZZZ";
|
||||
//audioInputSelect.appendChild(option); // NO AUDIO OPTION
|
||||
|
||||
option = document.createElement('option');
|
||||
option.text = "Disable Video";
|
||||
@ -1519,7 +1684,6 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
|
||||
}
|
||||
});
|
||||
|
||||
//audioInputSelect.selectedIndex = 0;
|
||||
} catch (e){
|
||||
errorlog(e);
|
||||
}
|
||||
@ -1628,12 +1792,8 @@ function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) {
|
||||
}
|
||||
}
|
||||
|
||||
function grabVideo(quality=0, audioEnable=false){
|
||||
if( activatedPreview == true){log("activated preview return 2");return;}
|
||||
activatedPreview = true;
|
||||
log("trying with quality:"+quality);
|
||||
|
||||
var videoSelect = document.querySelector('select#videoSource');
|
||||
function changeVideo(deviceID="default", quality=0){
|
||||
|
||||
var sq=0;
|
||||
if (session.quality>2){ // 1080, 720, and 360p
|
||||
@ -1657,79 +1817,186 @@ function grabVideo(quality=0, audioEnable=false){
|
||||
quality=1;
|
||||
}
|
||||
}
|
||||
var constraints = {
|
||||
audio: false,
|
||||
video: getUserMediaVideoParams(quality, iOS)
|
||||
};
|
||||
|
||||
//enumerateDevices().then(gotDevices).then(function(){
|
||||
if ((iOS) || (iPad)){
|
||||
constraints.video.deviceId = deviceID; // iPhone 6s compatible ?
|
||||
} else {
|
||||
constraints.video.deviceId = deviceID; // NDI Compatible
|
||||
}
|
||||
//}
|
||||
|
||||
var audio = false;
|
||||
var streams = [];
|
||||
if ((videoSelect.value == "ZZZ") || (audioEnable==true)){ // if there is no video, or if manually set to audio ready, then do this step.
|
||||
if (session.width){
|
||||
constraints.video.width = {exact: session.width};
|
||||
}
|
||||
if (session.height){
|
||||
constraints.video.height = {exact: session.height};
|
||||
}
|
||||
if (session.framerate){
|
||||
constraints.video.frameRate = {exact: session.framerate};
|
||||
} else if (session.maxframerate){
|
||||
constraints.video.frameRate = {max: session.maxframerate};
|
||||
}
|
||||
|
||||
warnlog(constraints);
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
|
||||
|
||||
var audioSelect = document.querySelector('#audioSource').querySelectorAll("input");
|
||||
var audioList = [];
|
||||
|
||||
for (var i=0; i<audioSelect.length;i++){
|
||||
if (audioSelect[i].value=="ZZZ"){
|
||||
continue;
|
||||
}
|
||||
if (audioSelect[i].checked){
|
||||
audioList.push(audioSelect[i]);
|
||||
log("adding tracks");
|
||||
session.streamSrc.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
session.streamSrc.removeTrack(track);
|
||||
});
|
||||
stream.getVideoTracks().forEach((track) => {
|
||||
session.streamSrc.addTrack(track);
|
||||
});
|
||||
}).catch(function(e){
|
||||
errorlog(e);
|
||||
if (e.name === "OverconstrainedError"){
|
||||
errorlog(e.message);
|
||||
log("Resolution or framerate didn't work");
|
||||
} else if (e.name === "NotReadableError"){
|
||||
if (iOS){
|
||||
alert("An error occured. Upgrading to at least iOS 13.4 should fix this glitch from happening again");
|
||||
} else {
|
||||
alert("Error Listing Media Devices.\n\nThe default Camera may already be in use with another app. Typically webcams can only be accessed by one program at a time.\n\nThe selected device may also not be supported.");
|
||||
}
|
||||
return;
|
||||
} else if (e.name === "NavigatorUserMediaError"){
|
||||
alert("Unknown error: 'NavigatorUserMediaError'");
|
||||
return;
|
||||
} else {
|
||||
errorlog("An unknown camera error occured");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function getAudioOnly(){
|
||||
var audioSelect = document.querySelector('#audioSource').querySelectorAll("input");
|
||||
var audioList = [];
|
||||
var streams = [];
|
||||
|
||||
for (var i=0; i<audioSelect.length;i++){
|
||||
if (audioSelect[i].value=="ZZZ"){
|
||||
continue;
|
||||
}
|
||||
if (audioSelect[i].checked){
|
||||
audioList.push(audioSelect[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i=0; i<audioList.length;i++){
|
||||
|
||||
for (var i=1; i<audioList.length;i++){
|
||||
if ((audioList[i].value=="default") && session.echoCancellation && session.autoGainControl && session.noiseSuppression){
|
||||
var constraint = {audio: true};
|
||||
} else if (session.echoCancellation && session.autoGainControl && session.noiseSuppression){ // Just trying to avoid problems with some systems that don't support these features
|
||||
var constraint = {audio: {deviceId: {exact: audioList[i].value}}};
|
||||
} else {
|
||||
var constraint = {audio: {deviceId: {exact: audioList[i].value}}};
|
||||
|
||||
constraint.audio.echoCancellation = session.echoCancellation;
|
||||
constraint.audio.autoGainControl = session.autoGainControl;
|
||||
constraint.audio.noiseSuppression = session.noiseSuppression;
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraint).then(function (stream2){
|
||||
streams.push(stream2);
|
||||
}).catch(errorlog);
|
||||
}
|
||||
|
||||
if (audioList.length){
|
||||
audio = {deviceId: {exact: audioList[0].value}};
|
||||
|
||||
audio.echoCancellation = session.echoCancellation;
|
||||
audio.autoGainControl = session.autoGainControl;
|
||||
audio.noiseSuppression = session.noiseSuppression;
|
||||
|
||||
}
|
||||
}
|
||||
constraint.video = false;
|
||||
warnlog(constraint);
|
||||
var stream = await navigator.mediaDevices.getUserMedia(constraint).then(function (stream2){
|
||||
log("pushing stream2");
|
||||
return stream2;
|
||||
}).catch(errorlog); // More error reporting maybe?
|
||||
if (stream){
|
||||
streams.push(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return streams;
|
||||
}
|
||||
|
||||
function applyMirror(mirror){
|
||||
if (mirror){
|
||||
if (session.mirrored && session.flipped){
|
||||
getById('previewWebcam').style.transform = " scaleX(-1) scaleY(-1)";
|
||||
} else if (session.mirrored){
|
||||
getById('previewWebcam').style.transform = "scaleX(-1)";
|
||||
} else if (session.flipped){
|
||||
getById('previewWebcam').style.transform = "scaleY(-1) scaleX(1)";
|
||||
} else {
|
||||
getById('previewWebcam').style.transform = "scaleX(1)";
|
||||
}
|
||||
} else {
|
||||
if (session.mirrored && session.flipped){
|
||||
getById('previewWebcam').style.transform = " scaleX(1) scaleY(-1)";
|
||||
} else if (session.mirrored){
|
||||
getById('previewWebcam').style.transform = "scaleX(1)";
|
||||
} else if (session.flipped){
|
||||
getById('previewWebcam').style.transform = "scaleY(-1) scaleX(-1)";
|
||||
} else {
|
||||
getById('previewWebcam').style.transform = "scaleX(-1)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function grabVideo(quality=0){
|
||||
if( activatedPreview == true){log("activated preview return 2");return;}
|
||||
activatedPreview = true;
|
||||
|
||||
try {
|
||||
log("Resetting Stream");
|
||||
var oldstream = getById('previewWebcam').srcObject;
|
||||
if (oldstream){
|
||||
oldstream.getVideoTracks().forEach(function(track) {
|
||||
track.stop();
|
||||
oldstream.removeTrack(track);
|
||||
});
|
||||
} else {
|
||||
getById('previewWebcam').srcObject = new MediaStream();
|
||||
}
|
||||
} catch(e){
|
||||
errorlog(e);
|
||||
}
|
||||
|
||||
var videoSelect = document.querySelector('select#videoSource');
|
||||
var mirror=false;
|
||||
|
||||
if (videoSelect.value == "ZZZ"){ // without video. Nice and quick
|
||||
var constraints = {
|
||||
audio: audio,
|
||||
video: false
|
||||
};
|
||||
log(constraints);
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
|
||||
log("adding additional audio tracks");
|
||||
for (var i=0; i<streams.length;i++){
|
||||
streams[i].getAudioTracks().forEach(function(track){
|
||||
stream.addTrack(track);
|
||||
log(track);
|
||||
});
|
||||
if (videoSelect.value == "ZZZ"){ // if there is no video, or if manually set to audio ready, then do this step.
|
||||
// revisit
|
||||
applyMirror(mirror);
|
||||
|
||||
var gowebcam = getById("gowebcam");
|
||||
gowebcam.disabled =false;
|
||||
gowebcam.style.backgroundColor = "#3C3";
|
||||
gowebcam.style.color = "black";
|
||||
gowebcam.style.fontWeight="bold";
|
||||
gowebcam.innerHTML = "START";
|
||||
} else {
|
||||
|
||||
var sq=0;
|
||||
if (session.quality>2){ // 1080, 720, and 360p
|
||||
sq = 2; // hacking my own code. TODO: ugly, so I need to revisit this.
|
||||
} else {
|
||||
sq = session.quality;
|
||||
}
|
||||
|
||||
if (sq!==false){
|
||||
if (quality>sq){
|
||||
quality=sq; // override the user's setting
|
||||
}
|
||||
streams = null;
|
||||
|
||||
getById('previewWebcam').srcObject = stream; // set the preview window and run with it
|
||||
|
||||
var gowebcam = getById("gowebcam");
|
||||
gowebcam.disabled =false;
|
||||
gowebcam.style.backgroundColor = "#3C3";
|
||||
gowebcam.style.color = "black";
|
||||
gowebcam.style.fontWeight="bold";
|
||||
gowebcam.innerHTML = "PRESS WHEN READY!";
|
||||
|
||||
}).catch(function(e){
|
||||
errorlog(e);
|
||||
alert("Error: Media stream creation failed.");
|
||||
});
|
||||
|
||||
} else { // with video
|
||||
}
|
||||
|
||||
if (iOS){ // iOS will not work correctly at 1080p; likely a h264 codec issue.
|
||||
if (quality==0){
|
||||
quality=1;
|
||||
}
|
||||
} else if (iPad){
|
||||
if (quality==0){
|
||||
quality=1;
|
||||
}
|
||||
}
|
||||
|
||||
var constraints = {
|
||||
audio: audio,
|
||||
audio: false,
|
||||
video: getUserMediaVideoParams(quality, iOS)
|
||||
};
|
||||
if ((iOS) || (iPad)){
|
||||
@ -1737,6 +2004,19 @@ function grabVideo(quality=0, audioEnable=false){
|
||||
} else {
|
||||
constraints.video.deviceId = videoSelect.value; // NDI Compatible
|
||||
}
|
||||
|
||||
log(videoSelect.options[videoSelect.selectedIndex].text);
|
||||
if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("OBS-Camera")){
|
||||
mirror=true;
|
||||
} else if (videoSelect.options[videoSelect.selectedIndex].text.includes(" back")){
|
||||
mirror=true;
|
||||
} else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Back Camera")){
|
||||
mirror=true;
|
||||
} else {
|
||||
mirror=false;
|
||||
}
|
||||
|
||||
|
||||
if (session.width){
|
||||
constraints.video.width = {exact: session.width};
|
||||
}
|
||||
@ -1749,94 +2029,116 @@ function grabVideo(quality=0, audioEnable=false){
|
||||
constraints.video.frameRate = {max: session.maxframerate};
|
||||
}
|
||||
|
||||
log(constraints);
|
||||
|
||||
setTimeout(()=>{
|
||||
try {
|
||||
|
||||
log("Trying Constraints");
|
||||
var oldstream = getById('previewWebcam').srcObject;
|
||||
if (oldstream){
|
||||
oldstream.getTracks().forEach(function(track) {
|
||||
track.stop();
|
||||
});
|
||||
}
|
||||
} catch(e){
|
||||
errorlog(e);
|
||||
}
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
|
||||
if (audioEnable == false){
|
||||
stream.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
|
||||
track.stop();
|
||||
});
|
||||
log("GOT IT BUT WITH NO AUDIO");
|
||||
activatedPreview = false;
|
||||
grabVideo(quality,true);
|
||||
} else {
|
||||
log("adding tracks");
|
||||
for (var i=0; i<streams.length;i++){
|
||||
streams[i].getAudioTracks().forEach(function(track){
|
||||
stream.addTrack(track);
|
||||
log(track);
|
||||
});
|
||||
}
|
||||
streams = null;
|
||||
|
||||
getById('previewWebcam').srcObject = stream; // set the preview window and run with it
|
||||
|
||||
|
||||
var gowebcam = getById("gowebcam");
|
||||
gowebcam.disabled =false;
|
||||
gowebcam.style.backgroundColor = "#3C3";
|
||||
gowebcam.style.color = "black";
|
||||
gowebcam.style.fontWeight="bold";
|
||||
gowebcam.innerHTML = "PRESS WHEN READY!";
|
||||
|
||||
// Once crbug.com/711524 is fixed, we won't need to wait anymore. This is
|
||||
// currently needed because capabilities can only be retrieved after the
|
||||
// device starts streaming. This happens after and asynchronously w.r.t.
|
||||
// getUserMedia() returns.
|
||||
setTimeout(function(){dragElement(getById('previewWebcam'));},1000); // focus
|
||||
|
||||
log("DONE - found stream");
|
||||
|
||||
}
|
||||
}).catch(function(e){
|
||||
activatedPreview = false;
|
||||
errorlog(e);
|
||||
if (e.name === "OverconstrainedError"){
|
||||
errorlog(e.message);
|
||||
log("Resolution or framerate didn't work");
|
||||
} else if (e.name === "NotReadableError"){
|
||||
if (iOS){
|
||||
alert("An error occured. Upgrading to at least iOS 13.4 should fix this glitch from happening again");
|
||||
} else {
|
||||
alert("Error Listing Media Devices.\n\nThe default Camera may already be in use with another app. Typically webcams can only be accessed by one program at a time.\n\nThe selected device may also not be supported.");
|
||||
}
|
||||
getById('gowebcam').innerHTML="Problem with Camera";
|
||||
activatedPreview=true;
|
||||
return;
|
||||
} else if (e.name === "NavigatorUserMediaError"){
|
||||
getById('gowebcam').innerHTML="Problem with Camera";
|
||||
alert("Unknown error: 'NavigatorUserMediaError'");
|
||||
return;
|
||||
} else {
|
||||
errorlog("An unknown camera error occured");
|
||||
}
|
||||
if (quality<=9){
|
||||
grabVideo(quality+1);
|
||||
} else {
|
||||
errorlog("********Camera failed to work");
|
||||
activatedPreview=true;
|
||||
getById('gowebcam').innerHTML="Problem with Camera";
|
||||
alert("Camera failed to load. \n\nPlease make sure it is not already in use by another application.\n\nPlease make sure you have accepted the camera permissions.");
|
||||
}
|
||||
warnlog(constraints);
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
|
||||
|
||||
log("adding video tracks");
|
||||
|
||||
volumeStream(stream);
|
||||
stream.getVideoTracks().forEach(function(track){
|
||||
getById('previewWebcam').srcObject.addTrack(track);
|
||||
});
|
||||
},1);
|
||||
|
||||
applyMirror(mirror);
|
||||
|
||||
var gowebcam = getById("gowebcam");
|
||||
gowebcam.disabled =false;
|
||||
gowebcam.style.backgroundColor = "#3C3";
|
||||
gowebcam.style.color = "black";
|
||||
gowebcam.style.fontWeight="bold";
|
||||
gowebcam.innerHTML = "START";
|
||||
|
||||
// Once crbug.com/711524 is fixed, we won't need to wait anymore. This is
|
||||
// currently needed because capabilities can only be retrieved after the
|
||||
// device starts streaming. This happens after and asynchronously w.r.t.
|
||||
// getUserMedia() returns.
|
||||
setTimeout(function(){dragElement(getById('previewWebcam'));},1000); // focus
|
||||
|
||||
log("DONE - found stream");
|
||||
}).catch(function(e){
|
||||
activatedPreview = false;
|
||||
errorlog(e);
|
||||
if (e.name === "OverconstrainedError"){
|
||||
errorlog(e.message);
|
||||
log("Resolution or framerate didn't work");
|
||||
} else if (e.name === "NotReadableError"){
|
||||
if (iOS){
|
||||
alert("An error occured. Closing existing tabs in Safari may solve this issue.");
|
||||
} else {
|
||||
alert("Error Listing Media Devices.\n\nThe default Camera may already be in use with another app. Typically webcams can only be accessed by one program at a time.\n\nThe selected device may also not be supported.");
|
||||
}
|
||||
getById('gowebcam').innerHTML="Problem with Camera";
|
||||
activatedPreview=true;
|
||||
return;
|
||||
} else if (e.name === "NavigatorUserMediaError"){
|
||||
getById('gowebcam').innerHTML="Problem with Camera";
|
||||
alert("Unknown error: 'NavigatorUserMediaError'");
|
||||
return;
|
||||
} else {
|
||||
errorlog("An unknown camera error occured");
|
||||
}
|
||||
if (quality<=9){
|
||||
grabVideo(quality+1);
|
||||
} else {
|
||||
errorlog("********Camera failed to work");
|
||||
activatedPreview=true;
|
||||
getById('gowebcam').innerHTML="Problem with Camera";
|
||||
alert("Camera failed to load. \n\nPlease make sure it is not already in use by another application.\n\nPlease make sure you have accepted the camera permissions.");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function grabAudio(){
|
||||
if( activatedPreview == true){log("activated preview return 2");return;}
|
||||
activatedPreview = true;
|
||||
|
||||
try {
|
||||
log("Resetting Audio Streams");
|
||||
var oldstream = getById('previewWebcam').srcObject;
|
||||
if (oldstream){
|
||||
oldstream.getAudioTracks().forEach(function(track) {
|
||||
track.stop();
|
||||
oldstream.removeTrack(track);
|
||||
});
|
||||
} else { // if no stream exists
|
||||
getById('previewWebcam').srcObject = new MediaStream();
|
||||
}
|
||||
} catch(e){
|
||||
errorlog(e);
|
||||
}
|
||||
|
||||
var videoSelect = document.querySelector('select#videoSource');
|
||||
|
||||
// if there is no video, or if manually set to audio ready, then do this step.
|
||||
var mirror=false; // revisit
|
||||
applyMirror(mirror);
|
||||
var streams = await getAudioOnly();
|
||||
log("adding additional audio tracks");
|
||||
|
||||
for (var i=0; i<streams.length;i++){
|
||||
streams[i].getAudioTracks().forEach(function(track){
|
||||
getById('previewWebcam').srcObject.addTrack(track);
|
||||
log(track);
|
||||
});
|
||||
}
|
||||
|
||||
log(getById('previewWebcam').srcObject);
|
||||
|
||||
if (streams.length){
|
||||
volumeStream(getById('previewWebcam').srcObject);
|
||||
}
|
||||
|
||||
var gowebcam = getById("gowebcam");
|
||||
gowebcam.disabled =false;
|
||||
gowebcam.style.backgroundColor = "#3C3";
|
||||
gowebcam.style.color = "black";
|
||||
gowebcam.style.fontWeight="bold";
|
||||
gowebcam.innerHTML = "START";
|
||||
}
|
||||
|
||||
|
||||
function enterPressed(event, callback){
|
||||
// Number 13 is the "Enter" key on the keyboard
|
||||
@ -1968,10 +2270,16 @@ function dragElement(elmnt) {
|
||||
}
|
||||
|
||||
|
||||
function setupWebcamSelection(){
|
||||
function setupWebcamSelection(stream){
|
||||
log("setup webcam");
|
||||
try {
|
||||
return enumerateDevices().then(gotDevices).then(function(){
|
||||
|
||||
stream.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
|
||||
track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels
|
||||
stream.removeTrack(track);
|
||||
});
|
||||
|
||||
log("enumerated");
|
||||
if (parseInt(getById("webcamquality").elements.namedItem("resolution").value)==3){
|
||||
session.maxframerate = 30;
|
||||
@ -1986,6 +2294,7 @@ function setupWebcamSelection(){
|
||||
|
||||
var audioSelect = document.querySelector('#audioSource');
|
||||
var videoSelect = document.querySelector('select#videoSource');
|
||||
var outputSelect = document.querySelector('select#outputSource');
|
||||
|
||||
audioSelect.onchange = function(){
|
||||
|
||||
@ -1997,7 +2306,7 @@ function setupWebcamSelection(){
|
||||
|
||||
log("AUDIO source CHANGED");
|
||||
activatedPreview=false;
|
||||
grabVideo(parseInt(getById("webcamquality").elements.namedItem("resolution").value));
|
||||
grabAudio();
|
||||
};
|
||||
videoSelect.onchange = function(){
|
||||
|
||||
@ -2011,6 +2320,19 @@ function setupWebcamSelection(){
|
||||
activatedPreview=false;
|
||||
grabVideo(parseInt(getById("webcamquality").elements.namedItem("resolution").value));
|
||||
};
|
||||
|
||||
outputSelect.onchange = function(){
|
||||
session.sink = outputSelect.options[outputSelect.selectedIndex].value;
|
||||
if (session.sink=="default"){session.sink=false;} else {
|
||||
getById("previewWebcam").setSinkId(session.sink).then(() => {
|
||||
log("New Output Device:"+session.sink);
|
||||
}).catch(error => {
|
||||
errorlog(error);
|
||||
setTimeout(function(){alert("Failed to change audio output destination.");},1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getById("webcamquality").onchange = function(){
|
||||
var gowebcam = getById("gowebcam");
|
||||
gowebcam.disabled = true;
|
||||
@ -2030,6 +2352,8 @@ function setupWebcamSelection(){
|
||||
|
||||
activatedPreview = false;
|
||||
grabVideo(parseInt(getById("webcamquality").elements.namedItem("resolution").value));
|
||||
activatedPreview = false;
|
||||
grabAudio();
|
||||
|
||||
}).catch(e => {errorlog(e);})
|
||||
} catch (e){errorlog(e);}
|
||||
@ -2072,6 +2396,7 @@ function previewWebcam(){
|
||||
log("old stream found");
|
||||
oldstream.getTracks().forEach(function(track) {
|
||||
track.stop();
|
||||
oldstream.removeTrack(track);
|
||||
log("stopping old track");
|
||||
});
|
||||
}
|
||||
@ -2079,15 +2404,11 @@ function previewWebcam(){
|
||||
} catch (e){
|
||||
errorlog(e);
|
||||
}
|
||||
|
||||
try {
|
||||
navigator.mediaDevices.getUserMedia({audio:true, video:true }).timeout(15000).then(function(stream){ // Apple needs thi to happen before I can access EnumerateDevices.
|
||||
log("got first stream");
|
||||
setupWebcamSelection().then(()=>{
|
||||
log("Got second stream");
|
||||
stream.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now.
|
||||
track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels
|
||||
});
|
||||
});
|
||||
setupWebcamSelection(stream);
|
||||
}).catch(function(err){
|
||||
errorlog(err); /* handle the error */
|
||||
if (err.name == "NotFoundError" || err.name == "DevicesNotFoundError") {
|
||||
@ -2107,7 +2428,7 @@ function previewWebcam(){
|
||||
setTimeout(function(){alert(err);},1);
|
||||
}
|
||||
errorlog("trying to list webcam again");
|
||||
setupWebcamSelection();
|
||||
setupWebcamSelection(stream);
|
||||
});
|
||||
} catch (e){
|
||||
if (window.isSecureContext) {
|
||||
|
||||
97
thirdparty/CodecsHandler.js
vendored
97
thirdparty/CodecsHandler.js
vendored
@ -20,6 +20,7 @@ Copyright (c) 2012-2020 [Muaz Khan](https://github.com/muaz-khan)
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Sourced from: https://cdn.webrtc-experiment.com/CodecsHandler.js
|
||||
|
||||
// *FILE HAS BEEN HEAVILY MODIFIED BY STEVE SEGUIN. ALL RIGHTS RESERVED WHERE APPLICABLE *
|
||||
@ -171,10 +172,12 @@ var CodecsHandler = (function() {
|
||||
|
||||
function getVideoBitrates(sdp) {
|
||||
|
||||
var defaultBitrate = 2500;
|
||||
|
||||
var sdpLines = sdp.split('\r\n');
|
||||
var mLineIndex = findLine(sdpLines, 'm=', 'video');
|
||||
if (mLineIndex === null) {
|
||||
return 2500;
|
||||
return defaultBitrate;
|
||||
}
|
||||
var videoMLine = sdpLines[mLineIndex];
|
||||
var pattern = new RegExp('m=video\\s\\d+\\s[A-Z/]+\\s');
|
||||
@ -189,7 +192,7 @@ var CodecsHandler = (function() {
|
||||
}
|
||||
|
||||
if (!codecPayload) {
|
||||
return 2500;
|
||||
return defaultBitrate;
|
||||
}
|
||||
|
||||
var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000');
|
||||
@ -199,7 +202,7 @@ var CodecsHandler = (function() {
|
||||
}
|
||||
|
||||
if (!rtxIndex) {
|
||||
return 2500;
|
||||
return defaultBitrate;
|
||||
}
|
||||
|
||||
var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString());
|
||||
@ -208,7 +211,7 @@ var CodecsHandler = (function() {
|
||||
var maxBitrate = parseInt(sdpLines[rtxFmtpLineIndex].split("x-google-max-bitrate=")[1].split(";")[0]);
|
||||
var minBitrate = parseInt(sdpLines[rtxFmtpLineIndex].split("x-google-min-bitrate=")[1].split(";")[0]);
|
||||
} catch(e){
|
||||
return 2500;
|
||||
return defaultBitrate;
|
||||
}
|
||||
|
||||
if (minBitrate>maxBitrate){
|
||||
@ -217,7 +220,7 @@ var CodecsHandler = (function() {
|
||||
if (maxBitrate<1){maxBitrate=1;}
|
||||
return maxBitrate
|
||||
} else {
|
||||
return 2500;
|
||||
return defaultBitrate;
|
||||
}
|
||||
|
||||
|
||||
@ -275,7 +278,7 @@ var CodecsHandler = (function() {
|
||||
var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString());
|
||||
if (rtxFmtpLineIndex !== null) {
|
||||
var appendrtxNext = '\r\n';
|
||||
appendrtxNext += 'a=fmtp:' + codecPayload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228');
|
||||
appendrtxNext += 'a=fmtp:' + codecPayload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '2500') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '2500');
|
||||
sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext);
|
||||
sdp = sdpLines.join('\r\n');
|
||||
}
|
||||
@ -283,12 +286,11 @@ var CodecsHandler = (function() {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function setOpusAttributes(sdp, params) {
|
||||
function setOpusAttributes(sdp, params) {
|
||||
params = params || {};
|
||||
|
||||
var sdpLines = sdp.split('\r\n');
|
||||
|
||||
// Opus
|
||||
var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000');
|
||||
var opusPayload;
|
||||
if (opusIndex) {
|
||||
@ -305,65 +307,58 @@ var CodecsHandler = (function() {
|
||||
}
|
||||
|
||||
var appendOpusNext = '';
|
||||
appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1');
|
||||
appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1');
|
||||
|
||||
|
||||
// Please see https://tools.ietf.org/html/rfc7587 for more details on OPUS settings
|
||||
|
||||
if (typeof params.maxptime != 'undefined') { // max packet size in milliseconds
|
||||
appendOpusNext += ';maxptime:' + params.maxptime; // 3, 5, 10, 20, 40, 60 and the default is 120. (20 is minimum recommended for webrtc)
|
||||
}
|
||||
|
||||
if (typeof params.ptime != 'undefined') { // packet size; webrtc doesn't support less than 10 or 20 I think.
|
||||
appendOpusNext += ';ptime:' + params.ptime;
|
||||
}
|
||||
|
||||
if (typeof params.stereo != 'undefined'){
|
||||
if (params.stereo==0){
|
||||
appendOpusNext += ';stereo=0;sprop-stereo=0'; // defaults to 0
|
||||
} else if (params.stereo==1){
|
||||
appendOpusNext += ';stereo=1;sprop-stereo=1'; // defaults to 0
|
||||
} else if (params.stereo==2){
|
||||
sdpLines[opusIndex] = sdpLines[opusIndex].replace("opus/48000/2", "multiopus/48000/6");
|
||||
appendOpusNext += ';channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2'; // Multi-channel 5.1 audio
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof params.maxaveragebitrate != 'undefined') {
|
||||
appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8);
|
||||
appendOpusNext += ';maxaveragebitrate=' + params.maxaveragebitrate; // default 2500 (kbps)
|
||||
}
|
||||
|
||||
if (typeof params.maxplaybackrate != 'undefined') {
|
||||
appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8);
|
||||
appendOpusNext += ';maxplaybackrate=' + params.maxplaybackrate; // Default should be 48000 (hz) , 8000 to 48000 are valid options
|
||||
}
|
||||
|
||||
if (typeof params.cbr != 'undefined') {
|
||||
appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1');
|
||||
appendOpusNext += ';cbr=' + params.cbr; // default is 0 (vbr)
|
||||
}
|
||||
|
||||
if (typeof params.useinbandfec != 'undefined') {
|
||||
appendOpusNext += '; useinbandfec=' + params.useinbandfec;
|
||||
//if (typeof params.useinbandfec != 'undefined') { // useful for handling packet loss
|
||||
// appendOpusNext += '; useinbandfec=' + params.useinbandfec; // Defaults to 0
|
||||
//}
|
||||
|
||||
if (typeof params.usedtx != 'undefined') { // Default is 0
|
||||
appendOpusNext += ';usedtx=' + params.usedtx; // if decoder prefers the use of DTX.
|
||||
}
|
||||
|
||||
if (typeof params.usedtx != 'undefined') {
|
||||
appendOpusNext += '; usedtx=' + params.usedtx;
|
||||
}
|
||||
|
||||
if (typeof params.maxptime != 'undefined') {
|
||||
appendOpusNext += '\r\na=maxptime:' + params.maxptime;
|
||||
}
|
||||
|
||||
|
||||
sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext);
|
||||
|
||||
sdp = sdpLines.join('\r\n');
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
// forceStereoAudio => via webrtcexample.com
|
||||
// requires getUserMedia => echoCancellation:false
|
||||
function forceStereoAudio(sdp) {
|
||||
var sdpLines = sdp.split('\r\n');
|
||||
var fmtpLineIndex = null;
|
||||
for (var i = 0; i < sdpLines.length; i++) {
|
||||
if (sdpLines[i].search('opus/48000') !== -1) {
|
||||
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < sdpLines.length; i++) {
|
||||
if (sdpLines[i].search('a=fmtp') !== -1) {
|
||||
var payload = extractSdp(sdpLines[i], /a=fmtp:(\d+)/);
|
||||
if (payload === opusPayload) {
|
||||
fmtpLineIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fmtpLineIndex === null) return sdp;
|
||||
sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat('; stereo=1; sprop-stereo=1');
|
||||
sdp = sdpLines.join('\r\n');
|
||||
return sdp;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
disableNACK: disableNACK,
|
||||
|
||||
@ -378,9 +373,7 @@ var CodecsHandler = (function() {
|
||||
return setOpusAttributes(sdp, params);
|
||||
},
|
||||
|
||||
preferCodec: preferCodec,
|
||||
|
||||
forceStereoAudio: forceStereoAudio
|
||||
preferCodec: preferCodec
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user