mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-15 23:58:29 +00:00
commit
852f9d5fca
131
examples/esports.html
Normal file
131
examples/esports.html
Normal file
@ -0,0 +1,131 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>IFRAME Example</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<style>
|
||||
body{
|
||||
padding:0;
|
||||
margin:0;
|
||||
background-color: #0000;
|
||||
}
|
||||
iframe {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
display:block;
|
||||
width:100%;
|
||||
height:90%
|
||||
}
|
||||
#viewlink {
|
||||
width:400px;
|
||||
}
|
||||
#container {
|
||||
display:block;
|
||||
padding:0px;
|
||||
}
|
||||
input{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
button{
|
||||
padding:5px;
|
||||
margin:5px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
function loadIframe(){
|
||||
|
||||
document.getElementById("container").innerHTML = "";
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
var iframeContainer = document.createElement("div");
|
||||
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;";
|
||||
|
||||
|
||||
var iframesrc = "https://vdo.ninja/?transparent&cleanoutput&bitrate=200&manual&noaudio&view=";
|
||||
|
||||
var listOfStreamIDs = [
|
||||
"1234_pov",
|
||||
"2345_pov",
|
||||
"3456_pov",
|
||||
"4567_pov",
|
||||
"5678_pov"
|
||||
];
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "List connected StreamIDs";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "HIDE ALL";
|
||||
button.dataset.sid = listOfStreamIDs[i];
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "remove":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
for (var i=0;i<listOfStreamIDs.length;i++){
|
||||
if (i!==0){
|
||||
iframesrc+=",";
|
||||
}
|
||||
iframesrc+=listOfStreamIDs[i];
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "SHOW "+listOfStreamIDs[i];
|
||||
button.dataset.sid = listOfStreamIDs[i];
|
||||
button.title = "Publish using: https://vdo.ninja/?push="+listOfStreamIDs[i];
|
||||
button.onclick = function(){
|
||||
iframe.contentWindow.postMessage({"target":"*", "remove":true}, '*');
|
||||
iframe.contentWindow.postMessage({"target":this.dataset.sid, "add":true, "settings":{"style":{"width":"100%", "height":"100%", "display":"block"}}}, '*');
|
||||
}; // target can be a stream ID or * for all.
|
||||
iframeContainer.appendChild(button);
|
||||
}
|
||||
|
||||
iframe.src = iframesrc;
|
||||
iframeContainer.appendChild(iframe);
|
||||
document.getElementById("container").appendChild(iframeContainer);
|
||||
|
||||
//////////// LISTEN FOR EVENTS
|
||||
|
||||
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
||||
var eventer = window[eventMethod];
|
||||
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
||||
|
||||
|
||||
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
|
||||
|
||||
eventer(messageEvent, function (e) {
|
||||
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
|
||||
|
||||
|
||||
if ("action" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "event: "+e.data.action+"<br />";
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
|
||||
if ("streamIDs" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "streamID list:<br />";
|
||||
for (var key in e.data.streamIDs) {
|
||||
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
|
||||
}
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="container">
|
||||
<button onclick="loadIframe();">CONNECT</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
16
index.html
16
index.html
@ -54,7 +54,7 @@
|
||||
transition: opacity .1s linear;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="./main.css?ver=141" />
|
||||
<link rel="stylesheet" href="./main.css?ver=142" />
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.min.js"></script>
|
||||
<style id="lightbox-animations" type="text/css"></style>
|
||||
</head>
|
||||
@ -67,7 +67,7 @@
|
||||
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
|
||||
</span>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=34"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=301"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=326"></script>
|
||||
<input id="zoomSlider" type="range" style="display: none;" />
|
||||
<div id="header">
|
||||
|
||||
@ -747,7 +747,7 @@
|
||||
👋 👀 Welcome to VDO Ninja! We've rebranded! 📼 Nothing else is changing and we're staying 100% free.
|
||||
</h4>
|
||||
<br />
|
||||
🎁 Site updated October 6th. The <a href="https://docs.vdo.ninja/release-notes/v19">v19 release notes are here</a>. If new issues occur, the previous version can be <a href="https://vdo.ninja/v183/">found here</a>.
|
||||
🎁 Site updated October 15th (v19.4). The <a href="https://docs.vdo.ninja/release-notes/v19">v19 release notes are here</a>. If new issues occur, the older v18 can be <a href="https://vdo.ninja/v183/">found here</a>.
|
||||
|
||||
<br />
|
||||
<br />
|
||||
@ -1150,8 +1150,8 @@
|
||||
</div>
|
||||
<div id="hiddenElements"></div>
|
||||
<div id="overlayClockContainer" data-initial="600" class="advanced"><span id="overlayClock"></span></div>
|
||||
<div id="overlayMsgs" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
|
||||
<div id="bigPlayButton" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
|
||||
<div id="overlayMsgs" onclick="this.innerHTML = '';" style="display:none"></div>
|
||||
<div id="bigPlayButton" onclick="this.innerHTML = '';" style="display:none"></div>
|
||||
<div id="controls_blank" style="display: none;">
|
||||
<div class="controlsGrid">
|
||||
|
||||
@ -1772,7 +1772,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 = "19.3";
|
||||
session.version = "19.4";
|
||||
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
|
||||
|
||||
session.defaultPassword = "someEncryptionKey123"; // Change this password if self-deploying for added security/privacy
|
||||
@ -1849,11 +1849,11 @@
|
||||
// session.title // "zzzz"
|
||||
</script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=199"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=202"></script>
|
||||
<!--
|
||||
// If you wish to change branding, blank offers a good clean start.
|
||||
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
|
||||
-->
|
||||
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=272"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=275"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
55
lib.js
55
lib.js
@ -180,8 +180,8 @@ function saveRoom(ele){
|
||||
//this.title = "Quick load settings stored locally";
|
||||
session.sticky = true;
|
||||
ele.parentNode.removeChild(ele);
|
||||
setStorage("permission", "yes", 999);
|
||||
setStorage("settings", encodeURI(window.location.href), 90);
|
||||
setStorage("permission", "yes");
|
||||
setStorage("settings", encodeURI(window.location.href), 999);
|
||||
}
|
||||
|
||||
function updateURL(param, force = false, cleanUrl = false) {
|
||||
@ -225,7 +225,7 @@ function updateURL(param, force = false, cleanUrl = false) {
|
||||
}
|
||||
}
|
||||
if (session.sticky) {
|
||||
setStorage("settings", encodeURI(window.location.href), 90);
|
||||
setStorage("settings", encodeURI(window.location.href), 999);
|
||||
}
|
||||
urlParams = new URLSearchParams(window.location.search);
|
||||
}
|
||||
@ -1182,11 +1182,11 @@ function removeStorage(cname){
|
||||
localStorage.removeItem(cname);
|
||||
}
|
||||
|
||||
function setStorage(cname, cvalue, exdays=999){ // not actually a cookie
|
||||
function setStorage(cname, cvalue, hours=9999){ // not actually a cookie
|
||||
var now = new Date();
|
||||
var item = {
|
||||
value: cvalue,
|
||||
expiry: now.getTime() + (exdays * 24 * 60 * 60 * 1000),
|
||||
expiry: now.getTime() + (hours * 60 * 60 * 1000),
|
||||
};
|
||||
try{
|
||||
localStorage.setItem(cname, JSON.stringify(item));
|
||||
@ -1325,7 +1325,7 @@ function setupIncomingVideoTracking(v, UUID){ // video element.
|
||||
v.style.display="block";
|
||||
} else { // group scene I guess; needs to be added manually
|
||||
v.style.display="none";
|
||||
v.mutedStateScene = true;
|
||||
session.rpcs[UUID].mutedStateScene = true;
|
||||
}
|
||||
|
||||
setTimeout(function(){updateMixer();},1);
|
||||
@ -1504,7 +1504,7 @@ function updateVolume(update=false){
|
||||
if (session.roomid){
|
||||
var pswd = session.password || "";
|
||||
generateHash(session.streamID + session.roomid + pswd + session.salt, 6).then(function(hash) {
|
||||
setStorage("micVolume_"+hash, session.audioGain, exdays=999);
|
||||
setStorage("micVolume_"+hash, session.audioGain, hours=6);
|
||||
});
|
||||
}
|
||||
if (session.audioGain == 0){
|
||||
@ -5788,8 +5788,8 @@ function applyMuteState(UUID){ // this is the mute state of PLAYBACK audio; not
|
||||
var muteOutcome = session.rpcs[UUID].mutedState || session.rpcs[UUID].mutedStateMixer || session.rpcs[UUID].mutedStateScene || session.speakerMuted;
|
||||
if (session.rpcs[UUID].videoElement){
|
||||
session.rpcs[UUID].videoElement.muted = muteOutcome;
|
||||
|
||||
}
|
||||
// session.scene
|
||||
return muteOutcome;
|
||||
}
|
||||
|
||||
@ -7080,6 +7080,10 @@ function joinRoom(roomname) {
|
||||
if (session.directorList.indexOf(response[i].UUID)>=0){
|
||||
warnlog("PLAYING DIRECTOR");
|
||||
play(streamID, response[i].UUID);
|
||||
} else if (session.view && (session.view === streamID)){
|
||||
play(streamID, response[i].UUID);
|
||||
} else if (session.view_set && session.view_set.includes(streamID)){
|
||||
play(streamID, response[i].UUID);
|
||||
} else if (session.queueList.length<5000){
|
||||
if (!session.queueList.includes(streamID)){
|
||||
session.queueList.push(streamID);
|
||||
@ -7107,6 +7111,10 @@ function joinRoom(roomname) {
|
||||
if (session.queue){
|
||||
if (session.directorList.indexOf(response[i].UUID)>=0){
|
||||
play(streamID, response[i].UUID);
|
||||
} else if (session.view && (session.view === streamID)){
|
||||
play(streamID,response[i].UUID);
|
||||
} else if (session.view_set && session.view_set.includes(streamID)){
|
||||
play(streamID, response[i].UUID);
|
||||
} else if (session.queueList.length<5000){
|
||||
if (!session.queueList.includes(streamID)){
|
||||
session.queueList.push(streamID);
|
||||
@ -11767,7 +11775,7 @@ session.publishStream = function(v){ // stream is used to generated an SDP
|
||||
if (session.director){ // the director doesn't load a webcam by default anyways.
|
||||
// audio is not mucked with
|
||||
} else if (session.scene!==false){ // it's a scene, and there are no previews in a scene.
|
||||
setTimeout(function(){updateMixer();},1);
|
||||
setTimeout(function(){updateMixer();},10);
|
||||
} else if (session.roomid!==false){
|
||||
if (session.roomid===""){
|
||||
if (!(session.view) || (session.view==="")){
|
||||
@ -11793,7 +11801,7 @@ session.publishStream = function(v){ // stream is used to generated an SDP
|
||||
session.windowed = false;
|
||||
applyMirror(session.mirrorExclude, 'videosource');
|
||||
play();
|
||||
setTimeout(function(){updateMixer();},1);
|
||||
setTimeout(function(){updateMixer();},10);
|
||||
}
|
||||
} else {
|
||||
//session.cbr=0; // we're just going to override it
|
||||
@ -11802,7 +11810,7 @@ session.publishStream = function(v){ // stream is used to generated an SDP
|
||||
}
|
||||
session.windowed = false;
|
||||
applyMirror(session.mirrorExclude, 'videosource');
|
||||
setTimeout(function(){updateMixer();},1);
|
||||
setTimeout(function(){updateMixer();},10);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -15293,11 +15301,22 @@ Promise.wait = function(ms) {
|
||||
Promise.prototype.timeout = function(ms) {
|
||||
return Promise.race([
|
||||
this, Promise.wait(ms).then(function() {
|
||||
var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page.");
|
||||
errormsg.name = "timedOut";
|
||||
errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page."
|
||||
throw errormsg;
|
||||
|
||||
if (iOS || iPad){
|
||||
var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nIf using an iPhone or iPad, try fully closing your browser and open it again; Safari sometimes jams up the camera.");
|
||||
errormsg.name = "timedOut";
|
||||
errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nIf using an iPhone or iPad, try fully closing your browser and open it again; Safari sometimes jams up the camera."
|
||||
throw errormsg;
|
||||
} else if (session.mobile){
|
||||
var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nMake sure no other application is using the camera already and that you are using a compatible browser. If issues persist, maybe try the official native mobile app.");
|
||||
errormsg.name = "timedOut";
|
||||
errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nMake sure no other application is using the camera already and that you are using a compatible browser. If issues persist, maybe try the official native mobile app."
|
||||
throw errormsg;
|
||||
} else {
|
||||
var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page.");
|
||||
errormsg.name = "timedOut";
|
||||
errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page."
|
||||
throw errormsg;
|
||||
}
|
||||
})
|
||||
])
|
||||
};
|
||||
@ -18423,6 +18442,10 @@ function setupCommands(){
|
||||
}
|
||||
};
|
||||
|
||||
commands.forceKeyframe = function(value=null){
|
||||
session.forcePLI();
|
||||
};
|
||||
|
||||
commands.panning = function(value){
|
||||
if (value===false){
|
||||
value = 90;
|
||||
|
||||
11
main.css
11
main.css
@ -14,6 +14,8 @@
|
||||
--video-rounded: 0px;
|
||||
--color-mode: light;
|
||||
--button-radius: 2px;
|
||||
--myvideo-max-width: min(800px,100vw);
|
||||
--myvideo-width:800px;
|
||||
}
|
||||
|
||||
* {
|
||||
@ -46,7 +48,7 @@ table {
|
||||
#bigPlayButton {
|
||||
margin:0 auto;
|
||||
background-color: #0000;
|
||||
color: #;
|
||||
cursor:pointer;
|
||||
font-family: Cousine, monospace;
|
||||
font-size: 4em;
|
||||
line-height: 1.5em;
|
||||
@ -61,7 +63,6 @@ table {
|
||||
position: fixed;
|
||||
overflow-wrap: anywhere;
|
||||
padding:3%;
|
||||
pointer-events: none
|
||||
}
|
||||
|
||||
#playButton {
|
||||
@ -1518,8 +1519,8 @@ img {
|
||||
|
||||
.myVideo {
|
||||
box-shadow: rgb(88, 88, 88) 0px 0px 5px 1px;
|
||||
width: var(--myvideo-width);
|
||||
max-width: 800px !important;
|
||||
max-width: min(800px,100vw) !important;
|
||||
max-height: 100% !important;
|
||||
height: auto !important;
|
||||
display: block !important;
|
||||
@ -1527,6 +1528,8 @@ img {
|
||||
position: relative !important;
|
||||
top: 50% !important;
|
||||
background-color: #FFF1 !important;
|
||||
object-fit: var(--fit-style);
|
||||
max-width: var(--myvideo-max-width) !important;
|
||||
}
|
||||
#calendarButton {
|
||||
cursor: pointer;
|
||||
@ -3308,6 +3311,8 @@ input:checked + .slider:before {
|
||||
content: "\f05a"; }
|
||||
.la-play:before {
|
||||
content: "\f04b"; }
|
||||
.la-square:before {
|
||||
content: "\f0c8"; }
|
||||
.la-play-circle:before {
|
||||
content: "\f144"; }
|
||||
.la.la-hdd-o:before {
|
||||
|
||||
22
main.js
22
main.js
@ -505,6 +505,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (urlParams.has('cover')) {
|
||||
session.cover = true;
|
||||
document.documentElement.style.setProperty('--fit-style', 'cover');
|
||||
document.documentElement.style.setProperty('--myvideo-max-width', '100vw');
|
||||
document.documentElement.style.setProperty('--myvideo-width', '100vw');
|
||||
}
|
||||
|
||||
if (urlParams.has('record')) {
|
||||
@ -548,12 +550,16 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
session.audioEffects = false;
|
||||
session.audioMeterGuest = false;
|
||||
}
|
||||
if (session.scene!=="1"){ // scene =0 and 1 should load instantly.
|
||||
session.hiddenSceneViewBitrate = 0; // By default this is ~ 400kbps, but if you have 10 scenes, i don't want to kill things.
|
||||
//if (session.scene!=="1"){ // scene =0 and 1 should load instantly.
|
||||
// session.hiddenSceneViewBitrate = 0; // By default this is ~ 400kbps, but if you have 10 scenes, i don't want to kill things.
|
||||
//}
|
||||
|
||||
if (urlParams.has('hiddenscenebitrate')) {
|
||||
session.hiddenSceneViewBitrate = parseInt(urlParams.get('hiddenscenebitrate')) || 0;
|
||||
}
|
||||
|
||||
if (urlParams.has('preloadbitrate')) {
|
||||
session.hiddenSceneViewBitrate = parseInt(urlParams.get('preloadbitrate')) || 500;
|
||||
session.preloadbitrate = parseInt(urlParams.get('preloadbitrate')) || 0; // 1000
|
||||
}
|
||||
|
||||
if (urlParams.has('scenetype') || urlParams.has('type')) {
|
||||
@ -736,7 +742,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('transparent')) { // sets the window to be transparent - useful for IFRAMES?
|
||||
if (urlParams.has('transparent') || urlParams.has('transparency')) { // sets the window to be transparent - useful for IFRAMES?
|
||||
getById("main").style.backgroundColor = "rgba(0,0,0,0)";
|
||||
document.documentElement.style.setProperty('--container-color', '#0000');
|
||||
document.documentElement.style.setProperty('--background-color', '#0000');
|
||||
@ -1132,9 +1138,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
}
|
||||
|
||||
if (navigator.userAgent.indexOf('Mac OS X') != -1) {
|
||||
session.codec = "h264"; // default the codec to h264 if OBS is on macOS (that's all it supports with hardware)
|
||||
}
|
||||
//if (navigator.userAgent.indexOf('Mac OS X') != -1) {
|
||||
// session.codec = "h264"; // default the codec to h264 if OBS is on macOS (that's all it supports with hardware) // oct 2021, OBS now supports vp8 and actually breaks with h264 android devices.
|
||||
//}
|
||||
|
||||
if (session.disableOBS===false){
|
||||
window.addEventListener("obsSourceVisibleChanged", obsSourceVisibleChanged);
|
||||
@ -1155,6 +1161,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (urlParams.has('chroma')) {
|
||||
log("Chroma ENABLED");
|
||||
getById("main").style.backgroundColor = "#" + (urlParams.get('chroma') || "0F0");
|
||||
} else if (window.obsstudio || (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){
|
||||
getById("main").style.backgroundColor = "rgba(0,0,0,0)";
|
||||
}
|
||||
|
||||
if (urlParams.has('margin')) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user