Merge pull request #746 from steveseguin/v16-beta

V16 Release
This commit is contained in:
Steve Seguin 2021-02-24 05:34:55 -05:00 committed by GitHub
commit b36b52ad8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 550 additions and 180 deletions

View File

@ -55,7 +55,7 @@
}
</style>
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css" />
<link rel="stylesheet" href="./main.css?ver=46" />
<link rel="stylesheet" href="./main.css?ver=48" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.min.js"></script>
</head>
<body id="main" class="hidden">
@ -66,8 +66,8 @@
<span itemprop="thumbnail" itemscope itemtype="http://schema.org/ImageObject">
<link itemprop="url" href="./media/obsNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=26"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=177"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=29"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=179"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px;">
@ -103,7 +103,6 @@
</div>
<div id="head2" class="advanced" style="display: inline-block; text-decoration: none; font-size: 60%; color: white;">
<span data-translate="joining-room">You are in room</span>:
<div id="roomid" style="display: inline-block;"></div>
</div>
@ -165,7 +164,7 @@
<span
id="helpbutton"
title="Show Help Info"
onclick="alert('Email steve@seguin.email if the system breaks or check https://reddit.com/r/obsninja for support.\n\nThe Wiki contains many help guides and advanced settings.\n\nAccess the debug menu by pressing CTRL (command) and Left-Clicking on a video.\n\nMost issues can be fixed by using Wired Internet instead of Wi-Fi.')"
onclick="alert('For support, please browse https://reddit.com/r/obsninja or join the live chat on Discord at https://discord.obs.ninja.\n\nThe Wiki also contains many help guides and advanced settings, located at https://wiki.obs.ninja.\n\nTo access the video stats menu, hold CTRL (command) and Left-Click on a video. Most video issues can be fixed by using Wired Internet instead of Wi-Fi.')"
style="cursor: pointer; display:none;"
alt="How to Use This with OBS"
>
@ -563,7 +562,8 @@
<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/>
<br /><br />
<p style="margin:10px">Keep this tab visible if using Chrome, else the video playback will stop</p>
<p style="margin:10px">(Media file streaming is still quite experimental)</p>
<p style="margin:10px">(Media file streaming is still quite experimental)</p><br />
<p style="margin:10px">File Sharing seems to be broken on Chrome v88. <br />Using The Electron Capture app instead of Chrome should work: <a href="https://github.com/steveseguin/electroncapture/releases/tag/1.1.3" style="color:blue"><u>GET IT HERE</u></a><br />You can also <a href="https://github.com/aws/amazon-chime-sdk-js/issues/1031" style="color:blue">turn off hardware-accleration</a> in Chrome/Edge to fix the issue.</p>
</div>
<div class="outer close">
@ -587,9 +587,9 @@
<button onclick="previewIframe(getById('iframeURL').value);" >Preview</button>
<button onclick="this.innerHTML = 'Update'; session.publishIFrame(getById('iframeURL').value);" >Share</button><br />
<small class="iframeblob">
<li>Remote website must be CORS/IFrame compatible with full SSL-encryption enabled.</li>
<li>Not all websites will work with this feature and many will actively not allow embedding like this.</li>
<li>If sharing a Youtube video, try using the embeddable link version and ensure that the video is set to allow embedding.</li>
<li>Not all websites will work with this feature as some sites disallow embedding.</li>
<li>The site will try to auto-optimize standard Youtube or Twitch links.</li>
<li>Remote websites must be CORS/IFrame compatible with full SSL-encryption enabled.</li>
</small>
<div id="iFramePreview" style=" width: 1280px; height: 720px; margin: auto; padding: 10px;"></div>
</div>
@ -603,7 +603,7 @@
</div>
<div id="container-7" class="column columnfade pointer card advanced" style="overflow: hidden;" onclick="window.location = './speedtest';">
<div id="container-7" class="column columnfade pointer card advanced" style="overflow: hidden;" onclick="window.location = './speedtest.html';">
<h2><span data-translate="run-a-speed-test">Run a Speed Test</span></h2>
<i style="margin-top:30px;font-size:600%;overflow:hidden;" class="las la-tachometer-alt"></i>
</div>
@ -620,10 +620,8 @@
<span data-translate="info-blob">
<h2>What is OBS.Ninja</h2>
<br />
<li>100%
<b>free</b>; no downloads; no personal data collection; no sign-in
</li>
<li>Bring video from your smartphone, computer, or friends directly into your OBS video stream</li>
<li>100% <b>free</b>; no downloads; no personal data collection; no sign-in</li>
<li>Bring live video from your smartphone, remote computer, or friends directly into OBS or other studio software.</li>
<li>We use cutting edge Peer-to-Peer forwarding technology that offers privacy and ultra-low latency</li>
<br />
<li>Youtube video
@ -631,41 +629,32 @@
<a href="https://www.youtube.com/watch?v=vLpRzMjUDaE&list=PLWodc2tCfAH1WHjl4WAOOoRSscJ8CHACe&index=2" alt="Youtube video demoing OBS.Ninja">Demoing it here</a>
</li>
<br />
🥳 Over 350,000 streamers have used OBS.Ninja this month alone, including major TV networks and some of the biggest Youtube/Twitch stars.
<br />
<br />
<br />
<i>
<font style="color: red;">Known issues:</font>
</i>
<br />
<li>
MacOS users using OBS will need to update to <a href="https://github.com/obsproject/obs-studio/releases/tag/26.1.2">OBS Studio 26.1.2</a> or resort to
window-capturing with the provided <a href="https://github.com/steveseguin/electroncapture">Electron-Capture app</a>.
</li>
<li>If you have <a href="https://github.com/steveseguin/obsninja/wiki/FAQ#video-is-pixelated">"pixel smearing"</a> or corrupted video, try adding <b>&codec=vp9</b> or &codec=h264 to the OBS view link. Using Wi-Fi will make the issue worse.
</li>
<li>
iOS devices may have occasional audio or camera issues, such as no sound or distorted sound. <a href="https://bugs.webkit.org/show_bug.cgi?id=218762">Partially fixed in iOS 14.3</a>
If you have <a href="https://github.com/steveseguin/obsninja/wiki/FAQ#video-is-pixelated">"pixel smearing"</a> or corrupted video, try adding <i>&codec=h264</i> or <i>&codec=vp9</i> to the OBS view link. Using Wi-Fi will make the issue worse.
</li>
<li>
Chrome on Android 11 has an issue with the browser freezing at times. To unfreeze it, background the browser and then foreground it again.
</li>
<li>
The VP9 codec on Chromium-based browsers seems to lag when screen-sharing at the moment. Use the OBS Virtual Camera as a capture source instead.
</li>
<br />
🥳 Site Updated: <a href="https://github.com/steveseguin/obsninja/wiki/v16-release-notes">Fed 3rd, 2021</a>. The previous version can be found at
<a href="https://obs.ninja/v15/">https://obs.ninja/v15/</a> if you are having new issues.
Site Updated: <a href="https://github.com/steveseguin/obsninja/wiki/v16.3-update-notes">Feb 24th, 2021</a> (v16.3). The previous version can be found at <a href="https://obs.ninja/v16/">https://obs.ninja/v16/</a> if you are having issues with this minor update.
<br />
<br />
<h3>
<i>
Check out the
<a href="https://www.reddit.com/r/OBSNinja/">sub-reddit
<i class="lab la-reddit-alien"></i> </a>for help and see the <a href="https://github.com/steveseguin/obsninja/wiki/">Wiki for advanced info</a>. I'm also on
<a href="https://discord.gg/T4xpQVv">Discord <i class="lab la-discord"></i></a> or email me at steve@seguin.email
🛠 For support, see the <a href="https://www.reddit.com/r/OBSNinja/">sub-reddit <i class="lab la-reddit-alien"></i></a> or join the <a href="https://discord.gg/T4xpQVv">Discord <i class="lab la-discord"></i></a>. The <a href="https://github.com/steveseguin/obsninja/wiki/">Wiki is here</a> and my personal email is <i>steve@seguin.email</i>
</h3>
</i>
</h3>
</span>
</div>
</center>
@ -933,7 +922,7 @@
<span data-translate="send-direct-chat"><i class="las la-envelope"></i> Message</span>
</button>
<button data-action-type="addToScene" style="grid-column: 1;" data-value="0" title="Add this Video to any remote '&scene=1'" onclick="directEnable(this, event);">
<button data-action-type="addToScene" style="grid-column: 1;" data-value="0" title="Add this Video to any remote '&scene=1'" onclick="directEnable(this, event, 1);">
<i class="las la-plus-square"></i>
<span data-translate="add-to-scene">Add to Scene</span>
</button>
@ -942,9 +931,33 @@
<span data-translate="mute-scene" >mute in scene</span>
</button>
<font class="tooltip" style="height: 0; border: 0;">
<input data-action-type="volume" type="range" min="0" max="200" value="100" title="Remotely change the volume of this guest" oninput="remoteVolumeUI(this)" onclick="remoteVolume(this);" style="grid-column: 1; margin:5px; width: 93%; position: relative;top: 0.6em; background-color:#fff0;"/><span class="tooltiptext" style='float: right; overflow: auto; left: 40px; width: 2.5em; top: -13px; margin: 0; position:relative;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus,Code2000, Code2001, Code2002, Musica, serif, LastResort;' >100</span></font>
<span id="sceneGroup1" style="display:none">
<button style="width: 35.2px" data-action-type="add-scene-2" title="Add to Scene 2" onclick="directEnable(this, event, 2);">
<span >S2</span>
</button>
<button style="width:35.2px;" data-action-type="add-scene-3" title="Add to Scene 3" onclick="directEnable(this, event, 3);">
<span >S3</span>
</button>
<button style="width: 35.2px" data-action-type="add-scene-4" title="Add to Scene 4" onclick="directEnable(this, event, 4);">
<span >S4</span>
</button>
</span>
<font class="tooltip" style="height: 0; border: 0;">
<input data-action-type="volume" type="range" min="0" max="200" value="100" title="Remotely change the volume of this guest" oninput="remoteVolumeUI(this)" onclick="remoteVolume(this);" style="grid-column: 2; margin:5px; width: 93%; position: relative;top: 0.6em; background-color:#fff0;"/><span class="tooltiptext" style='float: right; overflow: auto; left: 40px; width: 2.5em; top: -13px; margin: 0; position:relative;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus,Code2000, Code2001, Code2002, Musica, serif, LastResort;' >100</span>
</font>
<span id="sceneGroup2" style="display:none">
<button style="width: 35.2px" data-action-type="add-scene-5" title="Add to Scene 5" onclick="directEnable(this, event, 5);">
<span >S5</span>
</button>
<button style="width: 35.2px" data-action-type="add-scene-6" title="Add to Scene 6" onclick="directEnable(this, event, 6);">
<span >S6</span>
</button>
<button style="width: 35.2px" data-action-type="add-scene-7" title="Add to Scene 7" onclick="directEnable(this, event, 7);">
<span >S7</span>
</button>
</span>
<button data-action-type="mute-guest" style="grid-column: 2;" title="Mute this guest everywhere" onclick="remoteMute(this, event);">
<i class="las la-microphone-slash"></i>
<span data-translate="mute-guest" >mute guest</span>
@ -992,13 +1005,51 @@
</button>
</span>
<button data-action-type="toggle-remote-speaker" style="grid-column: 1;" title="Toggle the remote guest's speaker output" onclick="remoteSpeakerMute(this, event);">
<span id="channelGroup1" style="display:none">
<button style="width: 35.2px" data-action-type="add-channel" class="pressed" title="Set to Default Audio Channel" onclick="changeChannelOffset(this.dataset.UUID, false);">
<span >00</span>
</button>
<button style="width:35.2px;" data-action-type="add-channel" title="Set to Audio Channel 1" onclick="changeChannelOffset(this.dataset.UUID, 0);">
<span >C1</span>
</button>
<button style="width: 35.2px" data-action-type="add-channel" title="Set to Audio Channel 2" onclick="changeChannelOffset(this.dataset.UUID, 1);">
<span >C2</span>
</button>
</span>
<button data-action-type="toggle-remote-speaker" title="Toggle the remote guest's speaker output" onclick="remoteSpeakerMute(this, event);">
<i class="las la-volume-off"></i> <span data-translate="toggle-remote-speaker">Deafen Guest</span>
</button>
<span id="channelGroup2" style="display:none" >
<button style="width: 35.2px" data-action-type="add-channel" title="Set to Audio Channel 3" onclick="changeChannelOffset(this.dataset.UUID, 2);">
<span >C3</span>
</button>
<button style="width:35.2px;" data-action-type="add-channel" title="Set to Audio Channel 4" onclick="changeChannelOffset(this.dataset.UUID,3);">
<span >C4</span>
</button>
<button style="width: 35.2px" data-action-type="add-channel" title="Set to Audio Channel 5" onclick="changeChannelOffset(this.dataset.UUID, 4);">
<span >C5</span>
</button>
</span>
<button data-action-type="toggle-remote-display" style="grid-column: 2;" title="Toggle the remote guest's display output" onclick="remoteDisplayMute(this, event);">
<i class="las la-eye-slash"></i> <span data-translate="toggle-remote-display">Blind Guest</span>
</button>
<span id="channelGroup3" style="display:none" >
<button style="width: 35.2px" data-action-type="add-channel" title="Set to Audio Channel 6" onclick="changeChannelOffset(this.dataset.UUID, 5);">
<span >C6</span>
</button>
<button style="width:35.2px;" data-action-type="add-channel" title="Set to Audio Channel 7" onclick="changeChannelOffset(this.dataset.UUID, 6);">
<span >C7</span>
</button>
<button style="width: 35.2px" data-action-type="add-channel" title="Set to Audio Channel 8" onclick="changeChannelOffset(this.dataset.UUID, 7);">
<span >C8</span>
</button>
</span>
<button class="" data-action-type="advanced-audio-settings" data-active="false" style="grid-column: 1;" title="Remote Audio Settings" onclick="requestAudioSettings(this);">
<span data-translate="advanced-audio-settings"><i class="las la-sliders-h"></i> Audio Settings</span>
</button>
@ -1158,7 +1209,7 @@
}
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
session.version = "16.2";
session.version = "16.3";
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.
@ -1223,7 +1274,7 @@
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
<script type="text/javascript" crossorigin="anonymous" id="mixer-js" src="./mixer.js?ver=2"></script>
-->
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=163"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=168"></script>
<script type="text/javascript">
setTimeout(function(){ // lazy load
var script = document.createElement('script');

View File

@ -486,9 +486,9 @@ button.glyphicon-button.active.focus {
#container.vidcon {
height:100%;
}
@media only screen and (max-width: 700px){
@media only screen and (max-width: 640px){
#controlButtons {
transform: scale(0.8) translateY(20%);
transform: scale(0.9) translateY(10%);
}
}
@media only screen and (max-width: 400px){
@ -521,10 +521,21 @@ button.btnArmTransferRoom.selected{
@media only screen and (max-height: 540px){
#subControlButtons {
transform: scale(0.90);
transform: scale(0.88);
}
#gridlayout>#container.vidcon {
height:90%
height:88%
}
#controlButtons {
height:54px;
}
}
@media only screen and (max-height: 500px){
#subControlButtons {
transform: scale(0.87);
}
#gridlayout>#container.vidcon {
height:87%
}
#controlButtons {
height:54px;
@ -534,7 +545,16 @@ button.btnArmTransferRoom.selected{
#subControlButtons {
transform: scale(0.85);
}
#header{
#logoname{
display:none;
}
#head1{
display:none;
}
#head4{
display:none;
}
#head2{
display:none;
}
#gridlayout>#container.vidcon {
@ -544,6 +564,20 @@ button.btnArmTransferRoom.selected{
height:50px;
}
}
@media only screen and (max-height: 300px){
#gridlayout>#container.vidcon {
height:81%
}
#subControlButtons {
transform: scale(0.81);
}
#controlButtons {
height:46.2px;
}
#head2 {
display:none !important;
}
}
@media only screen and (max-height: 240px){
#gridlayout>#container.vidcon {
height:78%
@ -589,6 +623,9 @@ button.btnArmTransferRoom.selected{
}
}
#header:empty{
display:none;
}
@keyframes pulse {
0% {
@ -949,6 +986,18 @@ input[type=range]:focus::-ms-fill-upper {
}
}
@media only screen and (max-height: 355px) {
#popupSelector {
padding: 0 !important;
}
}
@media only screen and (max-height: 330px) {
#popupSelector {
padding: 0 !important;
font-size: 92%;
}
}
.popupSelector_constraints{
margin:30px 9% 0 7%;
}
@ -2160,36 +2209,21 @@ span#guestTips {
.video-label.zoom {
position: absolute;
bottom: 0;
left: 0;
margin: 0px;
color: white;
padding: 5px 10px;
background: rgba(0, 0, 0, .5);
pointer-events:none;
}
.video-label.teams {
position: absolute;
bottom: 0.6vh;
left: 0.5vh;
margin: 0px;
color: white;
padding: 5px 10px;
background: rgba(0, 0, 0, .4);
pointer-events:none;
border-radius: 5px;
}
.video-label.skype {
position: absolute;
bottom: 2vh;
left: 50%;
transform: translateX(-50%);
margin: 0px;
color: white;
padding: 5px 10px;
background: rgba(0, 0, 0, .8);
pointer-events:none;
border-radius: 5px;
@ -2197,7 +2231,6 @@ span#guestTips {
}
.video-label.ninjablue {
position: absolute;
bottom: 5%;
left: 0;
background: #141926;
@ -2205,7 +2238,6 @@ span#guestTips {
}
.video-label.toprounded {
position: absolute;
top: 0;
bottom: unset;
background: rgb(0 0 0 / 70%);
@ -2223,11 +2255,10 @@ span#guestTips {
}
.video-label.fire {
color: #FFFFFF;
text-shadow: 0 -1px 4px #FFF, 0 -2px 10px #ff0, 0 -10px 20px #ff8000, 0 -18px 40px #F00;
font-weight: bold;
position: absolute;
bottom: 2vh;
left: 0;
width: 100%;
text-align: center;
}
@ -2237,22 +2268,25 @@ span#guestTips {
display:block;
width:0.5vh;
height:0.5vh;
min-width:10px;
min-height:10px;
top: 2vh;
right: 2vh;
background-color:green;
position:absolute;
display:none;
border-radius: 1vh;
border-radius: 2vh;
pointer-events:none;
}
.video-mute-state {
top: 0.5em;
right: 0.5em;
top: 2vh;
right: 2vh;
position: absolute;
color:white;
border-radius: 1vh;
border-radius: 2vh;
background-color:#b11313;
padding: 2px 2px 2px 1px;
}
#help_directors_room{

273
main.js
View File

@ -669,6 +669,23 @@ if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(naviga
}
if ((iOS) || (iPad)) {
window.addEventListener('resize', function() { // Safari is the new IE.
if ( window.matchMedia("(orientation: portrait)").matches ) {
document.getElementsByTagName("html")[0].style.height = "100vh";
setTimeout(function(){
document.getElementsByTagName("html")[0].style.height = "100%";
}, 1000);
} else if ( window.matchMedia("(orientation: landscape)").matches ) {
document.getElementsByTagName("html")[0].style.height = "100vh";
setTimeout(function(){
document.getElementsByTagName("html")[0].style.height = "100%";
}, 1000);
}
});
}
if (/CriOS/i.test(navigator.userAgent) && (iOS || iPad)) {
if (!(session.cleanOutput)) {
try {
@ -682,11 +699,11 @@ if (/CriOS/i.test(navigator.userAgent) && (iOS || iPad)) {
if (urlParams.has('broadcast') || urlParams.has('bc')) {
log("Broadcast flag set");
session.broadcast = urlParams.get('broadcast') || urlParams.get('bc');
if ((iOS) || (iPad)) {
session.nopreview = false;
} else {
session.nopreview = true;
}
//if ((iOS) || (iPad)) {
// session.nopreview = false;
//} else {
// session.nopreview = true;
//}
session.style = 1;
getById("header").style.display = "none";
getById("header").style.opacity = 0;
@ -774,9 +791,9 @@ if (urlParams.has('blind')) {
if (urlParams.has('dpi') || urlParams.has('dpr')) {
session.devicePixelRatio = urlParams.get('dpi') || urlParams.get('dpr') || 2.0;
} else if (window.devicePixelRatio && window.devicePixelRatio!==1){
session.devicePixelRatio = window.devicePixelRatio; // this annoys me to no end.
}
} //else if (window.devicePixelRatio && window.devicePixelRatio!==1){
// session.devicePixelRatio = window.devicePixelRatio; // this annoys me to no end.
//}
if (urlParams.has('speakermute') || urlParams.has('mutespeaker') || urlParams.has('sm') || urlParams.has('ms')) {
session.speakerMuted = true;
@ -861,6 +878,11 @@ if (urlParams.has('scene')) {
session.audioMeterGuest = false;
}
if (urlParams.has('scenes')) {
getById("sceneGroup1").style.display = "block";
getById("sceneGroup2").style.display = "block";
}
if (urlParams.has('mediasettings')) {
session.forceMediaSettings = true;
}
@ -1897,6 +1919,14 @@ if (urlParams.has('channeloffset')) {
log("max channels is 32; channels offset");
session.audioEffects = true;
}
if (urlParams.has('showchannels')) {
getById("channelGroup1").style.display = "block";
getById("channelGroup2").style.display = "block";
getById("channelGroup3").style.display = "block";
session.audioEffects = true;
}
if (urlParams.has('enhance')) {
//if (parseInt(urlParams.get('enhance')>0){
session.enhance = true; //parseInt(urlParams.get('enhance'));
@ -2147,6 +2177,8 @@ if (urlParams.has('turn')) {
if (urlParams.has('privacy') || urlParams.has('private') || urlParams.has('relay')) { // please only use if you are also using your own TURN service.
session.privacy = true;
try {
session.configuration.iceTransportPolicy = "relay"; // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/address
} catch (e) {
@ -2916,8 +2948,13 @@ if ((session.roomid) || (urlParams.has('roomid')) || (urlParams.has('r')) || (ur
if (session.roomid.length > 0) {
if (session.videoDevice === 0) {
getById("add_camera").innerHTML = "Join room with Microphone";
miniTranslate(getById("add_camera"), "join-room-with-mic");
if (session.audioDevice === 0) {
getById("add_camera").innerHTML = "Join room";
miniTranslate(getById("add_camera"), "join-room");
} else {
getById("add_camera").innerHTML = "Join room with Microphone";
miniTranslate(getById("add_camera"), "join-room-with-mic");
}
} else {
getById("add_camera").innerHTML = "Join Room with Camera";
miniTranslate(getById("add_camera"), "join-room-with-camera");
@ -3723,10 +3760,10 @@ function lowerhand() {
var previousRoom = "";
var stillNeedRoom = true;
var transferCancelled = false;
var armedTransfer = true;
var armedTransfer = false;
function directMigrate(ele, event) { // everyone in the room will hangup this guest also? I like that idea. What about the STREAM ID? I suppose we don't kick out if the viewID matches.
log("directMigrate");
if (event === false) {
if (previousRoom === null) { // user cancelled in previous callback
ele.innerHTML = '<i class="las la-paper-plane"></i> <span data-translate="forward-to-room">Transfer</span>';
@ -3743,6 +3780,7 @@ function directMigrate(ele, event) { // everyone in the room will hangup this gu
ele.innerHTML = '<i class="las la-check"></i> <span data-translate="forward-to-room">Armed</span>';
ele.style.backgroundColor = "#BF3F3F";
transferCancelled = false;
armedTransfer=true;
Callbacks.push([directMigrate, ele, stillNeedRoom]);
stillNeedRoom = false;
log("Migrate queued");
@ -3826,28 +3864,33 @@ function directHangup(ele, event) { // everyone in the room will hangup this gue
}
}
function directEnable(ele, event) { // A directing room only is controlled by the Director, with the exception of MUTE.
function directEnable(ele, event, scene=1) { // A directing room only is controlled by the Director, with the exception of MUTE.
if (!((event.ctrlKey) || (event.metaKey))) {
if (ele.dataset.enable == 1) {
ele.dataset.enable = 0;
ele.className = "";
ele.children[1].innerHTML = "Add to Scene";
getById("container_" + ele.dataset.UUID).style.backgroundColor = null;
if (ele.children[1]){
ele.children[1].innerHTML = "Add to Scene";
getById("container_" + ele.dataset.UUID).style.backgroundColor = null;
}
} else {
getById("container_" + ele.dataset.UUID).style.backgroundColor = "#649166";
ele.dataset.enable = 1;
ele.className = "pressed";
ele.children[1].innerHTML = "Remove";
if (ele.children[1]){
ele.children[1].innerHTML = "Remove";
getById("container_" + ele.dataset.UUID).style.backgroundColor = "#649166";
}
}
}
var msg = {};
msg.request = "sendroom";
//msg.roomid = session.roomid;
msg.scene = "1"; // scene
msg.scene = scene;
msg.action = "display";
msg.value = ele.dataset.enable;
msg.target = ele.dataset.UUID;
//session.anysend(msg);
session.sendMsg(msg); // send to everyone in the room, so they know if they are on air or not.
}
@ -4916,6 +4959,8 @@ function createRoomCallback(passAdd, passAdd2) {
codecGroupFlag = "&codec=h264";
} else if (codecGroupFlag.value === "vp8") {
codecGroupFlag = "&codec=vp8";
} else if (codecGroupFlag.value === "av1") {
codecGroupFlag = "&codec=av1";
} else {
codecGroupFlag = "";
}
@ -4939,11 +4984,7 @@ function createRoomCallback(passAdd, passAdd2) {
try {
if (session.label === false) {
if (document.title == "") {
document.title = "Control Room";
} else {
document.title += " - Control Room";
}
document.title = "Control Room";
}
} catch (e) {
errorlog(e);
@ -5022,7 +5063,14 @@ function createRoomCallback(passAdd, passAdd2) {
getById("chatbutton").classList.add("advanced");
}
joinRoom(session.roomid);
if (session.autostart){
press2talk(true);
} else {
session.seeding=true;
session.seedStream();
}
joinRoom(session.roomid);
}
/**
@ -5474,12 +5522,17 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
if ((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.audioDevice))) {
tmp.push(deviceInfo);
log("A DEVICE FOUND = " + deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase());
} else if (deviceInfo.deviceId === session.audioDevice){
tmp.push(deviceInfo);
log("EXACT A DEVICE FOUND");
}
}
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if (!((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.audioDevice)))) {
tmp.push(deviceInfo);
if (deviceInfo.deviceId !== session.audioDevice){
tmp.push(deviceInfo);
}
}
}
@ -5496,12 +5549,17 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.videoDevice))) {
tmp.push(deviceInfo);
log("V DEVICE FOUND = " + deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase());
} else if (deviceInfo.deviceId === session.videoDevice){
tmp.push(deviceInfo);
log("EXACT V DEVICE FOUND");
}
}
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if (!((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase().includes(session.videoDevice)))) {
tmp.push(deviceInfo);
if (deviceInfo.deviceId !== session.videoDevice){
tmp.push(deviceInfo);
}
}
}
deviceInfos = tmp;
@ -7854,6 +7912,36 @@ function previewIframe(iframesrc) { // this is pretty important if you want to a
iframesrc = "./";
}
if (iframesrc.startsWith("https://") || iframesrc.startsWith("http://")){
var domain = (new URL(iframesrc));
domain = domain.hostname;
log(domain);
if ((domain=="www.youtube.com") || (domain=="youtube.com")){
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
var match = iframesrc.match(regExp);
var vidid = (match&&match[7].length==11)? match[7] : false;
if(vidid){
iframesrc = "https://www.youtube.com/embed/"+vidid+"?autoplay=1&modestbranding=1";
log(iframesrc);
}
} else if (domain=="www.twitch.tv"){
var vidid = iframesrc.split('/').pop().split('#')[0].split('?')[0];
if (vidid){
iframesrc = "https://player.twitch.tv/?channel="+vidid+"&parent="+location.hostname;
log(iframesrc);
}
} else if (domain=="twitch.tv"){
var vidid = iframesrc.split('/').pop().split('#')[0].split('?')[0];
if (vidid){
iframesrc = "https://player.twitch.tv/?channel="+vidid+"&parent="+location.hostname;
log(iframesrc);
}
}
}
iframe.src = iframesrc;
getById("previewIframe").innerHTML = "";
getById("previewIframe").style.width = "640px";
@ -9481,42 +9569,7 @@ function createIframePopup() {
getById("screenshare2button").classList.remove("float2");
return;
}
//var iframeContainer = document.createElement("div");
//iframeContainer.className="popup";
//iframeContainer.style.zIndex = ""+22;
//iframeContainer.style.width="640px";
//iframeContainer.style.height="480px";
//iframeContainer.style.position = "fixed";
//iframeContainer.style.top = "60px";
//iframeContainer.style.left = "0";
//iframeContainer.style.border = "dotted 3px #777";
//iframeContainer.style.overflow="auto";
//iframeContainer.style.resize="both";
//session.screenShareElement = iframeContainer;
//session.screenShareElement.dataset.doNotMove = true;
//var button1 = document.createElement("button");
//button1.innerHTML = "<i class='las la-arrows-alt'></i> Move";
//button1.className = "popup-header menu";
//iframeContainer.appendChild(button1);
//var button2 = document.createElement("button");
//button2.innerHTML = "<i class='las la-times'></i> Close";
//button2.className = "menu";
//button2.onclick = function(){
// iframe.contentWindow.postMessage({"close":true}, '*');
// iframe.parentNode.parentNode.removeChild(iframeContainer);
// session.screenShareElement = false;
//}
//iframeContainer.appendChild(button2);
//var button3 = document.createElement("button");
//button3.innerHTML = "<i class='las la-sync'></i> Reload";
//button3.className = "menu reload";
//button3.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');}
//iframeContainer.appendChild(button3);
if (session.screenshareid) {
var iFrameID = session.screenshareid;
} else {
@ -9533,15 +9586,26 @@ function createIframePopup() {
var iframe = document.createElement("iframe");
iframe.allow = "autoplay";
iframe.allowtransparency = "true";
var pass = "";
if (session.password) {
pass = "&password=" + session.password; // encodeURIComponent(
var extras = "";
if (session.password){
extras += "&password=" + session.password; // encodeURIComponent(
}
if (session.privacy){
extras += "&privacy";
}
if (session.quality){
extras += "&q="+session.quality;
} else {
extras += "&q=0";
}
if (session.muted){
iframe.src = "./?screenshare&transparent&cleanoutput&noheader&autostart&view&muted&room=" + session.roomid + "&push=" + iFrameID + pass;
iframe.src = "./?ad=1&screenshare&transparent&cleanoutput&noheader&autostart&view&muted&room=" + session.roomid + "&push=" + iFrameID + extras;
} else {
iframe.src = "./?screenshare&transparent&cleanoutput&noheader&autostart&view&room=" + session.roomid + "&push=" + iFrameID + pass;
iframe.src = "./?ad=1&screenshare&transparent&cleanoutput&noheader&autostart&view&room=" + session.roomid + "&push=" + iFrameID + extras;
}
iframe.style.width = "100%";
@ -9549,8 +9613,6 @@ function createIframePopup() {
iframe.style.overflow = "hidden";
iframe.id = "screensharesource";
//iframeContainer.appendChild(iframe);
session.screenShareElement = iframe;
session.screenShareElement.dataset.doNotMove = true;
@ -10081,7 +10143,7 @@ if ((session.view) && (session.roomid === false)) {
if (document.title == "") {
document.title = "Room=" + session.roomid.toString();
} else {
document.title += ", Room=" + session.roomid.toString();
document.title += ": " + session.roomid.toString();
}
}
} catch (e) {
@ -10461,7 +10523,7 @@ function toggleQualityDirector(bitrate, UUID, ele = null) { // ele is specific t
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');
window.open('./popout.html?id=' + randid, 'popup', 'width=600,height=480,toolbar=no,menubar=no,resizable=yes');
session.broadcastChannel = new BroadcastChannel(randid);
session.broadcastChannel.onmessage = function(e) {
if ("loaded" in e.data) {
@ -10496,9 +10558,10 @@ function getChatMessage(msg, label = false, director = false, overlay = false) {
} else {
data.label = "<b>" + data.label + ":</b> ";
}
label = label+":";
} else if (director) {
data.label = "<b><i>Director:</i></b> ";
label = "Director";
label = "Director:";
} else {
data.label = "";
label = "";
@ -10512,20 +10575,22 @@ function getChatMessage(msg, label = false, director = false, overlay = false) {
}
updateMessages();
if (overlay && director) {
var textOverlay = getById("overlayMsgs");
if (textOverlay) {
var spanOverlay = document.createElement("span");
spanOverlay.innerHTML = "<b><i>" + label + ":</i></b> " + msg + "<br />";
textOverlay.appendChild(spanOverlay);
textOverlay.style.display = "block";
var showtime = msg.length * 200 + 3000;
if (showtime > 8000) {
showtime = 8000;
if (overlay) {
if (!(session.cleanOutput)){
var textOverlay = getById("overlayMsgs");
if (textOverlay) {
var spanOverlay = document.createElement("span");
spanOverlay.innerHTML = "<b><i>" + label + "</i></b> " + msg + "<br />";
textOverlay.appendChild(spanOverlay);
textOverlay.style.display = "block";
var showtime = msg.length * 200 + 3000;
if (showtime > 8000) {
showtime = 8000;
}
setTimeout(function(ele) {
ele.parentNode.removeChild(ele);
}, showtime, spanOverlay);
}
setTimeout(function(ele) {
ele.parentNode.removeChild(ele);
}, showtime, spanOverlay);
}
}
@ -11337,10 +11402,15 @@ function addAudioPipeline(stream, UUID, track){ // INBOUND AUDIO EFFECTS
source = audioMeterGuest(source, UUID, trackid);
}
if (session.offsetChannel !==false){ // proably better to do this last.
if (session.rpcs[UUID].channelOffset !== false){
log("custom offset set");
session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination();
source = offsetChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.rpcs[UUID].channelOffset);
screwedUp = true;
} else if (session.offsetChannel !== false){ // proably better to do this last.
log("adding offset channels");
session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination();
source = offsetChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source);
source = offsetChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.offsetChannel);
screwedUp = true;
}
@ -11364,7 +11434,28 @@ function addAudioPipeline(stream, UUID, track){ // INBOUND AUDIO EFFECTS
return stream;
}
function offsetChannel( destination, source){
function changeChannelOffset(UUID, channel){
session.rpcs[UUID].channelOffset = channel;
var tracks = session.rpcs[UUID].streamSrc.getAudioTracks();
if (tracks.length){
var track = tracks[0];
session.rpcs[UUID].videoElement.srcObject = addAudioPipeline(session.rpcs[UUID].streamSrc, UUID, track);
}
var ele = document.querySelectorAll('[data-action-type="add-channel"][data--u-u-i-d="' + UUID + '"]');
for (var i=0;i<ele.length;i++){
if ((i==0) && (channel===false)){
ele[i].classList.add("pressed");
} else if (channel===i-1){
ele[i].classList.add("pressed");
} else {
ele[i].classList.remove("pressed");
}
}
}
function offsetChannel( destination, source, offset){
session.audioCtx.destination.channelCountMode = 'explicit';
session.audioCtx.destination.channelInterpretation = 'discrete';
destination.channelCountMode = 'explicit';
@ -11375,13 +11466,13 @@ function offsetChannel( destination, source){
} catch (e){errorlog("Max channels: "+destination.channelCount);}
var splitter = session.audioCtx.createChannelSplitter(2);
var merger = session.audioCtx.createChannelMerger(2+session.offsetChannel);
var merger = session.audioCtx.createChannelMerger(2+offset);
source.connect(splitter);
splitter.connect(merger, 0,session.offsetChannel);
splitter.connect(merger, 0,offset);
if ((session.stereo) && (session.stereo!=3)){
splitter.connect(merger, 1, 1+session.offsetChannel);
splitter.connect(merger, 1, 1+offset);
}
return merger;
}

View File

@ -1,2 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="65" height="64"><style> .a{stroke-width:10;stroke:#000;}</style><title> background</title><rect height="66" width="67" y="-1" x="-1" fill="#0000"/><g height="100" width="100"><rect y="28.56" x="29.5" height="600" width="800" fill="url(#gridpattern)"/></g><title> Layer 1</title><rect height="3" width="1" y="27.02" x="302" style="fill:#0000;stroke-width:2;stroke:#0000"/><rect height="43" width="48" y="10.8" x="8.38" style="fill-opacity:null;fill:#0000;stroke-opacity:null;stroke-width:2;stroke:#FFF"/>
<text font-family="Helvetica, Arial, sans-serif" font-size="24" y="40.49" x="15.38" style="fill:#FFF;font-weight:bold"> HQ</text></svg>
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="white" x="0px" y="0px" viewBox="0 0 122.88 122.87" style="enable-background:new 0 0 122.88 122.87" xml:space="preserve"><g><path d="M122.88,77.63v41.12c0,2.28-1.85,4.12-4.12,4.12H77.33v-9.62h35.95c0-12.34,0-23.27,0-35.62H122.88L122.88,77.63z M77.39,9.53V0h41.37c2.28,0,4.12,1.85,4.12,4.12v41.18h-9.63V9.53H77.39L77.39,9.53z M9.63,45.24H0V4.12C0,1.85,1.85,0,4.12,0h41 v9.64H9.63V45.24L9.63,45.24z M45.07,113.27v9.6H4.12c-2.28,0-4.12-1.85-4.12-4.13V77.57h9.63v35.71H45.07L45.07,113.27z"/></g></svg>

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 650 B

View File

@ -1,2 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="65" height="64"><style> .a{stroke-width:10;stroke:#000;}</style><title> background</title><rect height="66" width="67" y="-1" x="-1" fill="#0000"/><g height="100" width="100"><rect y="28.56" x="29.5" height="600" width="800" fill="url(#gridpattern)"/></g><title> Layer 1</title><rect height="3" width="1" y="27.02" x="302" style="fill:#0000;stroke-width:5;stroke:#0000"/><rect height="43" width="48" y="10.8" x="8.38" style="fill-opacity:null;fill:#0000;stroke-opacity:null;stroke-width:5;stroke:#FFF"/>
<text font-family="Helvetica, Arial, sans-serif" font-size="24" y="40.49" x="15.38" style="fill:#FFF;font-weight:bold"> LQ</text></svg>
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" fill="white" width="122.88px" height="122.871px" viewBox="0 0 122.88 122.871" enable-background="new 0 0 122.88 122.871" xml:space="preserve"><g><path d="M122.88,35.775v9.529H81.515c-2.278,0-4.125-1.847-4.125-4.125V0h9.63v35.775H122.88L122.88,35.775z M35.499,0h9.63v41.118 c0,2.278-1.847,4.125-4.125,4.125H0v-9.644h35.499V0L35.499,0z M0,87.164v-9.598h40.942c2.277,0,4.125,1.846,4.125,4.125v41.18 h-9.633V87.164H0L0,87.164z M77.328,122.871V81.752c0-2.277,1.847-4.125,4.125-4.125h41.427v9.625H86.931 c0,12.338-0.003,23.271-0.003,35.619H77.328L77.328,122.871z"/></g></svg>

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 733 B

2
media/svg.md Normal file
View File

@ -0,0 +1,2 @@
https://uxwing.com/collapse-icon/
https://uxwing.com/expand-icon/

View File

@ -6,6 +6,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OBSN Speed Test</title>
<script>
function getChromeVersion() {
var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
return raw ? parseInt(raw[2], 10) : false;
}
if (!getChromeVersion()){
alert("This speedtest is optimized for Chromium-based browsers; graphs will not work for Firefox or Safari browsers.");
}
(function (w) {
w.URLSearchParams =
w.URLSearchParams ||

View File

@ -31,17 +31,13 @@ var CodecsHandler = (function() {
if (!info.videoCodecNumbers) {
return sdp;
}
if (codecName === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) {
} else if (codecName === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
if (codecName === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) {
} else if (codecName === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
if (codecName === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) {
} else if (codecName === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) {
return sdp;
} else if (codecName === 'av1' && info.av1LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
@ -58,21 +54,24 @@ var CodecsHandler = (function() {
return sdp;
}
preferCodecNumber = info.vp8LineNumber;
}
if (codec === 'vp9') {
} else if (codec === 'vp9') {
if (!info.vp9LineNumber) {
return sdp;
}
preferCodecNumber = info.vp9LineNumber;
}
if (codec === 'h264') {
} else if (codec === 'h264') {
if (!info.h264LineNumber) {
return sdp;
}
preferCodecNumber = info.h264LineNumber;
} else if (codec === 'av1') {
if (!info.av1LineNumber) {
return sdp;
}
preferCodecNumber = info.av1LineNumber;
}
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
@ -117,6 +116,10 @@ var CodecsHandler = (function() {
if (line.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('AV1X/90000') !== -1 && !info.av1LineNumber) {
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
});

File diff suppressed because one or more lines are too long

182
zoom.html Normal file
View File

@ -0,0 +1,182 @@
<html>
<head><style>
span{margin:10px 0 0 0;display:block;}
body {
background-color:#cdf;
padding:0;
width;100%;height:100%
}
input{padding:5px;}
button {margin:10px 3px;}
#stream{
display:block;
}
</style></head>
<body id="main" style="margin:5%;"
<meta charset="utf-8"/>
<video id="video" autoplay="true" muted="true" playsinline style='height:420px;background-color:black;display:block;margin:0 0 10px 0;'></video>
<div id="devices">
<div class="select">
<label for="videoSource">Video source: </label><select id="videoSource"></select>
</div>
<div class="select">
<label for="audioSource">Audio source: </label><select id="audioSource"></select>
</div>
</div>
<button onclick="fullwindow()">FULL WINDOW</button>
<script>
window.onerror = function backupErr(errorMsg, url=false, lineNumber=false) {
console.error(errorMsg);
console.error(lineNumber);
console.error("Unhandeled Error occured"); //or any message
return false;
};
function fullwindow(){
videoElement.style.width="100%";
videoElement.style.padding= "0";
videoElement.style.margin="0";
videoElement.style.height="100%";
videoElement.style.zIndex="5";
videoElement.style.position = "absolute";
videoElement.style.top="0px";
videoElement.style.left="0px";
document.getElementById("main").style.overflow = "hidden";
videoElement.style.overflow = "hidden"
document.getElementById("main").style.backgroundColor="#000";
videoElement.style.cursor="none";
document.getElementById("main").style.cursor="none";
}
var videoElement = document.getElementById("video");
var gotDev = false;
async function gotDevices() {
if (gotDev){return;}
gotDev=true;
await navigator.mediaDevices.getUserMedia({audio:true, video:true}); // is needed to ask for permissinos.
navigator.mediaDevices.enumerateDevices().then((deviceInfos)=>{
for (let i = 0; i !== deviceInfos.length; ++i) {
var deviceInfo = deviceInfos[i];
var option = document.createElement("option");
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === "audioinput") {
option.text = deviceInfo.label || "microphone " + (audioSelect.length + 1);
audioSelect.appendChild(option);
if (option.text.startsWith("CABLE")){
option.selected =true;
}
} else if (deviceInfo.kind === "videoinput") {
option.text = deviceInfo.label || "camera " + (videoSelect.length + 1);
if (option.text.startsWith("NewTek")){
continue;
}
videoSelect.appendChild(option);
if (option.text.startsWith("OBS")){
option.selected =true;
}
}
}
getStream();
});
}
function getStream() {
if (window.stream) {
window.stream.getTracks().forEach(function (track) {
track.stop();
log("TRack stopping");
});
}
const constraints = {
audio: {
deviceId: { exact: audioSelect.value },
echoCancellation : false,
autoGainControl : false,
noiseSuppression : false
},
video: {
deviceId: { exact: videoSelect.value },
width: { min: 1280, ideal: 1920, max: 1920 },
height: { min: 720, ideal: 1080, max: 1080 }
}
};
return navigator.mediaDevices.getUserMedia(constraints)
.then(gotStream)
.catch(console.error);
}
function gotStream(stream) {
if (window.stream) {
window.stream = stream; // make stream available to console
videoElement.srcObject = stream;
var senders = session.pc.getSenders();
videoElement.srcObject.getVideoTracks().forEach((track)=>{
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 && sender.track.kind == "video") {
sender.replaceTrack(track); // replace may not be supported by all browsers. eek.
track.enabled = notCensored;
added = true;
}
}
});
if (added==false){
session.pc.addTrack(track);
log("ADDED NOT REPLACED?");
}
});
videoElement.srcObject.getAudioTracks().forEach((track)=>{
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 && sender.track.kind == "audio") {
sender.replaceTrack(track); // replace may not be supported by all browsers. eek.
track.enabled = notCensored;
added = true;
}
}
});
if (added==false){
session.pc.addTrack(track);
log("ADDED NOT REPLACED?");
}
});
} else {
window.stream = stream; // make stream available to console
videoElement.srcObject = stream;
}
}
var audioSelect = document.querySelector("select#audioSource");
var videoSelect = document.querySelector("select#videoSource");
audioSelect.onchange = getStream;
videoSelect.onchange = getStream;
gotDevices();
</script>
</body>
</html>