mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-15 07:38:32 +00:00
audio/video retry logic
This commit is contained in:
parent
1d93b28595
commit
6bc6736cdf
30
index.html
30
index.html
@ -119,13 +119,22 @@
|
||||
<i id="mutevideotoggle" class="toggleSize las la-eye my-float"></i>
|
||||
</div>
|
||||
<div id="settingsbutton" title="Settings" onclick="toggleSettings()" class="advanced float" style="cursor: pointer;" alt="Toggle the Settings Menu">
|
||||
<i id="settingstoggle" class="toggleSize las la-cog my-float"></i>
|
||||
<i id="settingstoggle" class="toggleSize las la-sliders-h my-float"></i>
|
||||
</div>
|
||||
<div id="hangupbutton" title="Hangup the Call" onclick="hangup()" class="advanced float" style="cursor: pointer;" alt="Disconnect and End">
|
||||
<i class="toggleSize my-float las la-phone rotate225" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<span
|
||||
id="reportbutton"
|
||||
title="Submit any error logs"
|
||||
onclick="submitDebugLog();"
|
||||
style="cursor: pointer; visibility: hidden; display:none;"
|
||||
>
|
||||
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 46px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-bug" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span
|
||||
id="helpbutton"
|
||||
title="Show Help Info"
|
||||
@ -226,7 +235,7 @@
|
||||
<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: 140%; vertical-align: middle;" aria-hidden="true"></i>
|
||||
<i class="las la-sliders-h" style="font-size: 140%; vertical-align: middle;" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
<br />
|
||||
@ -303,7 +312,7 @@
|
||||
</button>
|
||||
<span id="gear_screen" style="display: inline-block; cursor: pointer;" onclick="toggle(document.getElementById('videoSettings2'));">
|
||||
|
||||
<i class="las la-cog" style="font-size: 170%; vertical-align: middle;" aria-hidden="true"></i>
|
||||
<i class="las la-sliders-h" 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 10px 5px 10px; margin: 10px 0 5px 0;">
|
||||
@ -635,7 +644,7 @@
|
||||
</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>
|
||||
<span data-translate="advanced-camera-settings"><i class="las la-sliders-h"></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>
|
||||
@ -675,8 +684,10 @@
|
||||
<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='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>
|
||||
<button id='advancedOptionsCamera' onclick="this.style.display = 'none'; toggle(getById('popupSelector_constraints_video'),false,false); getById('popupSelector_constraints_loading').style.visibility='visible';" 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); getById('popupSelector_constraints_loading').style.visibility='visible';" 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;">
|
||||
|
||||
@ -684,6 +695,9 @@
|
||||
<span id="popupSelector_constraints_video" class="popupSelector_constraints" style="display: none;">
|
||||
|
||||
</span>
|
||||
<span id="popupSelector_constraints_loading" style="display: none; visibility:hidden">
|
||||
<i class='las la-spinner icn-spinner' style='margin:30px;font-size:400%;color:white;'></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<nav id="context-menu" class="context-menu">
|
||||
@ -807,6 +821,10 @@
|
||||
|
||||
<script>
|
||||
|
||||
if (window.location.hostname.indexOf("www.obs.ninja") == 0) {
|
||||
window.location = window.location.href.replace("www.obs.ninja","obs.ninja"); // the session.salt is domain specific; let's consolidate www as a result.
|
||||
}
|
||||
|
||||
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.5b";
|
||||
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
|
||||
|
||||
18
main.css
18
main.css
@ -394,8 +394,23 @@ button.glyphicon-button.active.focus {
|
||||
}
|
||||
}
|
||||
|
||||
.la-sliders-h {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.la-sliders-h {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
input[type='checkbox'] { cursor:pointer; }
|
||||
input[type='radio'] { cursor:pointer; }
|
||||
|
||||
.icn-spinner {
|
||||
/* animation: spin-animation 3s infinite; */
|
||||
animation: spin-animation 3s infinite;
|
||||
display: inline-block;
|
||||
z-index: 10;
|
||||
}
|
||||
@ -818,7 +833,6 @@ label {
|
||||
/* Add shadows to create the "card" effect */
|
||||
}
|
||||
|
||||
|
||||
.card {
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, .1);
|
||||
background-color: #ddd;
|
||||
|
||||
192
main.js
192
main.js
@ -256,7 +256,10 @@ if (window.obsstudio){
|
||||
|
||||
window.onload = function() { // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending
|
||||
window.addEventListener("beforeunload", function (e) {
|
||||
session.ws.close();
|
||||
try{
|
||||
session.ws.close();
|
||||
} catch (e){
|
||||
}
|
||||
//setTimeout(function(){session.hangup();},0);
|
||||
return undefined; // ADDED OCT 29th; get rid of popup. Just close the socket connection if the user is refreshing the page. It's one or the other.
|
||||
|
||||
@ -1019,6 +1022,7 @@ if (ln_template){ // checking if manual lanuage override enabled
|
||||
getById("qos").innerHTML = location.hostname;
|
||||
getById("logoname").innerHTML = getById("qos").outerHTML;
|
||||
getById("helpbutton").style.display = "none";
|
||||
getById("reportbutton").style.display = "none";
|
||||
}
|
||||
} else if (location.hostname === "rtc.ninja"){
|
||||
try{
|
||||
@ -1029,6 +1033,7 @@ if (ln_template){ // checking if manual lanuage override enabled
|
||||
getById("logoname").innerHTML = "";
|
||||
getById("helpbutton").style.display = "none";
|
||||
getById("helpbutton").style.opacity = 0;
|
||||
getById("reportbutton").style.display = "none";
|
||||
getById("mainmenu").style.opacity = 1;
|
||||
getById("mainmenu").style.margin = "30px 0";
|
||||
getById("translateButton").style.display = "none";
|
||||
@ -1079,6 +1084,7 @@ if (ln_template){ // checking if manual lanuage override enabled
|
||||
getById("qos").innerHTML = location.hostname;
|
||||
getById("logoname").innerHTML = getById("qos").outerHTML ;
|
||||
getById("helpbutton").style.display = "none";
|
||||
getById("reportbutton").style.display = "none";
|
||||
getById("mainmenu").style.opacity = 1;
|
||||
}).catch(function(err){
|
||||
errorlog(err);
|
||||
@ -1094,6 +1100,7 @@ if (ln_template){ // checking if manual lanuage override enabled
|
||||
getById("qos").innerHTML = location.hostname;
|
||||
getById("logoname").innerHTML = getById("qos").outerHTML;
|
||||
getById("helpbutton").style.display = "none";
|
||||
getById("reportbutton").style.display = "none";
|
||||
getById("chatBody").innerHTML = "";
|
||||
} catch (error){
|
||||
errorlog(error);
|
||||
@ -2161,7 +2168,6 @@ 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;
|
||||
@ -2483,7 +2489,6 @@ function toggleSettings(){ // TODO: I need to have this be MUTE, toggle, with vo
|
||||
enumerateDevices().then(gotDevices2).then(function(){});
|
||||
|
||||
getById("popupSelector").style.display="inline-block"
|
||||
getById("settingstoggle").classList.add("icn-spinner");
|
||||
getById("settingsbutton").classList.add("float2");
|
||||
getById("settingsbutton").classList.remove("float");
|
||||
setTimeout(function(){getById("popupSelector").style.right="0px";},1);
|
||||
@ -2496,7 +2501,6 @@ function toggleSettings(){ // TODO: I need to have this be MUTE, toggle, with vo
|
||||
});
|
||||
|
||||
getById("popupSelector").style.right="-400px";
|
||||
getById("settingstoggle").classList.remove("icn-spinner");
|
||||
|
||||
getById("settingsbutton").classList.add("float");
|
||||
getById("settingsbutton").classList.remove("float2");
|
||||
@ -2792,6 +2796,7 @@ function publishScreen(){
|
||||
}
|
||||
getById("controlButtons").style.display="flex";
|
||||
getById("helpbutton").style.display = "inherit";
|
||||
getById("reportbutton").style.display = "";
|
||||
} else {
|
||||
getById("controlButtons").style.display="none";
|
||||
}
|
||||
@ -2855,6 +2860,7 @@ function publishWebcam(btn = false){
|
||||
}
|
||||
getById("controlButtons").style.display="flex";
|
||||
getById("helpbutton").style.display = "inherit";
|
||||
getById("reportbutton").style.display = "";
|
||||
} else {
|
||||
getById("controlButtons").style.display="none";
|
||||
}
|
||||
@ -3226,7 +3232,7 @@ function createDirectorCam(vid){
|
||||
vid.style.height="35px";
|
||||
vid.style.padding ="0";
|
||||
getById("press2talk").innerHTML = "<span data-translate='Push-to-Mute'>🔴 Push to Mute</span>";
|
||||
getById("press2talk").outerHTML += '<button class="grey" style="margin: 10px 15px;" onclick="toggleSettings()"><i class="las la-cog"></i></button>';
|
||||
getById("press2talk").outerHTML += '<button class="grey" style="margin: 10px 15px;" onclick="toggleSettings()"><i class="las la-sliders-h"></i></button>';
|
||||
getById("miniPerformer").appendChild(vid);
|
||||
getById("press2talk").dataset.enabled="true";
|
||||
session.muted=false;
|
||||
@ -3997,6 +4003,7 @@ function gotDevices2(deviceInfos){
|
||||
option.checked = false;
|
||||
}
|
||||
option.onchange = function(event){ // make sure to clear 'no audio option' if anything else is selected
|
||||
log("Audio OPTION HAS CHANGED?");
|
||||
if (!(CtrlPressed)){
|
||||
document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) {
|
||||
if (event.currentTarget.value !== item.value){
|
||||
@ -4035,6 +4042,7 @@ function gotDevices2(deviceInfos){
|
||||
});
|
||||
|
||||
audioInputSelect.onchange = function(){
|
||||
log("Audio OPTION HAS CHANGED? 2");
|
||||
activatedPreview=false;
|
||||
grabAudio("videosource","#audioSource3");
|
||||
};
|
||||
@ -4095,8 +4103,10 @@ async function getAudioOnly(selector, trackid=null, override=false){
|
||||
var audioList = [];
|
||||
var streams = [];
|
||||
log("getAudioOnly()");
|
||||
log(trackid);
|
||||
for (var i=0; i<audioSelect.length;i++){
|
||||
if (audioSelect[i].value=="ZZZ"){
|
||||
log("ZZZ as Audio");
|
||||
continue;
|
||||
} else if (trackid==audioSelect[i].value){ // skip already excluded
|
||||
continue;
|
||||
@ -4187,6 +4197,62 @@ function applyMirror(mirror, eleName='previewWebcam'){ // true unmirrors as its
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect system changes; handle change or use for debugging
|
||||
var audioReconnectTimeout = null;
|
||||
var videoReconnectTimeout = null;
|
||||
function reconnectDevices(event){
|
||||
warnlog("A media device has changed");
|
||||
errorlog(event);
|
||||
enumerateDevices().then(gotDevices2).then(function(){
|
||||
|
||||
clearTimeout(audioReconnectTimeout);
|
||||
audioReconnectTimeout = setTimeout(function(){
|
||||
activatedPreview=false;
|
||||
grabAudio("videosource","#audioSource3", "default");
|
||||
},1000);
|
||||
|
||||
clearTimeout(videoReconnectTimeout);
|
||||
videoReconnectTimeout = setTimeout(function(){
|
||||
activatedPreview=false;
|
||||
grabVideo(session.quality, "videosource", "select#videoSource3");
|
||||
},1000);;
|
||||
|
||||
var outputSelect = document.querySelector('select#outputSource3');
|
||||
session.sink = outputSelect.options[outputSelect.selectedIndex].value;
|
||||
|
||||
getById("videosource").setSinkId(session.sink).then(() => {
|
||||
log("New Output Device:"+session.sink);
|
||||
}).catch(error => {
|
||||
errorlog(error);
|
||||
});
|
||||
for (UUID in session.rpcs){
|
||||
session.rpcs[UUID].videoElement.setSinkId(session.sink).then(() => {
|
||||
log("New Output Device for: "+UUID);
|
||||
}).catch(error => {
|
||||
errorlog(error);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
navigator.mediaDevices.ondevicechange = reconnectDevices;
|
||||
} catch(e){errorlog(e);}
|
||||
|
||||
|
||||
function updateConnectionStatus() {
|
||||
warnlog("Connection type changed from " + session.stats.network_type + " to " + Connection.effectiveType);
|
||||
session.stats.network_type = Connection.effectiveType + " / " +Connection.type;
|
||||
}
|
||||
|
||||
try{
|
||||
var Connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
||||
session.stats.network_type = Connection.effectiveType + " / " +Connection.type;
|
||||
Connection.addEventListener('change', updateConnectionStatus);
|
||||
} catch(e){}
|
||||
|
||||
|
||||
|
||||
async function grabScreen(quality=0, audio=true){
|
||||
if (!navigator.mediaDevices.getDisplayMedia){
|
||||
@ -4558,10 +4624,20 @@ async function grabVideo(quality=0, eleName='previewWebcam', selector="select#vi
|
||||
getById(eleName).controls=true;
|
||||
}
|
||||
}
|
||||
if (getById("popupSelector_constraints")){
|
||||
getById("popupSelector_constraints").innerHTML = "";
|
||||
if (getById("popupSelector_constraints_video")){
|
||||
getById("popupSelector_constraints_video").innerHTML = "";
|
||||
}
|
||||
if (getById("popupSelector_constraints_audio")){
|
||||
getById("popupSelector_constraints_audio").innerHTML = "";
|
||||
}
|
||||
if (getById("popupSelector_constraints_loading")){
|
||||
getById("popupSelector_constraints_loading").style.display="";
|
||||
}
|
||||
|
||||
grabVideoTimer = setTimeout(function(){
|
||||
if (getById("popupSelector_constraints_loading")){
|
||||
getById("popupSelector_constraints_loading").style.display="none";
|
||||
}
|
||||
if (eleName=="previewWebcam"){
|
||||
getById(eleName).controls=true;
|
||||
}
|
||||
@ -4692,7 +4768,7 @@ async function grabAudio(eleName="previewWebcam", selector="#audioSource", track
|
||||
for (UUID in session.pcs){
|
||||
if (session.pcs[UUID].allowAudio==true){ // allow
|
||||
var sender = session.pcs[UUID].addTrack(track, streams[i]);
|
||||
//sender.track.onended = tryAgain;
|
||||
sender.track.onended = tryAgain;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -4715,88 +4791,9 @@ async function grabAudio(eleName="previewWebcam", selector="#audioSource", track
|
||||
}
|
||||
}
|
||||
|
||||
var tryAgainTimer=null;
|
||||
|
||||
function tryAgain(event){ // audio or video agnostic track reconnect ------------not actually in use,. maybe out of date
|
||||
warnlog(event.currentTarget);
|
||||
if (getById("videosource")==null){ // Don't bother with this if just a preview stream
|
||||
return;
|
||||
}
|
||||
|
||||
var deviceType = event.currentTarget.kind;
|
||||
var deviceId= event.currentTarget.id;
|
||||
|
||||
if (tryAgainTimer!=null){
|
||||
clearTimeout(tryAgainTimer);
|
||||
tryAgainTimer=null;
|
||||
}
|
||||
tryAgainTimer = setTimeout(function(){
|
||||
navigator.mediaDevices.ondevicechange = null; // we only give it 10-seconds to reconnect.
|
||||
},10000);
|
||||
|
||||
navigator.mediaDevices.ondevicechange = function(){
|
||||
clearTimeout(tryAgainTimer);
|
||||
tryAgainTimer=null;
|
||||
navigator.mediaDevices.ondevicechange=null; // clear
|
||||
|
||||
|
||||
if (deviceType=="audio"){
|
||||
if ((deviceId=="default") && (session.echoCancellation!==false) && (session.autoGainControl!==false) && (session.noiseSuppression!==false)){
|
||||
var constraint = {audio: true};
|
||||
} else {
|
||||
var constraint = {audio: {deviceId: {exact: deviceId}}};
|
||||
if (session.echoCancellation==false){
|
||||
constraint.audio.echoCancellation=false;
|
||||
}
|
||||
if (session.autoGainControl==false){
|
||||
constraint.audio.autoGainControl=false;
|
||||
}
|
||||
if (session.noiseSuppression==false){
|
||||
constraint.audio.noiseSuppression=false;
|
||||
}
|
||||
}
|
||||
constraint.video = false;
|
||||
} else if (deviceType=="video"){
|
||||
var constraint = {
|
||||
video: {deviceId: {exact: deviceId}},
|
||||
audio: false
|
||||
};
|
||||
} else {
|
||||
return; // no idea what this is? fail gently.
|
||||
}
|
||||
|
||||
//warnlog(constraint);
|
||||
navigator.mediaDevices.getUserMedia(constraint).timeout(3000).then(function (stream){
|
||||
stream.getTracks().forEach(function(track){
|
||||
|
||||
getById("videosource").srcObject.addTrack(track, stream); // add video track to the preview video
|
||||
session.streamSrc = getById(eleName).srcObject;
|
||||
toggleMute(true);
|
||||
|
||||
for (UUID in session.pcs){
|
||||
if (session.pcs[UUID].allowAudio==true){ // allow
|
||||
var senders = session.pcs[UUID].getSenders(); // for any connected peer, update the video they have if connected with a video already.
|
||||
var added=false;
|
||||
senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams?
|
||||
if (sender.track){
|
||||
if (sender.track.id == track.id){
|
||||
added=true;
|
||||
//warnlog(sender.track);
|
||||
if (sender.track.readyState=="ended"){
|
||||
sender.replaceTrack(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (added==false){
|
||||
sender = session.pcs[UUID].addTrack(track, stream);
|
||||
sender.track.onended = tryAgain;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}).catch(errorlog); // console error message only
|
||||
}
|
||||
log("TRY AGAIN TRIGGERED");
|
||||
errorlog(event);
|
||||
}
|
||||
|
||||
|
||||
@ -4963,13 +4960,18 @@ function listAudioSettings(){
|
||||
getById("popupSelector_constraints_audio").innerHTML = "";
|
||||
try {
|
||||
var track0 = session.streamSrc.getAudioTracks();
|
||||
track0 = track0[0];
|
||||
if (track0.getCapabilities){
|
||||
session.cameraConstaints = track0.getCapabilities();
|
||||
if (track0.length){
|
||||
track0 = track0[0];
|
||||
if (track0.getCapabilities){
|
||||
session.cameraConstaints = track0.getCapabilities();
|
||||
}
|
||||
log(session.cameraConstaints);
|
||||
} else {
|
||||
warnlog("session.streamSrc contains no audio tracks");
|
||||
return;
|
||||
}
|
||||
|
||||
log(session.cameraConstaints);
|
||||
} catch(e){
|
||||
warnlog("session.streamSrc contains no audio tracks");
|
||||
errorlog(e);
|
||||
return;
|
||||
}
|
||||
@ -5121,7 +5123,7 @@ function applyAudioHack(track, constraint, value=null){
|
||||
} else if (value == "false"){
|
||||
value = false;
|
||||
}
|
||||
log("CONTREAIT",constraint);
|
||||
log(constraint);
|
||||
var new_constraints = Object.assign(track.getSettings(), {[constraint]:value}, );
|
||||
new_constraints = {audio: new_constraints, video:false};
|
||||
log(new_constraints);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user