tinkering -- EXPERIMENTAL 13.5-alpha branch

This commit is contained in:
STeve Seguin 2020-11-28 04:11:04 -05:00
parent d48483f113
commit e8abdb4e96
6 changed files with 705 additions and 158 deletions

24
devices.json.html Normal file
View File

@ -0,0 +1,24 @@
<html>
<head><meta charset="UTF-8"></head>
<body>
<script>
var list = [];
navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
devices.forEach(function(device) {
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
list.push(device);
});
document.write(JSON.stringify(list, null, 2));
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
});
</script>
</body>
</html>

View File

@ -207,6 +207,9 @@
<video id="previewWebcam" class="previewWebcam" oncanplay="updateStats();" disablePictureInPicture controlsList="nodownload" muted autoplay playsinline ></video>
</p>
<div id="infof"></div>
<button onclick="this.disabled=true;setTimeout(function(){requestBasicPermissions();},20);" id="getPermissions" style="display:none;" data-ready="false" >
<span data-translate="ask-for-permissions">Allow Access to Camera/Microphone</span>
</button>
<button onclick="publishWebcam(this)" id="gowebcam" class="gowebcam" disabled data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
@ -463,6 +466,7 @@
SELECT THE VIDEO FILE TO SHARE<br /><br />
<input id="fileselector" onchange="session.publishFile(this,event);" type="file" accept="video/*,audio/*" alt="Hold CTRL (or CMD) to select multiple files" title="Hold CTRL (or CMD) to select multiple files" multiple/>
</div>
<p>Keep this tab visible if using Chrome, else the video playback will stop</p>
<div class="outer close">
<div class="inner">
<label class="labelclass">
@ -472,7 +476,27 @@
</div>
</div>
<div id="container-6" class="column columnfade pointer" style=" overflow-y: auto;">
<h2><span data-translate="share-website-iframe">Share Remote Website</span></h2>
<div class="container-inner">
<br /><br />
Enter the URL website you wish to share.<br /><br />
<input id="iframeURL" type="text" style="margin:10px; border:2px solid; padding:10px; width:400px;" title="Enter an HTTPS URL" multiple/><br />
<button onclick="loadIframe(getById('iframeURL').value);" >Preview</button>
<button onclick="this.innerHTML = 'Update';session.publishIFrame(getById('iframeURL').value);" >Share</button><br />
<small>Remote website must be CORS/IFrame compatible with full SSL-encryption enabled.</small>
<div id="iFramePreview" style=" width: 1280px; height: 720px; margin: auto; padding: 10px;"></div>
</div>
<div class="outer close">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
</label>
</div>
</div>
</div>
<p></p>
<div id="info" class="fullcolumn columnfade">
<center>
@ -510,7 +534,7 @@
</li>
<br />
🎈 Site Updated: <a href="https://www.reddit.com/r/OBSNinja/comments/k02enh/version_134_of_obsninja_released_change_log_here/">November 24th, 2020</a>. The previous version can be found at
🎈 Site Updated: <a href="https://www.reddit.com/r/OBSNinja/comments/k02enh/version_134_of_obsninja_released_change_log_here/">November 27th, 2020</a>. The previous version can be found at
<a href="https://obs.ninja/v12/">https://obs.ninja/v12/</a> if you are having new issues.
<br />
@ -577,7 +601,7 @@
<button style="width: 36px" data-action-type="change-quality" title="Disable Video Preview" onclick="toggleQualityDirector(0, this.dataset.UUID, this);">
<span data-translate="change-to-low-quality">&nbsp;&nbsp;<i class="las la-video-slash"></i></span>
</button>
<button style="width: 36px" data-action-type="change-quality" title="Low-Quality Preview" onclick="toggleQualityDirector(50, this.dataset.UUID, this);">
<button class="pressed" style="width:36px;" data-action-type="change-quality" title="Low-Quality Preview" onclick="toggleQualityDirector(50, this.dataset.UUID, this);">
<span data-translate="change-to-medium-quality">&nbsp;&nbsp;<i class="las la-video"></i></span>
</button>
<button style="width: 36px" data-action-type="change-quality" title="High-Quality Preview" onclick="toggleQualityDirector(1200, this.dataset.UUID, this);">
@ -588,6 +612,15 @@
<span data-translate="send-direct-chat"><i class="las la-envelope"></i> Message</span>
</button>
<button data-action-type="advanced-camera-settings" title="Advanced Settings and Remote Control" onclick="directorSendMessage(this);">
<span data-translate="advanced-camera-settings"><i class="las la-cog"></i> Advanced</span>
</button>
<button data-action-type="voice-chat" title="Toggle Voice Chat with this Guest" onclick="directorSendMessage(this);">
<span data-translate="voice-chat"><i class="las la-microphone"></i> Voice Chat</span>
</button>
</div>
</div>
<div id="popupSelector" style="display:none;">
@ -620,8 +653,13 @@
<button id="shareScreenGear" style="width: 135px; padding:20px;text-align:center;" onclick="grabScreen()"><b>Share Screen</b><br /><i style="padding:5px; font-size:300%;" class="las la-desktop"></i></button><br />
<button onclick="toggleSettings()" style="width: 135px; background-color:#EFEFEF;padding:10px 12px 12px 2px;margin: 10px 0px 20px 0"><i class="chevron right" style="font-size:150%;top:3px;position:relative;"></i> <b>Close Settings</b></button>
<button id='advancedOptions' onclick="this.style.display = 'none'; toggle(getById('popupSelector_constraints'),false,false); " style="display:none; background-color:#EFEFEF;padding:10px 12px 12px 2px;margin: 10px 0px 0px 10px"><i class="chevron bottom" style="font-size:150%;top:3px;position:relative;"></i> <b>Show Advanced</b></button>
<span id="popupSelector_constraints" style="display: none;">
<button id='advancedOptionsCamera' onclick="this.style.display = 'none'; toggle(getById('popupSelector_constraints_camera'),false,false); " style="display:none; background-color:#EFEFEF;padding:10px 12px 12px 2px;margin: 10px 0px 0px 10px"><i class="chevron bottom" style="font-size:150%;top:3px;position:relative;"></i> <b>Advanced Video</b></button>
<button id='advancedOptionsAudio' onclick="this.style.display = 'none'; toggle(getById('popupSelector_constraints_audio'),false,false); " style="display:none; background-color:#EFEFEF;padding:10px 12px 12px 2px;margin: 10px 0px 0px 10px"><i class="chevron bottom" style="font-size:150%;top:3px;position:relative;"></i> <b>Advanced Audio</b></button>
<span id="popupSelector_constraints_audio" class="popupSelector_constraints" style="display: none;">
</span>
<span id="popupSelector_constraints_video" class="popupSelector_constraints" style="display: none;">
</span>
</p>
@ -687,6 +725,7 @@
</div>
<div id="chatModule" style="display:none;">
<a target="popup" style="cursor:pointer;" onclick="createPopoutChat();">💢</a>
<div id="chatBody">
<div class="inMessage" data-translate='welcome-to-obs-ninja-chat'>
Welcome to OBS.Ninja! You can send text messages directly to connected peers from here.
@ -707,6 +746,8 @@
<!-- This image is used when dragging elements -->
<img src="./images/favicon-32x32.png" id="dragImage" />
</div>
<div id="request_info_prompt" style="display:none">
</div>
<div id="messagePopup" class="popup-message"></div>
<div id="languages" class="popup-message" style="display: none; right: 0; bottom: 25px; position: absolute;">
<b data-translate='available-languages'>Available Languages:</b>
@ -745,7 +786,7 @@
<script>
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
session.version = "13.4";
session.version = "13.5b";
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
session.defaultPassword = "someEncryptionKey123"; // Disabling improves compatibility and is helpful for debugging.

View File

@ -160,9 +160,7 @@ button.white:active {
width:100%;
pointer-events: none
}
#popupSelector_constraints{
margin:30px 9% 0 7%;
}
.credits {
color: #101020;
position: fixed;
@ -458,6 +456,16 @@ body {
animation: fading 0.2s;
}
#getPermissions{
font-size: 110%;
border: 3px solid #99A;
cursor: pointer;
background-color: #cce0ff;
margin: 20px;
padding: 10px 50px;
text-align:center;
}
.gowebcam {
font-size: 110%;
border: 3px solid #DDDDDD;
@ -698,8 +706,10 @@ input[type=range]:focus::-ms-fill-upper {
}
}
#popupSelector_constraints label{
.popupSelector_constraints{
margin:30px 9% 0 7%;
}
.popupSelector_constraints label{
color:white;
text-shadow: 0px 0px 6px #000000FF;
font-weight: 700;
@ -1128,6 +1138,17 @@ img {
opacity: 1
}
}
#request_info_prompt{
z-index: 20;
color: white;
font-size: 30px;
font-size: 3.5vw;
top: 0;
align-self: center;
margin: 25vh 0;
position: absolute;
}
video {
background-color: transparent !important;
border: 0;

576
main.js
View File

@ -196,7 +196,7 @@ sanitizeRoomName = function(roomid){
sanitizePassword = function(passwrd){
if (passwrd===""){return passwrd;}
else if (passwrd===false){return passwrd;}
else if (passwrd===null){return passwrd;}
passwrd = passwrd.trim();
if (passwrd.length < 1){
if (!(session.cleanOutput)){
@ -423,7 +423,7 @@ if (/CriOS/i.test(navigator.userAgent) && /iphone|ipod|ipad/i.test(navigator.use
}
if (urlParams.has('broadcast') || urlParams.has('bc')){
log("Room BITRATE SET");
log("Broadcast flag set");
session.broadcast = urlParams.get('broadcast') || urlParams.get('bc');
session.nopreview=true;
session.style=1;
@ -490,7 +490,9 @@ if (session.password){
session.defaultPassword = false;
}
if (urlParams.has('hash') || urlParams.has('crc') || urlParams.has('check')){ // could be brute forced in theory, so not as safe as just not using a hash check.
session.taintedSession=null; // waiting to see if valid or not.
var hash_input = urlParams.get('hash') || urlParams.get('crc') || urlParams.get('check');
if (session.password===false){
session.password = prompt("Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)");
@ -502,10 +504,22 @@ if (urlParams.has('hash') || urlParams.has('crc') || urlParams.has('check')){ /
session.generateHash(session.password+session.salt,6).then(function(hash){ // million to one error.
log("hash is "+hash);
if (hash.substring(0, 4) !== hash_input){ // hash crc checks are just first 4 characters.
session.taintedSession=true;
if (!(session.cleanOutput)){
alert("The password was incorrect.\n\nRefresh and try again.");
getById("request_info_prompt").innerHTML = "The password was incorrect.\n\nRefresh and try again.";
getById("request_info_prompt").style.display = "block";
getById("mainmenu").style.display = "none";
getById("head1").style.display = "none";
session.cleanOutput = true;
} else {
getById("request_info_prompt").innerHTML = "";
getById("request_info_prompt").style.display = "block";
getById("mainmenu").style.display = "none";
getById("head1").style.display = "none";
}
} else {
session.taintedSession=false;
session.hash = hash;
}
});
@ -1120,8 +1134,8 @@ if (urlParams.has('quality') || urlParams.has('q')){
if (urlParams.has('sink')){
session.sink = urlParams.get('sink');
} else if (urlParams.has('outputdevice') || urlParams.has('od')){
session.outputDevice = urlParams.get('outputdevice') || urlParams.get('od');
} else if (urlParams.has('outputdevice') || urlParams.has('od') || urlParams.has('audiooutput')){
session.outputDevice = urlParams.get('outputdevice') || urlParams.get('od') || urlParams.get('audiooutput');
if (session.outputDevice){
session.outputDevice = session.outputDevice.toLowerCase().replace(/[\W]+/g,"_");
} else {
@ -1509,6 +1523,34 @@ window.onmessage = function(e){ // iFRAME support
for (var i in session.rpcs){
stats.inbound_stats[session.rpcs[i].streamID] = session.rpcs[i].stats;
}
for (var uuid in session.pcs){
session.pcs[uuid].getStats().then(function(stats){
stats.forEach(stat=>{
if (stat.type=="outbound-rtp"){
if (stat.kind=="video"){
if ("qualityLimitationReason" in stat){
session.pcs[uuid].stats.quality_Limitation_Reason = stat.qualityLimitationReason;
}
if ("framesPerSecond" in stat){
session.pcs[uuid].stats.resolution = stat.frameWidth+" x "+ stat.frameHeight +" @ "+stat.framesPerSecond;
}
if ("encoderImplementation" in stat){
session.pcs[uuid].stats.encoder = stat.encoderImplementation;
}
}
}
});
});
}
stats.outbound_stats = {};
for (var i in session.pcs){
stats.outbound_stats[i] = session.pcs[i].stats;
}
parent.postMessage({"stats": stats }, "*");
}
@ -1627,7 +1669,7 @@ function sleep(ms = 0){
return new Promise(r => setTimeout(r, ms)); // LOLz!
}
session.connect();
// session.volume = 100; // needs to be set after?
@ -1969,6 +2011,7 @@ if (urlParams.has('hideheader') || urlParams.has('noheader') || urlParams.has('h
}
function checkConnection(){
if (session.ws===null){return;}
if (document.getElementById("qos")){ // true or false; null might cause problems?
if ((session.ws) && (session.ws.readyState === WebSocket.OPEN)) {
getById("qos").style.color = "white";
@ -1981,9 +2024,12 @@ setInterval(function(){checkConnection();},5000);
function printViewStats(menu, statsObj, streamID){ // Stats for viewing a remote video
var scrollLeft = menu.scrollLeft;
var scrollTop = menu.scrollTop;
menu.innerHTML="StreamID: <b>"+streamID+"</b><br />";
menu.innerHTML+= printValues(statsObj);
menu.scrollTop = scrollTop;
menu.scrollLeft = scrollLeft;
}
function printValues(obj) { // see: printViewStats
@ -2025,6 +2071,10 @@ function printValues(obj) { // see: printViewStats
function printMyStats(menu){ // see: setupStatsMenu
var scrollLeft = getById("menuStatsBox").scrollLeft;
var scrollTop = getById("menuStatsBox").scrollTop;
log(scrollTop + " " + scrollLeft);
menu.innerHTML="";
session.stats.outbound_connections = Object.keys(session.pcs).length;
@ -2061,9 +2111,17 @@ function printMyStats(menu){ // see: setupStatsMenu
}
});
printViewValues(session.pcs[uuid].stats);
menu.innerHTML+="<br /><br />"
menu.innerHTML+="<br /><br />";
try{
getById("menuStatsBox").scrollLeft = scrollLeft;
getById("menuStatsBox").scrollTop = scrollTop;
} catch(e){}
});
}
try{
getById("menuStatsBox").scrollLeft = scrollLeft;
getById("menuStatsBox").scrollTop = scrollTop;
} catch(e){}
}
@ -2183,7 +2241,7 @@ function toggleSpeakerMute(apply=false){ // TODO: I need to have this be MUTE, t
}
function toggleChat(ele=null){ // TODO: I need to have this be MUTE, toggle, with volume not touched.
function toggleChat(event=null){ // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (session.chat==false){
setTimeout(function(){document.addEventListener("click", toggleChat);},10);
@ -2649,7 +2707,6 @@ function publishScreen(){
}
function publishWebcam(btn = false){
log(btn);
if (btn){
if (btn.dataset.ready == "false"){
warnlog("Clicked too quickly; button not enabled yet");
@ -2714,7 +2771,6 @@ function publishWebcam(btn = false){
updateURL("push="+session.streamID);
}
session.publishStream(ele, title);
}
@ -3940,7 +3996,7 @@ function playtone(screen=false){
}
}
async function getAudioOnly(selector, trackid=null){
async function getAudioOnly(selector, trackid=null, override=false){
var audioSelect = document.querySelector(selector).querySelectorAll("input");
var audioList = [];
var streams = [];
@ -3974,6 +4030,10 @@ async function getAudioOnly(selector, trackid=null){
}
constraint.video = false;
//warnlog(constraint);
if (override!==false){
constraint = override;
}
var stream = await navigator.mediaDevices.getUserMedia(constraint).then(function (stream2){
log("pushing stream2");
return stream2;
@ -4484,7 +4544,7 @@ async function grabVideo(quality=0, eleName='previewWebcam', selector="select#vi
}
async function grabAudio(eleName="previewWebcam", selector="#audioSource", trackid = null){ // trackid is the excluded track
async function grabAudio(eleName="previewWebcam", selector="#audioSource", trackid = null, override=false){ // trackid is the excluded track
if( activatedPreview == true){log("activated preview return 2");return;}
activatedPreview = true;
try {
@ -4526,7 +4586,7 @@ async function grabAudio(eleName="previewWebcam", selector="#audioSource", track
errorlog(e);
}
var streams = await getAudioOnly(selector, trackid); // Get audio streams
var streams = await getAudioOnly(selector, trackid, override); // Get audio streams
try {
for (var i=0; i<streams.length;i++){
streams[i].getAudioTracks().forEach(function(track){
@ -4767,11 +4827,219 @@ function dragElement(elmnt) {
}
}
function loadIframe(iframesrc){ // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: <body onload=>loadIframe();"> , but don't call it before the page loads.
var iframe = document.createElement("iframe");
var iframeContainer = getById("iFramePreview");
iframe.allow="autoplay;camera;microphone";
iframe.allowtransparency="true";
iframe.allowfullscreen ="true";
iframe.style.width="100%";
iframe.style.height="100%";
if (iframesrc==""){
iframesrc="./";
}
iframe.src = iframesrc;
iframeContainer.appendChild(iframe);
}
function updateConstraintSliders(){
getById("popupSelector_constraints").innerHTML = "";
log("updateConstraintSliders");
listAudioSettings();
listCameraSettings();
}
function listAudioSettings(){
getById("popupSelector_constraints_audio").innerHTML = "";
try {
var track0 = session.streamSrc.getAudioTracks();
track0 = track0[0];
if (track0.getCapabilities){
session.cameraConstaints = track0.getCapabilities();
}
log(session.cameraConstaints);
} catch(e){
errorlog(e);
return;
}
try {
if (track0.getSettings){
session.currentCameraConstaints = track0.getSettings();
}
} catch(e){
errorlog(e);
}
for (var i in session.cameraConstaints){
try {
log(i);
log(session.cameraConstaints[i]);
if ((typeof session.cameraConstaints[i] ==='object') && (session.cameraConstaints[i] !== null) && ("max" in session.cameraConstaints[i]) && ("min" in session.cameraConstaints[i])){
if (i==="aspectRatio"){continue;}
else if (i==="width"){continue;}
else if (i==="height"){continue;}
else if (i==="frameRate"){continue;}
var label = document.createElement("label");
label.id= "label_"+i;
label.htmlFor = "constraints_"+i;
label.innerHTML = i+":";
var input = document.createElement("input");
input.min = session.cameraConstaints[i].min;
input.max = session.cameraConstaints[i].max;
if (input.min == input.max){continue;}
if (getById("popupSelector_constraints_audio").style.display == "none"){
getById("advancedOptionsAudio").style.display="inline-block";
}
if (i in session.currentCameraConstaints){
input.value = session.currentCameraConstaints[i];
label.innerHTML = i+": "+session.currentCameraConstaints[i];
} else {
label.innerHTML = i;
}
if ("step" in session.cameraConstaints[i]){
input.step = session.cameraConstaints[i].step;
}
input.type = "range";
input.dataset.keyname = i;
input.id = "constraints_"+i;
input.style="display:block; width:100%;";
input.name = "constraints_"+i;
input.onchange = function(e){
getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
//updateAudioConstraints(e.target.dataset.keyname, e.target.value);
applyAudioHack(track0,e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints_audio").appendChild(label);
getById("popupSelector_constraints_audio").appendChild(input);
} else if ((typeof session.cameraConstaints[i] ==='object') && (session.cameraConstaints[i] !== null)){
if (i == "resizeMode"){continue;}
var div = document.createElement("div");
var label = document.createElement("label");
label.id= "label_"+i;
label.htmlFor = "constraints_"+i;
label.innerHTML = i+":";
label.style="display:inline-block; padding:0;margin: 15px 0px 29px;";
label.dataset.keyname = i;
var input = document.createElement("select");
var c = document.createElement("option");
if (session.cameraConstaints[i].length>1){
for (var opts in session.cameraConstaints[i]) {
log(opts);
var opt = new Option(session.cameraConstaints[i][opts], session.cameraConstaints[i][opts]);
input.options.add(opt);
}
} else if (i.toLowerCase == "torch"){
var opt = new Option("Off", false);
input.options.add(opt);
opt = new Option("On", true);
input.options.add(opt);
} else {
continue;
}
input.id = "constraints_"+i;
input.className="constraintCameraInput";
input.name = "constraints_"+i;
input.style = "display:inline; padding:2px; margin:0 10px;";
input.dataset.keyname = i;
input.onchange = function(e){
//getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
//updateAudioConstraints(e.target.dataset.keyname, e.target.value);
applyAudioHack(track0,e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints_audio").appendChild(div);
div.appendChild(label);
div.appendChild(input);
} else if (typeof session.cameraConstaints[i] === 'boolean'){
var div = document.createElement("div");
var label = document.createElement("label");
label.id= "label_"+i;
label.htmlFor = "constraints_"+i;
label.innerHTML = i+":";
label.style="display:inline-block; padding:0;margin: 15px 0px 29px;";
label.dataset.keyname = i;
var input = document.createElement("select");
var c = document.createElement("option");
var opt = new Option("Off", false);
input.options.add(opt);
opt = new Option("On", true);
input.options.add(opt);
input.id = "constraints_"+i;
input.className="constraintCameraInput";
input.name = "constraints_"+i;
input.style = "display:inline; padding:2px; margin:0 10px;";
input.dataset.keyname = i;
input.onchange = function(e){
//getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
//updateAudioConstraints(e.target.dataset.keyname, e.target.value);
applyAudioHack(track0,e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints_audio").appendChild(div);
div.appendChild(label);
div.appendChild(input);
}
} catch(e){errorlog(e);}
}
}
function applyAudioHack(track, constraint, value=null){
if (value == parseFloat(value)){
value = parseFloat(value);
value = {exact: value};
} else if (value == "true"){
value = true;
} else if (value == "false"){
value = false;
}
log("CONTREAIT",constraint);
var new_constraints = Object.assign(track.getSettings(), {[constraint]:value}, );
new_constraints = {audio: new_constraints, video:false};
log(new_constraints);
activatedPreview=false;
grabAudio("videosource","#audioSource3", null, new_constraints);
}
function updateAudioConstraints(constraint, value=null){ // this is what it SHOULD be, but this doesn't work yet.
var track0 = session.streamSrc.getAudioTracks();
track0 = track0[0];
if (value == parseFloat(value)){
value = parseFloat(value);
} else if (value == "true"){
value = true;
} else if (value == "false"){
value = false;
}
log({advanced: [ {[constraint]: value} ]});
track0.applyConstraints({advanced: [ {[constraint]: value} ]});
return;
}
function listCameraSettings(){
getById("popupSelector_constraints_video").innerHTML = "";
try {
var track0 = session.streamSrc.getVideoTracks();
track0 = track0[0];
@ -4804,9 +5072,7 @@ function updateConstraintSliders(){
else if (i==="height"){continue;}
else if (i==="frameRate"){continue;}
if (getById("popupSelector_constraints").style.display == "none"){
getById("advancedOptions").style.display="inline-block";
}
var label = document.createElement("label");
label.id= "label_"+i;
@ -4817,6 +5083,12 @@ function updateConstraintSliders(){
input.min = session.cameraConstaints[i].min;
input.max = session.cameraConstaints[i].max;
if (input.min == input.max){continue;}
if (getById("popupSelector_constraints_video").style.display == "none"){
getById("advancedOptionsCamera").style.display="inline-block";
}
if (i in session.currentCameraConstaints){
input.value = session.currentCameraConstaints[i];
label.innerHTML = i+": "+session.currentCameraConstaints[i];
@ -4839,8 +5111,8 @@ function updateConstraintSliders(){
};
getById("popupSelector_constraints").appendChild(label);
getById("popupSelector_constraints").appendChild(input);
getById("popupSelector_constraints_video").appendChild(label);
getById("popupSelector_constraints_video").appendChild(input);
} else if ((typeof session.cameraConstaints[i] ==='object') && (session.cameraConstaints[i] !== null)){
if (i == "resizeMode"){continue;}
@ -4879,7 +5151,7 @@ function updateConstraintSliders(){
updateCameraConstraints(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints").appendChild(div);
getById("popupSelector_constraints_video").appendChild(div);
div.appendChild(label);
div.appendChild(input);
} else if (typeof session.cameraConstaints[i] === 'boolean'){
@ -4909,7 +5181,7 @@ function updateConstraintSliders(){
updateCameraConstraints(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints").appendChild(div);
getById("popupSelector_constraints_video").appendChild(div);
div.appendChild(label);
div.appendChild(input);
}
@ -5099,33 +5371,24 @@ Promise.prototype.timeout = function(ms) {
};
function previewWebcam(){
if (session.taintedSession===null){
log("STILL WAITING ON HASH TO VALIDATE")
setTimeout(function(){previewWebcam();},1000);
return;
} else if (session.taintedSession===true){
warnlog("HASH FAILED; PASSWORD NOT VALID");
return;
} else {
log("NOT TAINTED");
}
if( activatedPreview == true){
log("activeated preview return 1");
return;
}
activatedPreview = true;
try{
var oldstream = getById('previewWebcam').srcObject;
if (oldstream){
log("old stream found");
oldstream.getTracks().forEach(function(track) {
track.stop();
oldstream.removeTrack(track);
log("stopping old track");
});
} else {
getById('previewWebcam').srcObject = new MediaStream();
session.streamSrc = getById('previewWebcam').srcObject;
log("CREATE NEW STREAM");
}
} catch (e){
errorlog(e);
}
if (session.audioDevice===0){ // OFF
var constraint = {audio: false};
} else if ((session.echoCancellation !== false) && (session.autoGainControl !== false) && (session.noiseSuppression !== false)){ // AUTO
@ -5155,104 +5418,127 @@ function previewWebcam(){
constraint.video = true;
}
try {
log("getting permission from navigator");
if ("permissions" in navigator){
log("queried0");
navigator.permissions.query({name: 'camera'}).then((permissionObj) => {
log("queried camera permissions successfully:");
log(permissionObj.state);
if (permissionObj.state!=="prompt"){ // trying to bypass any NDI problems
enumerateDevices().then(function(devices){
var vtrue = false;
var atrue = false;
devices.forEach(function(device) {
if (device.kind === 'audioinput'){
atrue=true;
} else if (device.kind === 'videoinput'){
vtrue = true;
// try {
// var autoPlayAllowed = false;
// log("trying to play video");
//
// var vid = document.createElement('video');
// vid.src = ""; // we need this.play();
// playPromise = vid.play();
// // In browsers that dont yet support this functionality,
// // playPromise wont be defined.
// if (playPromise !== undefined) {
// getById("getPermissions").style.display="";
// getById("gowebcam").style.display="none";
// //activatedPreview=false;
// log("checking promise");
// log(playPromise);
// playPromise.catch(function(error) {
// if (error.name !== 'NotAllowedError') {
// autoPlayAllowed=true;
// log("PROMISE GOOD");
// }
// }).finally(function() {
// log("FINALLY ");
// delete vid;
// if (autoPlayAllowed) { /////// GOOD //////
// log("ALLOWED TO PLAY");
// getById("gowebcam").style.display="";
// getById("getPermissions").style.display="none";
enumerateDevices().then(function(devices){
log("enumeratated");
log(devices);
var vtrue = false;
var atrue = false;
devices.forEach(function(device) {
if (device.kind === 'audioinput'){
atrue=true;
} else if (device.kind === 'videoinput'){
vtrue = true;
}
});
if (atrue===false){
constraint.audio = false;
}
if (vtrue===false){
constraint.video = false;
}
setTimeout(function(constraint){requestBasicPermissions(constraint);},0,constraint);
}).catch((error)=>{
log("enumeratated failed. Seeking permissions.");
setTimeout(function(constraint){requestBasicPermissions(constraint);},0,constraint);
});
if (atrue===false){
constraint.audio = false;
requestBasicPermissions(constraint);
} else {
constraint.video = false;
requestBasicPermissions(constraint);
}
}).catch((error)=>{
requestBasicPermissions(constraint)
});
} else {
log("constraint: "+constraint);
enumerateDevices().then(function(devices){
log("enumeratated");
var vtrue = false;
var atrue = false;
devices.forEach(function(device) {
if (device.kind === 'audioinput'){
atrue=true;
} else if (device.kind === 'videoinput'){
vtrue = true;
}
});
if (atrue===false){
constraint.audio = false;
}
if (vtrue===false){
constraint.video = false;
}
requestBasicPermissions(constraint);
}).catch((error)=>{
log("enumeratated failed. Seeking permissions.");
requestBasicPermissions(constraint)
});
}
}).catch((error) => {
log("queried camera permissions returned error");
enumerateDevices().then(function(devices){
var vtrue = false;
var atrue = false;
devices.forEach(function(device) {
if (device.kind === 'audioinput'){
atrue=true;
} else if (device.kind === 'videoinput'){
vtrue = true;
}
});
if (atrue===false){
constraint.audio = false;
}
if (vtrue===false){
constraint.video = false;
}
requestBasicPermissions(constraint);
}).catch((error)=>{
requestBasicPermissions(constraint)
});
});
} else {
log("queried not supported");
requestBasicPermissions(constraint);
}
} catch(e){
requestBasicPermissions(constraint)
errorlog(e);
}
// return;
//
// } else {
// // BUTTON.
// log("NOT ALLOWED TO CONTINUE -");
// }
// });
// } else {
// delete vid;
// enumerateDevices().then(function(devices){
// log("enumeratated");
// log(devices);
// var vtrue = false;
// var atrue = false;
// devices.forEach(function(device) {
// if (device.kind === 'audioinput'){
// atrue=true;
// } else if (device.kind === 'videoinput'){
// vtrue = true;
// }
// });
// if (atrue===false){
// constraint.audio = false;
// }
// if (vtrue===false){
// constraint.video = false;
// }
// setTimeout(function(constraint){requestBasicPermissions(constraint);},0,constraint);
// }).catch((error)=>{
// log("enumeratated failed. Seeking permissions.");
// setTimeout(function(constraint){requestBasicPermissions(constraint);},0,constraint);
// });
// }
//
// } catch(e){
// setTimeout(function(constraint){requestBasicPermissions(constraint);},0,constraint);
// errorlog(e);
// }
}
function requestBasicPermissions(constraint){
log("PreviewWebcam");
log(constraint);
function requestBasicPermissions(constraint={video:true,audio:true}){
if (session.taintedSession===null){
log("STILL WAITING ON HASH TO VALIDATE")
setTimeout(function(constraint){requestBasicPermissions(constraint);},1000,constraint);
return;
} else if (session.taintedSession===true){
warnlog("HASH FAILED; PASSWORD NOT VALID");
return;
} else {
log("NOT TAINTED 1");
}
setTimeout(function(){
getById("getPermissions").style.display="none";
getById("gowebcam").style.display="";
},0);
log("REQUESTING BASIC PERMISSIONS");
try {
var timerBasicCheck = setTimeout(function(){alert("Camera Access Request Timed Out\n\nDid you accept camera permissions? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling NDI tools.\n\nPlease also double check that your camera and audio devices are correctly connected. You may also need to refresh the page.");},10000);
var timerBasicCheck=null;
if (!(session.cleanOutput)){
log("Setting Timer for getUserMedia");
timerBasicCheck = setTimeout(function(){
if (!(session.cleanOutput)){
alert("Camera Access Request Timed Out\n\nDid you accept camera permissions? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling NDI tools.\n\nPlease also double check that your camera and audio devices are correctly connected. You may also need to refresh the page.");
}},10000);
}
log(constraint);
navigator.mediaDevices.getUserMedia(constraint).then(function(stream){ // Apple needs thi to happen before I can access EnumerateDevices.
log("got first stream");
clearTimeout(timerBasicCheck);
log("got first stream");
setupWebcamSelection(stream);
}).catch(function(err){
clearTimeout(timerBasicCheck);
@ -5875,10 +6161,13 @@ function sendChatMessage(){ // filtered + visual
document.getElementById('chatInput').value = "";
messageList.push(data);
messageList = messageList.slice(-100);
if(session.broadcastChannel!==false){
session.broadcastChannel.postMessage(data);
}
updateMessages();
}
function toggleQualityDirector(bitrate, UUID, ele = null){
function toggleQualityDirector(bitrate, UUID, ele = null){ // ele is specific to the button in the director's room
var eles = ele.parentNode.childNodes;
for (i in eles){
eles[i].className="";
@ -5887,6 +6176,17 @@ function toggleQualityDirector(bitrate, UUID, ele = null){
session.requestRateLimit(bitrate, UUID);
}
function createPopoutChat(){
var randid = session.generateStreamID(8);
log(randid);
window.open('./popout?id='+randid,'popup','width=600,height=480,toolbar=no,menubar=no,resizable=yes');
session.broadcastChannel = new BroadcastChannel(randid);
setTimeout(function(){
session.broadcastChannel.postMessage({messageList:messageList}); /// all data. delayed to ensure it loads. Should probably have a callback instead.
},5000);
return false;
}
function getChatMessage(msg, label=false, director=false, overlay=false){
msg = sanitize(msg); // keep it clean.
if (msg==""){return;}
@ -5944,6 +6244,9 @@ function getChatMessage(msg, label=false, director=false, overlay=false){
if (parent){
parent.postMessage({"gotChat": data }, "*");
}
if (session.broadcastChannel!==false){
session.broadcastChannel.postMessage(data); /* send */
}
}
@ -6019,6 +6322,8 @@ function recordVideo(target, event, videoKbps = false, interval=30){ // event.c
log("Record Video queued");
defaultRecordingBitrate=false;
return;
} else {
defaultRecordingBitrate=false;
}
log("Record Video Clicked");
@ -6086,6 +6391,7 @@ function recordVideo(target, event, videoKbps = false, interval=30){ // event.c
video.recorder.mediaRecorder = new MediaRecorder(video.srcObject, options);
function download() {
const blob = new Blob(recordedBlobs, { type: "video/webm" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');

146
popout.html Normal file
View File

@ -0,0 +1,146 @@
<html>
<head>
<meta name="theme-color" content="#ffffff" />
<!-- <script src="//console.re/connector.js" data-channel="obsninjadev" type="text/javascript" id="consolerescript"></script>-->
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css">
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter-latest.js"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/qrcode.min.js"></script>
<script type="text/javascript" src="./thirdparty/jquery.min.js"></script>
<link rel="stylesheet" href="./main.css?ver=22" />
</head>
<body>
<div id="chatModule" >
<div id="chatBody">
<div class="inMessage" data-translate='welcome-to-obs-ninja-chat'>
Welcome to OBS.Ninja! You can send text messages directly to connected peers from here.
</div>
<div class="outMessage" data-translate='names-and-labels-coming-soon'>
Names identifying connected peers will be a feature in an upcoming release.
</div>
</div>
<input id="chatInput" placeholder="Enter chat message to send here" onkeypress="EnterButtonChat(event)" />
<button style="width:60px;background-color:#EEE;" onclick="sendChatMessage()" data-translate='send-chat'>Send</button>
</div>
<script>
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
var chatUpdateTimeout = null;
var messageList = [];
(function (w) {
w.URLSearchParams = w.URLSearchParams || function (searchString) {
var self = this;
self.searchString = searchString;
self.get = function (name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
}
else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlParams = new URLSearchParams(window.location.search);
if (urlParams.has("id")){
var bid = urlParams.get("id");
}
var bc = new BroadcastChannel(bid);
bc.onmessage = function (e) {
//if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("data" in e){
if ("msg" in e.data){
messageList.push(e.data);
messageList = messageList.slice(-100);
updateMessages();
} else if ("messageList" in e.data){
messageList = e.data.messageList;
updateMessages();
}
}
};
function timeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = seconds / 31536000;
if (interval > 1) {
return Math.floor(interval) + " years";
}
interval = seconds / 2592000;
if (interval > 1) {
return Math.floor(interval) + " months";
}
interval = seconds / 86400;
if (interval > 1) {
return Math.floor(interval) + " days";
}
interval = seconds / 3600;
if (interval > 1) {
return Math.floor(interval) + " hours";
}
interval = seconds / 60;
if (interval > 1) {
return Math.floor(interval) + " minutes";
}
return "Seconds ago";
}
function toggleQualityDirector(bitrate, UUID, ele = null){ // ele is specific to the button in the director's room
var eles = ele.parentNode.childNodes;
for (i in eles){
eles[i].className="";
}
ele.className="pressed";
session.requestRateLimit(bitrate, UUID);
}
function updateMessages(){
document.getElementById("chatBody").innerHTML = "";
for (i in messageList){
var time = timeSince(messageList[i].time);
var msg = document.createElement("div");
////// KEEP THIS IN /////////
console.log(messageList[i].msg); // Display Recieved messages for View-Only clients.
/////////////////////////////
if (messageList[i].type == "sent"){
msg.innerHTML = messageList[i].msg + " <i><small> <small>- "+time+"</small></small></i>";
msg.classList.add("outMessage");
} else if (messageList[i].type == "recv"){
var label = "";
if (messageList[i].label){
label = messageList[i].label;
}
msg.innerHTML = label+messageList[i].msg + " <i><small> <small>- "+time+"</small></small></i>";
msg.classList.add("inMessage");
} else if (messageList[i].type == "alert"){
msg.innerHTML = messageList[i].msg + " <i><small> <small>- "+time+"</small></small></i>";
msg.classList.add("inMessage");
} else {
msg.innerHTML = messageList[i].msg + " <i><small> <small>- "+time+"</small></small></i>";
msg.classList.add("inMessage");
}
document.getElementById("chatBody").appendChild(msg);
}
if (chatUpdateTimeout){
clearInterval(chatUpdateTimeout);
}
document.getElementById("chatBody").scrollTop = document.getElementById("chatBody").scrollHeight;
chatUpdateTimeout = setTimeout(function(){updateMessages()},60000);
}
</script>
</body>
</html>

View File

@ -85,13 +85,13 @@ function loadIframe(){ // this is pretty important if you want to avoid camera p
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
setInterval(function(){iframe.contentWindow.postMessage({"getStats":true}, '*');},5000);
var iframe = document.createElement("iframe");
var iframe2 = document.createElement("iframe");
var iframeContainer = document.createElement("span");
iframe.allow="autoplay";
iframe2.allow="autoplay";
var srcString = "./?view="+streamID+"&cleanoutput&privacy&noaudio";
if (urlParams.has('turn')){
@ -102,9 +102,9 @@ function loadIframe(){ // this is pretty important if you want to avoid camera p
srcString = srcString+"&buffer="+urlParams.get("buffer");
}
iframe.src = srcString;
iframe2.src = srcString;
iframeContainer.appendChild(iframe);
iframeContainer.appendChild(iframe2);
document.getElementById("container").appendChild(iframeContainer);
var button = document.createElement("br");
@ -112,12 +112,12 @@ function loadIframe(){ // this is pretty important if you want to avoid camera p
var button = document.createElement("button");
button.innerHTML = "Disconnect";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');}
button.onclick = function(){iframe2.contentWindow.postMessage({"close":true}, '*');}
document.getElementById("container").appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Low Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":30}, '*');}
button.onclick = function(){iframe2.contentWindow.postMessage({"bitrate":100}, '*');}
document.getElementById("container").appendChild(button);
@ -125,15 +125,15 @@ function loadIframe(){ // this is pretty important if you want to avoid camera p
var button = document.createElement("button");
button.innerHTML = "High Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":6000}, '*');}
button.onclick = function(){iframe2.contentWindow.postMessage({"bitrate":6000}, '*');}
document.getElementById("container").appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Default Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":-1}, '*');}
button.onclick = function(){iframe2.contentWindow.postMessage({"bitrate":-1}, '*');}
document.getElementById("container").appendChild(button);
setInterval(function(){iframe.contentWindow.postMessage({"getStats":true}, '*');},1000);
setInterval(function(){iframe2.contentWindow.postMessage({"getStats":true}, '*');},1000);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
@ -143,13 +143,20 @@ function loadIframe(){ // this is pretty important if you want to avoid camera p
if ("stats" in e.data){
var out = "";
console.log( e.data.stats);
for (var streamID in e.data.stats.inbound_stats){
out += printValues(e.data.stats.inbound_stats[streamID]);
}
if (out.split("Bitrate_in_kbps").length>1){
document.getElementById("statsdiv").innerHTML = "<b>Bitrate_in_kbps</b>"+out.split("Bitrate_in_kbps")[1];
}
for (var i in e.data.stats.outbound_stats){
if ("quality_Limitation_Reason" in e.data.stats.outbound_stats[i]){
document.getElementById("statsdivOut").innerHTML = "<b>Quality Limitation Reasons:</b> "+e.data.stats.outbound_stats[i].quality_Limitation_Reason+"";
}
}
}
});
@ -176,17 +183,19 @@ function printValues( obj) {
<body onload="loadIframe();">
<div id="container">
</div>
<div style="width:48%;padding:0;margin:0;border:0;display:inline-block;">
<div style="width:48%;padding:0;margin:0 10px;border:0;display:inline-block;">
<h3>OBS.Ninja Speed Test - prototype version</h3>
(Tests connection to TURN server and back)<br /><br />
<li>1.Select your camera.</li>
<li>2.Hit start</li>
<li>3.Wait for the video to load side-by-side. *If it does not auto-load within 20s, refresh and try again.*</li>
<li>4.Stats will load on the right-hand side of the page here. (or press CTRL + LeftClick on the new video to open stats that way)</li>
<li>4.Stats will load on the right-hand side of the page here. (or press CTRL + LeftClick on the new video to open stats)</li>
<li>5.Bitrate_in_kbps, Buffer_Delay_in_ms, and packetLoss_percentage are important connection quality metrics</li>
<li>6.Increase the video bitrate by pressing <i>High Bitrate</i>; it should approach 6000-kbps if the network allows.</li>
</div>
<div id="statsdiv" style="width:48%;padding:0;margin:0;border:0;display:inline-block;">
</div>
<div id="statsdivOut" style="font-size:200%;width:48%;padding:20px;margin:0;border:0;display:inline-block;">
</div>
</body>
</html>